Skip to content

Instantly share code, notes, and snippets.

@nerg4l
Forked from jelies/MyRepositoryImpl.java
Last active January 3, 2019 08:25
Show Gist options
  • Save nerg4l/a79030f8280ddb2a7d0ce8af262f7837 to your computer and use it in GitHub Desktop.
Save nerg4l/a79030f8280ddb2a7d0ce8af262f7837 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(EntityManagerFactory entityManagerFactory) {
return new StatelessSessionFactoryBean(entityManagerFactory);
}
...
}
package com.jelies.spring3tomcat7.config.spring;
import static com.google.common.base.Preconditions.checkState;
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.engine.spi.SessionImplementor;
import org.hibernate.internal.StatelessSessionImpl;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.FactoryBean;
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 EntityManagerFactory entityManagerFactory;
private SessionFactory sessionFactory;
public StatelessSessionFactoryBean(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
this.sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
}
/**
* 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() {
checkState(TransactionSynchronizationManager.isActualTransactionActive(), "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.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection();
}
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) {
((StatelessSessionImpl) statelessSession).flushBeforeTransactionCompletion();
}
}
@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