SSM保姆级从创建项目到使用,包括事务和设置回滚
1. 简介
Spring 和 Mybaits整合
2. 创建项目
负责将代理类记性扫描,扫描的是Mapper接口所在的包,这个是mybatis提供的,所以会去找SqlSessionFactory
2.1 mybaits和Spring整合的jar包
mybaits和 Spring整合的官网:http://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="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://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> <!-- https://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="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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://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" "http://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" "http://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="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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://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" "http://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" "http://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="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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://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. 事务属性:事务的隔离级别:
事务的隔离级别和事务的传播行为,都是指事务和事务之间的关系。 之前说的事务的属性,超时,回滚,只读都是事务考虑一个事务内部之前是事情。
SSM保姆级从创建项目到使用,包括事务和设置回滚的更多相关文章
- GitHub Desktop的使用,创建项目、上传文件,设置忽略文件
下载登陆之后 新建项目File--第一个New repository 然后输入项目名称,选择项目文件夹,最后点Creata repository创建项目 这只是在本地建了项目. 项目文件夹中有其他文件 ...
- 【Java EE 学习 54】【OA项目第一天】【SSH事务管理不能回滚问题解决】【struts2流程回顾】
一.SSH整合之后事务问题和总结 1.引入问题:DAO层测试 假设将User对象设置为懒加载模式,在dao层使用load方法. 注意,注释不要放开. 使用如下的代码块进行测试: 会报错:no sess ...
- 使用Jenkins结合Gogs和SonarQube对项目代码进行测试、部署、回滚,以及使用keepalived+haproxy调度至后端tomcat
0 环境说明 主tomcat:192.168.0.112 备tomcat:192.168.0.183 haproxy+keepalived-1:192.168.0.156 haproxy+keepal ...
- 【保姆级】Python项目(Flask网页)部署到Docker的完整过程
大家好,我是辰哥~ 前提:相信看到这篇文章的读者应该已经学会了Docker的安装以及Docker的基本使用,如果还不会的可以参考我之前的文章进行详细学习! 1.安装版:2300+字!在不同系统上安装D ...
- day2-pycharm创建项目,driver下载和浏览器设置
对于ie需要设置,才能使用ie做测试 火狐的使用不能超过43版本,ie11现在有多次弹出alert无法识别其内容的问题 https://github.com/SeleniumHQ/selenium/w ...
- docker swarm实现java项目的发布/滚动更新/回滚/镜像管理
使用docker swarm滚动更新java项目,部署集群,这一切的前提是使用Jenkins+maven进行项目打包,分发等功能 具体可以参考我的另外三篇文章 https://www.cnblogs. ...
- 创建 PDO 实例并在构造函数中设置错误模式
PDO 将只简单地设置错误码,可使用 PDO::errorCode() 和 PDO::errorInfo() 方法来检查语句和数据库对象.如果错误是由于对语句对象的调用而产生的,那么可以调用那个对象的 ...
- SSM实战——秒杀系统之创建项目、管理依赖、设计数据库
注:本项目使用Myeclipse开发. 一:项目创建 1:使用Myeclipse创建一个web project,命名为MySeckill,并转换为Maven项目. 2:创建项目文件目录如下: 上面四个 ...
- 保姆级神器 Maven,再也不用担心项目构建搞崩了
今天来给大家介绍一款项目构建神器--Maven,不仅能帮我们自动化构建,还能够抽象构建过程,提供构建任务实现:它跨平台,对外提供了一致的操作接口,这一切足以使它成为优秀的.流行的构建工具,从此以后,再 ...
随机推荐
- ASP.NET Core 根据环境变量支持多个 appsettings.json配置文件 (开发和生产)
新建一个项目,web根目录会出现一个 appsettings.json 配置文件, 此时添加--新建项,输入 appsettings.Development.json 再新增一个,appsetti ...
- NC16783 [NOIP1998]拼数
NC16783 [NOIP1998]拼数 题目 题目描述 设有 \(n\) 个正整数(\(n ≤ 20\)),将它们联接成一排,组成一个最大的多位整数. 例如:\(n=3\) 时,\(3\) 个整数 ...
- 数据孤岛下的新破局 Real Time DaaS:面向 AP+TP 业务的数据平台架构
从传统数仓,到大数据平台,再到数据中台和湖仓一体新数据平台,在日益加重的数据孤岛困扰下,面向AP场景的解决方案可谓浩如烟海.但实际上,企业在TP类型业务上的投入和AP的比率却高达9:1,为什么没有为T ...
- 解读Go分布式链路追踪实现原理
摘要:本文将详细介绍分布式链路的核心概念.架构原理和相关开源标准协议,并分享我们在实现无侵入 Go 采集 Sdk 方面的一些实践. 本文分享自华为云社区<一文详解|Go 分布式链路追踪实现原理& ...
- OneOS下调试支持的几种方式
方法论 当我们遇到问题,应该怎么办?这不仅应用于程序开发,也是我们在生活中遇到问题的时候,应该想的事儿,怎么办!趁着此次机会,我好好想了七秒钟. 先问是不是问题,如果不是就不用解决了 如果确实是问题, ...
- Solution -「HNOI2013」消毒
弱化一下,先考虑在二维上解决问题. 题目就转化为:有 \(n\) 个点 \((i, j)\) 需要被覆盖,而我们每次可以选一行或一列去覆盖,求覆盖所有点的最少选择次数. 如果我们对于每一个 \((i, ...
- Solution -「HDU」Professor Ben
Description 有 \(Q\) 个询问.每次给定一个正整数 \(n\),求它的所有因数的质因数个数的和. Solution 就讲中间的一个 Trick. 我们定义正整数 \(x\) 有 \(f ...
- Node.js精进(11)——Socket.IO
Socket.IO 是一个建立在 WebSocket 协议之上的库,可以在客户端和服务器之间实现低延迟.双向和基于事件的通信. 并且提供额外的保证,例如回退到 HTTP 长轮询.自动重连.数据包缓冲. ...
- 没错,请求DNS服务器还可以使用UDP协议
目录 简介 搭建netty客户端 在netty中发送DNS查询请求 DNS消息的处理 总结 简介 之前我们讲到了如何在netty中构建client向DNS服务器进行域名解析请求.使用的是最常见的TCP ...
- 基于gRPC编写golang简单C2远控
概述 构建一个简单的远控木马需要编写三个独立的部分:植入程序.服务端程序和管理程序. 植入程序是运行在目标机器上的远控木马的一部分.植入程序会定期轮询服务器以查找新的命令,然后将命令输出发回给服务器. ...