Programmatically create SessionFactory in Spring - spring

Suppose I programmatically create a AnnotationSessionFactoryBean and set the various properties correctly. How can I then extract the Hibernate SessionFactory, since all methods that pertain to creating the SessionFactory are protected?
AnnotationSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean();
SessionFactory sessionFactory = sessionFactoryBean.newSessionFactory(); // Protected!!

Use getObject(), after calling afterPropertiesSet():
sessionFactoryBean.afterPropertiesSet();
SessionFactory sessionFactory = sessionFactoryBean.getObject();
(AnnotationSessionFactoryBean implements FactoryBean<SessionFactory>)
Be careful, though: by doing this, it becomes your responsibility to make sure the SessionFactory is closed when you're finished with it.

Related

How does a bean, with #Autowired, gets its dependency if the dependency's interface does not exist in the container

I am learning Spring framework and I am stuck now about dependency injection.
In this question, I am asking about Java Configuration.
I know #Autowired annotation automatically wires the dependency and the bean by referencing a qualified implementation class of the declared interface.
However, I do not know how the function, transactionManager(SessionFactory sessionFactory), gets its SessionFactory argument instance when there is no bean returns SessionFactory in my studying code.
#Bean
public LocalSessionFactoryBean sessionFactory(){
// create session factorys
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
// set the properties
sessionFactory.setDataSource(securityDataSource());
sessionFactory.setPackagesToScan(env.getProperty("hiberante.packagesToScan"));
sessionFactory.setHibernateProperties(getHibernateProperties());
return sessionFactory;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
// setup transaction manager based on session factory
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
I assume that the transactionManager() gets its argument from sessionFactory(). But sessionFactory() does not return SessionFactory instance, it returns LocalSessionFactoryBean. The LocalSessionFactoryBean does not implement SessionFactory either, it implements FactoryBean<SessionFactory>.
In this case, doesn't transactionManager() suppose to use argument type LocalSessionFactoryBean or FactoryBean<SessionFactory> instead of having SessionFactory? Like: transactionManager(FactoryBean<SessionFactory> sessionFactory)
I am confused with how transactionManager() gets its dependency.
Thank you,
If am understanding it right you are telling spring to create session factory for you using the configurations provided via LocalSessionFactoryBean, see details here, it creates hibernate session factory for you and auto wire it. Actually its Spring frameworks common practice to save developer work here by letting you create session factory with minimum code.

Need to configure my JPA layer to use a TransactionManager (Spring Cloud Task + Batch register a PlatformTransactionManager unexpectedly)

I am using Spring Cloud Task + Batch in a project.
I plan to use different datasources for business data and Spring audit data on the task. So I configured something like:
#Bean
public TaskConfigurer taskConfigurer() {
return new DefaultTaskConfigurer(this.singletonNotExposedSpringDatasource());
}
#Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer(this.singletonNotExposedSpringDatasource());
}
whereas main datasource is autoconfigured through JpaBaseConfiguration.
The problem comes when SimpleBatchConfiguration+DefaultBatchConfigurer expose a PlatformTransactionManager bean, since JpaBaseConfiguration has a #ConditionalOnMissingBean on PlatformTransactionManager. Therefore Batch's PlatformTransactionManager, binded to the spring.datasource takes place.
So far, this seems to be caused because this bug
So I tried to emulate what JpaBaseConfiguration does, defining my own PlatformTransactionManager over my biz datasource/entityManager.
#Primary
#Bean
public PlatformTransactionManager appTransactionManager(final LocalContainerEntityManagerFactoryBean appEntityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(appEntityManager.getObject());
this.appTransactionManager = transactionManager;
return transactionManager;
}
Note I have to define it with a name other than transactionManager, otherwise Spring finds 2 beans and complains (unregardless of #Primary!)
But now it comes the funny part. When running the tests, everything runs smooth, tests finish and DDLs are properly created for both business and Batch/Task's databases, database reads work flawlessly, but business data is not persisted in my testing database, so final assertThats fail when counting. If I #Autowire in my test PlatformTransactionManager or ÈntityManager, everything indicates they are the proper ones. But if I debug within entityRepository.save, and execute org.springframework.transaction.interceptor.TransactionAspectSupport.currentTransactionStatus(), it seems the DatasourceTransactionManager from Batch's configuration is overriding, so my custom exposed PlatformTransactionManager is not being used.
So I guess it is not a problem of my PlatformManager being the primary, but that something is configuring my JPA layer TransactionInterceptor to use the non primary but transactionManager named bean of Batch.
I also tried with making my #Configuration implement TransactionManagementConfigurer and override PlatformTransactionManager annotationDrivenTransactionManager() but still no luck
Thus, I guess what I am asking is whether there is a way to configure the primary TransactionManager for the JPA Layer.
The problem comes when SimpleBatchConfiguration+DefaultBatchConfigurer expose a PlatformTransactionManager bean,
As you mentioned, this is indeed what was reported in BATCH-2788. The solution we are exploring is to expose the transaction manager bean only if Spring Batch creates it.
In the meantime you can set the property spring.main.allow-bean-definition-overriding=true to allow bean definition overriding and set the transaction manager you want Spring Batch to use with BatchConfigurer#getTransactionManager. In your case, it would be something like:
#Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer(this.singletonNotExposedSpringDatasource()) {
#Override
public PlatformTransactionManager getTransactionManager() {
return new MyTransactionManager();
}
};
}
Hope this helps.

Why adding #Bean to a method I'll call directly

I have seen lots of examples about Spring configuration through #Configuration and #Bean annotations. But I relealized that it's a common practice to add #Bean annotation to methods that are called directly to populate other beans. For example:
#Bean
public Properties hibernateProperties() {
Properties hibernateProp = new Properties();
hibernateProp.put("hibernate.dialect",
"org.hibernate.dialect.H2Dialect");
hibernateProp.put("hibernate.hbm2ddl.auto", "create-drop");
hibernateProp.put("hibernate.format_sql", true);
hibernateProp.put("hibernate.use_sql_comments", true);
hibernateProp.put("hibernate.show_sql", true);
return hibernateProp;
}
#Bean public SessionFactory sessionFactory() {
return new LocalSessionFactoryBuilder(dataSource())
.scanPackages("com.ps.ents")
.addProperties(hibernateProperties())
.buildSessionFactory();}
So, I'm wondering if it's better just declaring the hibernateProperties() as private without the #Bean annotation.
I would like to know if this is a bad/unneeded common practice or there is a reason behind.
Thanks in advance!
According to Spring Documentation inter-bean dependency injection is one good approach in order to define the bean dependencies in a simple form. Of course if you define your hibernateProperties() as private it will work but it could not be injected to other components in your application through Spring Container.
Just decide depending on how many classes depends on your bean and also if you need to reuse it in order to call its methods or inject it to other classes.
Decorating a method in #Configuration class with #Bean means that the return value of that method will become a Spring bean.
By default those beans are singletons(only one instance for the lifespan of the application).
In your example Spring knows that hibernateProperties() is a singleton bean and will create it only ones. So here:
#Bean public SessionFactory sessionFactory() {
return new LocalSessionFactoryBuilder(dataSource())
.scanPackages("com.ps.ents")
.addProperties(hibernateProperties())
.buildSessionFactory();
}
hibernateProperties() method will not be executed again, but the bean will be taken from the application context. If you don't annotate hibernateProperties() with #Bean it will be a simple java method and it will be executed whenever it's called. It depends on what you want.
Just to mention, the other way to do dependency injection in #Configuration classes is add a parameter. Like this:
#Bean public SessionFactory sessionFactory(Properties props) {
return new LocalSessionFactoryBuilder(dataSource())
.scanPackages("com.ps.ents")
.addProperties(props)
.buildSessionFactory();
}
When Spring tries to create the sessionFactory() bean it will first look in the application context for a bean of type Properties.

Hibernate Filters with JPA EntityManagerFactory and Spring

I have a jersey + spring application that is using EntityManagerFactory and spring JpaTransactionManager to handle the db manipulations. The JPA implementation is hibernate.
I would like to know is there a way to enable hibernate filters in this scenario.
I tried extending org.springframework.orm.jpa.JpaTransactionManager and overriding
#Override
protected EntityManager createEntityManagerForTransaction() {
EntityManager manager = super.createEntityManagerForTransaction();
Session session = manager.unwrap(Session.class);
return manager;
}
But I am not sure that this method is called at the right place.
EDIT:
I am using spring JpaRepositories to persist/query entities.
I found an answer for this problem. It seems like other people are still struggling with this, I will post my solution.
I am enabling the filters that I need in business service methods that are marked as #Transactional (that is important).
In your service, inject the entity manager factory:
#Autowired
private EntityManagerFactory entityManagerFactory;
Inside the #Transactional method obtain the entity manager from the transactionManager, unwrap it to hibernate session and enable the filters that you need. This is done as follows:
//Obtain the entity manager for the current transaction
EntityManagerHolder holder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(entityManagerFactory);
EntityManager entityManager = holder.getEntityManager();
//Unwrap to get the underlying hibernate session
Session hibernateSession = entityManager.unwrap(Session.class);
//Configure your filters
Filter publishedAfterFilter = hibernateSession.enableFilter("...");
publishedAfterFilter.setParameter("...", ...);
publishedAfterFilter.validate();

JPA's EntityManager or Hibernate's HibernateTemplate with Spring

I have decided to use Spring, Hibernate, and Restlet to create a web service. I am new to all of these three technologies. My question is this: How do I decide whether to use JPA's EntityManager or Hibernate's HibernateTemplate?
Code snippet for JPA's EntityManager:
protected EntityManager entityManager;
#PersistenceContext
public void setEngityManager(EntityManager entityManger) {
this.entityManager = entityManager;
}
Code snippet for Hibernate's HibernateTemplate:
private SessionFactory sessionFactory;
private HibernateTemplate hibernateTemplate;
#Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
If you are someone who like interface-based-implementation, then JPA is the interface definition and Hibernate is one of the implementations. We decided to use JPA semantics in our project (with a very long term plan to replace Hibernate with something similar and light-weight probably).
If you want to stick to hibernate in future, then no point in using EntityManagerFactory, you can go ahead and use SessionFactory. And just inject definition for this using java based configuration or xml. It must be available in application context.
But in future if you might want to move to different jpa provider, like toplink etc, then you should use EntityManagerFactory as it allows you to have implementation from various provider. Because providers implement jpa specification. So in future you just need to have different configuration in your application context and you should be able to use that.

Resources