3

I faced an error while launching the Spring-Boot application.

Now, I want to use HibernateDaoSupport in the DAO repository as Spring boot doesn't create SessionFactory automatically. So, I have created SessionFactory bean from EntityManagerFactory and tried to autowire it in the DAO class.

But I got the follow error :

Description:

The dependencies of some of the beans in the application context form a cycle:

   fooDao defined in file [/home/user/test/out/production/classes/by/test/testing_the_java_service_layer/repository/FooDao.class]
┌─────┐
|  sessionFactory defined in class path resource [by/test/testing_the_java_service_layer/configuration/Config.class]
└─────┘

I don't understand why SessionFactory is referring to FooDao class.

Following are the code samples :

FooDao.java

@Repository
public class FooDao extends HibernateDaoSupport
{
    @Autowired
    public void setSessionFactories( SessionFactory sessionFactory )
    {
        setSessionFactory( sessionFactory );

    }

    @Transactional
    public int create( Foo entity )
    {
        return (int) this.getHibernateTemplate().save( entity );
    }
}

Config.java

@ComponentScan( basePackages = { "by.test" } )
@Configuration
public class Config
{
    /*
     *     Spring boot doesn't create SessionFactory bean, so we have to create it manually, using EntityManagerFactory
     */
    @Bean
    public SessionFactory sessionFactory( EntityManagerFactory entityManagerFactory )
    {
        return entityManagerFactory.unwrap( SessionFactory.class );
    }
}

Foo.java

@Entity
@Table( name = "bibis" )
public class Foo
{
    @Id
    @Column( name = "foo", nullable = false )
    public int foo;

    @Column( name = "bar" )
    public String bar;
}

TestApplication.java

@SpringBootApplication
public class TestApplication
{

    public static void main( String[] args )
    {
        SpringApplication.run( TestApplication.class, args );
    }
}

application.yaml

spring:
  datasource:
    username: 'bibis'
    password: 'bibis'
    schema: 'bibis'
    host: 'localhost:3306'
    url: 'jdbc:mariadb://localhost:3306/bibis'

and gradle dependencies from build.gradle

implementation('org.mariadb.jdbc:mariadb-java-client')
developmentOnly 'org.springframework.boot:spring-boot-devtools'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.1.8.RELEASE'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

I tried to use @Lazy annotation, constructor or field injection but it didn't help.

Anish B.
  • 9,111
  • 3
  • 21
  • 41
Simpson
  • 173
  • 9

3 Answers3

1

I have referred the answer from Spring Boot - Handle to Hibernate SessionFactory

Return HibernateJpaSessionFactoryBean obj instead of SessionFactory object from Config class.

The modified code will be :

@ComponentScan( basePackages = { "by.test" } )
@Configuration
public class Config
{
    @Bean
    public HibernateJpaSessionFactoryBean sessionFactory(EntityManagerFactory entityManagerFactory)
    {
        HibernateJpaSessionFactoryBean hibernateJpaSessionFactoryBean = new HibernateJpaSessionFactoryBean();
        hibernateJpaSessionFactoryBean.setEntityManagerFactory(entityManagerFactory); 
        return hibernateJpaSessionFactoryBean;
    }
}

Or you can do :

@Repository
public class FooDao extends HibernateDaoSupport
{
    @Autowired
    public void setSessionFactories(EntityManagerFactory entityManagerFactory)
    {
        setSessionFactory(entityManagerFactory.unwrap(SessionFactory.class));
    }

    @Transactional
    public int create(Foo entity)
    {
        return (int) this.getHibernateTemplate().save( entity );
    }
}
Anish B.
  • 9,111
  • 3
  • 21
  • 41
  • 1
    First variant didn't help to me. Second variant is OK. thank you! But I still don't understand, why I have circular dependency. Where does SessionFactory referenced to my FooDao? – Simpson Oct 04 '19 at 06:54
0

Appear to me that when you're using the @Autowired in the setter value, it's creating the circle dependency. It's a better approach that might solve your problem to do this as a property constructor

@Repository
public class FooDao extends HibernateDaoSupport
{
    @Autowired
    public FooDao(SessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory
    }

    @Transactional
    public int create( Foo entity )
    {
        return (int) this.getHibernateTemplate().save( entity );
    }
}
  • Unfortunately it doesn't help. looks like it doesn't depends on constructor or field injection. – Simpson Oct 03 '19 at 17:08
0

Well, you have to clarify if you are going to use JPA or Hibernate (implementation of JPA), EntityManager corresponds to JPA and SessionFactory to Hibernate, if you use EntityManager no need to use SessionFactory, the EM invokes the hibernate session under the hood. And if you need some specific features that are not available in the EntityManager, you can obtain the session by calling:

Session session = entityManager.unwrap(Session.class);

refactor that and try

  • Thanks for your answer but i don't need Session object, I just want to use HibernateTemplate which requires SessionFactory. it isn't production code, it's for training purposes only. – Simpson Oct 04 '19 at 06:50