繁体   English   中英

Spring boot hibernate 5 集成错误

[英]Spring boot hibernate 5 integration error

我正在尝试使用 Java 配置将 Spring Boot 与 Hibernate 5 集成。 自从我使用 Hibernate 以来已经有一段时间了。 我收到下面提到的异常。 有人可以建议如何解决这个问题吗?

以下是我的 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);
    }
}

例外。

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]

问题是您没有在 Spring Boot 检测到休眠和 JPA 并自动配置它旁边定义HibernateTransactionManager 这两种技术都尝试启动事务,但都失败了。 取而代之的是使用框架,Spring Boot 已经为您自动配置了DataSourceEntityManagerFactorySessionFactory的 JPA 变体)。

缺少HibernateTransactionManager

如果您真的想使用普通的 Hibernate,请添加一个事务管理器。

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

使用 Spring Boot 配置 DataSource

但是,我会敦促您使用该框架。 src/main/resources创建一个名为application.properties的文件,并将以下属性放入其中。

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

现在您可以删除DataSource并且您可以重写sessionFactory方法以将DataSource作为方法参数。

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

使用 JPA 而不是普通的 Hibernate

但是,我建议删除所有内容并将以下内容添加到您的配置( application.properties )中,而不是这样做。

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

现在,在您的SpringHibernateDemoApplication您可以删除除CustomerDao所有内容(除非用@Repository注释,您也可以删除它)。 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);
            }            
        };
    }
}

您唯一要做的就是使用EntityManager替换HibernateTemplate的使用(首先不推荐使用!)。 请参阅参考指南的JPA 部分

@Repository
public class CustomerDao {

    @PersistenceContext
    private EntityManager em;

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

使用 Spring Data JPA

但是,如果您想真正减少代码,我建议添加spring-boot-starter-data-jpa 这将添加Spring Data JPA并允许您删除当前的CustomerDao并简单地将其替换为一个接口。 (下面的代码假设Customer有一个Long类型的 id。)。

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

现在用CommandLineRunner save替换saveCustomer (我添加了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);
            }            
        };
    }
}

我强烈建议阅读 Spring Boot 参考指南并了解它是什么以及它的作用。 我还建议学习新的和公认的技术,如 JPA,以及 Spring Data JPA 等 Spring 项目如何帮助你解决这些问题。

您遇到的根本问题基本上是将LocalSessionFactoryBean与已经定义了EntityManagerFactory实例的 spring-data 一起使用; 人们可能没有理由直接使用它,但HibernateTemplate可能是一个很好的理由; 如果你真的需要使用HibernateTemplate不是像下面那样创建它,以避免创建额外的EntityManagerFactory实例:

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

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

暂无
暂无

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

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