目录 [hide]
当我们在使用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中,我们需要更改HibernateUtil的代码才能实现。否则使用上面的那个不能达到我们的需求。在这个新的HibernateUtil代码中,利用了ThreadLocal
的线程内的局部变量来保存hibernate的session对象。这样就可以在不同的class中使用同一个session对象,而不用传递参数。
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结束。
getCurrentSession()与openSession()区别
getCurrentSession创建的session会和绑定到当前线程,而openSession不会。
getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭
采用getCurrentSession()创建的session会绑定到当前线程中,而采用openSession() 创建的session则不会 * 采用getCurrentSession()创建的session在commit或rollback时会自动关闭,而采用openSession() 创建的session必须手动关闭
2、使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置:
- 如果使用的是本地事务(jdbc事务) <property name=”hibernate.current_session_context_class”>thread</property>
- 如果使用的是全局事务(jta事务) <property name=”hibernate.current_session_context_class”>jta</property>
getCurrentSession()与openSession()关联
在 SessionFactory 启动的时候, Hibernate 会根据配置创建相应的 CurrentSessionContext ,在 getCurrentSession() 被调用的时候,实际被执行的方法是 CurrentSessionContext.currentSession() 。在 currentSession() 执行时,如果当前 Session 为空, currentSession 会调用 SessionFactory 的 openSession 。所以 getCurrentSession() 对于 Java EE 来说是更好的获取 Session 的方法。
参考资料
- http://blog.csdn.net/xiao__gui/article/details/7695698
- http://blog.csdn.net/loveyout/article/details/4193894