【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.****.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("初始化多数据源: 1->" + mainDataSource.getClass().getName() + " 2->" + infoDataSource.getClass().getName() + " 3->" + 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获取数据库连接呢?
略。。。