Hibernate session在service实现事务
当我们在使用Hibernate作为数据库操作的类库时,我们一般在DAO层里与数据库相关的操作,把业务逻辑写在service层里。但是如果我们的项目比较小,那么直接在dao层里写事务也是可以的,这个就是看个人了,没有什么特别的规定。但是如果项目比较大,那么DAO应该只做单纯的数据库的操作,service写事务的操作,即整个业务逻辑。
例如:业务逻辑要求向数据库中的用户表增加一个用户,同时向日志表中加入一条日志,而这需要调用DAO的两个方法(UserDao的saveUser和LogDao的saveLog)。这显然是一个事务,也就是如果一个操作出现了问题,就要回滚到初始的状态。那么如何在Service层控制事务呢,本文就以此例的代码说明。
在DAO进行Session事务出现的问题
我们先看看在DAO层里写Hibernate的session的事务。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.xxg;
import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory(); private static SessionFactory buildSessionFactory() { try { // Create the SessionFactory from hibernate.cfg.xml return new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { // Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } } |
创建用户表T_user(id,username)和日志表T_log(id,content),以及它们对应的实体类User、Log及映射文件,这里就不一一贴出代码。
01 02 03 04 05 06 07 08 09 10 11 12 13 | public class UserDao {
public void saveUser(User user){ SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory Session session = sessionFactory.openSession();// openSession session.beginTransaction(); //开始事务
session.save(user);
session.getTransaction().commit(); //事务提交 session.close(); //关闭session } } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | public class LogDao {
public void saveLog(Log log){ SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory Session session = sessionFactory.openSession();// openSession session.beginTransaction(); //开始事务
session.save(log);
session.getTransaction().commit(); //事务提交 session.close(); //关闭session }
} |
接下来我们看看在service中写一个业务逻辑
01 02 03 04 05 06 07 08 09 10 11 12 13 | public class TestService {
public void save(User user){ UserDao userDao = new UserDao(); userDao.saveUser(user);
LogDao logDao = new LogDao(); Log log = new Log(); log.setContent("插入一个用户"); logDao.saveLog(log); }
} |
可以看到,我们在两个DAO里写了数据库的事务,session.beginTransaction()显示声明事务的开始。
这样写是不对的,因为这两个事情作为一个事务来进行的,会出现一个事务成功提交,而另外一个可能提交失败,导致不一致的情况,这样这两个操作不算是一个事务transaction,所以这么写就是一个失败的事务。
因此,我们要将事务在service中进行声明。
在service层写session的数据库事务
为了将事务控制在service层,我们不能够在dao中打开关闭各自的事务。而是在service层打开、关闭事务,然后在每一个dao中利用sessionFactotry的getCurrentSession方法获取当前线程的session,且每一个dao中只负责操作数据而不用操作事务的打开和关闭,这样就实现了service涉及的dao共用同一个session且每一个dao操作都处在同一个事务管理中。service层可以显示的打开关闭事务,也可以通过spring的事务管理器利用aop切面生成代理类原理智能的打开关闭事务。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class HibernateUtil { public static final ThreadLocal session = new ThreadLocal();
public static final SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure().buildSessionFactory(); } catch ( Throwable ex ) { throw new ExceptionInInitializerError( ex ); } }
public static Session currentSession() throws HibernateException { Session s = session.get(); if ( s == null ) { s = sessionFactory.openSession(); session.set( s ); } return(s); }
public static void closeSession() throws HibernateException { Session s = session.get(); if ( s != null ) { s.close(); } session.set( null ); } } |
接下来,我们将事务放在service中。看代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | public class TestService {
public void save(User user){
SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory Session session = sessionFactory.getCurrentSession();//getCurrentSession session.beginTransaction();//事务开始
UserDao userDao = new UserDao(); userDao.saveUser(user);
LogDao logDao = new LogDao(); Log log = new Log(); log.setContent("插入一个用户"); logDao.saveLog(log); session.getTransaction().commit();//事务提交 }
} |
01 02 03 04 05 06 07 08 09 10 11 | public class LogDao {
public void saveLog(Log log) throws RuntimeException{ SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory Session session = sessionFactory.getCurrentSession(); //getCurrentSession
session.save(log);
throw new RuntimeException(); } } |
01 02 03 04 05 06 07 08 09 10 11 | public class UserDao {
public void saveUser(User user){ SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); //获取SessionFactory Session session = sessionFactory.getCurrentSession();//getCurrentSession
session.save(user);
}
} |
通过getCurrentSession()可以获得当前线程的session对象,通过它来进行共享session。这样事务就从service开始,然后再service结束。