【sping揭秘】22、事务管理
有关事务的楔子
什么是事务???
事务就是以可控的方式对数据资源进行访问的一组操作。
事务本身持有四个限定属性
原子性,一致性,隔离性,持久性
事务家族
Resource Manager RM,负责存储并管理系统数据资源的状态。数据库服务器、JMS消息服务器等都是。
Transaction Processing Monitor。 TPM或者 TP Monitor, 分布式事务场景中协调多个RM的事务处理。
Transaction Manager。TM 是TP Monitor的核心模块,直接负责多RM之间事务处理的协调工作,并且提供事务界定、事务上下文传播等功能接口。
Application。 事务边界的触发点
还可以根据事务的多寡区分:
全局事务和局部事务
全局事务牵扯多个RM参与,那么需要TP Monitor进行协调,可以称之为 分布式事务 来来,划重点
局部事务
群雄逐鹿下的java事务管理
使用JTA或者JCA提供支持
这里插播一条广告:
对于threadlocal大家了解多少呢?
ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
大家可以去
https://blog.csdn.net/lufeng20/article/details/24314381 看看,还不错
使用spring进行事务管理
编程式事务管理
直接使用platformtransactionManager进行编程式事务管理
这里我们封装一下jdbc的事务操作
package cn.cutter.start.transaction; import java.sql.Connection;
import java.sql.SQLException; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.support.DefaultTransactionStatus; /**
*
* spring 架构中对事务的核心接口对于jdbc的实现
* @author xiaof
*
*/
@Component
public class JdbcTransactionManager implements PlatformTransactionManager { @Autowired
@Qualifier("liferayDataSource1")
private DataSource dataSource; public JdbcTransactionManager() {
} public JdbcTransactionManager(DataSource dataSource) {
this.dataSource = dataSource;
} @Override
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { Connection connection; try {
//获取数据源的链接对象
connection = dataSource.getConnection();
connection.setAutoCommit(false);
//绑定当前链接到当前线程
TransactionResourceManager.bindResource(connection);
//返回默认事务状态对象
return new DefaultTransactionStatus(connection, true, true, false, true, null); } catch (SQLException e) {
throw new CannotCreateTransactionException("当前事务无法获取链接", e);
} } @Override
public void commit(TransactionStatus status) throws TransactionException { //获取事务链接对象,从当前线程threadlocal对象中获取,并且在获取之后要解绑,也就是提交之后,线程解绑事务
Connection connection = (Connection) TransactionResourceManager.unbindResource();
//获取链接之后提交
try {
connection.commit();
} catch (SQLException e) {
throw new TransactionSystemException("提交事务失败", e);
} finally {
try {
connection.close(); //关闭连接,释放资源
} catch (Exception e2) {
e2.printStackTrace();
}
}
} @Override
public void rollback(TransactionStatus status) throws TransactionException { //事务回滚,,事务回滚的时候,我们也要进行事务对象和当前线程的解绑
Connection connection = (Connection) TransactionResourceManager.unbindResource(); try {
connection.rollback();
} catch (SQLException e) {
throw new UnexpectedRollbackException("回滚事务失败", e);
} finally {
try {
connection.close();//不管如何,切记,连接资源一定要释放啊啊啊啊啊啊啊啊啊啊!!!!!!!!
} catch (SQLException e) {
e.printStackTrace();
}
} } }
这里注意了,开启事务管理的核心,就是不能设置自动提交
Spring事务处理
@Test
public void testTransactionManager() { ApplicationContext ctx = this.before(); // 循环向数据库插入数据
String sql = "insert into multipleDataSourceTestTable values (?, ?)";
PlatformTransactionManager transactionManager = (PlatformTransactionManager) ctx.getBean("jdbcTransactionManager");
DefaultTransactionDefinition definition = null;
TransactionStatus txStatus = null; try { // 创建事务对象
definition = new DefaultTransactionDefinition();
definition.setTimeout(20); txStatus = transactionManager.getTransaction(definition); for (int i = 0; i < 10; ++i) {
// 获取相应的数据连接 模拟项目中不同的业务场景获取jdbctemplate对象
// JdbcTemplate jdbcTemplate = (JdbcTemplate)
// ctx.getBean("multipleJdbcTemplate");
JdbcTemplate jdbcTemplate = new JdbcTemplate((DataSource) ctx.getBean("liferayDataSource1")); DataVo dataVo = new DataVo();
dataVo.setNum(i); jdbcTemplate.update(sql, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, dataVo.getNum());
ps.setString(2, dataVo.getName());
}
}); if (i == 7) {
throw new Exception("测试");
} } transactionManager.commit(txStatus);
} catch (DataAccessException e) {
transactionManager.rollback(txStatus);
e.printStackTrace();
} catch (Exception e) {
transactionManager.rollback(txStatus);
e.printStackTrace();
} finally {
transactionManager.rollback(txStatus);
} }
这里就是测试,在添加到第7个的时候,我们直接抛出异常,进行事务回滚,结果应是一条都没有插入进去
使用TransactionTemplate进行编程式事务管理
这个其实就是对platformTransactionManager的相关事务操作进行事务模板化封装
Spring对TransactionTemplate提供2个callback接口
分别是:TransactionCallback,TransactionCallbackWithoutResult
后面那个是一个抽象类,实现了TransactionCallback接口,吧里面的方法进行转发,就是转而调用本地的方法
首先把template加入sprig容器中
package cn.cutter.start.transaction; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate; /**
* spring的transactiontemplate加入spring容器
* @author xiaof
*
*/
@Component
public class TransactionTemplate1FactoryBean implements FactoryBean<TransactionTemplate>{ @Autowired
@Qualifier("jdbcTransactionManager")
private PlatformTransactionManager transactionManager; @Override
public TransactionTemplate getObject() throws Exception { //设置template数据对象
//这里template需要transactionManager对象
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); return transactionTemplate;
} @Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return TransactionTemplate.class;
} public PlatformTransactionManager getTransactionManager() {
return transactionManager;
} public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
} }
测试代码:
@Test
public void testTransactionTemplate() { //获取对应的bean对象,然后利用template进行事务操作
ApplicationContext ac = this.before();
//获取template
TransactionTemplate transactionTemplate = (TransactionTemplate) ac.getBean("transactionTemplate1FactoryBean");
JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate"); String sql = "insert into multipleDataSourceTestTable values (?, ?)"; //进行插入操作
transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// TODO Auto-generated method stub
//这里进行业务操作,一般是调用manager层,或者dao层的数据
for (int i = 0; i < 10; ++i) {
// 获取相应的数据连接 模拟项目中不同的业务场景获取jdbctemplate对象
// JdbcTemplate jdbcTemplate = (JdbcTemplate)
// ctx.getBean("multipleJdbcTemplate");
DataVo dataVo = new DataVo();
dataVo.setNum(i); jdbcTemplate.update(sql, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, dataVo.getNum());
ps.setString(2, dataVo.getName());
}
}); if (i == 7) {
//标识业务回滚
logger.warn("这里进行业务回滚");
status.setRollbackOnly();
} }
}
}); }
进行操作,当到达第七个的时候,我们会进行事务的回滚,吧数据还原
编程创建基于savepoint的嵌套事务
坑爹,这么老的技术了,我现在在网上还找不到对应的jar,我现在用的这个驱动不支持3.0???
如果是Oracle的话要用ojdbc14.jar支持保存点(Savepoint)
但是我这里用的是mysql!!!
这就很尴尬了,我们去mysql的官网下载一波
https://dev.mysql.com
@Test
public void testTransactionSavepoint() { //对于事务保存点的测试
//获取对应的bean对象,然后利用template进行事务操作
ApplicationContext ac = this.before(); // DataSource dataSource = (DataSource) ac.getBean("iomTestDataSource");
// TransactionTemplate transactionTemplate = (TransactionTemplate) ac.getBean("transactionTemplateIomTestFactoryBean");
//
// JdbcTemplate jdbctemplate = new JdbcTemplate(dataSource); //传入数据源 TransactionTemplate transactionTemplate = (TransactionTemplate) ac.getBean("transactionTemplate1FactoryBean");
JdbcTemplate jdbctemplate = (JdbcTemplate) ac.getBean("jdbcTemplateFactoryTestBean"); // String sql = "insert into item_check_info(order_id, chk_order_child_code, file_name, chk_result_code, chk_result_name, sheet_name, row_num) values(?, ?, ?, ?, ?, ?, ?)"; String sql = "insert into multipleDataSourceTestTable(num, name) values (?, ?)"; List params = new ArrayList();
params.add("1");
// params.add("1");params.add("1");params.add("1"); params.add("1");params.add("1");
List params2 = new ArrayList();
params2.add("2");params2.add("2");
// params2.add("2");params2.add("2");params2.add("2"); params2.add("2");params2.add("2"); transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) { //创建事务存放点
List params0 = new ArrayList();
// params0.add("0");params0.add("0");params0.add("0");params0.add("0");params0.add("0");
params0.add("0");params0.add("0"); jdbctemplate.update(sql, params0.toArray()); Object savePointBeforeInsert = status.createSavepoint();
//循环插入3条数据
try {
for(int i = 0; i < 3; ++i) {
params.add(i);
jdbctemplate.update(sql, params.toArray());
//在我们跳到第二条记录的时候,我们回退
if(i == 2) {
logger.info("事务抛出异常,回退数据");
throw new Exception("回退");
}
}
} catch (DataAccessException e) {
logger.info("真的异常,目前不处理");
e.printStackTrace();
} catch (Exception e) {
//在这里我们进行事务的回退
logger.info("开始回退操作,回退到指定位置");
status.rollbackToSavepoint(savePointBeforeInsert);
//c存放其余数据
jdbctemplate.update(sql, params2.toArray());
} finally {
//最后的最后一定要注意释放资源
status.setRollbackOnly();
}
}
});
}
声明式事务管理
主要是想解放数据库操作和事务操作的混杂,吧数据库操作和事务管理操作解耦剥离开来
XML元数据驱动的声明
Spring事务管理之扩展篇
理解并使用threadlocal
这个对象其实也不难理解,这个方法的作用就是把一个对象在不同的线程中存放不同的副本
在多线程中使用connection的时候,我们就可以使用这个,配置多数据源,在不同的线程中更换不同的数据源
package cn.cutter.start.transaction.multiDatasources; /**
* 标识数据源的个数
* @author xiaof
*
*/
public enum DataSources {
MAIN,INFO,DBLINK
}
建立数据源和枚举对象的映射关系
package cn.cutter.start.transaction.multiDatasources; import java.util.HashMap; import javax.annotation.PostConstruct;
import javax.sql.DataSource; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component; /**
* 存放不同的数据源的map
* @author xiaof
*
*/
@Component
public class DataSourceMap extends HashMap<Object, Object> { private final Log logger = LogFactory.getLog(DataSourceMap.class); // @PostConstruct //创建对象之前注入对象数据
@Autowired
public void initMapValue(@Qualifier("liferayDataSource1") DataSource mainDataSource,
@Qualifier("liferayDataSource2") DataSource infoDataSource,
@Qualifier("liferayDataSource2") DataSource dblinkDataSource) {
//初始化数据
logger.info("初始化多数据源:\n1->" + mainDataSource.getClass().getName() +
"\n2->" + infoDataSource.getClass().getName() +
"\n3->" + dblinkDataSource.getClass().getName());
this.put(DataSources.MAIN, mainDataSource);
this.put(DataSources.INFO, infoDataSource);
this.put(DataSources.DBLINK, dblinkDataSource);
} }
控制线程切换的时候获取的数据源key
package cn.cutter.start.transaction.multiDatasources; /**
* 多数据源进行管理,对于不同的线程绑定不同的数据源
* @author xiaof
*
*/
public class DataSourceTypeManager { //这里通过threadlocal进行管理
private static final ThreadLocal<DataSources> dsTypes = new ThreadLocal<DataSources>(){
@Override
protected DataSources initialValue() {
return DataSources.MAIN; // 初始化默认是main数据源
}
}; public static void set(DataSources dataSourcesType) {
dsTypes.set(dataSourcesType);
} public static DataSources get() {
return dsTypes.get();
} public static void reset() {
dsTypes.set(DataSources.MAIN);
}
}
最后我们的数据源分片信息配置
package cn.cutter.start.transaction.multiDatasources; import java.util.Map; import javax.annotation.Resource; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component; /**
* 数据源分片
* @author xiaof
*
*/
@Component
public class ThreadLocalVariableRountingDataSource extends AbstractRoutingDataSource { private final Log logger = LogFactory.getLog(ThreadLocalVariableRountingDataSource.class); /**
* 初始化数据输入
*/
@Resource(name="liferayDataSource1")
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
}; @Autowired
@Qualifier("dataSourceMap")
public void setTargetDataSources(Map<Object,Object> targetDataSources) {
logger.info("开始map注入:" + targetDataSources.size());
super.setTargetDataSources(targetDataSources);
}; @Override
protected Object determineCurrentLookupKey() {
//获取当前管理对象的数据源
return DataSourceTypeManager.get();
} }
最后使用的时候
@Test
public void multiThreadlocal() {
ApplicationContext ac = this.before();
//获取对象
ThreadLocalVariableRountingDataSource threadLocalVariableRountingDataSource = (ThreadLocalVariableRountingDataSource) ac.getBean("threadLocalVariableRountingDataSource"); DataSourceMap dataSourceMap = (DataSourceMap) ac.getBean("dataSourceMap");
//使用数据源,也就是说
JdbcTemplate jdbcTemplate = new JdbcTemplate(threadLocalVariableRountingDataSource);
//数据源切换方式,这样不同的数据,可以根据要求切换不同的数据源
DataSourceTypeManager.set(DataSources.MAIN);
DataSourceTypeManager.set(DataSources.INFO);
DataSourceTypeManager.set(DataSources.DBLINK); System.out.println("ok!!"); }
谈strategy模式在开发过程中的应用(策略模式)
关于策略模式,大家可以去参考我前面的博客,我不是吹,我那博客写的真的是一朵小金花,大家可以收藏一波,我完全不介意
http://www.cnblogs.com/cutter-point/p/5259874.html
我们在使用这个模式的时候,着眼于剥离客户端代码和关注点之间的依赖关系
Spring与JTA背后的奥秘
Spring的分布式事务强调使用JNDI服务获取datasource
那么为什么要用JNDI获取数据库连接呢?
略。。。
【sping揭秘】22、事务管理的更多相关文章
- Spring Boot 揭秘与实战(二) 数据存储篇 - 声明式事务管理
文章目录 1. 声明式事务 2. Spring Boot默认集成事务 3. 实战演练4. 源代码 3.1. 实体对象 3.2. DAO 相关 3.3. Service 相关 3.4. 测试,测试 本文 ...
- sping 对 hibernate进行事务管理--Annotation, xml, 大多数使用XML
1. UserServiceTest.java: package com.bjsxt.service; import org.junit.Test; import org.springframewor ...
- spring boot 或 spring 集成 atomikos jta 完成多数据源事务管理
前言:对于事务,spring 不提供自己的实现,只是定义了一个接口来供其他厂商实现,具体些的请看我的这篇文章: https://www.cnblogs.com/qiaoyutao/p/11289996 ...
- 27Spring_的事务管理_银行转账业务加上事务控制_基于tx.aop进行声明式事务管理
上一篇文章中,银行转账业务没有使用事务,会出现问题,所以这篇文章对上篇文章出现的问题进行修改. 事务 依赖 AOP , AOP需要定义切面, 切面由Advice(通知) 和 PointCut(切点) ...
- Spring第13篇—–Spring整合Hibernate之声明式事务管理
不容置疑的我们可以知道Spring的事务管理是通过AOP(AOP把我们的事务管理织入到我们的业务逻辑里面了)的方式来实现的,因为事务方面的代码与spring的绑定并以一种样板式结构使用.(面向切面编程 ...
- Spring对Hibernate事务管理
谈Spring事务管理之前我们想一下在我们不用Spring的时候,在Hibernate中我们是怎么进行数据操作的.在Hibernate中 我们每次进行一个操作的的时候我们都是要先开启事务,然后进行数据 ...
- Spring高级事务管理难点剖析
1Spring事务传播行为 所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播.Spring支持7种事务传播行为 PROPAGATION_REQUIRED(加入已有事务) 如果当前没 ...
- Spring 事务管理高级应用难点剖析--转
第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN& ...
- Spring对Hibernate事务管理【转】
在谈Spring事务管理之前我们想一下在我们不用Spring的时候,在Hibernate中我们是怎么进行数据操作的.在Hibernate中我们每次进行一个操作的的时候我们都是要先开启事务,然后进行数据 ...
随机推荐
- MySQL 详细学习笔记
Windows服务 -- 启动MySQL net start mysql -- 创建Windows服务 sc create mysql binPath= mysqld_bin_path(注意:等号与值 ...
- poj 1639 最小k度限制生成树
题目链接:https://vjudge.net/problem 题意: 给各位看一下题意,算法详解看下面大佬博客吧,写的很好. 参考博客:最小k度限制生成树 - chty - 博客园 https:/ ...
- CentOS7 firewalld防火墙配置
[root@ecs ~]# firewall-cmd --version //查看版本0.3.9 [root@ecs ~]# firewall-cmd --state //查 ...
- FortiGate安全策略说明
1.安全策略原理 1)为了对数据流进行统一控制,方便用户配置和管理,FGT设备引入了安全策略的概念.通过配置安全策略,防火墙能够对经过设备的数据流进行有效的控制和管理. 2)当防火墙收到数据报文时,把 ...
- 源发行版 1.8 需要目标发行版 1.8以及usage of api documented as @since 1.8+
Maven项目每个Module都有单独的pom.xml,如果不在pom.xml中进行配置,则默认将Module的Language Level设置为5.所以要在pom.xml文件中添加插件进行配置. & ...
- 微信小程序之----制作视频弹幕
1. 文件目录 使用微信, 长度单位使用 rpx 可以避免不同设备的样式调试问题 经验总结,之前一直使用px ,发现换了测试机就崩了 2. index.wxml页面设置v ...
- Vmware unknow Interface ens33
vmare打开虚拟网络编辑器,按图示操作
- Jmeter获取redis数据
背景:jmeter写注册登录接口时,需要获取验短信验证码,一般都是存在数据库,但我们的开发把验证码存到redis里面了 步骤:1.写个redis工具类 2.打成jar包,导入jmeter lib\ex ...
- UI与开发的必备神器!— iDoc一键适配不同平台尺寸(iDoc201902-2新功能)
一.自动换算不同平台尺寸在一个项目从设计到开发的过程中,为了适配不同设备,一份设计稿,UI需要花大量的时间去制作各种尺寸的切图,耗时耗力. 那有没有一种高效的办法,让UI只需要设计一份设计稿就可以了呢 ...
- spring jdbc 记录
@Repository("com.example.demo.dao.impl.SmpUserDaoImpl") public class SmpUserDaoImpl implem ...