Skip to content

Instantly share code, notes, and snippets.

@rponte
Forked from jelies/MyRepositoryImpl.java
Last active January 29, 2021 19:16
Show Gist options
  • Select an option

  • Save rponte/f0cf5b6aaec4e5bc8c6f to your computer and use it in GitHub Desktop.

Select an option

Save rponte/f0cf5b6aaec4e5bc8c6f to your computer and use it in GitHub Desktop.
A Spring FactoryBean to create a Hibernate's StatelessSession to be injected in your custom repository implementation when using Spring Data JPA.
package com.jelies.spring3tomcat7.repository;
import org.hibernate.Criteria;
import org.hibernate.ScrollableResults;
import org.hibernate.StatelessSession;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.jelies.spring3tomcat7.model.entity.User;
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;
}
}
package com.jelies.spring3tomcat7.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import com.jelies.spring3tomcat7.config.util.hibernate.StatelessSessionFactoryBean;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories("com.jelies.spring3tomcat7.repository")
public class PersistenceConfig implements TransactionManagementConfigurer {
...
@Bean
public StatelessSessionFactoryBean statelessSessionFactory() {
return new StatelessSessionFactoryBean();
}
...
}
package com.jelies.spring3tomcat7.config.spring;
import static org.springframework.orm.jpa.EntityManagerFactoryUtils.ENTITY_MANAGER_SYNCHRONIZATION_ORDER;
import static org.springframework.util.ReflectionUtils.invokeMethod;
import java.sql.Connection;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.transaction.spi.TransactionContext;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.LocalEntityManagerFactoryBean;
import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Hibernate's {@link StatelessSession} factory which will be bound to the
* current transaction. This factory returns a Proxy which delegates method
* calls to the underlying {@link StatelessSession} bound to transaction. At the
* end of the transaction the session is automatically closed. This class
* borrows idea's from {@link DataSourceUtils},
* {@link EntityManagerFactoryUtils}, {@link ResourceHolderSynchronization} and
* {@link LocalEntityManagerFactoryBean}.
*/
public class StatelessSessionFactoryBean implements FactoryBean<StatelessSession> {
private final HibernateEntityManagerFactory entityManagerFactory;
private SessionFactory sessionFactory;
@Autowired
public StatelessSessionFactoryBean(HibernateEntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
this.sessionFactory = entityManagerFactory.getSessionFactory();
}
/**
* Use this to override the {@link SessionFactory} obtained from the
* {@link EntityManagerFactory}. Please note that the connection will still
* be used from the {@link EntityManager}.
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public StatelessSession getObject() throws Exception {
StatelessSessionInterceptor statelessSessionInterceptor = new StatelessSessionInterceptor(
entityManagerFactory, sessionFactory);
return ProxyFactory.getProxy(StatelessSession.class, statelessSessionInterceptor);
}
@Override
public Class<?> getObjectType() {
return StatelessSession.class;
}
@Override
public boolean isSingleton() {
return true;
}
private static class StatelessSessionInterceptor implements MethodInterceptor {
private final EntityManagerFactory entityManagerFactory;
private final SessionFactory sessionFactory;
public StatelessSessionInterceptor(EntityManagerFactory entityManagerFactory,
SessionFactory sessionFactory) {
this.entityManagerFactory = entityManagerFactory;
this.sessionFactory = sessionFactory;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
StatelessSession statelessSession = getCurrentSession();
return invokeMethod(invocation.getMethod(), statelessSession, invocation.getArguments());
}
private StatelessSession getCurrentSession() {
if (!TransactionSynchronizationManager.isActualTransactionActive()) {
throw new IllegalStateException(
"There should be an active transaction for the current thread.");
}
StatelessSession statelessSession = (StatelessSession) TransactionSynchronizationManager
.getResource(sessionFactory);
if (statelessSession == null) {
statelessSession = openNewStatelessSession();
bindWithTransaction(statelessSession);
}
return statelessSession;
}
private StatelessSession openNewStatelessSession() {
Connection connection = obtainPhysicalConnection();
return sessionFactory.openStatelessSession(connection);
}
/**
* It is important we obtain the physical (real) connection otherwise it
* will be double proxied and there will be problems releasing the
* connection.
*/
private Connection obtainPhysicalConnection() {
EntityManager entityManager = EntityManagerFactoryUtils
.getTransactionalEntityManager(entityManagerFactory);
SessionImplementor sessionImplementor = (SessionImplementor) entityManager
.getDelegate();
return sessionImplementor.getTransactionCoordinator().getJdbcCoordinator()
.getLogicalConnection().getConnection();
}
private void bindWithTransaction(StatelessSession statelessSession) {
TransactionSynchronizationManager
.registerSynchronization(new StatelessSessionSynchronization(sessionFactory,
statelessSession));
TransactionSynchronizationManager.bindResource(sessionFactory, statelessSession);
}
}
private static class StatelessSessionSynchronization extends TransactionSynchronizationAdapter {
private final SessionFactory sessionFactory;
private final StatelessSession statelessSession;
public StatelessSessionSynchronization(SessionFactory sessionFactory,
StatelessSession statelessSession) {
this.sessionFactory = sessionFactory;
this.statelessSession = statelessSession;
}
@Override
public int getOrder() {
return ENTITY_MANAGER_SYNCHRONIZATION_ORDER - 100;
}
@Override
public void beforeCommit(boolean readOnly) {
if (!readOnly) {
((TransactionContext) statelessSession).managedFlush();
}
}
@Override
public void beforeCompletion() {
TransactionSynchronizationManager.unbindResource(sessionFactory);
statelessSession.close();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment