SSM框架学习-6
本次学习的是Spring事务管理,它包含以下几点内容:
- Java事物导引
- Spring事务核心接口
- 编程式事务管理
- 声明式事务管理
- 事务最佳实现
- 课程总结
1.Java事物导引
- 什么是事务
事务是正确执行一系列的操作(或动作),使得数据库从一种状态转换成另一种状态,且保证操作全部成功,或者全部失败。
- 事务原则是什么
事务必须服从ISO/IEC所制定的ACID原则。
原子性:及不可分割性,事务要么全部被执行,要么就不执行。
一致性:事物的执行使得数据库从一种状态转换成另一种状态。
隔离性:在事务正确提交前,它可能的结果不应该显示给任何其他事务。
持久性:事务正确提交后,其结果永久保存在数据库中。
- Java事务的产生
程序操作数据库的需要,在Java 编写的程序或系统中,实现ACID操作。
- Java事务实现范围
通过JDBC相应方法间接实现对数据库的增删改查,把事务转移到Java程序代码中进行控制。
确保事务-要么全部执行成功要么撤销执行。
-
总结:Java事物机制和原理就是确保数据库操作的ACID特性。
-
Java事务的实现
通过Java代码来实现对数据库的事务性操作
- Java事务类型
JDBC事务:用Connection对象控制,包括手动模式和自动模式。
JTA(Java Transaction API)事务:与实现无关的,与协议无关的API(了解)。
容器事务:应用服务器提供,且大多基于JTA完成(通常基于JNDI的,相当复杂的API实现),我们只要会使用就好。
2.Spring事核心接口
事务接口的架构:TransctionDefinition对事务属性进行定义,传参数到getTransaction,PlatformTransactionManager接口通过特定平台的相关的事务实现对事务进行管理,getTransaction得到TransactionStatus事务状态。平台的相关的事务实现有JDBC,Hibernate,JPA,JTA事务管理器。
Spring事务属性接口
- 事务属性范围
- 传播行为
- 隔离规则
- 回滚规则
- 事务超时
- 是否只读
- 事务属性定义
public interface TransactionDefinition{
//返回事物的传播行为
int getPropagationBehavior();
//返回事务的隔离级别,事务管理器根据它来控制另一个事务可以看到本事务内的哪些数据
int getIoslationLevel()
//返回事务必须在多少秒内完成
int getTimeout();
//判断事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的
boolean isReadOnly
}
-
数据读取类型说明
-
脏读:事务没提交,提前读取;
-
不可重复读:两次数据读取不一致;
-
幻读:事务不是独立执行的时候发生的一种非预期的现象;
-
事务隔离级别
隔离级别|含义
:-: | :-:
ISOLATION_DEFAULT |使用后端数据库默认的隔离级别。
ISOLATION_READ_UNCOMMITTED |最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
ISOLATION_READ_COMMITTED | 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
ISOLATION_REPEATABLE_READ | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
ISOLATION_SERIALIZABLE | 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的。 -
事务传播行为
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播;
Spring的7种传播行为:
传播行为 | 含义 |
---|---|
PROPAGATION_REQUIRED | 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务 |
PROPAGATION_SUPPORTS | 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行 |
PROPAGATION_MANDATORY | 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 |
PROPAGATION_REQUIRED_NEW | 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NOT_SUPPORTED | 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NEVER | 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常PROPAGATION_NESTED表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务事务传播行为。 |
-
事务是否只读
-
利用数据库事物的“只读”属性,进行特定优化处理。
-
设置“只读”注意:
-
事务的是否是否“只读”属性,不同的数据库厂商支持不同。
-
通常而言:只读属性的应用要参考厂商的具体支持说明,比如Orcle的“readOnly”不起作用,不影响其增删改查,Mysql的“readOnly”为true,只能查,增删改则出现异常
-
事务超时
-
事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
-
设计事务时注意点:
-
为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。
-
事务回滚
-
默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚。
-
自定义回滚策略
-
声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚;
-
声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。
Spring事务接口
- 事务接口
- 通过事务管理器获取TransactionStatus实例;
- 控制事务在回滚或提交的时候需要应用对应的事务状态;
- Spring事务接口:
//Spring事务状态接口:
//通过调用PlatformTransactionManager的get Transaction()
//获取事务状态实例
public interface TransactionStatus{
//是否是新的事务
boolean isNewTransaction();;
//是否有恢复点
boolean hasSavePoint();
//设置为只回滚
void setRollbackOnly();
//是否已完成
boolean isCompletd
}
3.编程式事务管理
- 事务管理器(PlatforTransactionManager)方式
- 类似应用JTA UserTransaction API方式,但异常处理更简洁;
- 核心类为:Spring事务管理的三个接口类以及JdbcTemplate类
- 模板事务(TransactionTemplate)的方式
- 此为Spring官方团队推荐的编程式事务管理方式;
- 主要工具为JdbcTemplate类。
事务管理器方式
// 1、编程式事务管理:事务管理器PlatformTransactionManager方式实现
public void updateBookByIsbn(Book book) {
//第一步:获取JDBC事务管理器
DataSourceTransactionManager dtm = TemplateUtils.getDataSourceTransactionManager();
// 第二步:创建事务管理器属性对象
DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
// 根据需要,设置事务管理器的相关属性
// 设置传播行为属性
transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
// 第三步:获得事务状态对象
TransactionStatus ts = dtm.getTransaction(transDef);
// 第四步:基于当前事务管理器,获取数据源,创建操作数据库的JDBC模板对象
JdbcTemplate jt = new JdbcTemplate(dtm.getDataSource());
try {//第五步:业务操作
jt.update("update books set price="+book.getPrice()+",name='"+book.getName()
+"' where isbn='"+book.getIsbn()+"' ");
// 其它数据操作如增删
//第六步:提交事务
dtm.commit(ts); // 如果不commit,则更新无效果
} catch (Exception e) {
dtm.rollback(ts);
e.printStackTrace();
}
}
模板事务(Transaction)实例
// 事务模板:第二种事务编程模式
// private TransactionTemplate transactionTemplate ;
// 数据持久化操作
public void addBook(Book book) {
// 获取事务模板对象
TransactionTemplate tt = TemplateUtils.getTransactionTemplate();
// 可设置事务属性,如隔离级别、超时时间等,如:
// tt.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
tt.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus s) {
try {
// 数据库操作1
// JdbcTemplate jdbcTemplate
// =TemplateUtils.getJdbcTemplate();
// jdbcTemplate.execute(sql);
// 简单模板化新增数据
SimpleJdbcInsert simpleInsert = TemplateUtils.getSimpleJdbcTemplate();
simpleInsert.withTableName("books").usingColumns("isbn", "name", "price", "pubdate");
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("isbn", book.getIsbn());
parameters.put("name", book.getName());
parameters.put("price", book.getPrice());
parameters.put("pubdate", book.getPubdate());
simpleInsert.execute(parameters);
System.out.println("新增数据成功!");
// 或者DAO数据操作模式:
// BookDAO.save(book);
} catch (Exception e) {
s.setRollbackOnly();
e.printStackTrace();
}
}
});
}
(底层原理)
4.声明式事务管理
基于AOP模式机制,对方法前后进行拦截。
声明式事务实现方式
- 声明式事务管理的配置类型
- 5种类型(?):独立代理;共享代理;拦截器;tx拦截器;全注释(前三类不推荐使用)
- 声明式事务管理配置实现方式;
- 2种类型的配置实现参考(配置代码案例)
tx拦截器:
<!-- jdbc事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 1、通过事务通知的模式实现事务
事务通知:the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- 以get开头的所有方法都为只读事务:all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- 其它方法使用默认事务设置:other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 确保上述事务通知对定义在FooService接口中的方法都起作用(
ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface) -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* com.snake_lvyonghao.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
注释方法:
<!-- jdbc事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--事务模板 -->
<!-- 2、注释模式事务:启动使用注解实现声明式事务管理的支持 -->
<tx:annotation-driven transaction-manager="txManager" />
<!-- 想创建的服务对象:this is the service object that we want to make transactional -->
<bean id="fooService" class="com.snake_lvyonghao.service.DefaultFooService"/>
<bean id="xbeanService" class="com.snake_lvyonghao.service.XbeanServiceImpl"/>
5.总结
这一节的内容感觉跨度有点大,emmm了解一下就好了,日后会有ORM的框架去实现和数据库之间的操作的。
- 编程式事务允许用户在代码中精确定义事务的边界;
- 声明式事务有助于用户将操作与事务规则进行解耦
- 基于AOP交由Spring容器实现;
- 实现关注点聚焦在业务逻辑上。
- 概括而言:
- –编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。