简体   繁体   English

Spring boot hibernate 5 集成错误

[英]Spring boot hibernate 5 integration error

I am trying to integrate spring Boot with Hibernate 5 using Java configs.我正在尝试使用 Java 配置将 Spring Boot 与 Hibernate 5 集成。 It has been some time since I have used Hibernate.自从我使用 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.以下是我的 java 配置文件,后跟异常堆栈跟踪。

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.问题是您没有在 Spring Boot 检测到休眠和 JPA 并自动配置它旁边定义HibernateTransactionManager 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.取而代之的是使用框架,Spring Boot 已经为您自动配置了DataSourceEntityManagerFactorySessionFactory的 JPA 变体)。

Missing HibernateTransactionManager缺少HibernateTransactionManager

If you really want to use plain Hibernate add a transaction manager.如果您真的想使用普通的 Hibernate,请添加一个事务管理器。

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

Use Spring Boot to configure DataSource使用 Spring Boot 配置 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.src/main/resources创建一个名为application.properties的文件,并将以下属性放入其中。

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.现在您可以删除DataSource并且您可以重写sessionFactory方法以将DataSource作为方法参数。

@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使用 JPA 而不是普通的 Hibernate

However instead of doing this I suggest removing everything and add the following to your configuration (the application.properties ).但是,我建议删除所有内容并将以下内容添加到您的配置( 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).现在,在您的SpringHibernateDemoApplication您可以删除除CustomerDao所有内容(除非用@Repository注释,您也可以删除它)。 Spring Boot already detects and enables transactions so you can also remove the @EnableTransactionManagement . Spring Boot 已经检测并启用事务,因此您还可以删除@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 .您唯一要做的就是使用EntityManager替换HibernateTemplate的使用(首先不推荐使用!)。 See the JPA Section of the reference guide.请参阅参考指南的JPA 部分

@Repository
public class CustomerDao {

    @PersistenceContext
    private EntityManager em;

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

Use Spring Data JPA使用 Spring Data JPA

However if you want to really reduce your code I suggest adding the spring-boot-starter-data-jpa .但是,如果您想真正减少代码,我建议添加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.这将添加Spring Data JPA并允许您删除当前的CustomerDao并简单地将其替换为一个接口。 (Code below assumes Customer has an id of type Long .). (下面的代码假设Customer有一个Long类型的 id。)。

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

Now replace the saveCustomer with save in the CommandLineRunner .现在用CommandLineRunner save替换saveCustomer (I added the COmmandLineRunner as an inner bean to be able to inject the CustomerDao into the method as an argument). (我添加了COmmandLineRunner作为内部 bean,以便能够将CustomerDao作为参数注入到方法中)。

@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.我强烈建议阅读 Spring Boot 参考指南并了解它是什么以及它的作用。 I would also suggest learning new and accepted technologies like JPA and how Spring projects like Spring Data JPA can help you with those.我还建议学习新的和公认的技术,如 JPA,以及 Spring Data JPA 等 Spring 项目如何帮助你解决这些问题。

The root issue you have is basically the usage of LocalSessionFactoryBean with spring-data which already defines a EntityManagerFactory instance;您遇到的根本问题基本上是将LocalSessionFactoryBean与已经定义了EntityManagerFactory实例的 spring-data 一起使用; one would probably have no reason to use it directly but HibernateTemplate might appear as a good reason;人们可能没有理由直接使用它,但HibernateTemplate可能是一个很好的理由; if you really need to use HibernateTemplate than create it like below such that to avoid the creation of an additional EntityManagerFactory instance:如果你真的需要使用HibernateTemplate不是像下面那样创建它,以避免创建额外的EntityManagerFactory实例:

@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 PS:此解决方案不完整,因为我不知道如何将正确创建的HibernateTemplate (适用于读取但不适用于写入)与现有事务管理器进一步集成

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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