gpt4 book ai didi

java - 没有@Transactional的Hibernate sessionFactory.getCurrentSession()

转载 作者:行者123 更新时间:2023-12-02 12:21:24 37 4
gpt4 key购买 nike

在Hibernate4中,Spring4
我想使用没有sessionFactory.getCurrentSession()批注的@Transactional。有什么办法吗?

最佳答案

简单的答案是:,您当然可以,因为SessionFactory.getCurrentSession()只是接口(interface)的一种方法,因此您可以编写自己的实现类,该类可以为您提供所需的任何Session

但是,这可能不是您要查找的答案。

我们一直在问自己一个类似的问题:为什么在将Hibernate与Spring的事务管理一起使用时,为什么必须将@Transactional添加到我们的所有方法中,甚至那些仅包含SELECT数据并因此不需要在a上下文中执行的方法呢?数据库事务?

答案不是那么简单,但让我们看一下其中涉及的一些管道,看看是否可以理解。

首先,正如SO其他地方所提到的,Session的思想从根本上与事务的思想联系在一起。 javadoc中有一个关于Session接口(interface)的提示:

The lifecycle of a Session is bounded by the beginning and end of a logical transaction. (Long transactions might span several database transactions.)



并研究 @Transactional类的javadoc确认其目的是指示何时应在“事务上下文”中执行代码,而该事务上下文不一定是数据库事务的上下文。

这也解释了为什么Spring的 @Transactional批注允许您设置属性 readOnly=true,但稍后会进行更多说明。

回到Spring4和Hibernate4,当您调用 sessionFactory.getCurrentSession()时,它实际上在 SessionFactoryImpl中执行以下代码:
public Session getCurrentSession() throws HibernateException {
if ( currentSessionContext == null ) {
throw new HibernateException( "No CurrentSessionContext configured!" );
}
return currentSessionContext.currentSession();
}

因此它实际上是由 CurrentSessionContext类处理的 SpringSessionContext实现的(除非您使用的是JTA,并且您可能不想打开Pandora的盒子):
@Override
public Session currentSession() throws HibernateException {
Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
if (value instanceof Session) {
return (Session) value;
}
else if (value instanceof SessionHolder) {
SessionHolder sessionHolder = (SessionHolder) value;
Session session = sessionHolder.getSession();
if (!sessionHolder.isSynchronizedWithTransaction() &&
TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, this.sessionFactory, false));
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.equals(FlushMode.MANUAL) &&
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO);
sessionHolder.setPreviousFlushMode(flushMode);
}
}
return session;
}

if (this.transactionManager != null) {
try {
if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) {
Session session = this.jtaSessionContext.currentSession();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));
}
return session;
}
}
catch (SystemException ex) {
throw new HibernateException("JTA TransactionManager found but status check failed", ex);
}
}

if (TransactionSynchronizationManager.isSynchronizationActive()) {
Session session = this.sessionFactory.openSession();
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.MANUAL);
}
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, this.sessionFactory, true));
TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
sessionHolder.setSynchronizedWithTransaction(true);
return session;
}
else {
throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");
}
}

并说明了为什么会看到此异常:

Could not obtain transaction-synchronized Session for current thread



当您在未使用 sessionFactory.getCurrentSession()注释的方法中调用 @Transactional时,因为 TransactionSynchronizationManager.isSynchronizationActive()返回 false,因为没有 @Transactional注释,切入点未执行,这将创建一个同步事务。 (有关更多信息,请参见 org.springframework.transaction.interceptor.TransactionInterceptor。)

因此,这使我们回到了用例,即当我们只想对数据库执行 PlatformTransactionManager时,我们不需要调用 SELECT及其数据库事务代码的开销。实现此目的的简单方法是不调用 sessionFactory.getCurrentSession(),而是显式打开 Session。例如,使用以下Spring托管代码:
public class MyHibernateService {   

@Autowired
private SessionFactory sessionFactory;

protected Session transactionalSession() {
return sessionFactory.getCurrentSession();
}

protected Session readOnlySession() {
if(TransactionSynchronizationManager.isSynchronizationActive())
return transactionalSession();
Session session = this.sessionFactory.openSession();
session.setFlushMode(FlushMode.MANUAL);
return session;
}

public List<SalixUrl> activeUrls() {
return readOnlySession().createCriteria(SalixUrl.class)
.add(Restrictions.gt("published", LocalDateTime.now()))
.add(Restrictions.lt("removed", LocalDateTime.now()))
.list();
}

@Transactional
public List<SalixUrl> refreshUrls() {
List<SalixUrl> urls = activeUrls();
for(SalixUrl url : urls) {
url.setLastChecked(LocalDateTime.now());
transactionalSession().update(url);
}
}
}

这将允许您在不使用 myHibernateService.activeUrls()批注的情况下调用 @Transactional,但也可以使用 myHibernateService.refreshUrls()进行调用的 PlatformTransactionManager

如果这段代码看起来很熟悉,则可能是因为您已经查看了 OpenSessionInViewFilter(或拦截器)的源代码,后者通常用于缓解 LazyLoadingException,并且在程序员认为自己是通过使用 FetchType.LAZY定义实体关系来优化他们的ORM模型,但是还没有对他们的服务/存储库层进行编码以实际获取要生成的View需要获取的内容。

无论如何,您都不想使用 上面的代码。相反,您可能想使用 @Transactional批注,并让Spring和Hibernate框架决定实际需要哪种类型的数据库事务。

如果您担心性能,那么可以选择以下几种方法:

否1. 您可以使用Spring的 @Transactional(readOnly=true),但请注意,这不一定是个好主意。我不主张使用javax @Transactional,因为它更通用-如果您将颜色绑定(bind)到Spring桅杆上,则最好使用它提供的功能。相反,我很警惕,因为它所做的(对于当前的实现)是请求将来自基础数据库提供程序的 Connection对象标记为只读。出于两个原因,这可能会带来问题。

例如,您的数据库提供程序可能不支持只读连接(例如,用于MSSQL服务器的jTDS JDBC驱动程序),因此可能毫无意义。

第二个原因是由于连接池。如果您正在使用支持只读连接的数据库(例如PostgreSQL)和连接池(例如C3P0),那么您实际上并不想将某些连接标记为只读,然后将它们返回到池中,然后允许在需要执行数据库写操作的情况下提供它们。 (我尚未使用Hibernate4和Spring4对此进行测试,但肯定是Spring3,Hibernate3和C3P0的问题。)

2. 使用缓存。使用当今我们可以使用的硬件,缓存可能是答案,并且您有很多可用的选项。您可以为Hibernate实体配置一个二级缓存,Spring本身具有一个很好的spring-cache模块,该模块允许缓存服务/存储库方法-了解如何集成EhCache。

3. 使用JDBC或其他方式编写您自己的数据库查询。 Gavin King(Hibernate的作者)已经指出了很长时间,仅因为您将Hibernate用于ORM并不需要将其用于所有内容: https://plus.google.com/+GavinKing/posts/LGJU1NorAvY(我找不到明确的引文,他说“不要”请将Hibernate用于性能 SELECT,但我想几年前我读过一些东西)。

但是还有两个更重要的问题:

否1. 您不必担心性能。而且,如果您需要阅读,那么您不应该阅读本文,因为您应该已经了解所有内容;-)-但是忽略了我的烦恼,请不要浪费时间进行原子代码优化,相反,您需要像工程师一样工作,看上去对整个系统(例如Dirk Gently)进行评估,然后判断使系统尽可能高效的最有效方法。请记住:协和飞机不再飞行的原因有很多。

否2. 您可能不再需要使用 SessionFactory。 JPA 2和 EntityManager的设计是使 SessionFactory的明确使用变得不必要。甚至几年前,伊曼纽尔·伯纳德(Hibernate的另一位作者)都给了我们以下建议: http://www.theserverside.com/news/2240186700/The-JPA-20-EntityManager-vs-the-Hibernate-Session-Which-one-to-use

但是您知道什么:我喜欢 SessionFactory和Hibernate Criteria API及其附带的所有内容。因此,我将继续使用它,直到他们从Spring框架中弃用对它的支持为止。因为,正如我已经说过的,如果您将颜色钉在框架桅杆上,那么您也可以使用框架必须提供的所有功能。实际上,抽象的主要好处(可以交换基础的ORM或数据库提供程序)是您可能永远不必担心的。

(但是,是的,我也曾经去过那里并且做到了-我不得不将中等大小的代码库从MSSQL迁移到PostgreSQL,最大的问题不是Spring/ORM层,而是数据库特定的代码,例如存储过程和触发器。事实上,以前的开发人员曾尝试通过使用 @Transactional(readOnly=true)优化查询,却不了解MSSQL实际上不支持它,并且在使用PostgreSQL和C3P0时会中断。是的,我对此仍然很痛苦。)

关于java - 没有@Transactional的Hibernate sessionFactory.getCurrentSession(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29690240/

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com