持久层整合总述
1.Spring框架为什么要与持久层技术进行整合?
- javaEE开发需求持久层进行数据库的访问操作
- JDBC、Hibernate、Mybatis进行持久开发过程存在大量代码冗余
- Spring基于模板设计模式对于上述的持久层进行了封装
2.什么是模板设计模式?
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。Spring与Mybatis整合
1.Mybatis开发步骤的回顾
1.实体
2.实体别名
3.表
4.创建DAO接口
5.实现Mapper文件
6.注册MaPPer文件
7.Mybatis API的调用
实体类
public class User implements Serializable {private Integer id;private String name;private String password;public User() {}public User(Integer id, String name, String password) {this.id = id;this.name = name;this.password = password;}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 String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}
2.实体类别名
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Confi 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><typeAliases><typeAlias alias="user" type="com.yusael.mybatis.User"/></typeAliases><environments default="mysql"><environment id="mysql"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/yus?useSSL=false"/><property name="username" value="root"/><property name="password" value="1234"/></dataSource></environment></environments></configuration>
3.表
create table t_users values (id int(11) primary key auto_increment,name varchar(12),password varchar(12));
4.创建DAO接口:UserDAO
public interface UserDAO {public void save(User user);}
5. 实现Mapper文件:UserDAOMapper.xml
<?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.yusael.mybatis.UserDAO"><insert id="save" parameterType="user">insert into t_users(name, password) values (#{name}, #{password})</insert></mapper>
6. 注册 Mapper 文件 mybatis-config.xml
<mappers><mapper resource="UserDAOMapper.xml"/></mappers>
7. MybatisAPI 调用
public class TestMybatis {public static void main(String[] args) throws IOException {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession session = sqlSessionFactory.openSession();UserDAO userDAO = session.getMapper(UserDAO.class);User user = new User();user.setName("yusael");user.setPassword("123456");userDAO.save(user);session.commit();}}
Mybatis 开发中存在的问题
问题:配置繁琐、代码冗余
1. 实体
2. 实体别名 配置繁琐
3. 表
4. 创建 DAO 接口
5. 实现 Mapper 文件
6. 注册 Mapper 文件 配置繁琐
7. Mybatis API 调用 代码冗余
Spring与Mybatis的整合思路
Spring 与 Mybatis 整合的开发步骤
- 配置文件(ApplicationContext.xml)进行相关配置(只需要配置一次)
编码
1.实体类
2.表
3.创建DAO接口
4.Mapper文件配置Spring 与 Mybatis 整合的编码
搭建开发环境 pom.xml
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-test --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.9</version><scope>test</scope></dependency><!-- 注解--><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version><scope>provided</scope></dependency><!-- 包含Spring框架基本的核心工具类 --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.3.5</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-beans --><!-- 所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类--><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.3.5</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-context --><!-- 为Spring 核心提供了大量扩展 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.5</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --><!--Spring-aop(必须)包含在应用中使用Spring 的AOP 特性时所需的类和源码级元数据支持。--><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.5</version></dependency><!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt --><!-- aspectj的runtime包(必须) --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.9.1</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjtools</artifactId><version>1.9.5</version></dependency><!-- aspectjweaver是aspectj的织入包(必须) --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.0</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/druid --><!-- jdbc连接池druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.6</version></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><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.5</version></dependency><!--Spring事物依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.3.5</version></dependency><!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.2</version></dependency>
Spring配置文件的配置
```xml <beans xmlns=”http://www.springframework.org/schema/beans“
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/yus?useSSL=false"/><property name="username" value="root"/><property name="password" value="1234"/></bean><!--创建SqlSessionFactory SqlSessionFactoryBean--><bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><!-- 指定实体类所在的包 --><property name="typeAliasesPackage" value="com.yusael.entity"/><!--指定配置文件(映射文件)的路径,还有通用配置--><property name="mapperLocations"><list><value>classpath:com.yusael.dao/*Mapper.xml</value></list></property></bean><!--创建DAO对象 MapperScannerConfigure--><bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/><!--指定DAO接口放置的包--><property name="basePackage" value="com.yusael.dao"/></bean>
<a name="Rqq7p"></a># 编码**1.实体**```javapackage com.xiaohong.entity;/*** @Author 周海权* @PackageName Spring-Mybatis* @Package com.xiaohong.entity* @Date 2022/4/8 13:48* @Version 1.0*/public class User {private int id;public int getId() {return id;}public void setId(int id) {this.id = id;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", version=" + version +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getVersion() {return version;}public void setVersion(int version) {this.version = version;}private String name;private int version;}
2.表 t_user
create table t_user values{id int(11) not null Auto_increament,name varchar(12),version int(12)};
3.DAO接口
public interface UserDao {public void save(User user);}
4.Mapper文件配置 resources/applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/><property name="username" value="root"/><property name="password" value="123456"/></bean><!--创建SqlSessionFactory SqlSessionFactoryBean--><bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="typeAliasesPackage" value="com.xiaohong.entity"/><property name="mapperLocations"><list><value>classpath:mapper/*Mapper.xml</value></list></property></bean><!--创建DAO对象 MapperScannerConfigure--><bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/><property name="basePackage" value="com.xiaohong.dao"/></bean></beans>
Spring与MtBatis整合细节
问题:Spring与Mybatis整合后,为什么Dao不提交事务,但是数据能够插入数据库中?
1.Mybatis提供的连接池对象—》创建Connection
Connection.setAutoCommit(false)手工的控制了事务,操作完成后,需要手工提交。
2. Druid(C3P0、DBCP)作为连接池 —> 创建 Connection
Connection.setAutoCommit(true) 默认值为 true,保持自动控制事务,一条 sql 自动提交。
[Spring持久层] Spring事务开发、事务属性
什么是事务?
- 事务保证业务操作完整性的一种数据库机制。
事务的四大特点:A、C、I、D
- A 原子性
- C 一致性
- I 隔离性
- D 持久性
如果控制事务?(JDBC,Mybatis)
JDBC
- Connection.setAutoCommit(false)
- Connection.commit();
- Connection.rollback();
Mybatis
- Mybatis自动开启事务
- sqlsession.commit(),底层还是调用Connection
- sqlsession.rollback;底层还是调用Connection
结论:控制事务的底层,都是通过Connecrion对象完成的。
Spring控制事务的开发
Spring是通过AOP的方式进行事务开发
1.原始对象
public class XXXUserServiceImpl{private xxxDAO XXXDAO;set/get1.原始对象----->原始方法---->核心功能(业务处理+DAO调用)}
2.额外功能
1.MethodInterceptorpublic Object invock(MethodInvavation invocation){try{Connection.setAutoCommit(false);Object ret = invocation.process();Connection.commit;}catch(Exception e){Connection.rollback();}}return ret;}
Spring把这个封装成了
所以加一个注解就可以了,我们直到要进行事务的提交需要注入连接对象Connection,为了效率连接又来自连接池,等效于DataSourceTransactionManager需要连接池。注入DataSource即可。
3.切入点
4.组装切面

两个标签 transaction-manager写额外功能,annotation-driven写切入点,通过@Transcational扫描
Spring控制事务的编码
搭建开发环境(jar)
<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.3.15</version></dependency>
编码
<bean id="userService" class="com.yusael.service.UserServiceImpl"><property name="userDAO" ref="userDAO"/></bean><!--DataSourceTransactionManager--><bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean>@Transactionalpublic class UserServiceImpl implements UserService {private UserDAO userDAO;<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
细节:进行动态代理底层实现的切换,默认 false 是 JDK,true 是 Cglib。
Spring中的事务属性(Transaction Attribute)
什么是事务的属性
属性:描述物体特征的一系列值(性别、身高、体重)
事务属性:描述事务特征的一系列值
- 隔离属性
- 传播属性
- 只读属性
- 超时属性
- 异常属性
如何添加事务属性?
隔离属性(ISOLATION)
概念:描述了事务解决并发问题的特征。
什么是并发?
多个事务(用户)在同一时间,访问操作了相同的数据。
同一时间:0.000 几秒左右
并发会产生那些问题?
1. 脏读
2.不可重复读
3. 幻影读
并发问题如何解决?
通过隔离属性解决,隔离属性中设置不同过的值,解决并发处理的过程中的问题。
事务并发产生的问题:
脏读
一个事务,读取了另一个事务中没有提交的数据,会在本事务中产生数据不一样的现象
做一个实验看看,先把隔离属性设置为读未提交的
通过指令查看隔离属性
使用select语句查询account表,这里的balance为16
在开启一个事务2,那balance改成10,然后不提交
事务1查看account的数据,此时account已经变为10了,
此时事务2进行回滚操作,此时account变成了16,这两次的数据不相同此时就产生了脏读现象,
解决方案:@Transaction(isolation=Isolation.READ_COMMITTED)
不可重复读
一个事务中,多次读取相同的数据,但是读取结果不一样,会在本事务中产生数据不一样的现象
注意:1.不是脏读 2.在一个事务中
做一个实验:首先事务1先做一个查询account的值,查询结果如图所示:

然后在开启一个事务把balance设置为10并且提交,事务1在查找,如下图,查询前后balance的值不相同,出现了不可重复读。
解决方案:@Transaction(isolation=Isolation.REPEATABLE_READ)
本质:一把行锁(对数据库表的某一行加锁)
设置隔离属性

重复上述的操作,事务1第一次查询

事务2修改并提交,事务1再一次查询
二者结果相同解决了不可重复读的问题,
幻影读
一个事务中,多次对整表进行查询统计,但是结果不一样,会在本事务中产生数据不一致的问题
解决方案:@Transaction(isolation=Isolation.SERIALIZABLE)
本质:表锁(对数据库某个表加锁)
做个实验,先把隔离级别设置为可重复读,反正不设置成SERIALIZABLE就可以了。
开启事务1,查看account表的数据,如图所示,balance的总量有四条
开事务2,插入一条语句,并且提交,查看事务1,balance的总量。
这时又变成了五条,
解决办法就是把隔离属性改变了,设置成@Transaction(isolation=Isolation.SERIALIZABLE)
安全与效率对比:
- 并发安全:SERIALIZABLE > READ_ONLY > READ_COMMITTED
- 运行效率:READ_COMMITTED > READ_ONLY > SERIALIZABLE
默认的隔离属性:
Spring 会指定为 ISOLATION_DEFAULT,调用不同数据库所设置的默认隔离属性
MySQL:REPEATABLE_READ
查看数据库的默认隔离属性:
MySQL:SELECT @@tx_isolation
隔离属性在实验中的建议:
- 推荐使用 Spring 默认指定的 ISOLATION_DEFAULT
- 未来的实战中,遇到并发访问的情况,很少见
- 如果真的遇到并发问题,解决方案:乐观锁
MyBatis:通过拦截器自定义开发传播属性(PROPAGATION)
概率:描述了事务解决、嵌套问题的特征
事务的嵌套:指的是一个大的事务中,包含了若干个小事务。
事务嵌套产生的问题: 大事务中融入了很多小的事务,他们彼此影响,最终就导致外部大的事务丧失了事务的原子性。
传播属性的值及其用法:
Spring 中传播属性的默认值是:REQUIRED
推荐传播属性的使用方式:
增删改 方法:使用默认值 **REQUIRED**<br /> 查询 方法:显示指定传播属性的值为 **SUPPORTS**
只读属性(readOnly)
针对于 只进行查询操作的业务方法,可以加入只读属性,提高运行效率。
默认值:false
超时属性(timeout)
指定了事务等待的最长时间。
为什么事务会进行等待?<br /> 当前事务访问数据时,有可能访问的数据被别的事务进行加锁的处理,那么此时本事务就必须进行等待。<br /> 等待时间,单位是 秒<br /> 如何使用:@Transactional(timeout = 2)<br /> 超时属性的默认值:-1<br /> -1 表示超时属性由对应的数据库来指定(一般不会主动指定,-1 即可)
异常属性
Spring 事务处理过程中:
默认对于 RuntimeException 及其子类,采用 回滚 的策略。<br /> 默认对于 Exception 及其子类,采用 提交 的策略。<br /><br />建议:实战中使用 **RuntimeException** 及其子类,使用事务异常属性的默认值。<br />事务属性常见配置总结隔离属性 默认值<br /> 传播属性 **Required**(默认值)增删改、**Supports **查询操作<br /> 只读属性 **readOnly=false **增删改,**true **查询操作<br /> 超时属性 默认值 **-1**<br /> 异常属性 默认值
增删改操作:@Transactional
查询操作:@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
[
](https://blog.csdn.net/weixin_43734095/article/details/106505754)

