SSM保姆級從創建項目到使用,包括事務和設置回滾
- 2022 年 9 月 15 日
- 筆記
- SSM手把手教你操作Spring和Mybaits整合
1. 簡介
Spring 和 Mybaits整合
2. 創建項目
負責將代理類記性掃描,掃描的是Mapper介面所在的包,這個是mybatis提供的,所以會去找SqlSessionFactory
2.1 mybaits和Spring整合的jar包
mybaits和 Spring整合的官網://mybatis.org/spring/zh/index.html
2.1.1 思路:
2.1.2 MyBatis-Spring
這個jar包是mybaits提供的。
2.2 mybatis和spring整合所需要的jar包
要和 Spring 一起使用 MyBatis,需要在 Spring 應用上下文中定義至少兩樣東西:一個 SqlSessionFactory 和至少一個數據映射器類。
在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean來創建 SqlSessionFactory。 要配置這個工廠 bean,只需要把下面程式碼放在 Spring 的 XML 配置文件中:
<bean id=”sqlSessionFactory” class=”org.mybatis.spring.SqlSessionFactoryBean”>
<property name=”dataSource” ref=”dataSource” />
</bean>
還需要 Spring 對ORM框架支援的jar包
<!– Spring orm Spring提供spring-orm提供orm框架相關的支援。支援Hibernate、iBatis和JPA等 –> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.2.20.RELEASE</version> </dependency> |
2.3 spring和mybaits整合,完整的pom文件
<project xmlns=“//maven.apache.org/POM/4.0.0” xmlns:xsi=“//www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=“//maven.apache.org/POM/4.0.0 //maven.apache.org/xsd/maven-4.0.0.xsd”> <modelVersion>4.0.0</modelVersion> <groupId>com.hy</groupId> <artifactId>ssm01</artifactId> <version>0.0.1</version> <packaging>war</packaging>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> </properties>
<dependencies> <!– Spring core –> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!– Spring orm Spring提供spring-orm提供orm框架相關的支援。支援Hibernate、iBatis和JPA等 –> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!– spring-aspects會幫我們傳遞過來aspectjweaver –> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!– mybaits相關jar包 –> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency>
<!– mybaits和Spring整合包 –> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.7</version> </dependency>
<!– //mvnrepository.com/artifact/org.projectlombok/lombok –> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency>
<!– 資料庫連接池 –> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.9</version> </dependency>
<!– 連接資料庫驅動 –> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>
<!– Spring-Test –> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!– Junit測試 –> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
<!– logback日誌 –> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
<!– Mybatis EHCache整合包 –> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.1</version> </dependency> </dependencies>
<build> <plugins> <!– 指定jdk,防止update project –> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <!– 項目編碼 –> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project> |
2.4 創建jdbc.properties 和 logback.xml日誌文件
<?xml version=“1.0” encoding=“UTF-8”?> <configuration debug=“true”> <!– 指定日誌輸出的位置 –> <appender name=“STDOUT” class=“ch.qos.logback.core.ConsoleAppender”> <encoder> <!– 日誌輸出的格式 –> <!– 按照順序分別是:時間、日誌級別、執行緒名稱、列印日誌的類、日誌主體內容、換行 –> <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern> </encoder> </appender>
<!– 設置全局日誌級別。日誌級別按順序分別是:TRACE > DEBUG > INFO > WARN > ERROR > FATAL –> <!– 指定任何一個日誌級別都只列印當前級別和後面級別的日誌。 –> <root level=“INFO”> <!– 指定列印日誌的appender,這裡通過「STDOUT」引用了前面配置的appender –> <appender-ref ref=“STDOUT” /> </root>
<!– 根據特殊需求指定局部日誌級別 –> <logger name=“com.hy.mapper” level=“DEBUG” /> <logger name=“com.hy.test” level=“DEBUG” /> </configuration> |
2.5 首先建立spring和mybaits的配置的文件 spring-mybatis.xml
2.5.1 載入外部屬性文件
2.5.2 配置數據源
<?xml version=“1.0” encoding=“UTF-8”?> <beans xmlns=“//www.springframework.org/schema/beans” xmlns:xsi=“//www.w3.org/2001/XMLSchema-instance” xmlns:context=“//www.springframework.org/schema/context” xsi:schemaLocation=“//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/context //www.springframework.org/schema/context/spring-context-4.3.xsd”>
<!– 載入外部屬性文件 –> <context:property-placeholder location=“classpath:jdbc.properties”/>
<!– 配置數據源 –> <bean id=“druidDataSource” class=“com.alibaba.druid.pool.DruidDataSource”> <property name=“driverClassName” value=“${jdbc.dev.driver}”/> <property name=“url” value=“${jdbc.dev.url}”/> <property name=“username” value=“${jdbc.dev.username}”/> <property name=“password” value=“${jdbc.dev.password}”/> </bean> </beans> |
2.5.3 測試是否能連接上資料庫
package com.hy.test;
import java.sql.Connection; import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(“classpath:spring-mybatis.xml”) public class Test01 { @Autowired private DataSource dataSource;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test public void testConnection() throws SQLException { Connection connection = dataSource.getConnection();
logger.debug(connection.toString()); } }
|
3. 配置SqlSessionFactoryBean
SqlSessionFactoryBean 是由MyBatis提供的package org.mybatis.spring;
SqlSessionFactoryBean 實現了 FactoryBean這個介面,
這個介面是由Spring提供的。
會調用 getObject方法得到一個 對象,這個對象是
類型的對象。
3.1 配置方式[風格1]
1)風格1:保留Mybaits全局配置文件(核心配置文件mybatis-config.xml)
3.1.1 創建Mybatis全局配置文件
<?xml version=“1.0” encoding=“UTF-8” ?> <!DOCTYPE configuration PUBLIC “-//mybatis.org//DTD Config 3.0//EN” “//mybatis.org/dtd/mybatis-3-config.dtd”> <configuration> <settings> <!– 資料庫中的欄位的_規則,轉類中屬性的駝峰標示寫法 –> <setting name=“mapUnderscoreToCamelCase” value=“true”/> </settings> </configuration> |
3.1.2 配置SqlSessionFactoryBean
<!– 配置SqlSessionFactoryBean 創建的是 sqlSessionFactory,但是通過sqlSessionFactory 工廠類的對象給你 SqlSession對象–> <bean id=“sqlSessionFactoryBean” class=“org.mybatis.spring.SqlSessionFactoryBean”> <!– 裝配數據源 –> <property name=“dataSource” ref=“druidDataSource”/>
<!– 指定Mapper 映射文件的位置 –> <property name=“mapperLocations” value=“classpath:mappers/*Mapper.xml”/>
<!– 指定 MyBatis 全局配置文件位置 –> <property name=“configLocation” value=“classpath:mybatis-config.xml”/> </bean> |
3.1.3 配置Mapper介面類型的bean掃描器MapperScannerConfiguration
配置 Mapper介面類型的bean掃描器
<!– 配置 Mapper介面類型的bean掃描器 –> <bean id=“mapperScannerConfigurer” class=“org.mybatis.spring.mapper.MapperScannerConfigurer”> <property name=“basePackage” value=“com.hy.mapper”/> </bean> |
3.1.4 創建com.hy.bean.Emp & EmpMapper介面 & EmpMapper.xml映射文件
package com.hy.bean;
import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString;
@NoArgsConstructor @AllArgsConstructor @Setter @Getter @ToString public class Emp { private Long empId; //用包裝類有null值 private String empName; private String empPwd; private String empGender; private Double empSalary; //用包裝類有null值
//構造方法(去ID的) public Emp(String empName, String empPwd, String empGender, Double empSalary) { super(); this.empName = empName; this.empPwd = empPwd; this.empGender = empGender; this.empSalary = empSalary; } } |
package com.hy.mapper;
import java.util.List; import java.util.Map;
import com.hy.bean.Emp;
public interface EmpMapper { abstract public Emp selectById(long empId);
abstract public int insert(Emp emp);
abstract public int deleteById(long empId);
abstract public int update(Emp emp);
abstract public int updateByMap(Map<String, Object> paramMap);
abstract public Integer selectCount();
abstract public Map<String, Object> selectForMap(int empId);
abstract public List<Emp> selectAll();
abstract public int insertWithKey(Emp emp); } |
<?xml version=“1.0” encoding=“UTF-8” ?> <!DOCTYPE mapper PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN” “//mybatis.org/dtd/mybatis-3-mapper.dtd”>
<mapper namespace=“com.hy.mapper.EmpMapper”> <select id=“selectById” resultType=“com.hy.bean.Emp”> select emp_id empId,emp_name empName,emp_pwd empPwd,emp_gender empGender , emp_salary empSalary from sys_emp where emp_id = #{empId} </select> </mapper> |
3.1.5 測試
package com.hy.test;
import java.sql.SQLException;
import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.hy.bean.Emp; import com.hy.mapper.EmpMapper;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(“classpath:spring-mybatis.xml”) public class Test02 { @Autowired private EmpMapper empMapper;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test public void testEmpMapper () throws SQLException { Emp emp = empMapper.selectById(1);
logger.debug(emp.toString()); } } |
3.1 配置方式[風格2]
2)風格2:徹底捨棄Mybaits全局配置文件(核心配置文件mybatis-config.xml),所有的一切在spring的配置文件中配。
<!– 配置SqlSessionFactoryBean –> <bean id=“sqlSessionFactoryBean” class=“org.mybatis.spring.SqlSessionFactoryBean”> <!– 指定 MyBatis 全局配置文件位置 –> <!– <property name=”configLocation” value=”classpath:mybatis-config.xml”/> –>
<!– 捨棄mybatis–config全局配置文件,使用configuration屬性 –> <property name=“configuration”> <bean class=“org.apache.ibatis.session.Configuration”> <property name=“mapUnderscoreToCamelCase” value=“true”/> </bean> </property>
<!– 捨棄mybatis–config全局配置文件,使用typeAliasesPackage屬性,配置實體bean的別名 –> <property name=“typeAliasesPackage” value=“com.hy.bean”/>
<!– 指定Mapper 映射文件的位置 –> <property name=“mapperLocations” value=“classpath:mappers/*Mapper.xml”/>
<!– 裝配數據源 –> <property name=“dataSource” ref=“druidDataSource”/> </bean> |
3.2 注意,掃描包的時候分開掃描
spring-mybaits.xml只掃描Service,
而Mapper是用mybatis自帶的掃描器MapperScannerConfigurer掃描
3.3 spring-mybatis.xml完整的配置
<?xml version=“1.0” encoding=“UTF-8”?> <beans xmlns=“//www.springframework.org/schema/beans” xmlns:xsi=“//www.w3.org/2001/XMLSchema-instance” xmlns:context=“//www.springframework.org/schema/context” xmlns:tx=“//www.springframework.org/schema/tx” xsi:schemaLocation=“//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/context //www.springframework.org/schema/context/spring-context-4.3.xsd //www.springframework.org/schema/tx //www.springframework.org/schema/tx/spring-tx-4.3.xsd”>
<!– 在spring-mybatis中 掃描@Servie註解標識的組件 –> <context:component-scan base-package=“com.hy.service”/>
<!– 載入外部屬性文件 –> <context:property-placeholder location=“classpath:jdbc.properties”/>
<!– 配置數據源 –> <bean id=“druidDataSource” class=“com.alibaba.druid.pool.DruidDataSource”> <property name=“driverClassName” value=“${jdbc.dev.driver}”/> <property name=“url” value=“${jdbc.dev.url}”/> <property name=“username” value=“${jdbc.dev.username}”/> <property name=“password” value=“${jdbc.dev.password}”/> </bean>
<!– 配置SqlSessionFactoryBean –> <bean id=“sqlSessionFactoryBean” class=“org.mybatis.spring.SqlSessionFactoryBean”> <!– 指定 MyBatis 全局配置文件位置 –> <property name=“configLocation” value=“classpath:mybatis-config.xml”/>
<!– 捨棄mybatis–config全局配置文件,使用configuration屬性 <property name=”configuration”> <bean class=”org.apache.ibatis.session.Configuration”> <property name=”mapUnderscoreToCamelCase” value=”true”/> </bean> </property> –> <!– 捨棄mybatis–config全局配置文件,使用typeAliasesPackage屬性,配置實體bean的別名 <property name=”typeAliasesPackage” value=”com.hy.bean”/> –>
<!– 指定Mapper 映射文件的位置 –> <property name=“mapperLocations” value=“classpath:mappers/*Mapper.xml”/>
<!– 裝配數據源 –> <property name=“dataSource” ref=“druidDataSource”/> </bean>
<!– 配置 Mapper介面類型的bean掃描器 –> <bean id=“mapperScannerConfigurer” class=“org.mybatis.spring.mapper.MapperScannerConfigurer”> <property name=“basePackage” value=“com.hy.mapper”/> </bean> </beans> |
3.4 mybatis-config.xml
<?xml version=“1.0” encoding=“UTF-8” ?> <!DOCTYPE configuration PUBLIC “-//mybatis.org//DTD Config 3.0//EN” “//mybatis.org/dtd/mybatis-3-config.dtd”> <configuration> <settings> <!– 資料庫中的欄位的_規則,轉類中屬性的駝峰標示寫法 –> <setting name=“mapUnderscoreToCamelCase” value=“true”/> </settings> </configuration> |
4. 編程式事務
事務功能的相關操作全部通過自己編寫程式碼來實現:
Connection conn = …;
try { // 開啟事務:關閉事務的自動提交 conn.setAutoCommit(false);
// 核心操作
// 提交事務 conn.commit(); }catch(Exception e){ // 回滾事務 conn.rollBack(); }finally{ // 釋放資料庫連接 conn.close(); } |
編程式的實現方式存在缺陷:
1)具體操作過程中,所有細節都需要程式設計師自己來完成,比較繁瑣。
2)程式碼復用性不高:如果沒有有效抽取出來,每次實現功能都需要自己編寫程式碼,程式碼就沒有得到復用。
5. 聲明式事務
既然事務控制的程式碼有規律可循,程式碼的結構基本是確定的,所以框架就可以將固定模式的程式碼抽取出來,進行相關的封裝。
封裝起來後,我們只需要在配置文件中進行簡單的配置即可完成操作。
好處1:提高開發效率
好處2:消除了冗餘的程式碼
好處3:框架會綜合考慮相關領域中在實際開發環境下有可能遇到的各種問題,進行了健壯性、性能等各個方面的優化
編程式事務:自己寫程式碼實現功能
聲明式事務:通過配置讓框架實現功能
5.1事務管理器
5.1.1 Spring 5.2以前
5.1.2 從 Spring 5.2開始
PlatformTransactionManager 介面本身沒有變化,它繼承了 TransactionManager。TransactionManager介面中什麼都沒有,它的存在的意義是定義一個技術體系。
我們現在要使用的事務管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager,將來整合 Mybatis 用的也是這個類。
5.2 DataSourceTransactionManager類中的主要方法:
doBegin():開啟事務
doCommit():提交事務
doRollback():回滾事務
doSuspend():掛起事務
doResume():恢復掛起的事務
6. 添加Service層
6.1 EmpService介面 & EmpServiceImpl實現類
package com.hy.service;
import com.hy.bean.Emp; public interface EmpService { abstract public Emp listById(long empId); } |
@Service public class EmpServiceImpl implements EmpService{ @Autowired private EmpMapper empMapper;
public Emp listById(long empId) { Emp emp = empMapper.selectById(empId); return emp; } } |
6.2 掃描@Service 組件
6.3 測試
package com.hy.test;
import java.sql.SQLException;
import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.hy.bean.Emp; import com.hy.service.EmpService;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(“classpath:spring-mybatis.xml”) public class Test03 { @Autowired private EmpService empService; private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test public void testEmpService() throws SQLException { Emp emp = empService.listById(1);
logger.debug(emp.toString()); } } |
6.4 EmpMapper添加兩個更新方法 & EmpMapper.xml
package com.hy.mapper;
import java.util.List; import java.util.Map;
import org.apache.ibatis.annotations.Param;
import com.hy.bean.Emp;
public interface EmpMapper { abstract public void updateEmpNameById(@Param(“empId”) long empId, @Param(“empName”)String empName);
abstract public void updateEmpSalaryById(@Param(“empId”) long empId, @Param(“empSalary”)Double empSalary);
abstract public Emp selectById(long empId); } |
<?xml version=“1.0” encoding=“UTF-8” ?> <!DOCTYPE mapper PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN” “//mybatis.org/dtd/mybatis-3-mapper.dtd”>
<mapper namespace=“com.hy.mapper.EmpMapper”> <select id=“selectById” resultType=“com.hy.bean.Emp”> select emp_id empId,emp_name empName,emp_pwd empPwd,emp_gender empGender , emp_salary empSalary from sys_emp where emp_id = #{empId} </select>
<!– abstract public void updateEmpNameById(@Param(“empId”)long empId, @Param(“empName”)String empName); abstract public void updateEmpSalaryById(@Param(“empId”)long empId, @Param(“empSalary”)Double empsalary); –> <update id=“updateEmpNameById”> update sys_emp set emp_name = #{empName} where emp_id = #{empId} </update>
<update id=“updateEmpSalaryById”> update sys_emp set emp_salary = #{empSalary} where emp_id = #{empId} </update> </mapper>
|
6.5 EmpService介面 & EmpServiceImpl
package com.hy.service; import com.hy.bean.Emp; public interface EmpService { abstract public Emp listById(long empId);
abstract public int eidtEmp(Emp emp); } |
在editEmp方法中我們會根據empId,兩次修改sys_emp表列的值
package com.hy.service.impl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
import com.hy.bean.Emp; import com.hy.mapper.EmpMapper; import com.hy.service.EmpService;
@Service public class EmpServiceImpl implements EmpService{ @Autowired private EmpMapper empMapper;
public Emp listById(long empId) { Emp emp = empMapper.selectById(empId); return emp; }
@Override public int eidtEmp(Emp emp) { empMapper.updateEmpNameById(emp.getEmpId(), emp.getEmpName()); empMapper.updateEmpSalaryById(emp.getEmpId(), emp.getEmpSalary()); return 0; } }
|
6.6 測試類,測試不使用事務的情況
package com.hy.test;
import java.sql.SQLException;
import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.hy.bean.Emp; import com.hy.service.EmpService;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(“classpath:spring-mybatis.xml”) public class Test03 { @Autowired private EmpService empService;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test public void testEmpService() throws SQLException { Emp emp = empService.listById(1);
logger.debug(emp.toString()); }
@Test public void testEmpService2() throws SQLException { Emp emp = new Emp(1L, “范冰冰plus3″, “fbbplus”, “f”, 1315d);
empService.eidtEmp(emp); } } |
出現異常的情況
7. 事務
7.1 編程式事務
事務功能的相關操作全部通過自己編寫程式碼來實現:
Connection conn = …;
try { // 開啟事務:關閉事務的自動提交 conn.setAutoCommit(false);
// 核心操作
// 提交事務 conn.commit(); }catch(Exception e){ // 回滾事務 conn.rollBack(); }finally{ // 釋放資料庫連接 conn.close(); } |
編程式的實現方式存在缺陷:
1)具體操作過程中,所有細節都需要程式設計師自己來完成,比較繁瑣。
2) 程式碼復用性不高:如果沒有有效抽取出來,每次實現功能都需要自己編寫程式碼,程式碼就沒有得到復用。
7.2 聲明式事務
既然事務控制的程式碼有規律可循,程式碼的結構基本是確定的,所以框架就可以將固定模式的程式碼抽取出來,進行相關的封裝。
封裝起來後,我們只需要在配置文件中進行簡單的配置即可完成操作。
好處1:提高開發效率
好處2:消除了冗餘的程式碼
好處3:框架會綜合考慮相關領域中在實際開發環境下有可能遇到的各種問題,進行了健壯性、性能等各個方面的優化
編程式事務:自己寫程式碼實現功能
聲明式事務:通過配置讓框架實現功能
7.3 事務管理器
7.3.1 Spring 5.2以前
7.3.2 從 Spring 5.2開始
PlatformTransactionManager 介面本身沒有變化,它繼承了 TransactionManager。TransactionManager介面中什麼都沒有,它的存在的意義是定義一個技術體系。
我們現在要使用的事務管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager,將來整合 Mybatis 用的也是這個類。
7.4 DataSourceTransactionManager類中的主要方法:
doBegin():開啟事務
doCommit():提交事務
doRollback():回滾事務
doSuspend():掛起事務
doResume():恢復掛起的事務
事務的掛起和恢復,主要是事務傳播行為所體現的。
8 基於註解的聲明式事務
事務通常都是加到業務邏輯層,針對XxxService類使用事務
8.1 配置聲明式事務,需要添加新的依賴
<!– Spring orm Spring提供spring-orm提供orm框架相關的支援。支援Hibernate、iBatis和JPA等 –> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.2.20.RELEASE</version> </dependency>
|
Spring 持久化層支援的jar包,Spring在執行持久化操作與持久化技術進行整合過程中,需要使用orm,tx,jdbc三個jar包
導入orm包就可以通過maven的依賴傳遞把其他兩個也導入進來。
8.2 給事務管理器裝配一下數據源&。
<!– 配置事務管理器 –> <bean id=“transactionManager” class=“org.springframework.jdbc.datasource.DataSourceTransactionManager”> <!– 給事務管理器bean裝配數據源,其他屬性保持默認即可 –> <property name=“dataSource” ref=“druidDataSource”/> </bean> |
8.3 開啟基於註解的聲明式事務
<!– 開啟基於註解的聲明式事務 –> <tx:annotation-driven transaction-manager=“transactionManager”/> |
8.4 給EmpServiceImpl方法上加上註解@Transactional
給XxxServiceImpl類的方法中加上 @Transactional 註解,Spring會自動的給這個方法加上事務。
8.6 完整的配置spring-mybaits.xml
<?xml version=“1.0” encoding=“UTF-8”?> <beans xmlns=“//www.springframework.org/schema/beans” xmlns:xsi=“//www.w3.org/2001/XMLSchema-instance” xmlns:context=“//www.springframework.org/schema/context” xmlns:tx=“//www.springframework.org/schema/tx” xsi:schemaLocation=“//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/context //www.springframework.org/schema/context/spring-context-4.3.xsd //www.springframework.org/schema/tx //www.springframework.org/schema/tx/spring-tx-4.3.xsd”>
<!– 在spring-mybatis中 掃描@Servie註解標識的組件 –> <context:component-scan base-package=“com.hy.service”/>
<!– 載入外部屬性文件 –> <context:property-placeholder location=“classpath:jdbc.properties”/>
<!– 配置數據源 –> <bean id=“druidDataSource” class=“com.alibaba.druid.pool.DruidDataSource”> <property name=“driverClassName” value=“${jdbc.dev.driver}”/> <property name=“url” value=“${jdbc.dev.url}”/> <property name=“username” value=“${jdbc.dev.username}”/> <property name=“password” value=“${jdbc.dev.password}”/> </bean>
<!– 配置SqlSessionFactoryBean –> <bean id=“sqlSessionFactoryBean” class=“org.mybatis.spring.SqlSessionFactoryBean”> <!– 指定 MyBatis 全局配置文件位置 –> <property name=“configLocation” value=“classpath:mybatis-config.xml”/>
<!– 捨棄mybatis–config全局配置文件,使用configuration屬性 <property name=”configuration”> <bean class=”org.apache.ibatis.session.Configuration”> <property name=”mapUnderscoreToCamelCase” value=”true”/> </bean> </property> –> <!– 捨棄mybatis–config全局配置文件,使用typeAliasesPackage屬性,配置實體bean的別名 <property name=”typeAliasesPackage” value=”com.hy.bean”/> –>
<!– 指定Mapper 映射文件的位置 –> <property name=“mapperLocations” value=“classpath:mappers/*Mapper.xml”/>
<!– 裝配數據源 –> <property name=“dataSource” ref=“druidDataSource”/> </bean>
<!– 配置 Mapper介面類型的bean掃描器 –> <bean id=“mapperScannerConfigurer” class=“org.mybatis.spring.mapper.MapperScannerConfigurer”> <property name=“basePackage” value=“com.hy.mapper”/> </bean>
<!– 配置事務管理器 –> <bean id=“transactionManager” class=“org.springframework.jdbc.datasource.DataSourceTransactionManager”> <!– 裝配數據源 –> <property name=“dataSource” ref=“druidDataSource”/> </bean>
<!– 開啟基於註解的聲明式事務 –> <tx:annotation-driven transaction-manager=“transactionManager”/> </beans> |
8.6 測試
8.6.1 配置日誌觀察效果
<!– 根據特殊需求指定局部日誌級別 –> <logger name=“com.hy.mapper” level=“DEBUG” /> <logger name=“com.hy.test” level=“DEBUG” /> <logger name=“org.springframework.jdbc.datasource.DataSourceTransactionManager” level=“DEBUG” /> |
8.6.2 通過日誌觀察事務
8.6.3 通過debug源碼的模式觀察事務
1)開啟事務的方法:doBegin
2)提交事務的方法
3)回滾事務的方法
8.7 查詢開啟事務
8.7.1 創建一個EmpService & EmpServiceImpl
package com.hy.service;
public interface EmpService { abstract public Emp listById(long empId); } |
package com.hy.service.impl;
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;
import com.hy.mapper.EmpMapper; import com.hy.service.EmpService;
@Service public class EmpServiceImpl implements EmpService { @Autowired private EmpMapper empMapper;
@Transactional @Override public Emp listById(long empId) { Emp emp = empMapper.selectById(empId); return emp; } } |
獲取資料庫鏈接
切換資料庫鏈接為手動提交
提交事務
釋放鏈接
[org.springframework.jdbc.datasource.DataSourceTransactionManager] [Creating new transaction with name [com.hy.service.impl.EmpServiceImpl.listById]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly]
這裡為了現實效果,所以為查詢配置了事務。
9 事務的屬性,這些屬性可以設置但不是必須設置
1) 事務的只讀屬性
2) 事務的超時屬性
3) 回滾和不回滾的異常
4) 事務的隔離級別
5) 事務傳播行為
9.1 事務屬性:只讀
對一個查詢操作來說,如果我們把它設置成只讀,就能明確告訴資料庫,這個操作不涉及寫(添加,刪除,修改)操作,這樣資料庫就能夠針對查詢操作來進行優化。
readOnly並不是所有資料庫都支援。
9.1.1 設置方式
@Transaction(readOnly = true) //readOnly = true把當前事務屬性設置為只讀,默認為false
9.1.2 針對增刪改操作設置只讀屬性
加了只讀註解後,會有哪些影響呢?
比如做報表或者做統計:
只讀事務的好處,作為ORM框架優化的暗號,保證讀一致性,事務內不允許DML操作,否則報錯 只讀事務的場景,如統計,保證統計結果準確性。 只讀事務里,也可以在只讀事務里使用 select… for update 因為只讀事務,所有查詢都是在一個事務里,所以可以配合mysql的事務隔離級別理解一下 (比如,你的mysql隔離事務是RR的,那麼在只讀事務註解里,多次查詢同一個表的範圍數據, 結果是一致的,如果不是在同一個事務里,那麼前後查詢可能會讀到的數據條數不一致,造成幻讀),如果隔離級別是RC的,可以不用在只讀事務里,因為每次查詢都會讀取到已提交的數據 |
10.@Transactional註解放在類上
10.1 生效原則
如果一個類中每一個方法上都使用了@Transactional註解,那麼就可以將@Transactional註解提取到類上,反過來說:@Transactional註解在類級別上標記,會影響到類中的每一個方法。同時,類級別標記的@Transactional註解中設置的事務屬性也會延續影響到方法執行時的事務屬性。除非在方法上由設置了@Transactional註解。
對一個方法來說,離它最近的@Transactional註解中的事務屬性設置生效。
10.2 用法舉例
在類級別@Transactional註解中設置只讀,這樣類中所有的查詢方法都不需要設置@Transactional註解了,因為對查詢操作來說,其他屬性通常不需要設置,所以使用公共設置即可。
然後在這個基礎上,對增刪改方法設置@Transactional註解readOnly屬性為false。
11.事務的屬性:超時
事務在執行過程中,有可能因為遇到某些問題,導致程式卡主,從而長時間佔用資料庫資源,大概的原因可能是因為程式運行出現了問題(Java或是MySQL)或是網路出現問題。
此時,這個很可能出問題的程式應該被執行回滾操作,撤銷它已做的操作,事務回滾,把資源讓出來,讓其他正常程式可以執行。
總計:超時回滾,釋放資源。別讓一個事務佔用一個資源太長的時間。
單位是秒。
12. 事務屬性:回滾和不回顧你的異常
默認情況:只針對運行時異常進行事務回滾,編譯時異常不回滾。
//拋出編譯時異常,測試是否回滾
new FileInputStream(「xxxxx」); 方法後面throws FileNotFoundException
將回滾的異常擴大到Exception的範圍。
12.1 設置回滾的異常
12.2 設置不回滾的異常
12.3 回滾和不回滾異常同時設置
13. 事務屬性:事務的隔離級別:
事務的隔離級別和事務的傳播行為,都是指事務和事務之間的關係。 之前說的事務的屬性,超時,回滾,只讀都是事務考慮一個事務內部之前是事情。