Spring — 整合持久层
第一章 持久层整合
1.Spring框架为什么要与持久层进行整合
1. JavaEE开发需要持久层进行数据库的访问操作
2. JDBC Hibernate Mybatis进行持久层开发过程存在大量的代码冗余
3. Spring基于模板设计模式对于上述的持久进行封装
2.Spring可以与哪些持久层技术进行整合
1. JDBC
|- JDBCTemplate
2. Hibernate (JPA)
|- HibernateTemplate
3. MyBatis
|- SqlSessionFactoryBean MapperScannerConfiguare
第二章 Spring与MyBatis整合
1.MyBatis开发步骤的回顾
1. 实体
2. 实体别名
3. 表
4. 创建DAO接口
5. 实现Mapper文件
6. 注册Mapper文件
7. MyBatisAPI调用
-
编码
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> <typeAliases> <typeAlias type="com.hyy.mybatis.User" alias="user"></typeAlias> </typeAliases> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/testspring?serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="UserMapper.xml"></mapper> </mappers> </configuration>UserMapper
<?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.hyy.mybatis.UserDAO"> <insert id="save" parameterType="user"> INSERT INTO t_users(name,password) VALUES (#{name},#{password}) </insert> </mapper>public class TestMybatis { public static void main(String[] args) throws IOException { InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); UserDAO userDao = sqlSession.getMapper(UserDAO.class); User user = new User(1,"迷雾小神","123456"); userDao.save(user); sqlSession.commit(); } }
2.Mybatis在开发过程中存在问题
配置繁琐,代码冗余
3.Spring与Mybatis整合思路分析
4.Spring与Mybatis整合的开发步骤
-
配置文件 applicationContext.xml
<!--创建SqlSessionFactory--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"></bean> <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--指定实体类的包--> <property name="typeAliasesPackage" value="com.hyy.mybatis"/> <!--指定 映射文件的路劲,还有通配设置--> <property name="mapperLocations" value=""/> </bean> <!--DAO接口的实现类--> <bean id="configurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sessionFactory"></property> <property name="basePackage" value="com.hyy.mybatis"></property> </bean> -
编码
实战经常根据需求,编写代码 1. 实体 2. 表 3. 创建DAO接口 4. 实现Mapper文件
5.Spring与MyBatis整合编码
-
搭建开发环境
<!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.23</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency> -
配置文件
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/testspring?serverTimezone=UTC"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--指定实体类的包--> <property name="typeAliasesPackage" value="com.hyy.entity"/> <!--指定 映射文件的路劲,还有通配设置--> <property name="mapperLocations" > <list> <value>classpath:com.hyy.mapper/*mapper.xml</value> </list> </property> </bean> <!--DAO接口的实现类--> <bean id="configurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sessionFactory"></property> <property name="basePackage" value="com.hyy.dao"></property> </bean> -
编码
1. 实体 2. 表 3. 创建DAO接口 4. 实现Mapper文件
6.Spring与MyBatis整合细节
-
问题:Spring与MyBatis整合后,为什么DAO不提交事务,但数据能插入到数据库中
Connection ---> tx Mybatis(Connection) 本质上控制连接对象(Connection) ---> 连接池(DataSource) 1.MyBatis提供的连接对象 ---> 创建Connection Connection.setAutoCommit(false)手工控制了事务,操作完成后,手工提交 2.Druid作为连接池 ---> 创建Connection Connection.setAutoCommit(true) 保持自动控制了事务
第三章 Spring的事务处理
1.什么是事务?
保证业务操作完整性的一种数据库机制
事务的特点:A C I D
1.A 原子性
2.C 一致性
3.I 隔离性
4.D 持久性
2.如何控制事务
JDBC:
Connection.setAutoCommit(false);
Connection.commit();
Connection.rollback();
Mybatis:
Mybatis自动开启事务
sqlSession.commit();
sqlSession.rollback();
结论:控制事务的底层 都是Connection对象完成的。
3.Spring控制事务的开发
Spring是通过AOP方式进行事务开发
-
原始对象
public class XXXServiceImpl{ 1. 原始对象 --> 原始方法 --> 核心功能 2. DAO作为Service的成员变量,依赖注入的方式进行赋值 } -
额外功能
org.springframework.jdbc.datasource.DataSourceTransactionManger 注入Connection == 注入datasource 1.MethodInterceptor public Object invoke(MethodInvocation invocation){ try{ Connection.setAutoCommit(false); Object ret = invocation.proceed(); Connection.commit(); }catch(Exception e){ Connection.rollback(); } return ret } 2.@Aspect @Around -
切入点
@Transactionnal 事务的额外功能加入给哪些业务方法 1.类上:类中所有的方法都会加入事务 2.方法上:这个方法会加入事务 -
组装切面
1.切入点 2.额外功能 <tx:annontation-driven transaction-manager=""/>
4.Spring控制事务的编码
<bean id="userService" class="com.hyy.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"></property>
</bean>
<!--配置事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
public void register(User user) {
userDAO.save(user);
}
}
第四章 Spring中的事务属性(Transaction Attribute)
1.什么是事务属性
属性:描述物体特征的一系列值
事务属性:描述事务特征的一系列值
1.隔离属性
2.传播属性
3.只读属性
4.超时属性
5.异常属性
2.如何添加事务属性
@Transactional(isloation=,propagation=,readOnly=,timeout=,)
3.事务属性详解
1.1 隔离属性(ISLATION)
-
隔离属性的概念
概念:描述了事务解决并发问题的特征 1.什么是并发 多个事务(用户)在同一时间,访问操作了相同的数据 2.并发会产生哪些问题 脏读 不可重复读 幻读 3.并发问题如何解决 通过事务的隔离属性解决,隔离属性中设置不同的值,解决并发处理过程中的问题 -
事务并发会产生的问题
-
脏读
一个事务,读取了另一个事务中没有提交的数据。会在本事务中产生数据不一致的问题 解决方案 @Transactional(isolation = Isolation.READ_COMMITTED) 读已提交 -
不可重复读
一个事务中,多次读取相同的数据,但是读取结果不一样。会在本事务中产生数据不一致的问题 注意:1.不是脏读 2.一个事务中 解决方案 @Transactional(isolation = Isolation.REPEATABLE_READ) 可重复读 本质:加一把行锁 -
幻读
一个事务中,多次对整表进行查询统计,但是结果不一样。会在本事务中产生数据不一致的问题 解决方案 @Transactional(isolation = Isolation.SERIALIZABLE) 序列化 本质:表锁 -
总结
并发安全:SERIALIZABLE > REPEATABLE_READ > READ_COMMITTED 运行效率:SERIALIZABLE < REPEATABLE_READ < READ_COMMITTED
-
-
数据库对于隔离属性的支持
隔离属性的值 Mysql Oracle READ_COMMITTED √ √ REPEATABLE_READ √ × SERIALIZABLE √ √ Oracle不支持REPEATABLE_READ的值 -
默认的隔离属性
Isolation.DEFAULT:会调用不同数据所设置的默认隔离属性 MySQL:REPEATABLE_READ Oracle:READ_COMMITTED -
隔离属性在实战中的建议
推荐使用Spring指定的Isolation.DEFAULT
1.2 传播属性(PROPAGATION)
-
传播属性的概念
概念:描述了事务解决嵌套问题的特征 事务嵌套:指的是一个大的事务中,包含了若干个小的事务 问题:大事务中融入了很多小的事务,他们彼此影响,最终就会导致外部大的事务,丧失了事务的原子性 -
传播属性的值及其方法
传播属性的值 外部不存在事务 外部存在事务 用法 备注 REQUIRED 开启新的事务 融合到外部事务中 @Transactional(propagation = Propagation.REQUIRED)增、删、改方法 SUPPORTS 不开启事务 融合到外部事务中 @Transactional(propagation = Propagation.SUPPORTS)查询方法 REQUIRES_NEW 开启新的事务 挂起外部事务,创建新的事务 @Transactional(propagation = Propagation.REQUIRES_NEW)日志记录方法中 NOT_SUPPORTED 不开启事务 挂起外部事务 @Transactional(propagation = Propagation.NOT_SUPPORTED)极其不常用 NEVER 不开启事务 抛出异常 @Transactional(propagation = Propagation.NEVER)极其不常用 MANDATORY 抛出异常 融合到外部事物中 @Transactional(propagation = Propagation.MANDATORY)极其不常用 -
默认的传播属性
PROPAGATION_REQUIRED 是传播属性的默认值 -
推荐传播属性的使用方法
增、删、改方法:直接使用默认值REQUIRED 查询 操作:显示指定传播属性的值为SUPPORTS
1.3 只读属性(readOnly)
针对于只进行查询操作的业务方法,可以加只读属性,提供运行效率
默认值:false
1.4 超时属性(timeout)
指定了事务等待的最长时间
1.为什么事务进行等待
当前事务访问数据时,有可能访问的数据被别的事务进行加索的处理,那么本十五就必须进行等待
2.等待时间 秒
1.5 异常属性
Spring事务处理过程中
默认 对于RuntimeExeception及其子类 采用的是回滚的策略
默认 对于Exeception及其子类 采用的是提交的策略
rollbackFor = {java.lang.Exeception,xxx} 回滚
noRollbackFor = {java.lang.RuntimeExeception} 提交
4.事务属性常见配置总结
1.隔离属性 默认值
2.传播属性 Required(默认值)增删改、Supports 查询操作
3.只读属性 readOnly=false 增删改,true 查询操作
4.超时属性 默认值 -1
5.异常属性 默认值
增删改操作:@Transactional
查询操作:@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
5.基于标签的事务配置方式(事务开发的第二种形式)
基于注解@Transactional事务的回顾
<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"/>
基于标签的事务配置:
<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>
事务属性
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="register" isolation="DEFAULT" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.yusael.service.UserServiceImpl.register(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
-
基于标签的事务配置在实战中的应用方式
使用通配符的方式 <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager"> <tx:attributes> <tx:method name="register"/> <tx:method name="modify*"/> 编程时候, service中负责进行增删改操作的方法 都以 modify 开头 查询操作 命名无所谓 <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="pc" expression="execution(* com.yusael.service..*.*(..))"/> 应用的过程中, 将 service 都放到 service 包下 <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/> </aop:config>