持久层整合总述
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 configuration
PUBLIC "-//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/beans
http://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.实体**
```java
package 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;
}
@Override
public 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/beans
http://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/get
1.原始对象----->原始方法---->核心功能(业务处理+DAO调用)
}
2.额外功能
1.MethodInterceptor
public 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>
@Transactional
public 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 />![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 />事务属性常见配置总结
隔离属性 默认值<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)