简体   繁体   中英

Spring boot hibernate 5 integration error

I am trying to integrate spring Boot with Hibernate 5 using Java configs. It has been some time since I have used Hibernate. I am getting the below mentioned exception. Can someone please suggest how to resolve this ?

Following is my java config file followed by the exception stack trace.

package com.example;

import com.example.dao.CustomerDao;
import com.example.model.Customer;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.Properties;

@SpringBootApplication
@EnableTransactionManagement
public class SpringHibernateDemoApplication implements CommandLineRunner {

    public static void main(String[] args) {

        SpringApplication.run(SpringHibernateDemoApplication.class, args);
    }

    @Bean
    public DataSource dataSource() throws ClassNotFoundException {
        //SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
        // dataSource.setDriverClass((Class<? extends Driver>)Class.forName("com.mysql.jdbc.Driver"));
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/customerDB");
        dataSource.setUsername("root");
        dataSource.setPassword("welcome");
        return  dataSource;
    }

    @Bean
    public LocalSessionFactoryBean  sessionFactory() throws ClassNotFoundException {
        Properties properties = new Properties();
        properties.put("hibernate.dialect","org.hibernate.dialect.MySQLDialect");
        properties.put("hibernate.show_sql", "true");
        properties.put("hibernate.hbm2ddl.auto","create");
       // properties.put("hibernate.connection.provider_class","org.hibernate.connection.DatasourceConnectionProvider");
        LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
        localSessionFactoryBean.setDataSource(dataSource());
        localSessionFactoryBean.setPackagesToScan("com.example.model");
        localSessionFactoryBean.setHibernateProperties(properties);
        /*SessionFactory sessionFactory = new LocalSessionFactoryBuilder(dataSource()).
                addAnnotatedClass(Customer.class).setProperties(properties).buildSessionFactory();*/
        return localSessionFactoryBean;
    }

    @Bean(name = "hibernateTemplate")
    public HibernateTemplate hibernateTemplate() throws ClassNotFoundException {

        return new HibernateTemplate(sessionFactory().getObject());
    }

    @Bean
    public CustomerDao customerDao() {
        return  new CustomerDao();
    }

    @Override
    public void run(String... args) throws Exception {
        Customer customer = new Customer();
        customer.setFistName("amar");
        customer.setLastName("dev");
        customer.setEmail("amar@gmail.com");
        customerDao().saveCustomer(customer);
    }
}

The exception.

java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:809) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:790) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:777) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]
    at com.example.SpringHibernateDemoApplication.main(SpringHibernateDemoApplication.java:23) [main/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_74]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_74]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_74]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_74]
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) [idea_rt.jar:na]
Caused by: java.lang.IllegalStateException: Already value [org.springframework.orm.jpa.EntityManagerHolder@10753c9] for key [org.hibernate.internal.SessionFactoryImpl@ba5d30] bound to thread [main]
    at org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:190) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.orm.hibernate5.SpringSessionContext.currentSession(SpringSessionContext.java:127) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:454) ~[hibernate-core-5.2.0.Final.jar:5.2.0.Final]
    at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:326) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:309) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.orm.hibernate5.HibernateTemplate.save(HibernateTemplate.java:616) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at com.example.dao.CustomerDao.saveCustomer(CustomerDao.java:11) ~[main/:na]
    at com.example.dao.CustomerDao$$FastClassBySpringCGLIB$$fb200467.invoke(<generated>) ~[main/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at com.example.dao.CustomerDao$$EnhancerBySpringCGLIB$$22cf791b.saveCustomer(<generated>) ~[main/:na]
    at com.example.SpringHibernateDemoApplication.run(SpringHibernateDemoApplication.java:71) [main/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:806) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE]

The problem is you aren't defining a HibernateTransactionManager next to that Spring Boot detects hibernate and JPA and automatically configures it. Both technologies try to start a transaction and that fails. Instead work with the framework, Spring Boot already auto configures a DataSource and EntityManagerFactory (the JPA variant of a SessionFactory ) for you.

Missing HibernateTransactionManager

If you really want to use plain Hibernate add a transaction manager.

@Bean
public HibernateTransactionManager transactionManager(SessionFactory sf) {
    return new HibernateTransactionManager(sf);
}

Use Spring Boot to configure DataSource

I would however urge you to use the framework. Create a file called application.properties in src/main/resources and put the following properties in it.

spring.datasource.url=jdbc:mysql://localhost:3306/customerDB
spring.datasource.username=root
spring.datasource.password=welcome

Now you can remove the DataSource and you can rewrite your sessionFactory method to take a DataSource as method argument.

@Bean
public LocalSessionFactoryBean  sessionFactory(DataSource ds) throws ClassNotFoundException {
    LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
    localSessionFactoryBean.setDataSource(dataSource());
    ... // Other settings here
    return localSessionFactoryBean;
}

Use JPA instead of plain Hibernate

However instead of doing this I suggest removing everything and add the following to your configuration (the application.properties ).

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database-platform=org.hibernate.dialect.MySQL57InnoDBDialect

Now in your SpringHibernateDemoApplication you can remove everything but your CustomerDao (unless that is annotated with @Repository you could remove that as well). Spring Boot already detects and enables transactions so you can also remove the @EnableTransactionManagement .

@SpringBootApplication
public class SpringHibernateDemoApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringHibernateDemoApplication.class, args);
    }

    @Bean
    public CustomerDao customerDao() {
        return  new CustomerDao();
    }

    @Bean
    public CommandLineRunner runner(CustomerDao dao) {
        return new CommandLineRunner() {
            @Override
            public void run(String... args) throws Exception {
                Customer customer = new Customer();
                customer.setFistName("amar");
                customer.setLastName("dev");
                customer.setEmail("amar@gmail.com");
                dao.saveCustomer(customer);
            }            
        };
    }
}

The only thing for you to do is to replace the use of HibernateTemplate (which isn't recommended in the first place!) with the use of an EntityManager . See the JPA Section of the reference guide.

@Repository
public class CustomerDao {

    @PersistenceContext
    private EntityManager em;

    public void saveCustomer(Customer customer) {
        em.persist(customer);
    }
}

Use Spring Data JPA

However if you want to really reduce your code I suggest adding the spring-boot-starter-data-jpa . This will add Spring Data JPA and allows you to remove your current CustomerDao and simply replace it with an interface. (Code below assumes Customer has an id of type Long .).

public interface CustomerDao extends JpaRepository<Customer, Long> {}

Now replace the saveCustomer with save in the CommandLineRunner . (I added the COmmandLineRunner as an inner bean to be able to inject the CustomerDao into the method as an argument).

@SpringBootApplication
public class SpringHibernateDemoApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringHibernateDemoApplication.class, args);
    }

    @Bean
    public CommandLineRunner runner(CustomerDao dao) {
        return new CommandLineRunner() {
            @Override
            public void run(String... args) throws Exception {
                Customer customer = new Customer();
                customer.setFistName("amar");
                customer.setLastName("dev");
                customer.setEmail("amar@gmail.com");
                dao.save(customer);
            }            
        };
    }
}

I strongly suggest a read of the Spring Boot reference guide and learn what it is and what it does. I would also suggest learning new and accepted technologies like JPA and how Spring projects like Spring Data JPA can help you with those.

The root issue you have is basically the usage of LocalSessionFactoryBean with spring-data which already defines a EntityManagerFactory instance; one would probably have no reason to use it directly but HibernateTemplate might appear as a good reason; if you really need to use HibernateTemplate than create it like below such that to avoid the creation of an additional EntityManagerFactory instance:

@Bean
public HibernateTemplate hibernateTemplate(EntityManagerFactory entityManagerFactory) {
    return new HibernateTemplate(entityManagerFactory.unwrap(SessionFactory.class));
}

PS: this solution is incomplete because I don't know how to further integrate the correctly created HibernateTemplate (which works with reads but not writes) with the existing transaction manager

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM