Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hibernate4 version of SpringSessionContext.currentSession() does not create a session if TransactionSynchronizationManager does not contain one [SPR-9020] #13659

Closed
spring-projects-issues opened this issue Jan 12, 2012 · 17 comments
Assignees
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jan 12, 2012

Reto Urfer opened SPR-9020 and commented

The Hibernate4 support of Spring 3.1 does not open and register a session in case of the method called is annotated with @Transactional(propagation = Propagation.SUPPORTS).
In this case HibernateTransactionManager.doBegin() is never called which is the only place where hibernate session is opened. The result is, that all read operations which do not require a transaction will fail because the call to SessionFactory.currentSession() will result in an exception.

The Hibernate3 implementation contains a fallback for this case in SessionFactoryUtils.doGetSession(...) which is missing in the corresponding Hibernate4 implementation of SpringSessionContext.currentSession().

If the transaction propagation is changed to REQUIRES_NEW everything is working fine.


Affects: 3.1 GA

Issue Links:

19 votes, 28 watchers

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

This is actually not a bug but rather a limitation that originates in Hibernate itself: The standard Hibernate CurrentSessionContext implementations only really work within active transactions. Since we're trying to do things the Hibernate way as far as possible for Hibernate 4, our SpringSessionContext delegates to Hibernate's JTASessionContext now and hence inherits its limitations. In any case, I'll have a look at what we can do about this for Spring 3.1.1.

As an alternative to SUPPORTS, consider using REQUIRED in combination with readOnly=true. This is quite close in efficiency in many scenarios: There will be an active database transaction but just with read-only operations at runtime.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Jan Bols commented

Using @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) annotations is a very common usecase for db reads. Most of the time there's no need for a transaction when we just want to read data.

The workaround of using @Transactional(readOnly = true) works, but degrades db throughput and might also increase deadlocks due to the shared locks that are taken during transactional reads (guessing here...).

@spring-projects-issues
Copy link
Collaborator Author

jd commented

This issue is now a real show-stopper for us as this currently leads to severe performance issues and other database bottlenecks. Due to sheer size of project changes, it is unreasonable to go back to Hibernate 3. Can we expect any solutions to this problem and SUPPORTS read-only is such a common usecase and it is an absolute show stopper for us now. Is there any alternative - however ugly - I can try to workaround this problem?

@spring-projects-issues
Copy link
Collaborator Author

Francois Guitton commented

We have not moved to hibernate4 for the same reason. This is going to be a problem real soon as we would like to take advantage of the multi-tenancy features in h4.

@spring-projects-issues
Copy link
Collaborator Author

jd commented

Juergen can you please elaborate on "The standard Hibernate CurrentSessionContext implementations only really work within active transactions."

On looking into the org.hibernate.context.internal.ThreadLocalSessionContext.currentSession() and org.hibernate.context.internal.JTASessionContext.currentSession(), it seems like both openSession() causing a new session when no session is present.

Are you referring to the fact that when a org.hibernate.internal.SessionImpl/StatelessSessionImpl gets created, a new org.hibernate.internal.TransactionCoordinatorImpl gets created whose reset() creates a new TransactionImplementor, thereby requiring a "live" transaction to be associated with a session.

If sessions require a valid TransactionImplementor, what is your opinion to inject a TransactionFactory that instantiates a proxying TransactionImplementor that in turn creates a "real" transaction only when necessary?

Please advice.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

In standard Hibernate, JTASessionContext and ThreadLocalSessionContext do lazily create Sessions - but only within active transactions. The JTA variant will fail if no JTA transaction is active before even trying to create a Session, and the ThreadLocal variant will create a Session that is unusable before beginTransaction has been called on it.

So my point basically is: The Hibernate team is quite strongly against any access to the datastore outside of a transaction. That has been the case for many years, and in more recent Hibernate versions, they are trying to enforce it rather strongly. Spring has been supporting SUPPORTS-style access to Hibernate 3 as a Spring-specific feature, and we've had several accusations of working against Hibernate's intentions there.

As an important note, we've been using our own management of JTA-synchronized Hibernate 3 Sessions before which was quite involved and fragile to maintain. For our Hibernate 4 support, we are delegating to Hibernate's standard JTA synchronization now. This makes it harder to support a fallback to non-transactional Sessions since we can only do so through differentiating between JTA and non-JTA transactions in some custom way.

Now, I understand that this is a common scenario. We'll see what we can do without reintroducing the deeply involved arrangement (and its problems) that we had for Hibernate 3.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

A question to you guys: What kind of negative effects are you seeing when using REQUIRED instead of SUPPORTS? This should lead to a standard JDBC transaction with isolation level READ_COMMITTED, which is largely equivalent to non-transactional reading a.k.a. auto-commit reading when issuing a single SQL statement, since auto-commit operations basically use READ_COMMITTED isolation as well. The difference is just that multiple SQL statements get batched into one larger READ_COMMITTED operation instead of several.

If you are seeing negative effects, I'd love to hear about your database and how it handles READ_COMMITTED. I am aware that DBMS behavior varies widely but I'm sincerely wondering about when to use SUPPORTS over REQUIRED and how database-specific any advice there is.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Matías Mirabelli commented

Hibernate does actually supports different CurrentSessionContext from outside Spring, and the session factory can be configured to use another one. There's a ManagedSessionContext that allows to bind a Session to the current thread.

We've got the same issue migrating from Hibernate 3 to 4, and I found a workaround that works fine for us. I created a CurrentSessionContext that first delegates to SpringSessionContext and then it determines whether there's a Session bound to the current thread or not. If not, it means that there's no transaction in progress at all (or it was created with PROPAGATION_NEVER or PROPAGATION_SUPPORTS) so it opens a new Session and binds it to the current thread via ManagedSessionContext.

In order to cleanup and close the Session at the end of the current transaction (if any), this CurrentSessionContext registers a Synchronization into TransactionSynchronizationManager, so if there's a transaction opened as PROPAGATION_NEVER or PROPAGATION_SUPPORTS the Session will be closed at the end. If there's no transaction active (which means that commit() or rollback() will never be invoked), the Session will never close.

Here's the gist for this class:
https://gist.github.com/seykron/4770724

It can be configured in the hibernate.current_session_context_class property.

Hope this help.

Matías

@spring-projects-issues
Copy link
Collaborator Author

Burkhard Graves commented

Hi, any answers to Juergens question(s)?

Migrated a project to Hibernate 4 and run into this problem today, thinking about eliminating all Propagation.SUPPORTS now...

Cheers
Burkhard

@spring-projects-issues
Copy link
Collaborator Author

RamCh commented

Hi,

We used Matias work around and working well so far.

Thanks Matias.

Thanks,
Ram

@spring-projects-issues
Copy link
Collaborator Author

Andrew Goode commented

I used a slightly modified version of Matias's workaround, and it seems to work well.

See his comment above and my fork of his Gist for further details.

@spring-projects-issues
Copy link
Collaborator Author

Fabrício Barroso de Carvalho commented

The side effect caused by this approach is the overhead of creating and using a HibernateTransactionManager on simple queries. Not in resource consumption of the database, but in processing of the application server.

@spring-projects-issues
Copy link
Collaborator Author

thiago andrade commented

For when the spring developers are scheduling to fix this bug?

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Note that the effort of managing a Spring transaction scope within the application - just for the purpose of a read operation - is negligible compared to the amount of CPU cycles wasted by creating a new Hibernate Session itself... Admittedly this can become a bit more concerning with JTA in an application server environment, where the application server's JTA subsystem is also wasting some cycles on begin and commit. However, is that really significant? Have you tested that overhead or are you simply assuming that it's concerning?

In addition, even our old Hibernate 3 behavior of creating a locally synchronized Session within a SUPPORTS transaction involves the management of a local transaction scope. The only difference with using REQUIRED is that the JTA subsystem and/or the underlying database resource is also being told about the transaction scope and can consider it accordingly. In a well-optimized scenario, the latter effect can be positive in terms of optimized resource management within that transaction scope.

It's also still true that the Hibernate team has a very strong opinion on this: They want you to execute all Hibernate operations within a transaction, and - with every Hibernate release - make it harder and harder to achieve 100% correct behavior outside of a transaction, for both users and framework integrators. They simply don't consider that a first-class scenario and don't expose proper hooks etc (see JTASessionContext and its exception design for an example).

Finally, if this is really commonly desired among Spring users even at this point, we can revisit this for Spring Framework 4.1. We definitely won't do custom JTA-based synchronization (that's pointless since the JTA synchronization facility will only work within active JTA transactions, and that's covered by Hibernate's JTASessionContext already) but can at least consider a best-effort Spring-based synchronization arrangement as a fallback if none of the regular Session retrieval strategies worked out.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jun 25, 2014

Bilal Ahmed commented

Hi All,

I am migrating my application from Spring 3.0.5.RELEASE to 4.3.5.Final, and Hibernate 3.6.0.Beta2 to 4.3.5.Final.
Primary reason for this upgrade is to support multi-tenancy in our application using Hibernate 4.

Application has been working perfectly fine in older versions of Spring and Hibernate. To Juergen's point, of using "REQUIRED" instead of "SUPPORTS",
I am not marking my method by any Propagation strategy, hence by default it is "REQUIRED", according to Spring documentation ... but ... still getting this error. I even tried to explicitly mark my transactional method with "REQUIRED" .. no luck.

Need to know, what's the fate of this bug, as it is becoming an obstacle for us at this point.

Here are some details :

Transactional Method Added in Service Class - From Spring Log


[2014-06-25 15:00:33] [DEBUG ][org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource.getTransactionAttribute(AbstractFallbackTransactionAttributeSource.java:108)] "Adding transactional method 'UserServiceImpl.validateUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''"

Service Layer


@Service("userServiceBean")
public class UserServiceImpl implements UserService{

private static final Logger logger = MyVacationLogger.getLogger(UserServiceImpl.class);

@Autowired(required=true)
private UserDAO userDAO;

@Transactional(readOnly=true, propagation = Propagation.REQUIRED)
public UserBean validateUser(UserBean userbean) throws MyVacationRuntimeException {
   ....
}

}

DAO Layer


@Repository("UserDAOBean")
public class UserDAOImpl implements UserDAO {

@Autowired(required=true)
@Qualifier("commondbSessionFactory")
private SessionFactory sessFactory;

@Override
public UserDTO findUserbyEmail(String email) throws DataAccessException {
	Session sess = sessFactory.getCurrentSession(); << Error Line
    ....		
}

}

Error


Caused by: org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at com.ellisdon.portal.mv.dao.impl.UserDAOImpl.findUserbyEmail(UserDAOImpl.java:31)
at com.ellisdon.portal.mv.service.impl.UserServiceImpl.validateUser(UserServiceImpl.java:38)

@spring-projects-issues
Copy link
Collaborator Author

Luke Maurer commented

It appears that programmatic demarcation using TransactionTemplate suffers from this same issue.

Is it fair to say that Spring simply does not support Propagation.SUPPORTS with Hibernate 4? I fail to see what use SUPPORTS has if it doesn't bind a Hibernate session to the thread. By my understanding, if there's no transaction when a SUPPORTS method is called, the code runs non-transactionally but with a single Hibernate session used for all accesses. If SessionFactory.getCurrentSession() isn't the means to get at that single session, what is? Or, if there's no session bound to the thread at all in this case, how does @Transactional(propagation=SUPPORTS) differ from no annotation at all?

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Added for 4.1 RC2 now - please give it a try in the upcoming 4.1 snapshot, or in 4.1 RC2 itself once available!

We only delegate to Hibernate's JTASessionContext in case of an active JTA transaction now, manually checking the JTA status upfront. If no JTA transaction is available, we lazily create and bind a Hibernate Session to the current Spring transaction synchronization scope (for propagation SUPPORTS).

Note that in contrast to our Hibernate 3 support, we don't check for any interleaving with JTA transactions here. This propagation SUPPORTS mode is only meant to work within a HibernateTransactionManager arrangement, and generally only meant to be used for transaction scopes which won't be upgraded to a full REQUIRES transaction further down the call stack.

Juergen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants