版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lwz45698752/article/details/88840148
文章目录
事务概述
- Spring生态圈满足各种类型应用开发
- 事务管理属于Spring框架的数据访问部分
事务特点(原则)
- 执行事务,要么全部成功,要么全部失败(事务操作的完整性)
- 一致性(一边银行账户-1000,一边取得1000)
- 永久性:取完钱后要回写到银行账户的数据库中,从而永久保存
Java事务
- JTA事务:该事务模式基于应用服务器或中间件(它们提供JTA事务的包装)
- 容器事务:掌握编程范式
事务差异
- JTA事务能跨数据库,因为其与JDBC事务后台实现机制不同
- 容器事务局限于EJB(企业Javabean)相关操作
事务接口
- 三个核心接口
- 黄色的实现类不止这些,因为更多场景
- 事务管理器:按照给定的事务规则(TransactionDefinition)进行提交,回滚操作,即其负责执行事务
- TransactionDefinition定义事务
- 根据事务状态值(TransactionStatus),控制事务
- getTransaction得到事务状态对象
事务属性
- 事务属性用于控制事务的精细化操作
- Spring支持事务属性范围的定义,即通过TransactionDefinition接口来实现
数据读取类型
- 脏读: 事务A修改数据,未提交,事务B访问并使用该数据(还没提交啊)
- 不可重复读:控制事务使其不重复读,从而避免脏读,即两次读取的数据不同
- 幻读:事务A想要修改某表的全部性别为男,事务B新插入一条数据,性别为女,事务A表示:怎么没有全部改完?
事务隔离级别
- 事务隔离级别影响事务的读取的结果
- 应用于事务并发时
- Serializable级别:每一个操作都要通过事务底层控制——》最慢
事务传播行为
- 理解为事务的嵌套
事务是否只读
- Oracle事务设置为只读——》则数据库不启动回滚字段,不记录回滚日志,从而减少数据库操作,提高访问效率
事务超时
- 明确是Spring下的事务管理才会出现上述说明
- 遇到检查型异常,则手动抛出运行期异常,那么也能回滚
Spring事务状态
- 该接口能判定和控制事务状态
事务管理
- Spring的事务管理是对Java事务管理的高度抽象和封装
- 事务管理器方式:手动处理较多事务管理代码
- 模板事务编程方式:封装好常用代码,使得coder能集中业务逻辑
- JdbcTemplate类:具体操作类,实现数据操作的切入点
基于事务管理器
TemplateUtils类
public class TemplateUtils {
private final static String dbDriver = "com.mysql.jdbc.Driver" ;
private final static String dbUrl = "jdbc:mysql://127.0.0.1:3306/test" ;
private final static String dbUser = "root";
private final static String dbPwd = "root";
private static BasicDataSource dataSource ;
//静态方式:创建连接数据源
static {
//创建DBCP简单数据源并初始化相关数据源属性
//private void createSimpleDataSource(){
dataSource = new BasicDataSource() ;
dataSource.setDriverClassName(dbDriver);
dataSource.setUrl(dbUrl);
dataSource.setUsername(dbUser);
dataSource.setPassword(dbPwd);
//指定数据库连接池初始连接数
dataSource.setInitialSize(10);
//设定同时向数据库申请的最大连接数
dataSource.setMaxTotal(50);
//设置连接池中保持的最少连接数量
dataSource.setMinIdle(5);
//}
}
public static TransactionTemplate getTransactionTemplate() {
PlatformTransactionManager txManager = new DataSourceTransactionManager(
dataSource);
return new TransactionTemplate(txManager);
}
public static JdbcTemplate getJdbcTemplate() {
return new JdbcTemplate(dataSource);
}
public static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return new NamedParameterJdbcTemplate(dataSource);
}
public static SimpleJdbcInsert getSimpleJdbcTemplate() {
return new SimpleJdbcInsert(dataSource);
}
/**
* //获取事务管理器:TransactionManager
* 根据需要,可以是如JDBC、Hibernate,这里定义JDBC事务管理其
* @return DataSourceTransactionManager
*/
public static DataSourceTransactionManager getDataSourceTransactionManager(){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
// 设置数据源:此事务数据源须和正式事务管理器的数据源一致
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
事务管理类
public class TransManagerExample {
public static void main(String[] args) {
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=112.5,name='炎黄传奇' where isbn='128-166-890-China' ");
//其它数据操作如增删
dtm.commit(ts); //如果不commit,则更新无效果
} catch (Exception e) {
dtm.rollback(ts);
e.printStackTrace();
}
}
}
-
关注编程范式
-
通过工具类获取事务管理器
-
操作接口自己写
-工具类- 基于数据源(连接数据库的基本通道)获取事务管理器
- 静态方式创建数据源并初始化相关属性
-
TransactionDefinition.ISOLATION_READ_UNCOMMITTED //静态变量方式提供传播行为属性
-
基于事务管理器方式有较多非业务逻辑代码,且事务处理的相同部分(提交,回滚)可抽象化——》基于事务模板
基于事务模板的编程
public class ProTransExample {
// 事务模板:第二种事务编程模式
// 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) {//后台会使用事务状态参数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();
}
}
});
}
//带返回值的方法
public Book findBookByIsbn(String isbn) {
TransactionTemplate tt = TemplateUtils.getTransactionTemplate();
Book book = null;
@SuppressWarnings("unchecked")
List<Map<String, Object>> books = (List<Map<String, Object>>) tt.execute(new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus arg0) {
JdbcTemplate jdbcTemplate = TemplateUtils.getJdbcTemplate();
return jdbcTemplate
.queryForList("select isbn,name,price,pubdate from books where isbn ='" + isbn + "'");
}
});
if (books != null) {// 封装获取的数据,转换为实体类,一些持久化框架会封装这些代码,主要通过DAO注解方式实现
Map<String, Object> m = (Map) books.get(0);
book = new Book();
book.setIsbn(m.get("isbn").toString());
book.setName(m.get("name").toString());
book.setPrice((Float) m.get("price"));
book.setPubdate((Date) m.get("pubdate"));
}
return book;
}
}
- 分为事务处理有无返回值,从而对应不同接口
- 获取事务模板
- 事务模板执行相应方法(execute方法传入匿名内部类,匿名内部类定义具体事务操作)
总结
- Spring事务管理是基于数据库事务管理之上——》要有数据源
- 除了JdbcTemplate还有其他可用类,如各种模板类
- 编程式事务管理不常用,主要体会后台运行机制
声明式事务管理
- 实现原理:基于AOP机制,对方法前后进行拦截
- 方法前,开启事务,方法后,根据方法执行情况,提交或回滚
- 声明式事务管理使得我们只关注业务逻辑,其他事务层管理交给spring容器
- 声明式业务管理不需要掺杂事务管理代码(XML方式来实现)
- 前三种类型过时
- 拦截器即事务的底层支持,新版不需要手动配置拦截器事务了,直接使用声明式拦截器模式:tx拦截器
- 全注释:注解方式
实现方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 引入数据库连接属性配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:database.properties" />
</bean>
<!-- 配置数据源 -->
<!-- #DBCP数据库连接池配置属性详细内容可参考官网描述:
#http://commons.apache.org/proper/commons-dbcp/configuration.html
-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="${initialSize}"></property> //引用属性文件的参数
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 连接池最小空闲 -->
<property name="minIdle" value="${minIdle}"></property>
</bean>
<!-- jdbc事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 2、注释模式事务:启动使用注解实现声明式事务管理的支持 -->
<tx:annotation-driven transaction-manager="txManager" />
<!-- 要创建的事务服务对象 -->
<bean id="bookService" class="com.mooc.service.BookServiceImpl">
<property name="dataSource" ref="dataSource"/> //数据源属性引用
</bean>
<!-- 1、通过事务通知的(AOP)模式实现事务管理
事务通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--事务语义定义... -->
<tx:attributes>
<!-- 以get开头的所有方法都为只读事务 -->
<tx:method name="find*" read-only="true"/>
<!-- 其它方法使用默认事务设置 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 确保上述事务通知对定义在BookService接口中的方法都起作用,
即对每个方法都开启一个对应的事务 -->
<aop:config>
//定义切点,具体事务拦截的方法定义见上面
//类或其子类的相应方法进行事务拦截
<aop:pointcut id="bookServiceOperation" expression="execution(* com.mooc.service.BookService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="bookServiceOperation"/>
//引用前面事务通知——》从而对方法切入事务(执行相应方法,会自动进行事务处理)
</aop:config>
</beans>
-
XML文件的头文件处作用:配置规范文件,引入对应命名空间,进行标准化XML的配置
-
引入外部属性文件(包含数据库连接参数)来配置数据库
-
配置事务管理器——》该事务管理器基于JDBC事务——》使用JDBC管理器(一定要有数据源)
-
关注tx拦截器属性配置
-
ApplicationContext ctx = new ClassPathXmlApplicationContext("/springContext.xml");//创建spring的IOC容器
- 引入配置文件(含数据源参数),然后进行数据源配置(也可以用连接池配置)
- 事务管理器配置(引用了数据源)
- 启动注解扫描
- 配置事务管理的bean
- 配置事务通知,如tx:advice
- AOP配置:切点和切面(引用了事务通知)
注解方式
@Transactional
public class XbeanServiceImpl implements XbeanService{
@Override
public Xbean getXbean(int id) {
Xbean xb = new Xbean() ;
xb.setName("业务Bean的ID="+id);
xb.setName("Bean默认名称");
return xb ;
}
@Override
public void insertXbean(Xbean xb) {
throw new UnsupportedOperationException();
}
}
- 要开启注解扫描: <tx:annotation-driven transaction-manager=“txManager” />
- 可以单独注解某一方法,对其进行事务管理,而不是注解整个类
- 通过注解可以定义事务属性
实例
图书实体类
public class Book {
private String isbn;
private String name;
private float price;
private Date pubdate;
public Book() {
}
public Book(String isbn, String name, float price, Date pubdate) {
super();
this.isbn = isbn;
this.name = name;
this.price = price;
this.pubdate = pubdate;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public Date getPubdate() {
return pubdate;
}
public void setPubdate(Date pubdate) {
this.pubdate = pubdate;
}
@Override
public String toString() {
return "Book{" +
"isbn='" + isbn + '\'' +
", name='" + name + '\'' +
", price=" + price +
", pubdate=" + pubdate +
'}';
}
}
操作实体类接口
ublic interface BookService {
/**
* 向数据库新增加书籍信息
* @param book 新增的书籍对象
*/
void insertBook(Book book);
/**
* 更新数据库里的对应书籍信息
* @param book 待更新的书籍对象
*/
void updateBook(Book book);
/**
* 根据唯一编号查找对应的书籍
* @param isbn 书籍的唯一编号
* @return 返回查找的书籍对象
*/
Book findBookByIsbn(String isbn);
}
接口实现类
/**
* POJO服务类
*/
public class BookServiceImpl extends JdbcDaoSupport implements BookService {
//r若不继承JdbcDaoSupport,则可按下面方式进行数据源的注入
/*private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}*/
@Override
public void insertBook(Book book) {
String sql= "insert books(isbn,name,price,pubdate) values"
+"('"+book.getIsbn()+"','"+book.getName()+"',"+book.getPrice()+",'"+book.getPubdate()+"')" ;
this.getJdbcTemplate().update(sql);
}
@Override
public void updateBook(Book book) {
String sql = "update books set name='"+book.getName()+"' where isbn='"+book.getIsbn()+"' ";
this.getJdbcTemplate().update(sql);
}
@Override
public Book findBookByIsbn(String isbn) {
String sql = "select isbn,name,price,pubdate from books where isbn ='" + isbn + "'" ;
List<Book> books = this.getJdbcTemplate().query(sql,new RowMapper<Book>() {
public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
Book bk = new Book();
bk.setIsbn(rs.getString("isbn"));
bk.setName(rs.getString("name"));
bk.setPrice(rs.getFloat("price"));
bk.setPubdate(rs.getDate("pubdate"));
return bk;
}
});
return books!=null?books.get(0):null ;
}
}
- 接口实现类要有数据源(和数据库交互),且最好继承帮助类
- 帮助类提供了getJdbcTemplate()方法,本质还是基于JDBC模板类进行操作
- 不继承帮助类,则要定义自己的模板,如上图注释处
编程式与声明式事务管理辨析
事务管理器选择
- 编程式能手动控制事务边界和属性
- 两者底层都是基于AOP机制
- 声明式的事务管理由容器控制,不需要用户
- 设置事务名称后可以为关联操作提供相应支持
- 跨数据库,分布式的事务管理使用JTA事务管理器
总结
- Spring封装传统的事务管理机制