69

Does anyone know how to get a handle the Hibernate SessionFactory that is created by Spring Boot?

Neil Stockton
  • 11,383
  • 3
  • 34
  • 29
Peter
  • 799
  • 1
  • 7
  • 12
  • 4
    AFAIK Spring Boot does not autoconfigure a Hibernate SessionFactory. It creates a JPA EntityManagerFactory – geoand Jul 31 '14 at 16:28

7 Answers7

69

You can accomplish this with:

SessionFactory sessionFactory = 
    entityManagerFactory.unwrap(SessionFactory.class);

where entityManagerFactory is an JPA EntityManagerFactory.

package net.andreaskluth.hibernatesample;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class SomeService {

  private SessionFactory hibernateFactory;

  @Autowired
  public SomeService(EntityManagerFactory factory) {
    if(factory.unwrap(SessionFactory.class) == null){
      throw new NullPointerException("factory is not a hibernate factory");
    }
    this.hibernateFactory = factory.unwrap(SessionFactory.class);
  }

}
Koray Tugay
  • 22,894
  • 45
  • 188
  • 319
Andreas
  • 5,251
  • 30
  • 43
  • 4
    It would might be better if you moved the creation of the `SessionFactory` into a Spring `@Configuration` and making a `@Bean` method to create it. – geoand Jul 31 '14 at 17:49
  • 1
    @geoand Nothing is created here :-). Accessing the underlying `SessionFactory` should be a special case scenario (e.g. configuration purposes) and therefore not simple. If it would be easy to access the hibernate infrastructure you have to cope with developers used to hibernate accessing the `SessionFactory` instead of the `EntityManagerFactory`. – Andreas Jul 31 '14 at 18:16
  • 1
    What I am saying that it would be best to have a bean of type `SessionFactory` (created with code similar to yours) if it's needed in multiple places – geoand Jul 31 '14 at 20:53
  • 1
    @geoand Since Hibernate 5.2 SessionFactory extends EntityManagerFactory. You would consider problems if binding by type of EntityManagerFactory. – Amir Pashazadeh Feb 17 '18 at 12:23
  • 2
    For the community, I use that code in `@Bean` and the following is mandatory `spring.jpa.properties.hibernate.current_session_context_class = org.springframework.orm.hibernate5.SpringSessionContext` in the `application.properties` file, otherwise appears: `org.springframework.orm.jpa.JpaSystemException: No CurrentSessionContext configured!; nested exception is org.hibernate.HibernateException: No CurrentSessionContext configured!` – Manuel Jordan Feb 17 '18 at 16:20
  • Using this code results in this error for me: The dependencies of some of the beans in the application context form a cycle. Any idea why? – Calicoder Sep 17 '18 at 01:42
  • Transactions won't work directly with this implementation, getting the Session from the EntityManager in the other hand (Trần Quốc Vũ's Answer) will do the trick, and works for the same objective. – White_King Feb 02 '19 at 06:09
  • @White_King the question however was: How to get a handle to the `SessionFactory` and not how to get a handle to a hibernate `Session`. Trần Quốc Vũ' answer solves a different problem and is not an answer to this question at all. – Andreas Feb 02 '19 at 20:39
56

The simplest and least verbose way to autowire your Hibernate SessionFactory is:

This is the solution for Spring Boot 1.x with Hibernate 4:

application.properties:

spring.jpa.properties.hibernate.current_session_context_class=
org.springframework.orm.hibernate4.SpringSessionContext

Configuration class:

@Bean
public HibernateJpaSessionFactoryBean sessionFactory() {
    return new HibernateJpaSessionFactoryBean();
}

Then you can autowire the SessionFactory in your services as usual:

@Autowired
private SessionFactory sessionFactory;

As of Spring Boot 1.5 with Hibernate 5, this is now the preferred way:

application.properties:

spring.jpa.properties.hibernate.current_session_context_class=
org.springframework.orm.hibernate5.SpringSessionContext

Configuration class:

@EnableAutoConfiguration
...
...
@Bean
public HibernateJpaSessionFactoryBean sessionFactory(EntityManagerFactory emf) {
    HibernateJpaSessionFactoryBean fact = new HibernateJpaSessionFactoryBean();
    fact.setEntityManagerFactory(emf);
    return fact;
}
yglodt
  • 13,807
  • 14
  • 91
  • 127
19

Great work Andreas. I created a bean version so the SessionFactory could be autowired.

import javax.persistence.EntityManagerFactory;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;

....

@Autowired
private EntityManagerFactory entityManagerFactory;

@Bean
public SessionFactory getSessionFactory() {
    if (entityManagerFactory.unwrap(SessionFactory.class) == null) {
        throw new NullPointerException("factory is not a hibernate factory");
    }
    return entityManagerFactory.unwrap(SessionFactory.class);
}
HankCa
  • 9,129
  • 8
  • 62
  • 83
  • where did i create this bean? – gstackoverflow Nov 10 '15 at 16:22
  • Where you need a SessionFactory :) Could you make your question clearer? – HankCa Nov 10 '15 at 21:28
  • 1
    As Andreas' answer shows, just add this code to a @Component class, which is essentially a service class. It doesn't really matter where it goes - any Component that Spring wires up. By doing it this way you can include it with `@Autowired private SessionFactory sessionFactory` and use it. – HankCa Nov 19 '15 at 05:45
  • Thanks, how do you create a new session using this sessionFactory? – obesechicken13 Nov 19 '15 at 16:54
  • Spring will do that for you. Just add this where you want it - `@Autowired private SessionFactory sessionFactory` – HankCa Nov 20 '15 at 04:55
  • 1
    @obesechicken13 As a best practice @Bean definitions like these can be defined in your `@Configuration` class – Narasimha Dec 06 '16 at 14:37
  • @HankCa - I am getting "Is there an unresolvable circular reference" error after configuring this. What could have gone wrong? – Javadroider Apr 01 '18 at 11:51
  • @Javadroider look through the entire stack trace looking for clues. Perhaps you have an XML configuration as well as the annotations. You might need to post a separate question for this problem. – HankCa Apr 04 '18 at 03:36
  • @HankCa are you sure it will work that way? As per my understanding, you cannot create the SessionFactory bean that way because it will lead to a circular reference. EntityManagerFactory is created with SessionFactory being its one of the main building blocks but here you're trying to create the SessionFactory bean with the help of EntityManagerFactory(which can not be created untill the SessionFactory itself is built); thus leading to a BeanCreationException. – Sen Apr 05 '19 at 13:01
  • @Sen Its been a while since I've worked with Spring, however from what I understood (and looking at https://stackoverflow.com/a/5640796/1019307 for example), the EntityManager creates an instance of the JPA implementation it is using (Hibernate in this case) and is able to return it. It doesn't cause an error, or didn't for me and at least 14 others who up-voted this :). YMMV if your configuration is somehow different (the gotcha with any of these intricate frameworks). – HankCa Apr 17 '19 at 11:49
12

It works with Spring Boot 2.1.0 and Hibernate 5

@PersistenceContext
private EntityManager entityManager;

Then you can create new Session by using entityManager.unwrap(Session.class)

Session session = null;
if (entityManager == null
    || (session = entityManager.unwrap(Session.class)) == null) {

    throw new NullPointerException();
}

example create query:

session.createQuery("FROM Student");

application.properties:

spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:db11g
spring.datasource.username=admin
spring.datasource.password=admin
spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
  • Actually EntityManager itself is providing the needed methods like; createQuery etc.. and also if you deep dive into the implementation for those methods. entityManager is itself handing over the requests to the Session implementation classes. Thus I thing following a specific release. Hibernate guys intentionally made it hard to access to session but use entity manager for session operations. I read some stuff about this but could not find it directly. – Olgun Kaya Mar 08 '19 at 07:26
  • Thx for the sample - the only one which worked to get a valid (and not deprecated) session! (in Spring Boot 2.2. & Hibernate5) – LeO Feb 20 '20 at 09:36
  • @OlgunKaya But it does not provide batched queries. You kind of have to create a big ass string of multiple queries – TheRealChx101 Aug 19 '22 at 12:39
  • @Tran What's the package signature of the `Session.class`? Please always indicate these because in my imports, I have a list of Session classes. – TheRealChx101 Aug 19 '22 at 12:40
8

Another way similar to the yglodt's

In application.properties:

spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext

And in your configuration class:

@Bean
public SessionFactory sessionFactory(HibernateEntityManagerFactory hemf) {
    return hemf.getSessionFactory();
}

Then you can autowire the SessionFactory in your services as usual:

@Autowired
private SessionFactory sessionFactory;
Lorenzo Lerate
  • 3,552
  • 3
  • 26
  • 25
3

If it's really required to access SessionFactory through @Autowire, I'd rather configure another EntityManagerFactory and then use it to configure the SessionFactory bean, like following:

@Configuration
public class SessionFactoryConfig {

@Autowired 
DataSource dataSource;

@Autowired
JpaVendorAdapter jpaVendorAdapter;

@Bean
@Primary
public EntityManagerFactory entityManagerFactory() {
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setDataSource(dataSource);
    emf.setJpaVendorAdapter(jpaVendorAdapter);
    emf.setPackagesToScan("com.hibernateLearning");
    emf.setPersistenceUnitName("default");
    emf.afterPropertiesSet();
    return emf.getObject();
}

@Bean
public SessionFactory setSessionFactory(EntityManagerFactory entityManagerFactory) {
    return entityManagerFactory.unwrap(SessionFactory.class);
} }
Sen
  • 1,308
  • 12
  • 12
-3

SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);

where entityManagerFactory is an JPA EntityManagerFactory.

Simas Joneliunas
  • 2,890
  • 20
  • 28
  • 35
  • This copies the accepted answer. When you post an answer it should be because you have something that answers the question but is not covered by any existing answers. – Nathan Hughes Jan 03 '21 at 15:10