spring 事務
一、事務簡介
1、事務作用:在數據層保障一系列的數據庫操作同步成功同步失敗
2、Spring事務作用:在數據層或業務層保障一系列的數據庫操作同成功同失敗,其是使用JDBC的事務管理器 實現的,如果數據層使用的是JDBC,則可以使用Spring事務
其是通過內部接口和實現類實現的
// 接口 public interface PlatformTransactionManager { void begin(); void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; } // 實現類 public class DataSourceTransactionManager { public void begin() { System.out.println("開始事務"); } public void commit() { System.out.println("提交事務"); } public void rollback() { System.out.println("回滾事務"); } }
3、注意事項:
* Spring注解式事務通常添加在業務層接口中而不會添加到業務層實現類中,降低耦合
* 注解式事務可以添加到業務方法上表示當前方法開啟事務,也可以添加到接口上表示當前接口所有方法開啟事務
二、快速開始Spring事務
1、注入相關依賴
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.29</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.29</version>
</dependency>
</dependencies>
2、設置相關配置
創建config包,用于存儲配置相關文件。
1) 配置Spring,創建SpringConfig類文件
package com.itheima.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.annotation.EnableTransactionManagement; // 告知這是個配置類 @Configuration // 告知掃描哪些包 @ComponentScan("com.itheima") // 告知加載哪個配置文件 @PropertySource("classpath:jdbc.properties") // 告知加載哪些配置文件 @Import({JdbcConfig.class,MybatisConfig.class}) //開啟注解式事務驅動 @EnableTransactionManagement public class SpringConfig { }
2)配置JDBC,創建JdbcConfig文件
package com.itheima.config; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; @Configuration public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String userName; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } //配置事務管理器,mybatis使用的是jdbc事務 @Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } }
3)配置jdbc數據源,在resources下面創建jdbc.properties文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/test?useSSL=false jdbc.username=root jdbc.password=123456
4)配置Mybatis,在config文件夾下創建MybatisConfig
package com.itheima.config; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.context.annotation.Bean; import javax.sql.DataSource; public class MybatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){ // 獲取SqlSessionFactoryBean對象 SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); // 別名包 ssfb.setTypeAliasesPackage("com.itheima.domain"); // 數據源 ssfb.setDataSource(dataSource); return ssfb; } @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.itheima.dao"); return msc; } }
3、創建實體類
在domain文件夾下創建Account類文件
package com.itheima.domain; import java.io.Serializable; // 創建實體類 public class Account implements Serializable { private Integer id; private String name; private Double money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }
4、創建數據dao層
在dao文件夾下創建AccountDao接口文件
package com.itheima.dao; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Update; public interface AccountDao { @Update("update tbl_account set money = money + #{money} where name = #{name}") void inMoney(@Param("name") String name, @Param("money") Double money); @Update("update tbl_account set money = money - #{money} where name = #{name}") void outMoney(@Param("name") String name, @Param("money") Double money); }
5、創建service
1)創建service接口文件
在service文件夾下創建AccountService接口文件
package com.itheima.service; import org.springframework.transaction.annotation.Transactional; import java.io.FileNotFoundException; import java.io.IOException; public interface AccountService { /** * 轉賬操作 * @param out 傳出方 * @param in 轉入方 * @param money 金額 */ //配置當前接口方法具有事務 @Transactional public void transfer(String out,String in ,Double money) ; }
2)創建service接口實現類文件
在service文件夾下創建impl文件夾,并在其下面創建AccountServiceImpl實現類文件
package com.itheima.service.impl; import com.itheima.dao.AccountDao; import com.itheima.service.AccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.*; @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; public void transfer(String out,String in ,Double money) { accountDao.outMoney(out,money); // int i = 1/0; accountDao.inMoney(in,money); } }
6、創建測試文件,進行測試
在test文件夾下創建測試文件
package com.itheima.service; import com.itheima.config.SpringConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.io.IOException; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testTransfer() throws IOException { accountService.transfer("Tom","Jerry",100D); } }
7、最終文件目錄

三、為什么開啟spring事務

如上圖所示,第二版塊的示例子中執行了兩條sql語句,每條語句都是單獨的數據庫層面的事務(T1,T2),但是如果是業務層中發生了報錯,則不會自動回滾,所以,需要在Spring中建立事務(T)。通過Spring中的事務(T1)進行管理數據庫的事務(T1、T2)。
事務角色:
* 事務管理員:發起事務方,在Spring中通常指代業務層開啟事務的方法,如本文例子中的(事務T)
* 事務協調員:加入事務方,在Spring中通常指代數據層方法(如:T1、T2),也可以是業務層方法
四、事務相關配置

1、事務的傳播行為
為了在一個service方法里存在多個事務管理,而不是,所有的數據庫操作都必須是全部成功或全部失敗。比如,上面將的存錢例子,不論改變賬戶金額是否成功都進行日志記錄。
1)添加日志記錄的dao文件
package com.itheima.dao; import org.apache.ibatis.annotations.Insert; public interface LogDao { @Insert("insert into tbl_log (info,createDate) values(#{info},now())") void log(String info); }
2)添加日志記錄的service接口類,并設置事務傳播
package com.itheima.service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public interface LogService { //propagation設置事務屬性:傳播行為設置為當前操作需要新事務 @Transactional(propagation = Propagation.REQUIRES_NEW) void log(String out, String in, Double money); }
3)實現日志記錄接口的實現類
package com.itheima.service.impl; import com.itheima.dao.LogDao; import com.itheima.service.LogService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class LogServiceImpl implements LogService { @Autowired private LogDao logDao; public void log(String out,String in,Double money ) { logDao.log("轉賬操作由"+out+"到"+in+",金額:"+money); } }
4)修改轉賬service,修改AccountServiceImpl文件
package com.itheima.service.impl; import com.itheima.dao.AccountDao; import com.itheima.service.AccountService; import com.itheima.service.LogService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.*; @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Autowired private LogService logService; // 使用try{}finally{}結構,為了保證日志記錄一定會執行 public void transfer(String out,String in ,Double money) { try{ accountDao.outMoney(out,money); int i = 1/0; accountDao.inMoney(in,money); }finally { logService.log(out,in,money); } } }
2、事務傳播行為包含:


浙公網安備 33010602011771號