锁定老帖子 主题:Spring中事物管理一二
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-09-07
由于对Java中的事物管理的概念(本地事物和jta事物)不是很清楚,Google好多文章都是很抽象,希望在论坛中大家提供点比较实际的程序代码案例和必须使用jta事物的场景;好啦,下面给个具体的代码(mybatis,spring),做下说明,我使用两个数据源,两个sqlSessionFactory,两个事物管理器,分别用于两种方式(mybatis的MappBean和MappXML)操作数据库。
spring配置如下: <bean id="dataSource1" class="com.jolbox.bonecp.BoneCPDataSource"> <constructor-arg index="0" ref="dbConfig1"/> </bean> <bean id="dataSource2" class="com.jolbox.bonecp.BoneCPDataSource"> <constructor-arg index="0" ref="dbConfig2"/> </bean> <!-- <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" /> --> <bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="mapperLocations" value="classpath*:edu/silence/mybatis/dao/**/*.xml" /> <property name="dataSource" ref="dataSource1" /> </bean> <bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- <property name="mapperLocations" value="classpath*:edu/silence/mybatis/dao/**/*.xml" /> --> <property name="dataSource" ref="dataSource2" /> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory1" /> </bean> <bean id="userDAO" class="edu.silence.mybatis.dao.impl.userDAO"> <property name="sqlSessionTemplate" ref="sqlSession"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource1" /> </bean> <bean id="springTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource2" /> </bean> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="edu.silence.mybatis.dao.UserMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory2" /> </bean> <bean id="userService" class="edu.silence.mybatis.service.impl.UserService"> <property name="userMapper" ref="userMapper"/> <property name="userDAO" ref="userDAO"/> </bean> <tx:advice id="txAdvice" transaction-manager="springTransactionManager"> <tx:attributes> <tx:method name="find*" propagation="REQUIRED" read-only="true"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="*" propagation="SUPPORTS"/> </tx:attributes> </tx:advice> <tx:advice id="txAdvice1" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="find*" propagation="REQUIRED" read-only="true"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="*" propagation="SUPPORTS"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="interceptorPointCuts" expression="execution(* *..service.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" /> <aop:advisor advice-ref="txAdvice1" pointcut-ref="interceptorPointCuts" /> </aop:config>
dao代码如下: public class userDAO implements IUserDAO { private SqlSessionTemplate sqlSessionTemplate; public SqlSessionTemplate getSqlSessionTemplate() { return sqlSessionTemplate; } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSessionTemplate = sqlSessionTemplate; } @Override public User findUser(String uid) { return (User) sqlSessionTemplate.selectOne("edu.silence.mybatis.dao.UserMapper.selectByPrimaryKey", uid); } @Override public int saveUser(User user) { boolean flag = true; if(flag){ throw new RuntimeException("事物回滚......"); } return sqlSessionTemplate.insert("edu.silence.mybatis.dao.UserMapper.insertSelective", user); } }
业务层代码如下: private UserMapper userMapper; private IUserDAO userDAO; public UserMapper getUserMapper() { return userMapper; } public void setUserMapper(UserMapper userMapper) { this.userMapper = userMapper; } public IUserDAO getUserDAO() { return userDAO; } public void setUserDAO(IUserDAO userDAO) { this.userDAO = userDAO; } @Override public User findUser(String uid) { // TODO Auto-generated method stub return null; } @Override public int saveUser(User user) { user.setUid(UUID.randomUUID().toString()); int i = userMapper.insert(user); user.setAlias("就是我"); user.setUid(UUID.randomUUID().toString()); int j = userDAO.saveUser(user); if(i!=1 && j!=1){ throw new RuntimeException("事物回滚......"); } return i+j; } 测试代码如下: public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:**/*application*.xml"); IUserService service = ctx.getBean("userService", IUserService.class); User user = new User(); user.setAlias("Silence"); user.setBirthday(new Date()); user.setEmail("silence@email.com"); user.setGender(1); user.setMobile("18674970916"); user.setPassword("silence"); user.setPhoto("head.jpg"); user.setRealname("china"); user.setRole(1); user.setStudystep(1); user.setUsername("silence"); service.saveUser(user); }
上面使用本地事物,都能正常保存,如果失败都可以同时回滚,所以不明白jta到里在那个具体场景必须使用;也请各位讲解下上面代码在事物性方面有那些问题。
在下先O(∩_∩)O谢谢!!! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-09-08
其实就是jdbc事物和jta事物
jdbc就是基于一个连接内的实物 可以作为一个原子操作 jta是针对一个是原子操作内有多个数据源或者简单说有多个jdbc,然后把jdbc纳入jta容器管理,作为一个原子操作 |
|
返回顶楼 | |
发表时间:2011-09-08
最后修改:2011-09-08
a book for you --- <<Java Transaction>>
|
|
返回顶楼 | |
发表时间:2011-09-09
最后修改:2011-09-09
谢谢回复,算是明白啦! 其中上面的代码使用的是两个数据源,两个事务管理器;它们的工作结果大部分情况下能够保持和jta事务运行的结果,但不能完全保证,毕竟它们是俩个事务,只是两个事务定义了一个相同的事务边界。 把上面的代码使用jta实现(jotm2.2.1),唯一修改的是配置文件,和新增一个工厂bean(spring3中没有对jotm的官方支持),俺是从spring直接的版本拷贝过来的,代码如下: public class JotmFactoryBean implements FactoryBean, DisposableBean { private Current jotmCurrent; private Jotm jotm; public JotmFactoryBean() throws NamingException { // Check for already active JOTM instance. this.jotmCurrent = Current.getCurrent(); // If none found, create new local JOTM instance. if (this.jotmCurrent == null) { // Only for use within the current Spring context: // local, not bound to registry. this.jotm = new Jotm(true, false); this.jotmCurrent = Current.getCurrent(); } } /** * Set the default transaction timeout for the JOTM instance. * <p>Should only be called for a local JOTM instance, * not when accessing an existing (shared) JOTM instance. */ public void setDefaultTimeout(int defaultTimeout) { this.jotmCurrent.setDefaultTimeout(defaultTimeout); // The following is a JOTM oddity: should be used for demarcation transaction only, // but is required here in order to actually get rid of JOTM's default (60 seconds). try { this.jotmCurrent.setTransactionTimeout(defaultTimeout); } catch (SystemException ex) { // should never happen } } /** * Return the JOTM instance created by this factory bean, if any. * Will be <code>null</code> if an already active JOTM instance is used. * <p>Application code should never need to access this. */ public Jotm getJotm() { return this.jotm; } public Object getObject() { return this.jotmCurrent; } public Class getObjectType() { return this.jotmCurrent.getClass(); } public boolean isSingleton() { return true; } /** * Stop the local JOTM instance, if created by this FactoryBean. */ public void destroy() { if (this.jotm != null) { this.jotm.stop(); } } } xml修改如下: <!-- JOTM本地实例 --> <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" /> <!-- JTA事务管理器 --> <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <!-- 指定userTransaction属性 --> <property name="userTransaction" ref="jotm" /> </bean> <!-- XAPool配置,内部包含了一个XA数据源,对应test1数据库 --> <bean id="topicDS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> <property name="dataSource"> <!-- 内部XA数据源 --> <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> <property name="transactionManager" ref="jotm" /> <property name="driverName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://10.1.0.117:3306/test?characterEncoding=utf8" /> </bean> </property> <property name="user" value="mysql" /> <property name="password" value="silence" /> </bean> <!-- XAPool配置,内部包含了一个XA数据源,对应test2数据库 --> <bean id="postDS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> <property name="dataSource"> <!-- 内部XA数据源 --> <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> <property name="transactionManager" ref="jotm" /> <property name="driverName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://10.1.0.117:3306/test1?characterEncoding=utf8" /> </bean> </property> <property name="user" value="mysql" /> <property name="password" value="silence" /> </bean> <bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="mapperLocations" value="classpath*:edu/silence/mybatis/dao/**/*.xml" /> <property name="dataSource" ref="topicDS" /> </bean> <bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="postDS" /> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory1" /> </bean> <bean id="userDAO" class="edu.silence.mybatis.dao.impl.userDAO"> <property name="sqlSessionTemplate" ref="sqlSession"/> </bean> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="edu.silence.mybatis.dao.UserMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory2" /> </bean> <bean id="userService" class="edu.silence.mybatis.service.impl.UserService"> <property name="userMapper" ref="userMapper"/> <property name="userDAO" ref="userDAO"/> </bean> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="find*" propagation="REQUIRED" read-only="true"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="*" propagation="SUPPORTS"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="interceptorPointCuts" expression="execution(* *..service.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" /> </aop:config>
|
|
返回顶楼 | |
发表时间:2011-09-09
你这个ibatis配置有点老,我记得,有个什么scaner类,只要配置基础包,不需要写具体mapper的类名就可以自动找到所有mapper!!!!!!!!!
关于事务,你这里都是用切点配置的 我习惯于用注解配置,那样可以灵活的为每个方法配置详细事务属性!!!!!!!!! |
|
返回顶楼 | |
发表时间:2011-09-10
楼上所说的是 org.mybatis.spring.mapper.MapperScannerConfigurer类吧,谢谢提醒!个人也是第一次发帖,没注意那么多,写完可以运行就每管那么多啦 我上面的代码改写成如下: <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="edu.silence.mybatis.dao" /> <property name="sqlSessionFactory" ref="sqlSessionFactory2"/> </bean> spring提供四种事务配置方式 1、使用代理 2、使用拦截器 3、使用tx标签 4、使用注解 这些都是spring逐步发展添加进来的,个人平时用得最多的是后面两种,如果是一个新项目,或者项目命名比较统一时 我个人一般使用第三种,如果是遗留项目合作项目命名不是和统一,我使用第4种;第三种只要写一个配置就可以, 第4种要每个去写注解,比较浪费O(∩_∩)O~;两种各有各得好处,谢谢大家的回复,有什么经验分享的请大家跟帖,谢谢。。。
|
|
返回顶楼 | |
发表时间:2011-09-10
lz是讨论分布式事务?
|
|
返回顶楼 | |
发表时间:2011-09-10
blog 写道
楼上所说的是 org.mybatis.spring.mapper.MapperScannerConfigurer类吧,谢谢提醒!个人也是第一次发帖,没注意那么多,写完可以运行就每管那么多啦 我上面的代码改写成如下: <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="edu.silence.mybatis.dao" /> <property name="sqlSessionFactory" ref="sqlSessionFactory2"/> </bean> spring提供四种事务配置方式 1、使用代理 2、使用拦截器 3、使用tx标签 4、使用注解 这些都是spring逐步发展添加进来的,个人平时用得最多的是后面两种,如果是一个新项目,或者项目命名比较统一时 我个人一般使用第三种,如果是遗留项目合作项目命名不是和统一,我使用第4种;第三种只要写一个配置就可以, 第4种要每个去写注解,比较浪费O(∩_∩)O~;两种各有各得好处,谢谢大家的回复,有什么经验分享的请大家跟帖,谢谢。。。
5. Spring 提供了原子性的事务操作, TransactionTemplate,和其他 Template 一样操作,可以控制到代码块事务。
|
|
返回顶楼 | |
发表时间:2011-09-10
代码中如果同时使用两个数据源,或者还有其他,比如 JMS,理论必须使用JTA。
楼主根本不能保证事务一致性。 在一个方法(作为一个操作单元)中,有如下操作。 1.对 datasource 1 插入数据 2.对 datasource 2 插入数据 如果1 成功,2 失败,作为一个事务单元,它应该是失败的,所有事务应该回滚,但楼主方法无法控制这种情况发生。 |
|
返回顶楼 | |
发表时间:2011-09-17
最后修改:2011-09-17
感谢hantsy 同学给俺补全,spring提供的TransactionTemplate,俺确实没用过;加上,就有五种,不过就不能叫"spring提供四种事务配置方式",俺也不知道叫什么 列出来如下:
1、使用代理 2、使用拦截器 3、使用tx标签 4、使用注解 5、TransactionTemplate(编程式事务)
hantsy 写道
代码中如果同时使用两个数据源,或者还有其他,比如 JMS,理论必须使用JTA。
楼主根本不能保证事务一致性。 在一个方法(作为一个操作单元)中,有如下操作。 1.对 datasource 1 插入数据 2.对 datasource 2 插入数据 如果1 成功,2 失败,作为一个事务单元,它应该是失败的,所有事务应该回滚,但楼主方法无法控制这种情况发生。
对于这个"作为一个事务单元,它应该是失败的,所有事务应该回滚",确实是这样,一个事务管理多个数据源,保证操作原子性;我上面的方法并不是真正用来解决多数据源的事务,只是个人为对事务的一种探索;两个事务管理在同一个事务边界中的两个数据源的操作,一对一管理,在平常情况下一定程度上保证一个原子操作的假象。我尝试这种方式是想对本地事务和jta事务的学习;这段时间在学习这方面的东东,在开发中,我经常是在对业务设计上规避多数据源上的事务处理,尽可能做到短事务,jta事务对性能影响是本地事务的好多倍。
|
|
返回顶楼 | |