声明式的事务管理
a)事务加在DAO层还是Service层?
UserService调了UserDao,调了它的save方法,而UserDao它会去访问数据库。
在Dao里面,事务的还是就是save的开始,事务的结束就是save的结束,这是最直观的。也可以用AOP来写,Spring已经给你写好了。
那么事物的边界(beginTransaction()事务开始和事物结束getTransaction().commit())加在哪里比较好呢?
Dao一般都是固定的单一的CRUD,也就是核心的事务操作,像加日志这种事务的准备或者锦上添花的东西我们可以加在Service层。
我们在这里用一个例子说明:
在数据库创建一个日志信息表t_Log,我们准备在save事务之后向数据库添加一个日志信息,来记录User的存储情况。
首先回顾一下UserDao的存储:
User.java:
package cn.edu.hpu.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class User { private int id; private String name; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
UserDaoImpl.java:
package cn.edu.hpu.dao.Impl; import javax.annotation.Resource; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.stereotype.Component; import cn.edu.hpu.dao.UserDao; import cn.edu.hpu.model.User; @Component("u") public class UserDaoImpl implements UserDao{ private SessionFactory sessionFactory; public SessionFactory getSessionFactory() { return sessionFactory; } @Resource public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public void save(User u) { Session s=sessionFactory.openSession(); s.beginTransaction(); s.save(u); s.getTransaction().commit(); System.out.println("add success!!"); } }
之后我添加了日志:
package cn.edu.hpu.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="t_log") public class Log { private int id; private String msg; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
LogDaoImpl.java:
package cn.edu.hpu.dao.Impl; import javax.annotation.Resource; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.stereotype.Component; import cn.edu.hpu.dao.LogDao; import cn.edu.hpu.model.Log; @Component("logDao") public class LogDaoImpl implements LogDao{ private SessionFactory sessionFactory; public SessionFactory getSessionFactory() { return sessionFactory; } @Resource public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public void save(Log log) { Session s=sessionFactory.openSession(); s.beginTransaction(); s.save(log); s.getTransaction().commit(); System.out.println("Log add success!!"); } }
别忘记在配置文件beans.xml中加Log实体类<value>cn.edu.hpu.model.Log</value>:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="annotatedClasses"> <list> <value>cn.edu.hpu.model.User</value> <value>cn.edu.hpu.model.Log</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </prop> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean>
现在我们需要将日志添加事务放在存储User的后面,是放在Dao里面好还是Service里面好呢?按照前面说的,加载Service里面比较合适:
package cn.edu.hpu.service; import javax.annotation.Resource; import org.springframework.stereotype.Component; import cn.edu.hpu.dao.LogDao; import cn.edu.hpu.dao.UserDao; import cn.edu.hpu.model.Log; import cn.edu.hpu.model.User; @Component("userService") public class UserService { private UserDao userDao; private LogDao logDao; public UserDao getUserDao() { return userDao; } @Resource(name="u") public void setUserDao(UserDao userDao) { this.userDao = userDao; } public LogDao getLogDao() { return logDao; } @Resource(name="logDao") public void setLogDao(LogDao logDao) { this.logDao = logDao; } public void add(User u){ this.userDao.save(u); Log log=new Log(); log.setMsg("a user saved!"); logDao.save(log); } }
测试:
package cn.edu.hpu.service; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.edu.hpu.dao.UserDao; import cn.edu.hpu.model.User; public class UserServiceTest { @Test public void testAdd() throws Exception{ ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); UserService userService=(UserService)ctx.getBean("userService"); System.out.println(userService.getClass()); User u=new User(); u.setName("jack"); userService.add(u); ctx.destroy(); } }
测试结果:
class cn.edu.hpu.service.UserService
Hibernate: insert into User (name) values (?)
add success!!
Hibernate: insert into t_log (msg) values (?)
Log add success!!
Log日志添加成功!
这都是我们是手动添加的事务开始于结束(s.beginTransaction();与s.getTransaction().commit();),那么如何用Spring的机制来自动添加事务呢?下面先说一下Annotation的实现:
b)annotation
i.加入annotation.xsd(关于tx的)
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
ii.加入txManager bean
因为你结合的是hibernate,所以事务管理也是使用hibernate的事务管理。
所用类org.springframework.orm.hibernate3.HibernateTransactionManager
HibernateTransactionManager就是在方法之前加点东西,方法之后加点东西,也就是是一个Aspect切面类,应用了AOP。你让它管理事务,必须要告诉他数据库的连接是谁,采取的方式是将sessionFactory注进去:
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"> </bean>
iii.<tx:annotation-driven
<tx:annotation-driven transaction-manager="txManager"/>
iv.在需要事务的方法上加:@Transactional
例如你需要在add方法上有事务,那就在add方法上加@Transactional
@Transactional public void add(User u){ this.userDao.save(u); Log log=new Log(); log.setMsg("a user saved!"); this.logDao.save(log); }
它会自动在前后帮你加上关于业务的事务逻辑
这个时候User的save和Log的Save里面的s.beginTransaction();与s.getTransaction().commit();就不用写了
public void save(User u) { Session s=sessionFactory.getCurrentSession(); s.save(u); System.out.println("add success!!"); } public void save(Log log) { Session s=sessionFactory.openSession(); s.save(log); System.out.println("Log add success!!"); }
v.需要注意,使用SessionFactory.getCurrentSession 不要使用OpenSession
测试:
package cn.edu.hpu.service; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.edu.hpu.dao.UserDao; import cn.edu.hpu.model.User; public class UserServiceTest { @Test public void testAdd() throws Exception{ ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); UserService userService=(UserService)ctx.getBean("userService"); User u=new User(); u.setName("jack"); userService.add(u); ctx.destroy(); } }
测试结果:
Hibernate: insert into User (name) values (?)
add success!!
Hibernate: insert into t_log (msg) values (?)
Log add success!!
结果存储成功,说明@Transactional注解起作用了。而且如果@Transactional注解的方法的事务Runtime时出了问题,会自动回滚,测试:
在Log的save后面加一个RuntimeException异常:
public void save(Log log) { Session s=sessionFactory.openSession(); s.save(log); throw new RuntimeException("Error"); }
测试:
@Test public void testAdd() throws Exception{ ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); UserService userService=(UserService)ctx.getBean("userService"); User u=new User(); u.setName("jack"); userService.add(u); ctx.destroy(); }
结果:
Hibernate: insert into User (name) values (?)
add success!!
Hibernate: insert into t_log (msg) values (?)
没有将Log添加进去,说明事务回滚了。这是spring对于hibernate事务的管理。通常情况下add方法里面都要加try-catch包围的,有了上述管理,就不用try-catch了。
3.@Transactional 有关的设置
@Transactional 注解是用来指定接口、类或方法必须拥有事务语义的元数据。 如:“当一个方法开始调用时就开启一个新的只读事务,并停止掉任何现存的事务”。 默认的 @Transactional 设置如下:
事务传播设置是 PROPAGATION_REQUIRED
事务隔离级别是 ISOLATION_DEFAULT
事务是 读/写
事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚
这些默认的设置当然也是可以被改变的。 @Transactional 注解的各种属性设置总结如下:
表 9.3. @Transactional 注解的属性
属性 类型 描述
propagation 枚举型:Propagation 可选的传播性设置
isolation 枚举型:Isolation 可选的隔离性级别(默认值:ISOLATION_DEFAULT)
readOnly 布尔型 读写型事务 vs. 只读型事务
timeout int型(以秒为单位) 事务超时
rollbackFor 一组 Class 类的实例,必须是Throwable的子类一组异常类,遇到时必须进行回滚。默认情况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。
rollbackForClassname 一组Class类的名字,必须是Throwable的子类 一组异常类名,遇到时 必须进行回滚
noRollbackFor 一组 Class 类的实例,必须是Throwable的子类一组异常类,遇到时必须不回滚。
noRollbackForClassname 一组Class类的名字,必须是Throwable 的子类 一组异常类,遇到时 必须不回滚
在写代码的时候,不可能对事务的名字有个很清晰的认识,这里的名字是指会在事务监视器(比如WebLogic的事务管理器)或者日志输出中显示的名字, 对于声明式的事务设置,事务名字总是全限定名+"."+事务通知的类的方法名。比如BusinessService类的handlePayment(..)方法启动了一个事务,事务的名称是:
com.foo.BusinessService.handlePayment
最常用的是@Transactional(propagation=Propagation.REQUIRED)
是propagation一堆属性中唯一一个需要记住的。
意思是在当前的这个环境中,别人调我这个方法,比如说已经有了一个Transaction了,就用原来那个,把自己作为Transaction的一部分加进去就行了,如果没有就创建一个新的。
转载请注明出处:http://blog.csdn.net/acmman/article/details/44651921