持久层整合总述

1.Spring框架为什么要与持久层技术进行整合?

  • javaEE开发需求持久层进行数据库的访问操作
  • JDBCHibernateMybatis进行持久开发过程存在大量代码冗余
  • Spring基于模板设计模式对于上述的持久层进行了封装

    2.什么是模板设计模式?

    在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

    Spring与Mybatis整合

    1.Mybatis开发步骤的回顾


1.实体
2.实体别名
3.表
4.创建DAO接口
5.实现Mapper文件
6.注册MaPPer文件
7.Mybatis API的调用

实体类

  1. public class User implements Serializable {
  2. private Integer id;
  3. private String name;
  4. private String password;
  5. public User() {
  6. }
  7. public User(Integer id, String name, String password) {
  8. this.id = id;
  9. this.name = name;
  10. this.password = password;
  11. }
  12. public Integer getId() {
  13. return id;
  14. }
  15. public void setId(Integer id) {
  16. this.id = id;
  17. }
  18. public String getName() {
  19. return name;
  20. }
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24. public String getPassword() {
  25. return password;
  26. }
  27. public void setPassword(String password) {
  28. this.password = password;
  29. }
  30. }

2.实体类别名

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Confi 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <typeAliases>
  7. <typeAlias alias="user" type="com.yusael.mybatis.User"/>
  8. </typeAliases>
  9. <environments default="mysql">
  10. <environment id="mysql">
  11. <transactionManager type="JDBC"></transactionManager>
  12. <dataSource type="POOLED">
  13. <property name="driver" value="com.mysql.jdbc.Driver"/>
  14. <property name="url" value="jdbc:mysql://localhost:3306/yus?useSSL=false"/>
  15. <property name="username" value="root"/>
  16. <property name="password" value="1234"/>
  17. </dataSource>
  18. </environment>
  19. </environments>
  20. </configuration>

3.表

  1. create table t_users values (
  2. id int(11) primary key auto_increment,
  3. name varchar(12),
  4. password varchar(12)
  5. );

4.创建DAO接口:UserDAO

  1. public interface UserDAO {
  2. public void save(User user);
  3. }

5. 实现Mapper文件:UserDAOMapper.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4. <mapper namespace="com.yusael.mybatis.UserDAO">
  5. <insert id="save" parameterType="user">
  6. insert into t_users(name, password) values (#{name}, #{password})
  7. </insert>
  8. </mapper>

6. 注册 Mapper 文件 mybatis-config.xml

  1. <mappers>
  2. <mapper resource="UserDAOMapper.xml"/>
  3. </mappers>

7. MybatisAPI 调用

  1. public class TestMybatis {
  2. public static void main(String[] args) throws IOException {
  3. InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
  4. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  5. SqlSession session = sqlSessionFactory.openSession();
  6. UserDAO userDAO = session.getMapper(UserDAO.class);
  7. User user = new User();
  8. user.setName("yusael");
  9. user.setPassword("123456");
  10. userDAO.save(user);
  11. session.commit();
  12. }
  13. }

Mybatis 开发中存在的问题

问题:配置繁琐、代码冗余
1. 实体
2. 实体别名 配置繁琐
3. 表
4. 创建 DAO 接口
5. 实现 Mapper 文件
6. 注册 Mapper 文件 配置繁琐
7. Mybatis API 调用 代码冗余

Spring与Mybatis的整合思路

image.png
image.png

Spring 与 Mybatis 整合的开发步骤

  • 配置文件(ApplicationContext.xml)进行相关配置(只需要配置一次
  • 编码
    1.实体类
    2.表
    3.创建DAO接口
    4.Mapper文件配置

    Spring 与 Mybatis 整合的编码

    搭建开发环境 pom.xml

    1. <dependency>
    2. <groupId>junit</groupId>
    3. <artifactId>junit</artifactId>
    4. <version>4.11</version>
    5. <scope>test</scope>
    6. </dependency>
    7. <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
    8. <dependency>
    9. <groupId>org.springframework</groupId>
    10. <artifactId>spring-test</artifactId>
    11. <version>5.3.9</version>
    12. <scope>test</scope>
    13. </dependency>
    14. <!-- 注解-->
    15. <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    16. <dependency>
    17. <groupId>org.projectlombok</groupId>
    18. <artifactId>lombok</artifactId>
    19. <version>1.18.20</version>
    20. <scope>provided</scope>
    21. </dependency>
    22. <!-- 包含Spring框架基本的核心工具类 -->
    23. <dependency>
    24. <groupId>org.springframework</groupId>
    25. <artifactId>spring-core</artifactId>
    26. <version>5.3.5</version>
    27. </dependency>
    28. <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
    29. <!-- 所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及
    30. 进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类-->
    31. <dependency>
    32. <groupId>org.springframework</groupId>
    33. <artifactId>spring-beans</artifactId>
    34. <version>5.3.5</version>
    35. </dependency>
    36. <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    37. <!-- 为Spring 核心提供了大量扩展 -->
    38. <dependency>
    39. <groupId>org.springframework</groupId>
    40. <artifactId>spring-context</artifactId>
    41. <version>5.3.5</version>
    42. </dependency>
    43. <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    44. <!--Spring-aop(必须)
    45. 包含在应用中使用Spring 的AOP 特性时所需的类和源码级元数据支持。-->
    46. <dependency>
    47. <groupId>org.springframework</groupId>
    48. <artifactId>spring-aop</artifactId>
    49. <version>5.3.5</version>
    50. </dependency>
    51. <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
    52. <!-- aspectj的runtime包(必须) -->
    53. <dependency>
    54. <groupId>org.aspectj</groupId>
    55. <artifactId>aspectjrt</artifactId>
    56. <version>1.9.1</version>
    57. </dependency>
    58. <dependency>
    59. <groupId>org.aspectj</groupId>
    60. <artifactId>aspectjtools</artifactId>
    61. <version>1.9.5</version>
    62. </dependency>
    63. <!-- aspectjweaver是aspectj的织入包(必须) -->
    64. <dependency>
    65. <groupId>org.aspectj</groupId>
    66. <artifactId>aspectjweaver</artifactId>
    67. <version>1.9.0</version>
    68. </dependency>
    69. <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    70. <!-- jdbc连接池druid -->
    71. <dependency>
    72. <groupId>com.alibaba</groupId>
    73. <artifactId>druid</artifactId>
    74. <version>1.2.6</version>
    75. </dependency>
    76. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    77. <dependency>
    78. <groupId>mysql</groupId>
    79. <artifactId>mysql-connector-java</artifactId>
    80. <version>8.0.25</version>
    81. </dependency>
    82. <dependency>
    83. <groupId>org.springframework</groupId>
    84. <artifactId>spring-jdbc</artifactId>
    85. <version>5.3.5</version>
    86. </dependency>
    87. <!--Spring事物依赖 -->
    88. <dependency>
    89. <groupId>org.springframework</groupId>
    90. <artifactId>spring-tx</artifactId>
    91. <version>5.3.5</version>
    92. </dependency>
    93. <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    94. <dependency>
    95. <groupId>org.mybatis</groupId>
    96. <artifactId>mybatis</artifactId>
    97. <version>3.5.7</version>
    98. </dependency>
    99. <dependency>
    100. <groupId>org.mybatis</groupId>
    101. <artifactId>mybatis-spring</artifactId>
    102. <version>1.3.2</version>
    103. </dependency>

    Spring配置文件的配置

    ```xml <beans xmlns=”http://www.springframework.org/schema/beans

    1. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. xsi:schemaLocation="http://www.springframework.org/schema/beans
    3. http://www.springframework.org/schema/beans/spring-beans.xsd">
    4. <!--连接池-->
    5. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    6. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    7. <property name="url" value="jdbc:mysql://localhost:3306/yus?useSSL=false"/>
    8. <property name="username" value="root"/>
    9. <property name="password" value="1234"/>
    10. </bean>
    11. <!--创建SqlSessionFactory SqlSessionFactoryBean-->
    12. <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
    13. <property name="dataSource" ref="dataSource"/>
    14. <!-- 指定实体类所在的包 -->
    15. <property name="typeAliasesPackage" value="com.yusael.entity"/>
    16. <!--指定配置文件(映射文件)的路径,还有通用配置-->
    17. <property name="mapperLocations">
    18. <list>
    19. <value>classpath:com.yusael.dao/*Mapper.xml</value>
    20. </list>
    21. </property>
    22. </bean>
    23. <!--创建DAO对象 MapperScannerConfigure-->
    24. <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    25. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
    26. <!--指定DAO接口放置的包-->
    27. <property name="basePackage" value="com.yusael.dao"/>
    28. </bean>

  1. <a name="Rqq7p"></a>
  2. # 编码
  3. **1.实体**
  4. ```java
  5. package com.xiaohong.entity;
  6. /**
  7. * @Author 周海权
  8. * @PackageName Spring-Mybatis
  9. * @Package com.xiaohong.entity
  10. * @Date 2022/4/8 13:48
  11. * @Version 1.0
  12. */
  13. public class User {
  14. private int id;
  15. public int getId() {
  16. return id;
  17. }
  18. public void setId(int id) {
  19. this.id = id;
  20. }
  21. @Override
  22. public String toString() {
  23. return "User{" +
  24. "id=" + id +
  25. ", name='" + name + '\'' +
  26. ", version=" + version +
  27. '}';
  28. }
  29. public String getName() {
  30. return name;
  31. }
  32. public void setName(String name) {
  33. this.name = name;
  34. }
  35. public int getVersion() {
  36. return version;
  37. }
  38. public void setVersion(int version) {
  39. this.version = version;
  40. }
  41. private String name;
  42. private int version;
  43. }

2.表 t_user

  1. create table t_user values{
  2. id int(11) not null Auto_increament,
  3. name varchar(12),
  4. version int(12)
  5. };

3.DAO接口

  1. public interface UserDao {
  2. public void save(User user);
  3. }

4.Mapper文件配置 resources/applicationContext.xml

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans
  4. http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <!--连接池-->
  6. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  7. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  8. <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
  9. <property name="username" value="root"/>
  10. <property name="password" value="123456"/>
  11. </bean>
  12. <!--创建SqlSessionFactory SqlSessionFactoryBean-->
  13. <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
  14. <property name="dataSource" ref="dataSource"/>
  15. <property name="typeAliasesPackage" value="com.xiaohong.entity"/>
  16. <property name="mapperLocations">
  17. <list>
  18. <value>classpath:mapper/*Mapper.xml</value>
  19. </list>
  20. </property>
  21. </bean>
  22. <!--创建DAO对象 MapperScannerConfigure-->
  23. <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  24. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
  25. <property name="basePackage" value="com.xiaohong.dao"/>
  26. </bean>
  27. </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.原始对象

  1. public class XXXUserServiceImpl{
  2. private xxxDAO XXXDAO;
  3. set/get
  4. 1.原始对象----->原始方法---->核心功能(业务处理+DAO调用)
  5. }

2.额外功能

  1. 1.MethodInterceptor
  2. public Object invock(MethodInvavation invocation){
  3. try{
  4. Connection.setAutoCommit(false);
  5. Object ret = invocation.process();
  6. Connection.commit;
  7. }catch(Exception e){
  8. Connection.rollback();
  9. }
  10. }
  11. return ret;
  12. }

Spring把这个封装成了image.png
所以加一个注解就可以了,我们直到要进行事务的提交需要注入连接对象Connection,为了效率连接又来自连接池,等效于DataSourceTransactionManager需要连接池。注入DataSource即可。

3.切入点

image.png

4.组装切面

image.png
两个标签 transaction-manager写额外功能,annotation-driven写切入点,通过@Transcational扫描

Spring控制事务的编码

  • 搭建开发环境(jar)

    1. <dependency>
    2. <groupId>org.springframework</groupId>
    3. <artifactId>spring-tx</artifactId>
    4. <version>5.3.15</version>
    5. </dependency>
  • 编码

    1. <bean id="userService" class="com.yusael.service.UserServiceImpl">
    2. <property name="userDAO" ref="userDAO"/>
    3. </bean>
    4. <!--DataSourceTransactionManager-->
    5. <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    6. <property name="dataSource" ref="dataSource"/>
    7. </bean>
    8. @Transactional
    9. public class UserServiceImpl implements UserService {
    10. private UserDAO userDAO;
    11. <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

    细节:进行动态代理底层实现的切换,默认 false 是 JDK,true 是 Cglib。
    image.png

    Spring中的事务属性(Transaction Attribute)

    什么是事务的属性
    属性:描述物体特征的一系列值(性别、身高、体重)
    事务属性:描述事务特征的一系列值

  1. 隔离属性
  2. 传播属性
  3. 只读属性
  4. 超时属性
  5. 异常属性

如何添加事务属性?
image.png
隔离属性(ISOLATION)
概念:描述了事务解决并发问题的特征。
什么是并发?
多个事务(用户)在同一时间,访问操作了相同的数据。
同一时间:0.000 几秒左右
并发会产生那些问题
1. 脏读
2.不可重复读
3. 幻影读
并发问题如何解决?
通过隔离属性解决,隔离属性中设置不同过的值,解决并发处理的过程中的问题。
事务并发产生的问题:

脏读
一个事务,读取了另一个事务中没有提交的数据,会在本事务中产生数据不一样的现象
做一个实验看看,先把隔离属性设置为读未提交的
image.png
通过指令查看隔离属性
image.png
使用select语句查询account表,这里的balance为16
image.png
在开启一个事务2,那balance改成10,然后不提交
image.png
事务1查看account的数据,此时account已经变为10了,
image.png
此时事务2进行回滚操作,此时account变成了16,这两次的数据不相同此时就产生了脏读现象,
解决方案:@Transaction(isolation=Isolation.READ_COMMITTED)
不可重复读
一个事务中,多次读取相同的数据,但是读取结果不一样,会在本事务中产生数据不一样的现象
注意:1.不是脏读 2.在一个事务中

做一个实验:首先事务1先做一个查询account的值,查询结果如图所示:

image.png
然后在开启一个事务把balance设置为10并且提交,事务1在查找,如下图,查询前后balance的值不相同,出现了不可重复读。
image.png
解决方案:@Transaction(isolation=Isolation.REPEATABLE_READ)
本质:一把行锁(对数据库表的某一行加锁)
设置隔离属性
image.png
重复上述的操作,事务1第一次查询
image.png
事务2修改并提交,事务1再一次查询
image.png
二者结果相同解决了不可重复读的问题,
幻影读
一个事务中,多次对整表进行查询统计,但是结果不一样,会在本事务中产生数据不一致的问题
解决方案:@Transaction(isolation=Isolation.SERIALIZABLE)
本质:表锁(对数据库某个表加锁)
做个实验,先把隔离级别设置为可重复读,反正不设置成SERIALIZABLE就可以了。
image.png
开启事务1,查看account表的数据,如图所示,balance的总量有四条
image.png

开事务2,插入一条语句,并且提交,查看事务1,balance的总量。
image.png
这时又变成了五条,
解决办法就是把隔离属性改变了,设置成@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)

    概率:描述了事务解决、嵌套问题的特征
    事务的嵌套:指的是一个大的事务中,包含了若干个小事务。
    事务嵌套产生的问题: 大事务中融入了很多小的事务,他们彼此影响,最终就导致外部大的事务丧失了事务的原子性
    传播属性的值及其用法:
    image.png
    Spring 中传播属性的默认值是:REQUIRED

推荐传播属性的使用方式:

  1. 增删改 方法:使用默认值 **REQUIRED**<br /> 查询 方法:显示指定传播属性的值为 **SUPPORTS**

只读属性(readOnly

针对于 只进行查询操作的业务方法,可以加入只读属性,提高运行效率。
默认值:false
image.png
超时属性(timeout)

指定了事务等待的最长时间。

  1. 为什么事务会进行等待?<br /> 当前事务访问数据时,有可能访问的数据被别的事务进行加锁的处理,那么此时本事务就必须进行等待。<br /> 等待时间,单位是 秒<br /> 如何使用:@Transactional(timeout = 2)<br /> 超时属性的默认值:-1<br /> -1 表示超时属性由对应的数据库来指定(一般不会主动指定,-1 即可)

异常属性

Spring 事务处理过程中:

  1. 默认对于 RuntimeException 及其子类,采用 回滚 的策略。<br /> 默认对于 Exception 及其子类,采用 提交 的策略。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22732206/1649474169190-9713c929-c8c1-463b-ad7f-3dc392b26343.png#clientId=u57f8d3ad-4dd2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=81&id=u35343272&margin=%5Bobject%20Object%5D&name=image.png&originHeight=100&originWidth=1202&originalType=binary&ratio=1&rotation=0&showTitle=false&size=13100&status=done&style=none&taskId=uf6cfb5ad-77e7-441f-9d4c-3c645680b22&title=&width=969.6806411845539)<br />建议:实战中使用 **RuntimeException** 及其子类,使用事务异常属性的默认值。<br />事务属性常见配置总结
  2. 隔离属性 默认值<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)