How to use StatelessSession with Spring Data JPA and Hibernate?
Asked Answered
F

3

19

I'm using Spring + Spring Data JPA with Hibernate and I need to perform some large and expensive database operations.

How I can use a StatelessSession to perform these kind of operations?

Fluorescein answered 17/3, 2013 at 12:15 Comment(0)
F
13

A solution is to implement a Spring factory bean to create this StatelessSession and inject it in your custom repositories implementation:

public class MyRepositoryImpl implements MyRepositoryCustom {

    @Autowired
    private StatelessSession statelessSession;

    @Override
    @Transactional
    public void myBatchStatements() {
        Criteria c = statelessSession.createCriteria(User.class);

        ScrollableResults itemCursor = c.scroll();

        while (itemCursor.next()) {
            myUpdate((User) itemCursor.get(0));
        }
        itemCursor.close();

        return true;
    }

}

Check out the StatelessSessionFactoryBean and the full Gist here. Using Spring 3.2.2, Spring Data JPA 1.2.0 and Hibernate 4.1.9.

Thanks to this JIRA and the guy who attached StatelessSessionFactoryBean code. Hope this helps somebody, it worked like a charm for me.

Fluorescein answered 17/3, 2013 at 12:15 Comment(2)
Since a session holds a database connection, are you concerned about the lifetime of your statelessSession instance? Wouldn't the lifetime of the connection held by the statelessSession be essentially the same as the lifetime of the MyRepositoryImpl instance?Chemulpo
It looks like it is a proxy and the real statelessSession is closed by StatelessSessionSynchronizationCornered
C
5

To get even better performance results you can enable jdbc batch statements on the SessionFactory / EntityManager by setting the hibernate.jdbc.batch_size property on the SessionFactory configuration (i.e.: LocalEntityManagerFactoryBean).

To have an optimal benefit of the jdbc batch insert / updates write as much entities of the same type as possible. Hibernate will detect when you write another entity type and flushes the batch automatically even when it has not reached the configured batch size.

Using the StatelessSession behaves basically the same as using something like Spring's JdbcTemplate. The benefit of using the StatelessSession is that the mapping and translation to SQL is handled by Hibernate. When you use my StatelessSessionFactoryBean you can even mix the Session and the StatelessSession mixed in one transaction. But be careful of modifying an Entity loaded by the Session and persisting it with the StatelessSession because it will result into locking problems.

Clammy answered 9/4, 2013 at 11:27 Comment(1)
we used your StatelessSession code in our project. However, we have a requirement to migrate our code to SpringBoot 1.5.12 version. So it inherits Hibernate 5.0.12 . There we have an issue , TransactionContext doesn't exist. Even, HibernateEntityManagerFactory is depreciate. It would be really great, if you could help.Contreras
L
0

You can use StatelessSession without creating beans, dynamic proxies and other stuff. It can be just a repository method, that from the outside is no different from other Spring Data repository methods. The example below works for Hibernate 5+:

public class MyRepositoryImpl implements MyRepositoryCustom {

    // ...

    @Transactional(MANDATORY)
    public void myBatchStatements() {
        Session session = entityManager.unwrap(Session.class);
        session.doWork(connection -> {
            // re-use the existing connection
            var statelessSession = session.getSessionFactory().openStatelessSession(connection);
            try {

                // do your updates/inserts here...

                // flush using some Hibernare internals
                ((JdbcSessionOwner) statelessSession).flushBeforeTransactionCompletion();
            } finally {
                if (statelessSession != null) {
                    // this won't close neither outer connection nor even transaction
                    statelessSession.close();
                }
            }
        });
    }
}

The only difference for Hibernate 4 is the way you do flush:

((TransactionContext) statelessSession).managedFlush();
Lordly answered 29/1 at 9:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.