樂字節(jié)-Spring JDBC 和 事務(wù)控制

Spring JDBC 和 事務(wù)控制

主要內(nèi)容

Spring 整合 JDBC 環(huán)境

? Spring 框架除了提供 IOC 與 AOP 核心功能外,同樣提供了基于JDBC 的數(shù)據(jù)訪問功能,使得訪問持久層數(shù)據(jù)更加方便。使用 Spring JDBC 環(huán)境,首先需要一套 Spring 整合 JDBC 的環(huán)境。

添加依賴坐標(biāo)

<!-- 添加相關(guān)的依賴坐標(biāo) --><!-- spring 框架坐標(biāo)依賴添加 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.4.RELEASE</version></dependency><!-- spring 測(cè)試環(huán)境 --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.4.RELEASE</version><scope>test</scope></dependency><!-- aop --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version></dependency><!-- spring jdbc --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.4.RELEASE</version></dependency><!-- spring事物 --><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.2.4.RELEASE</version></dependency><!-- mysql 驅(qū)動(dòng)包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.19</version></dependency><!-- c3p0 連接池 --><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.5</version></dependency>

添加 jdbc 配置文件

在src/main/resources目錄下新建jdbc.properties配置文件,并設(shè)置對(duì)應(yīng)的配置信息

# 驅(qū)動(dòng)名

jdbc.driver=com.mysql.cj.jdbc.Driver

# 數(shù)據(jù)庫連接

jdbc.url=jdbc:mysql://localhost:3306/(數(shù)據(jù)庫名稱)?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false

# 數(shù)據(jù)庫用戶名稱

jdbc.user=(數(shù)據(jù)庫賬號(hào))

# 數(shù)據(jù)庫用戶密碼

jdbc.password=(數(shù)據(jù)庫密碼)

以下為可選配置

# 指定連接池的初始化連接數(shù)。取值應(yīng)在minPoolSize 與 maxPoolSize 之間.Default:3

initialPoolSize=20

# 指定連接池中保留的最大連接數(shù). Default:15

maxPoolSize=100

# 指定連接池中保留的最小連接數(shù)

minPoolSize=10

# 最大空閑時(shí)間,60秒內(nèi)未使用則連接被丟棄。若為0則永不丟棄。 Default:0

maxIdleTime=600

# 當(dāng)連接池中的連接耗盡的時(shí)候c3p0一次同時(shí)獲取的連接數(shù). Default:3

acquireIncrement=5

# JDBC的標(biāo)準(zhǔn),用以控制數(shù)據(jù)源內(nèi)加載的PreparedStatements數(shù)量。

maxStatements=5

# 每60秒檢查所有連接池中的空閑連接.Default:0

idleConnectionTestPeriod=60

修改 spring 配置文件

<!-- 加載properties 配置文件,用來讀取jdbc.properties文件中的數(shù)據(jù) --><context:property-placeholderlocation="jdbc.properties"/>

spring.xml

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans? ? ? https://www.springframework.org/schema/beans/spring-beans.xsd? ? ? http://www.springframework.org/schema/context? ? ? http://www.springframework.org/schema/context/spring-context.xsd"><!-- Spring掃描注解的配置 --><context:component-scanbase-package="com.xxxx"/><!-- 加載properties 配置文件 --><context:property-placeholderlocation="jdbc.properties"/></beans>

配置數(shù)據(jù)源

? 由于建立數(shù)據(jù)庫連接是一個(gè)非常耗時(shí)耗資源的行為,所以通過連接池預(yù)先同數(shù)據(jù)庫建立一些連接,放在內(nèi)存中,應(yīng)用程序需要建立數(shù)據(jù)庫連接時(shí)直接到連接池中申請(qǐng)一個(gè)就行,用完后再放回去。

C3P0 與 DBCP 二選一即可

? DBCP(DataBase connection pool),數(shù)據(jù)庫連接池。是 apache 上的一個(gè) java 連接池項(xiàng)目,也是 tomcat 使用的連接池組件。單獨(dú)使用dbcp需要2個(gè)包:commons-dbcp.jar,commons-pool.jar dbcp,沒有自動(dòng)回收空閑連接的功能。

? C3P0是一個(gè)開源的JDBC連接池,它實(shí)現(xiàn)了數(shù)據(jù)源,支持JDBC3規(guī)范和JDBC2的標(biāo)準(zhǔn)擴(kuò)展。目前使用它的開源項(xiàng)目有Hibernate,Spring等。c3p0有自動(dòng)回收空閑連接功能。

C3P0 數(shù)據(jù)源配置

<!-- 配置 c3p0 數(shù)據(jù)源 --><beanid="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource"><!-- property標(biāo)簽的value屬性對(duì)應(yīng)的是jdbc.properties中的值 --><propertyname="driverClass"value="${jdbc.driver}"></property><propertyname="jdbcUrl"value="${jdbc.url}"></property><propertyname="user"value="${jdbc.user}"></property><propertyname="password"value="${jdbc.password}"></property></bean>

C3P0 其他額外配置(對(duì)應(yīng)的值在jdbc.properties文件中指定)

<!-- 指定連接池中保留的最大連接數(shù)。 Default:15--><propertyname="maxPoolSize"value="${maxPoolSize}"/><!-- 指定連接池中保留的最小連接數(shù)。--><propertyname="minPoolSize"value="${minPoolSize}"/><!-- 指定連接池的初始化連接數(shù)。取值應(yīng)在minPoolSize 與 maxPoolSize 之間.Default:3--><propertyname="initialPoolSize"value="${initialPoolSize}"/><!-- 最大空閑時(shí)間,60秒內(nèi)未使用則連接被丟棄。若為0則永不丟棄。 Default:0--><propertyname="maxIdleTime"value="${maxIdleTime}"/><!-- 當(dāng)連接池中的連接耗盡的時(shí)候c3p0一次同時(shí)獲取的連接數(shù)。 Default:3--><propertyname="acquireIncrement"value="${acquireIncrement}"/><!-- JDBC的標(biāo)準(zhǔn),用以控制數(shù)據(jù)源內(nèi)加載的PreparedStatements數(shù)量。?

但由于預(yù)緩存的statements屬于單個(gè)connection,而不是整個(gè)連接池所以設(shè)置這個(gè)參數(shù)需要考慮到多方面的因數(shù)。如果maxStatements與maxStatementsPerConnection均為0,則緩存被關(guān)閉。Default:0--><propertyname="maxStatements"value="${maxStatements}"/><!-- 每60秒檢查所有連接池中的空閑連接。Default:0 --><propertyname="idleConnectionTestPeriod"value="${idleConnectionTestPeriod}"/>

DBCP 數(shù)據(jù)源配置

<!-- 配置dbcp數(shù)據(jù)源--><beanid="myDataSource"class="org.apache.commons.dbcp2.BasicDataSource"><propertyname="driverClassName"value="${jdbc.driver}"/><propertyname="url"value="${jdbc.url}"/><propertyname="username"value="${jdbc.user}"/><propertyname="password"value="${jdbc.password}"/><!-- 連接池啟動(dòng)時(shí)的初始值 --><propertyname="initialSize"value="1"/><!-- 最大空閑值.當(dāng)經(jīng)過一個(gè)高峰時(shí)間后,連接池可以將已經(jīng)用不到的連接慢慢釋放一部分,一直減少到maxIdle為止 --><propertyname="maxIdle"value="2"/><!-- 最小空閑值.當(dāng)空閑的連接數(shù)少于閥值時(shí),連接池就會(huì)預(yù)申請(qǐng)一些連接,以避免洪峰來時(shí)再申請(qǐng)而造成的性能開銷 --><propertyname="minIdle"value="1"/></bean>

模板類配置

? Spring把 JDBC 中重復(fù)的操作建立成了一個(gè)模板類:org.springframework.jdbc.core.JdbcTemplate 。

<!-- 配置JdbcTemplate實(shí)例,并注入一個(gè)dataSource數(shù)據(jù)源--><beanid="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate"><propertyname="dataSource"ref="dataSource"></property></bean>

JDBC 測(cè)試

創(chuàng)建指定數(shù)據(jù)庫

選擇連接,右鍵選擇"新建數(shù)據(jù)庫",設(shè)置數(shù)據(jù)庫的名稱和編碼格式

創(chuàng)建數(shù)據(jù)表

使用 JUnit 測(cè)試

通過 junit 測(cè)試 jdbcTemplate bean 是否獲取到

JUnit 測(cè)試

publicclassSpringJdbcTest01{@TestpublicvoidtestQueryCount(){// 獲取spring上下文環(huán)境ApplicationContext ctx=newClassPathXmlApplicationContext("spring.xml");// 得到模板類 JdbcTemplate對(duì)象JdbcTemplate jdbcTemplate=(JdbcTemplate)ctx.getBean("jdbcTemplate");// 定義sql語句String sql="select count(1) from tb_account";// 執(zhí)行查詢操作(無參數(shù))Integer total=jdbcTemplate.queryForObject(sql,Integer.class);System.out.println("總記錄數(shù):"+total);}@TestpublicvoidtestQueryCountByUserId(){// 獲取spring上下文環(huán)境ApplicationContext ctx=newClassPathXmlApplicationContext("spring.xml");// 得到模板類 JdbcTemplate對(duì)象JdbcTemplate jdbcTemplate=(JdbcTemplate)ctx.getBean("jdbcTemplate");// 定義sql語句String sql=" select count(1) from tb_account where user_id = ?";// 執(zhí)行查詢操作(有參數(shù))Integer total=jdbcTemplate.queryForObject(sql,Integer.class,1);System.out.println("總記錄數(shù):"+total);}}

簡單封裝

publicclassSpringJdbcTest02{privateJdbcTemplate jdbcTemplate;@Beforepublicvoidinit(){// 得到Spring上下文環(huán)境ApplicationContext ac=newClassPathXmlApplicationContext("spring.xml");// 得到模板類 JdbcTemplate對(duì)象jdbcTemplate=(JdbcTemplate)ac.getBean("jdbcTemplate");}@TestpublicvoidtestQueryCount(){// 定義sql語句String sql="select count(1) from tb_account";// 執(zhí)行查詢操作(無參數(shù))Integer total=jdbcTemplate.queryForObject(sql,Integer.class);System.out.println("總記錄數(shù):"+total);}@TestpublicvoidtestQueryCountByUserId(){// 定義sql語句String sql=" select count(1) from tb_account where user_id = ?";// 執(zhí)行查詢操作(有參數(shù))Integer total=jdbcTemplate.queryForObject(sql,Integer.class,1);System.out.println("總記錄數(shù):"+total);}}

注解封裝

@RunWith就是一個(gè)運(yùn)行器@RunWith(JUnit4.class)就是指用JUnit4來運(yùn)行@RunWith(SpringJUnit4ClassRunner.class)讓測(cè)試運(yùn)行于Spring測(cè)試環(huán)境@ContextConfigurationSpring整合JUnit4測(cè)試時(shí),使用注解引入多個(gè)配置文件@ContextConfiguration(Locations="classpath:applicationContext.xml")@ContextConfiguration(locations={"classpath:spring.xml","classpath:bean.xml"})

@RunWith(SpringJUnit4ClassRunner.class)// 將junit測(cè)試加到spring環(huán)境中@ContextConfiguration(locations={"classpath:spring.xml"})// 設(shè)置要加載的資源文件publicclassSpringJdbcTest03{@ResourceprivateJdbcTemplate jdbcTemplate;@TestpublicvoidtestQueryCount(){// 定義sql語句String sql="select count(1) from tb_account";// 執(zhí)行查詢操作(無參數(shù))Integer total=jdbcTemplate.queryForObject(sql,Integer.class);System.out.println("總記錄數(shù):"+total);}}

通用封裝

定義一個(gè)父類,設(shè)置通用的配置信息

/**

* 通用的測(cè)試環(huán)境,需要使用環(huán)境的直接繼承類即可

*/@RunWith(SpringJUnit4ClassRunner.class)// 將junit測(cè)試加到spring環(huán)境中@ContextConfiguration(locations={"classpath:spring.xml"})// 設(shè)置要加載的資源文件publicclassBaseTest{}

繼承通用的測(cè)試類

publicclassSpringJdbcTest04extendsBaseTest{@ResourceprivateJdbcTemplate jdbcTemplate;@TestpublicvoidtestQueryCount(){// 定義sql語句String sql="select count(1) from tb_account";// 執(zhí)行查詢操作(無參數(shù))Integer total=jdbcTemplate.queryForObject(sql,Integer.class);System.out.println("總記錄數(shù):"+total);}}

持久層賬戶模塊操作

? 當(dāng)完成 Spring Jdbc 環(huán)境集成后,這里使用spring jdbc 完成賬戶單表crud 操作。

賬戶接口方法定義

定義實(shí)體類

Account.java

packagecom.xxxx.entity;importjava.util.Date;/**

* 用戶賬戶類

*/publicclassAccount{privateInteger accountId;// 賬戶ID,主鍵privateString accountName;// 賬戶名稱privateString accountType;// 賬戶類型privateDouble money;// 賬戶金額privateString remark;// 賬戶備注privateInteger userId;// 用戶ID,賬戶所屬用戶privateDate createTime;// 創(chuàng)建時(shí)間privateDate updateTime;// 修改時(shí)間publicAccount(){}publicAccount(String accountName,String accountType,Double money,String remark,Integer userId){this.accountName=accountName;this.accountType=accountType;this.money=money;this.remark=remark;this.userId=userId;}@OverridepublicStringtoString(){return"Account{"+"accountId="+accountId+", accountName='"+accountName+'\''+", accountType='"+accountType+'\''+", money="+money+", remark='"+remark+'\''+", userId="+userId+", createTime="+createTime+", updateTime="+updateTime+'}';}publicIntegergetAccountId(){returnaccountId;}publicvoidsetAccountId(Integer accountId){this.accountId=accountId;}publicStringgetAccountName(){returnaccountName;}publicvoidsetAccountName(String accountName){this.accountName=accountName;}publicStringgetAccountType(){returnaccountType;}publicvoidsetAccountType(String accountType){this.accountType=accountType;}publicDoublegetMoney(){returnmoney;}publicvoidsetMoney(Double money){this.money=money;}publicStringgetRemark(){returnremark;}publicvoidsetRemark(String remark){this.remark=remark;}publicIntegergetUserId(){returnuserId;}publicvoidsetUserId(Integer userId){this.userId=userId;}publicDategetCreateTime(){returncreateTime;}publicvoidsetCreateTime(Date createTime){this.createTime=createTime;}publicDategetUpdateTime(){returnupdateTime;}publicvoidsetUpdateTime(Date updateTime){this.updateTime=updateTime;}}

定義接口類

IAccountDao.java

packagecom.xxxx.dao;importcom.xxxx.entity.Account;importjava.util.List;/**

* 用戶模塊 接口定義

*? ? ? 1. 添加賬戶

*? ? ? ? ? 添加賬戶記錄,返回受影響的行數(shù)

*? ? ? ? ? 添加賬戶記錄,返回記錄的主鍵

*? ? ? ? ? 批量添加賬戶記錄,返回受影響的行數(shù)

*? ? ? 2. 查詢賬戶

*? ? ? ? ? 查詢指定用戶的賬戶總記錄數(shù),返回記錄數(shù)

*? ? ? ? ? 查詢指定賬戶記錄詳情,返回賬戶對(duì)象

*? ? ? ? ? 多條件查詢指定用戶的賬戶列表,返回賬戶集合

*? ? ? 3. 更新賬戶

*? ? ? ? ? 更新賬戶記錄,返回受影響的行數(shù)

*? ? ? ? ? 批量更新賬戶記錄,返回受影響的行數(shù)

*? ? ? 4. 刪除賬戶

*? ? ? ? ? 刪除賬戶記錄,返回受影響的行數(shù)

*? ? ? ? ? 批量刪除賬戶記錄,返回受影響的行數(shù)

*/publicinterfaceIAccountDao{/**

? ? * 添加賬戶

? ? *? ? ? 添加賬戶記錄,返回受影響的行數(shù)

? ? * @param account

? ? * @return

? ? */publicintaddAccount(Account account);/**

? ? * 添加賬戶

? ? *? ? ? 添加賬戶記錄,返回記錄的主鍵

? ? * @param account

? ? * @return

? ? */publicintaddAccountHasKey(Account account);/**

? ? * 添加賬戶

? ? *? ? ? 批量添加賬戶記錄,返回受影響的行數(shù)

? ? * @param accounts

? ? * @return

? ? */publicintaddAccountBatch(List<Account>accounts);/**

? ? * 查詢賬戶

? ? *? ? ? 查詢指定用戶的賬戶總記錄數(shù),返回記錄數(shù)

? ? * @param userId

? ? * @return

? ? */publicintqueryAccountCount(Integer userId);/**

? ? * 查詢賬戶

? ? *? ? ? 查詢指定賬戶記錄詳情,返回賬戶對(duì)象

? ? * @param accountId

? ? * @return

? ? */publicAccountqueryAccountById(Integer accountId);/**

? ? * 查詢賬戶

? ? *? ? ? 多條件查詢指定用戶的賬戶列表,返回賬戶集合

? ? * @param userId

? ? * @param accountName

? ? * @param accountType

? ? * @param createTime

? ? * @return

? ? */publicList<Account>queryAccountsByParams(Integer userId,String accountName,String accountType,String createTime);/**

? ? * 更新賬戶

? ? *? ? ? 更新賬戶記錄,返回受影響的行數(shù)

? ? * @param account

? ? * @return

? ? */publicintupdateAccountById(Account account);/**

? ? * 更新賬戶

? ? *? ? ? 批量更新賬戶記錄,返回受影響的行數(shù)

? ? * @param accounts

? ? * @return

? ? */publicintupdateAccountBatch(List<Account>accounts);/**

? ? * 刪除賬戶

? ? *? ? ? 刪除賬戶記錄,返回受影響的行數(shù)

? ? * @param accountId

? ? * @return

? ? */publicIntegerdeleteAccoutById(Integer accountId);/**

? ? * 刪除用戶

? ? *? ? ? 批量刪除賬戶記錄,返回受影響的行數(shù)

? ? * @param ids

? ? * @return

? ? */publicintdeleteAccountBatch(Integer[]ids);}

定義接口實(shí)現(xiàn)類

packagecom.xxxx.dao.impl;importcom.xxxx.dao.IAccountDao;importcom.xxxx.entity.Account;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.stereotype.Component;importjavax.annotation.Resource;importjava.util.List;/**

* 賬戶模塊接口實(shí)現(xiàn)類

*/@RepositorypublicclassAccountDaoImplimplementsIAccountDao{// JdbcTemplate 模板類注入@ResourceprivateJdbcTemplate jdbcTemplate;@OverridepublicintaddAccount(Account account){return0;}@OverridepublicintaddAccountHasKey(Account account){return0;}@OverridepublicintaddAccountBatch(List<Account>accounts){return0;}@OverridepublicintqueryAccountCount(Integer userId){return0;}@OverridepublicAccountqueryAccountById(Integer accountId){returnnull;}@OverridepublicList<Account>queryAccountsByParams(Integer userId,String accountName,String accountType,String createTime){returnnull;}@OverridepublicintupdateAccountById(Account account){return0;}@OverridepublicintupdateAccountBatch(List<Account>accounts){return0;}@OverridepublicIntegerdeleteAccoutById(Integer accountId){returnnull;}@OverridepublicintdeleteAccountBatch(Integer[]ids){return0;}}

賬戶記錄添加實(shí)現(xiàn)

? 在企業(yè)項(xiàng)目開發(fā)時(shí),對(duì)于記錄的添加可能涉及到多種添加方式,比如添加單條記錄,批量添加多條記錄等情況。這里對(duì)于賬戶記錄添加方式分為三種方式:添加單條記錄返回受影響行數(shù)、添加單條記錄返回主鍵、批量添加多條記錄。

添加賬戶記錄

/**

? * 添加單條記錄,返回受影響的行數(shù)

? * @param account

? * @return

? */@OverridepublicintaddAccount(Account account){String sql="insert into tb_account(account_name,account_type,money,remark,"+"user_id,create_time,update_time) values (?,?,?,?,?,now(),now())";Object[]objs={account.getAccountName(),account.getAccountType(),account.getMoney(),account.getRemark(),account.getUserId()};returnjdbcTemplate.update(sql,objs);}

測(cè)試方法

/**

? * 添加賬戶記錄,得到受影響的行數(shù)

? */@TestpublicvoidtestAddAccount(){// 準(zhǔn)備要添加的數(shù)據(jù)Account account=newAccount("張三","建設(shè)銀行",100.0,"零花錢",1);// 調(diào)用對(duì)象的添加方法,返回受影響的行數(shù)introw=accountDao.addAccount(account);System.out.println("添加賬戶受影響的行數(shù):"+row);}

添加記錄返回主鍵

/**

? * 添加單條記錄,返回主鍵

? * @param account

? * @return

? */@OverridepublicintaddAccountHasKey(Account account){String sql="insert into tb_account(account_name,account_type,money,remark,"+"user_id,create_time,update_time) values (?,?,?,?,?,now(),now())";// 定義keyHolder 對(duì)象? 獲取記錄主鍵值KeyHolder keyHolder=newGeneratedKeyHolder();jdbcTemplate.update(connection->{// 預(yù)編譯sql語句,并設(shè)置返回主鍵PreparedStatement ps=connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);// 設(shè)置參數(shù)ps.setString(1,account.getAccountName());ps.setString(2,account.getAccountType());ps.setDouble(3,account.getMoney());ps.setString(4,account.getRemark());ps.setInt(5,account.getUserId());returnps;},keyHolder);// 得到返回的主鍵Integer key=keyHolder.getKey().intValue();returnkey;}

測(cè)試方法

/**

? * 添加賬戶記錄,返回主鍵

? */@TestpublicvoidtestAddAccountHasKey(){// 準(zhǔn)備要添加的數(shù)據(jù)Account account=newAccount("李四","招商銀行",200.0,"兼職費(fèi)",2);// 調(diào)用對(duì)象的添加方法,返回主鍵intkey=accountDao.addAccountHasKey(account);System.out.println("添加賬戶返回的主鍵:"+key);}

批量添加賬戶記錄

/**

? * 添加多條記錄,返回受影響的行數(shù)

? * @param accounts

? * @return

? */@OverridepublicintaddAccountBatch(finalList<Account>accounts){String sql="insert into tb_account(account_name,account_type,money,remark,"+"user_id,create_time,update_time) values (?,?,?,?,?,now(),now())";introws=jdbcTemplate.batchUpdate(sql,newBatchPreparedStatementSetter(){@OverridepublicvoidsetValues(PreparedStatement preparedStatement,inti)throwsSQLException{// 設(shè)置參數(shù)preparedStatement.setString(1,accounts.get(i).getAccountName());preparedStatement.setString(2,accounts.get(i).getAccountType());preparedStatement.setDouble(3,accounts.get(i).getMoney());preparedStatement.setString(4,accounts.get(i).getRemark());preparedStatement.setInt(5,accounts.get(i).getUserId());}@OverridepublicintgetBatchSize(){returnaccounts.size();}}).length;returnrows;}

測(cè)試方法

/**

? * 批量添加數(shù)據(jù),返回受影響的行數(shù)

? */@TestpublicvoidtestAddAccountBatch(){// 準(zhǔn)備要添加的數(shù)據(jù)Account account=newAccount("王五","農(nóng)業(yè)銀行",2000.0,"工資",3);Account account2=newAccount("趙六","中國銀行",280.0,"獎(jiǎng)金",3);Account account3=newAccount("田七","工商銀行",800.0,"零花錢",3);List<Account>accountList=newArrayList<>();accountList.add(account);accountList.add(account2);accountList.add(account3);// 調(diào)用對(duì)象的添加方法,返回主鍵introws=accountDao.addAccountBatch(accountList);System.out.println("批量添加賬戶受影響的行數(shù):"+rows);}

賬戶記錄查詢實(shí)現(xiàn)

? 賬戶記錄查詢這里提供了三種查詢方式,查詢指定用戶所有賬戶記錄數(shù),查詢單條賬戶記錄詳情,多條件查詢指定用戶賬戶記錄。

查詢用戶的賬戶總記錄數(shù)

/**

? * 查詢指定用戶的賬戶總記錄數(shù),返回記錄數(shù)

? * @param userId

? * @return

? */@OverridepublicintqueryAccountCount(Integer userId){String sql="select count(1) from tb_account where user_id = ?";intcount=jdbcTemplate.queryForObject(sql,Integer.class,userId);returncount;}

測(cè)試方法

/**

? * 查詢用戶的賬戶總記錄數(shù),返回總記錄數(shù)

? */@TestpublicvoidtestQueryAccountCount(){// 查詢ID為1的用戶的賬戶總記錄數(shù)inttotal=accountDao.queryAccountCount(1);System.out.println("總記錄數(shù):"+total);}

查詢指定賬戶記錄詳情

/**

? * 查詢某個(gè)賬戶記錄詳情,返回賬戶對(duì)象

? * @param accountId

? * @return

? */@OverridepublicAccountqueryAccountById(Integer accountId){String sql="select * from tb_account where account_id = ?";Account account=jdbcTemplate.queryForObject(sql,newObject[]{accountId},(resultSet,i)->{Account acc=newAccount();acc.setAccountId(resultSet.getInt("account_id"));acc.setMoney(resultSet.getDouble("money"));acc.setAccountName(resultSet.getString("account_name"));acc.setAccountType(resultSet.getString("account_type"));acc.setRemark(resultSet.getString("remark"));acc.setCreateTime(resultSet.getDate("create_time"));acc.setUpdateTime(resultSet.getDate("update_time"));acc.setUserId(resultSet.getInt("user_id"));returnacc;});returnaccount;}

測(cè)試方法

/**

? * 查詢指定賬戶的記錄詳情,返回賬戶對(duì)象

? */@TestpublicvoidtestQueryAccountById(){// 查詢ID為1的賬戶記錄的詳情Account account=accountDao.queryAccountById(1);System.out.println("賬戶詳情:"+account.toString());}

多條件查詢用戶賬戶記錄

/**

? * 多條件查詢指定用戶的賬戶列表,返回賬戶集合

? * @param userId 用戶Id

? * @param accountName 賬戶名稱 (模糊查詢)

? * @param accountType 賬戶類型

? * @param createTime? 賬戶創(chuàng)建時(shí)間

? * @return

? */@OverridepublicList<Account>queryAccountsByParams(Integer userId,String accountName,String accountType,String createTime){String sql="select * from tb_account where user_id = ? ";List<Object>params=newArrayList<>();params.add(userId);// 判斷是否有條件查詢// 如果賬戶名稱不為空,通過賬戶名稱模糊匹配if(StringUtils.isNotBlank(accountName)){sql+=" and? account_name like concat('%',?,'%') ";params.add(accountName);}// 如果賬戶類型不為空,通過指定類型名稱查詢if(StringUtils.isNotBlank(accountType)){sql+=" and? account_type = ? ";params.add(accountType);}// 如果創(chuàng)建時(shí)間不為空,查詢創(chuàng)建時(shí)間大于指定時(shí)間的賬戶記錄if(StringUtils.isNotBlank(createTime)){sql+=" and create_time > ? ";params.add(createTime);}// 將集合轉(zhuǎn)換成數(shù)組Object[]objs=params.toArray();List<Account>accountList=jdbcTemplate.query(sql,objs,(resultSet,rowNum)->{Account acc=newAccount();acc.setAccountId(resultSet.getInt("account_id"));acc.setMoney(resultSet.getDouble("money"));acc.setAccountName(resultSet.getString("account_name"));acc.setAccountType(resultSet.getString("account_type"));acc.setRemark(resultSet.getString("remark"));acc.setCreateTime(resultSet.getDate("create_time"));acc.setUpdateTime(resultSet.getDate("update_time"));acc.setUserId(resultSet.getInt("user_id"));returnacc;});returnaccountList;}

測(cè)試方法

/**

? * 多條件查詢用戶的賬戶記錄,返回賬戶集合

? */@TestpublicvoidtestQueryAccountByParams(){// 查詢用戶的賬戶列表List<Account>accountList=accountDao.queryAccountsByParams(3,null,null,null);// 通過指定條件查詢用戶的賬戶列表List<Account>accountList02=accountDao.queryAccountsByParams(3,"張",null,null);System.out.println(accountList.toString());System.out.println(accountList02.toString());}

賬戶記錄更新實(shí)現(xiàn)

更新賬戶記錄

/**

? * 更新指定賬戶記錄,返回受影響的行數(shù)

? * @param account

? * @return

? */@OverridepublicintupdateAccountById(Account account){String sql="update tb_account set account_name = ?, account_type = ?, "+" money = ? ,remark = ?,user_id = ? ,update_time = now() "+" where account_id = ? ";Object[]objs={account.getAccountName(),account.getAccountType(),account.getMoney(),account.getRemark(),account.getUserId(),account.getAccountId()};returnjdbcTemplate.update(sql,objs);}

測(cè)試方法

/**

? * 更新指定賬戶記錄,返回受影響的行數(shù)

? */@TestpublicvoidtestUpdateAccount(){// 準(zhǔn)備要修改的數(shù)據(jù)Account account=newAccount("張三1","建設(shè)銀行1",500.0,"零花錢加倍",1);account.setAccountId(1);introw=accountDao.updateAccountById(account);System.out.println("修改賬戶返回受影響的行數(shù):"+row);}

批量更新賬戶記錄

/**

? * 批量新賬戶記錄,返回受影響的行數(shù)

? * @param accounts

? * @return

? */@OverridepublicintupdateAccountBatch(List<Account>accounts){String sql="update tb_account set account_name = ?, account_type = ?, "+" money = ? ,remark = ?,user_id = ? ,update_time = now() "+" where account_id = ? ";introws=jdbcTemplate.batchUpdate(sql,newBatchPreparedStatementSetter(){@OverridepublicvoidsetValues(PreparedStatement ps,inti)throwsSQLException{// 設(shè)置參數(shù)ps.setString(1,accounts.get(i).getAccountName());ps.setString(2,accounts.get(i).getAccountType());ps.setDouble(3,accounts.get(i).getMoney());ps.setString(4,accounts.get(i).getRemark());ps.setInt(5,accounts.get(i).getUserId());ps.setInt(6,accounts.get(i).getAccountId());}@OverridepublicintgetBatchSize(){returnaccounts.size();}}).length;returnrows;}

測(cè)試方法

/**

? * 批量更新賬戶記錄,返回受影響的行數(shù)

? */@TestpublicvoidtestUpdateAccountBatch(){// 準(zhǔn)備要修改的數(shù)據(jù)Account account=newAccount("a3","建設(shè)銀行3",300.0,"零花錢加倍3",3);account.setAccountId(3);Account account2=newAccount("a4","建設(shè)銀行4",400.0,"零花錢加倍4",3);account2.setAccountId(4);List<Account>accountList=newArrayList<>();accountList.add(account);accountList.add(account2);introws=accountDao.updateAccountBatch(accountList);System.out.println("批量修改賬戶記錄返回受影響的行數(shù):"+rows);}

賬戶記錄刪除實(shí)現(xiàn)

刪除賬戶記錄

/**

? * 刪除賬戶記錄,返回受影響的行數(shù)

? * @param accountId

? * @return

? */@OverridepublicIntegerdeleteAccoutById(Integer accountId){String sql="delete from tb_account where account_id= ? ";Object[]objs={accountId};returnjdbcTemplate.update(sql,objs);}

測(cè)試方法

/**

? * 刪除賬戶記錄,返回受影響的行數(shù)

? */@TestpublicvoidtestDeleteAccount(){// 刪除ID為1的賬戶記錄introw=accountDao.deleteAccoutById(1);System.out.println("刪除賬戶記錄返回受影響的行數(shù):"+row);}

批量刪除賬戶記錄

/**

? * 批量刪除賬戶記錄,返回受影響的行數(shù)

? * @param ids

? * @return

? */@OverridepublicintdeleteAccountBatch(Integer[]ids){String sql="delete from tb_account where account_id = ?";introw=jdbcTemplate.batchUpdate(sql,newBatchPreparedStatementSetter(){@OverridepublicvoidsetValues(PreparedStatement ps,inti)throwsSQLException{ps.setInt(1,ids[i]);}@OverridepublicintgetBatchSize(){returnids.length;}}).length;returnrow;}

測(cè)試方法

/**

? * 批量刪除賬戶記錄,返回受影響的行數(shù)

? */@TestpublicvoidtestDeleteAccountBatch(){// 刪除多個(gè)id的賬戶記錄Integer[]ids=newInteger[]{2,3};introws=accountDao.deleteAccountBatch(ids);System.out.println("批量刪除賬戶記錄返回受影響的行數(shù):"+rows);}

Spring 事務(wù)控制

轉(zhuǎn)賬場(chǎng)景模擬實(shí)現(xiàn)

接口方法定義

/**

? * 收入

? * @param tarAid 收入金額的賬戶ID

? * @param money 收入金額

? * @return

? */publicintinAccount(Integer tarAid,Double money);/**

? * 支出

? * @param outAid 支出金額的賬戶ID

? * @param money? 支出金額

? * @return

? */publicintoutAccount(Integer outAid,Double money);

實(shí)現(xiàn)對(duì)應(yīng)接口

? 對(duì)于轉(zhuǎn)賬涉及到雙方賬戶以及對(duì)應(yīng)轉(zhuǎn)賬金額,所以有入賬和出賬兩個(gè)方法。

/**

? * 賬戶收入

? * @param tarAid 賬戶ID

? * @param money 收入金額

? * @return

? */@OverridepublicintinAccount(Integer tarAid,Double money){// 修改指定ID的金額 (加上金額)String sql="update tb_account set money = money + ? where account_id = ? ";Object[]objs={money,tarAid};returnjdbcTemplate.update(sql,objs);}/**

? * 賬戶支出

? * @param outAid 賬戶ID

? * @param money? 支出金額

? * @return

? */@OverridepublicintoutAccount(Integer outAid,Double money){// 修改指定ID的金額 (減去金額)String sql="update tb_account set money = money - ? where account_id = ? ";Object[]objs={money,outAid};returnjdbcTemplate.update(sql,objs);}

轉(zhuǎn)賬方法實(shí)現(xiàn)

packagecom.xxxx.service;importcom.xxxx.dao.IAccountDao;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;@ServicepublicclassAccountService{@ResourceprivateIAccountDao accountDao;/**

? ? * 轉(zhuǎn)賬業(yè)務(wù)操作

? ? * @param outAid? 支出賬戶

? ? * @param inAid? 收入賬戶

? ? * @param money? 支出金額/收入金額

? ? * @return

? ? */publicintupdateAccountByTransfer(Integer outAid,Integer inAid,Double money){introw=0;/**

? ? ? ? * 張三賬戶向李四賬戶轉(zhuǎn)賬100元

? ? ? ? *? 張三賬戶的金額 - 100

? ? ? ? *? 李四賬戶的金額 + 100

? ? ? ? */// 支出,修改金額返回受影響的行數(shù)intoutRow=accountDao.outAccount(1,100.0);// 收入,修改金額返回受影響的行數(shù)intinRow=accountDao.inAccount(2,100.0);// 當(dāng)兩個(gè)操作都執(zhí)行成功時(shí),轉(zhuǎn)賬成功if(outRow==1&&inRow==1){row=1;// 成功}returnrow;}}

? 仔細(xì)思考代碼會(huì)發(fā)現(xiàn),在程序運(yùn)行中無法保證 service 層業(yè)務(wù)代碼不發(fā)生異常,如果通過 jdbc 的方式處理事務(wù),此時(shí)需要手動(dòng)方式控制事務(wù),這樣的話凡是涉及到事務(wù)控制的業(yè)務(wù)方法均需要開發(fā)人員手動(dòng)來進(jìn)行事務(wù)處理,無法滿足生產(chǎn)的需要。

Spring 事務(wù)概念

事務(wù)的四大特性(ACID)

原子性(Atomicity)

? 共生死,要么全部成功,要么全部失??!

一致性(Consistency)

? 事務(wù)在執(zhí)行前后,數(shù)據(jù)庫中數(shù)據(jù)要保持一致性狀態(tài)。(如轉(zhuǎn)賬的過程 賬戶操作后數(shù)據(jù)必須保持一致)

隔離性(Isolation)

? 事務(wù)與事務(wù)之間的執(zhí)行應(yīng)當(dāng)是相互隔離互不影響的。(多個(gè)角色對(duì)統(tǒng)一記錄進(jìn)行操作必須保證沒有任何干擾),當(dāng)然沒有影響是不可能的,為了讓影響級(jí)別降到最低,通過隔離級(jí)別加以限制:

? 1. READ_UNCOMMITTED (讀未提交)

? 隔離級(jí)別最低的一種事務(wù)級(jí)別。在這種隔離級(jí)別下,會(huì)引發(fā)臟讀、不可重復(fù)讀和幻讀。

? 2. READ_COMMITTED (讀已提交)

? 讀到的都是別人提交后的值。這種隔離級(jí)別下,會(huì)引發(fā)不可重復(fù)讀和幻讀,但避免了臟讀。

? 3. REPEATABLE_READ (可重復(fù)讀)

? 這種隔離級(jí)別下,會(huì)引發(fā)幻讀,但避免了臟讀、不可重復(fù)讀。

? 4. SERIALIZABLE (串行化)

? 最嚴(yán)格的隔離級(jí)別。在Serializable隔離級(jí)別下,所有事務(wù)按照次序依次執(zhí)行。

? 臟讀、不可重復(fù)讀、幻讀都不會(huì)出現(xiàn)。

持久性(Durability)

? 事務(wù)提交完畢后,數(shù)據(jù)庫中的數(shù)據(jù)的改變是永久的。

Spring 事務(wù)核心接口

? Spring 事務(wù)管理的實(shí)現(xiàn)有許多細(xì)節(jié),如果對(duì)整個(gè)接口框架有個(gè)大體了解會(huì)非常有利于我們理解事務(wù),下面通過講解 Spring 的事務(wù)接口來了解 Spring 實(shí)現(xiàn)事務(wù)的具體策略。

? Spring 并不直接管理事務(wù),而是提供了多種事務(wù)管理器,他們將事務(wù)管理的職責(zé)委托給 Hibernate 或者 JTA 等持久化機(jī)制所提供的相關(guān)平臺(tái)框架的事務(wù)來實(shí)現(xiàn)。

? Spring 事務(wù)管理器的接口是org.springframework.transaction.PlatformTransactionManager,通過這個(gè)接口,Spring 為各個(gè)平臺(tái)如 JDBC、Hibernate 等都提供了對(duì)應(yīng)的事務(wù)管理器,但是具體的實(shí)現(xiàn)就是各個(gè)平臺(tái)自己的事情了。此接口的內(nèi)容如下:

publicinterfacePlatformTransactionManager(){// 由 TransactionDefinition 得到 TransactionStatus 對(duì)象 TransactionStatusgetTransaction(TransactionDefinition definition)throwsTransactionException;// 提交 voidcommit(TransactionStatus status)throwsTransactionException;// 回滾 voidrollback(TransactionStatus status)throwsTransactionException;}

? 從這里可知具體的具體的事務(wù)管理機(jī)制對(duì) Spring 來說是透明的,它并不關(guān)心那些,那些是對(duì)應(yīng)各個(gè)平臺(tái)需要關(guān)心的,所以 Spring 事務(wù)管理的一個(gè)優(yōu)點(diǎn)就是為不同的事務(wù) API 提供一致的編程模型,如 JTA、JDBC、Hibernate、JPA。下面分別介紹各個(gè)平臺(tái)框架實(shí)現(xiàn)事務(wù)管理的機(jī)制。

JDBC 事務(wù)

? 如果應(yīng)用程序中直接使用 JDBC 來進(jìn)行持久化,此時(shí)使用 DataSourceTransactionManager 來處理事務(wù)邊界。為了使用DataSourceTransactionManager,需要使用如下的 XML 將其裝配到應(yīng)用程序的上下文定義中:

<beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="dataSource"/></bean>

? 實(shí)際上,DataSourceTransactionManager 是通過調(diào)用 java.sql.Connection 來管理事務(wù),而后者是通過 DataSource 獲取到的。通過調(diào)用連接的 commit() 方法來提交事務(wù),同樣,事務(wù)失敗則通過調(diào)用 rollback() 方法進(jìn)行回滾。

Hibernate 事務(wù)

? 如果應(yīng)用程序的持久化是通過 Hibernate 實(shí)現(xiàn)的,那么你需要使用 HibernateTransactionManager。對(duì)于 Hibernate3,需要在 Spring 上下文定義中添加如下的聲明:

<beanid="transactionManager"class="org.springframework.orm.hibernate3.HibernateTransactionManager"><propertyname="sessionFactory"ref="sessionFactory"/></bean>

? sessionFactory 屬性需要裝配一個(gè) Hibernate 的 session 工廠,HibernateTransactionManager 的實(shí)現(xiàn)細(xì)節(jié)是它將事務(wù)管理的職責(zé)委托給 org.hibernate.Transaction 對(duì)象,而后者是從 Hibernate Session 中獲取到的。當(dāng)事務(wù)成功完成時(shí),HibernateTransactionManager 將會(huì)調(diào)用 Transaction 對(duì)象的 commit() 方法,反之,將會(huì)調(diào)用 rollback() 方法。

Java 持久化 API 事務(wù)(JPA)

? Hibernate 多年來一直是 Java 持久化標(biāo)準(zhǔn),但是現(xiàn)在 Java 持久化 API 作為真正的 Java 持久化標(biāo)準(zhǔn)進(jìn)入大家的視野。如果你計(jì)劃使用 JPA 的話,那你需要使用 Spring 的 JpaTransactionManager 來處理事務(wù)。你需要在 Spring 中這樣配置 JpaTransactionManager:

<beanid="transactionManager"class="org.springframework.orm.jpa.JpaTransactionManager"><propertyname="sessionFactory"ref="sessionFactory"/></bean>

? JpaTransactionManager 只需要裝配一個(gè) JPA 實(shí)體管理工廠(javax.persistence.EntityManagerFactory 接口的任意實(shí)現(xiàn))。 JpaTransactionManager 將與由工廠所產(chǎn)生的 JPA EntityManager 合作來構(gòu)建事務(wù)。

Java 原生 API 事務(wù)

? 如果應(yīng)用程序沒有使用以上所述的事務(wù)管理,或者是跨越了多個(gè)事務(wù)管理源(比如兩個(gè)或者是多個(gè)不同的數(shù)據(jù)源),此時(shí)需要使用 JtaTransactionManager:

<beanid="transactionManager"class="org.springframework.transaction.jta.JtaTransactionManager"><propertyname="transactionManagerName"value="java:/TransactionManager"/></bean>

? JtaTransactionManager 將事務(wù)管理的責(zé)任委托給 javax.transaction.UserTransaction 和javax.transaction.TransactionManager 對(duì)象,其中事務(wù)成功完成通過 UserTransaction.commit() 方法提交,事務(wù)失敗通過 UserTransaction.rollback() 方法回滾。

Spring 事務(wù)控制配置

? 通過 jdbc 持久化事務(wù),對(duì)于事務(wù)配置實(shí)現(xiàn)由兩種方式即:Xml 配置,注解配置。

XML 配置

添加命名空間

在spring.xml配置文件的添加事務(wù)和aop的命名空間

事務(wù)

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

AOP

xmlns:aop="http://www.springframework.org/schema/aop"

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd

配置如下

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans? ? ? https://www.springframework.org/schema/beans/spring-beans.xsd? ? ? http://www.springframework.org/schema/context? ? ? http://www.springframework.org/schema/context/spring-context.xsd? ? ? http://www.springframework.org/schema/tx? ? ? http://www.springframework.org/schema/tx/spring-tx.xsd? ? ? http://www.springframework.org/schema/aop? http://www.springframework.org/schema/aop/spring-aop.xsd">

設(shè)置aop代理

<!-- 開啟AOP代理 --><aop:aspectj-autoproxy/>

配置事務(wù)管理器

<!-- 事務(wù)管理器定義 --><beanid="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--數(shù)據(jù)源 --><propertyname="dataSource"ref="dataSource"></property></bean>

配置事務(wù)相關(guān)通知

一般來說增刪改方法 propagation=Required,對(duì)于查詢方法使用 read-only=“true”

<!-- 配置事務(wù)通知? transaction-manager屬性表示這個(gè)事務(wù)通知是哪個(gè)事務(wù)管理器管理的--><!--

? ? tx:method的屬性:

? ? ? name

是必須的,表示與事務(wù)屬性關(guān)聯(lián)的方法名(業(yè)務(wù)方法名),對(duì)切入點(diǎn)進(jìn)行細(xì)化。

? 通配符(*)可以用來指定一批關(guān)聯(lián)到相同的事務(wù)屬性的方法。

? ? ? 如:'get*'、'handle*'、'on*Event'等等.

? ? ? propagation

不是必須的,默認(rèn)值是REQUIRED

? ? ? 表示事務(wù)傳播行為, 包括:

REQUIRED,SUPPORTS,MANDATORY,NEVER

REQUIRES_NEW,NOT_SUPPORTED,NESTED

? ? ? isolation?

不是必須的,默認(rèn)值DEFAULT

? ? ? ? ? ? 表示事務(wù)隔離級(jí)別(數(shù)據(jù)庫的隔離級(jí)別)

? ? ? timeout

不是必須的,默認(rèn)值-1(永不超時(shí))

? ? ? ? ? ? 表示事務(wù)超時(shí)的時(shí)間(以秒為單位)

? ? ? read-only

不是必須的,默認(rèn)值false不是只讀的

? ? ? ? ? ? 表示事務(wù)是否只讀

? ? ? rollback-for

不是必須的

? ? ? ? ? ? 表示將被觸發(fā)進(jìn)行回滾的 Exception(s);以逗號(hào)分開。

? ? ? ? ? ? 如:'com.foo.MyBusinessException,ServletException'

? ? ? no-rollback-for

不是必須的

? ? ? ? ? ? 表示不被觸發(fā)進(jìn)行回滾的 Exception(s);以逗號(hào)分開。

? ? ? ? ? ? 如:'com.foo.MyBusinessException,ServletException'

? ? ? ? ? ? 任何 RuntimeException 將觸發(fā)事務(wù)回滾

? ? --><tx:adviceid="txAdvice"transaction-manager="txManager"><!--對(duì)以add update delete query開頭的所有方法進(jìn)行事務(wù)處理--><tx:attributes><!--定義什么方法需要使用事務(wù)? name代表的是方法名(或方法匹配)--><!-- 匹配以 add 開頭的所有方法均加入事務(wù) --><tx:methodname="add*"propagation="REQUIRED"/><!-- 匹配以 update 開頭的所有方法均加入事務(wù) --><tx:methodname="update*"propagation="REQUIRED"/><!-- 匹配以 delete 開頭的所有方法均加入事務(wù) --><tx:methodname="delete*"propagation="REQUIRED"/><!-- 匹配以 query 開頭的所有方法均加入事務(wù) --><tx:methodname="query*"read-only="true"/></tx:attributes></tx:advice>

事務(wù)傳播行為介紹:@Transactional(propagation=Propagation.REQUIRED)如果有事務(wù),那么加入事務(wù),沒有的話新建一個(gè)(默認(rèn)情況下)@Transactional(propagation=Propagation.NOT_SUPPORTED)容器不為這個(gè)方法開啟事務(wù)@Transactional(propagation=Propagation.REQUIRES_NEW)不管是否存在事務(wù),都創(chuàng)建一個(gè)新的事務(wù),原來的掛起,新的執(zhí)行完畢,繼續(xù)執(zhí)行老的事務(wù)@Transactional(propagation=Propagation.MANDATORY)必須在一個(gè)已有的事務(wù)中執(zhí)行,否則拋出異常@Transactional(propagation=Propagation.NEVER)必須在一個(gè)沒有的事務(wù)中執(zhí)行,否則拋出異常(與 Propagation.MANDATORY 相反)@Transactional(propagation=Propagation.SUPPORTS)如果其他 bean 調(diào)用這個(gè)方法,在其他 bean 中聲明事務(wù),那就用事務(wù).如果其他 bean 沒有聲明事務(wù),那就不用事務(wù).@Transactional(propagation=Propagation.NESTED)支持當(dāng)前事務(wù),如果當(dāng)前事務(wù)存在,則執(zhí)行一個(gè)嵌套事務(wù),如果當(dāng)前沒有事務(wù),就新建一個(gè)事務(wù)。

配置aop

<!-- aop 切面定義 (切入點(diǎn)和通知) --><aop:config><!-- 設(shè)置切入點(diǎn) 設(shè)置需要被攔截的方法 --><aop:pointcutexpression="execution(* com.xxxx.service..*.*(..) )"id="cut"/><!-- 設(shè)置通知 事務(wù)通知 --><aop:advisoradvice-ref="txAdvice"pointcut-ref="cut"/>

注解配置

配置事務(wù)管理器

<!-- spring 注解式事務(wù)聲明 --><!-- 事務(wù)管理器定義 --><beanid="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="dataSource"></property></bean>

配置注解支持

<tx:annotation-driventransaction-manager="txManager"/>

方法上加入事務(wù)注解

Service 方法上在需要添加事務(wù)的方法上加入事務(wù)注解

@Override@Transactional(propagation=Propagation.REQUIRED)publicvoidsaveUser(String userName,String userPwd){User user1=newUser();user1.setUserName(userName);user1.setUserPwd(userPwd);userDao.saveUser(user1);userDao.delUserById(2);}

備注:默認(rèn) spring 事務(wù)只在發(fā)生未被捕獲的 runtimeexcetpion 時(shí)才回滾。

spring aop 異常捕獲原理:

被攔截的方法需顯式拋出異常,并不能經(jīng)任何處理,這樣aop 代理才能捕獲到方法的異常,才能進(jìn)行回滾,默認(rèn)情況下 aop 只捕獲 runtimeexception 的異常,但可以通過配置來捕獲特定的異常并回滾換句話說在 service 的方法中不使用 try catch 或者在 catch 中最后加上 throw new RunTimeexcetpion(),這樣程序異常時(shí)才能被 aop 捕獲進(jìn)而回滾.

ropagation.MANDATORY 相反)

@Transactional(propagation=Propagation.SUPPORTS)

如果其他 bean 調(diào)用這個(gè)方法,在其他 bean 中聲明事務(wù),那就用事務(wù).

如果其他 bean 沒有聲明事務(wù),那就不用事務(wù).

@Transactional(propagation=Propagation.NESTED)

支持當(dāng)前事務(wù),如果當(dāng)前事務(wù)存在,則執(zhí)行一個(gè)嵌套事務(wù),如果當(dāng)前沒有事務(wù),就新建一個(gè)事務(wù)。

##### 配置aop

```xml

<!-- aop 切面定義 (切入點(diǎn)和通知) -->

<aop:config>

? ? <!-- 設(shè)置切入點(diǎn) 設(shè)置需要被攔截的方法 -->

<aop:pointcut expression="execution(* com.xxxx.service..*.*(..) )" id="cut" />

? ? <!-- 設(shè)置通知 事務(wù)通知 -->

? ? <aop:advisor advice-ref="txAdvice" pointcut-ref="cut"/>

<aop:adviso

注解配置

配置事務(wù)管理器

<!-- spring 注解式事務(wù)聲明 --><!-- 事務(wù)管理器定義 --><beanid="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="dataSource"></property></bean>

配置注解支持

<tx:annotation-driventransaction-manager="txManager"/>

方法上加入事務(wù)注解

Service 方法上在需要添加事務(wù)的方法上加入事務(wù)注解

@Override@Transactional(propagation=Propagation.REQUIRED)publicvoidsaveUser(String userName,String userPwd){User user1=newUser();user1.setUserName(userName);user1.setUserPwd(userPwd);userDao.saveUser(user1);userDao.delUserById(2);}

備注:默認(rèn) spring 事務(wù)只在發(fā)生未被捕獲的 runtimeexcetpion 時(shí)才回滾。

spring aop 異常捕獲原理:

被攔截的方法需顯式拋出異常,并不能經(jīng)任何處理,這樣aop 代理才能捕獲到方法的異常,才能進(jìn)行回滾,默認(rèn)情況下 aop 只捕獲 runtimeexception 的異常,但可以通過配置來捕獲特定的異常并回滾換句話說在 service 的方法中不使用 try catch 或者在 catch 中最后加上 throw new RunTimeexcetpion(),這樣程序異常時(shí)才能被 aop 捕獲進(jìn)而回滾.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容