-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
... | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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