SpringBoot 集成 Hibernate 及事务管理配置

    笔者之前维护了一个 spring mvc + hibernate 4.x 的旧项目,最近将这个旧项目升级到了 spring boot 2.3.12 + hibernate 5.x,现将集成过程和一些需要注意的地方记录下来,方便后续继续研究。

一、pom.xml 引入与 hibernate 相关的依赖

	 <!-- jpa配置,会自动引入 hibernate 相关依赖 -->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-jpa</artifactId>
     </dependency>
     
     <!-- 数据库连接 -->
     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
     </dependency>  

    添加 spring-boot-starter-data-jpa 依赖会自动引入 hibernate 的 lib 包,如图:

二、在application.properties 对 hibernate 进行基础配置及数据源配置

# spring boot 2.3.12 hiberate
spring.jpa.show-sql=true
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext

# jdbc driver
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/project-name?useUnicode\=true&characterEncoding\=UTF-8&zeroDateTimeBehavior\=convertToNull
spring.datasource.username=root
spring.datasource.password=1234

# hikari datasource configuration
spring.datasource.hikari.pool-name=KaolaHikariCp
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.idle-timeout=180000
spring.datasource.hikari.max-lifetime=180000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1

三、Hebernate 配置类

    可以发现,Hebernate 配置类中的一些属性是从 application.properties 文件里获取到的。

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import java.util.Properties;


@Configuration
public class HibernateConfig {
	
    @Autowired
    private Environment environment;
   

   /**
    * 配置SessionFactory会话工厂注入到spring容器
    * @return
    */
    @Bean
    LocalSessionFactoryBean sessionFactory() {   
	   
        LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
        localSessionFactoryBean.setDataSource(getDataSource());
        localSessionFactoryBean.setPackagesToScan("com.hibernate.demo"); //dao与entity的公共包,这里写错,请求接口会抛出异常:org.hibernate.MappingException: Unknown entity
        localSessionFactoryBean.setHibernateProperties(hibernateProperties());
        return localSessionFactoryBean;
        
    } 
    

    /**
     * 配置数据源
     * @return
     */
    private DriverManagerDataSource getDataSource() {
    
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(environment.getProperty("spring.datasource.driver-class-name"));
        dataSource.setUsername(environment.getProperty("spring.datasource.username"));
        dataSource.setPassword(environment.getProperty("spring.datasource.password"));
        dataSource.setUrl(environment.getProperty("spring.datasource.url"));
        return dataSource;
        
    }

    
    /**
     * 设置hibernate属性配置
     * @return
     */
    private Properties hibernateProperties() {
    	
        Properties properties = new Properties();
        properties.setProperty("hibernate.naming-strategy", environment.getProperty("spring.jpa.hibernate.naming-strategy"));
        properties.setProperty("hibernate.dialect", environment.getProperty("spring.jpa.properties.hibernate.dialect"));
        properties.setProperty("hibernate.current_session_context_class", environment.getProperty("spring.jpa.properties.hibernate.current_session_context_class"));
        properties.setProperty("hibernate.show_sql", environment.getProperty("spring.jpa.show-sql"));
        //根据需要配置 
        //properties.setProperty("hibernate.hbm2ddl.auto", environment.getProperty("spring.jpa.hibernate.ddl-auto"));
        return  properties;
        
    }
    
    
    /**
     * 配置事务管理:由HibernateTransactionManager处理
     * @param sessionFactory
     * @return
     */
    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
       HibernateTransactionManager txManager = new HibernateTransactionManager();
       txManager.setSessionFactory(sessionFactory);
       return txManager;
    }
    
}

 四、启动类配置

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.transaction.annotation.EnableTransactionManagement;


//开启事务管理
@EnableTransactionManagement(proxyTargetClass = true)//启用注解事务
@SpringBootApplication(exclude = { HibernateJpaAutoConfiguration.class })
public class App extends SpringBootServletInitializer {
	
    public static void main( String[] args ) {
    	SpringApplication.run(App.class, args);
    }
    
    

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(App.class);
    }    

}

    注意:在第三步的 hibernate 配置类里将事务管理交给 HibernateTransactionManager 来处理, 需要在 @SpringBootApplication注解里添加(exclude = { HibernateJpaAutoConfiguration.class }),否则会抛出异常:

java.lang.ClassCastException: org.springframework.orm.jpa.EntityManagerHolder cannot be cast to org.springframework.orm.hibernate5.SessionHolder

五、其他异常状况的分析

    如果在第三步的 hibernate 配置类里不配置事务管理(即:不将事务管理交给 HibernateTransactionManager 类来处理),并且启动类@SpringBootApplication注解不额外添加(exclude = { HibernateJpaAutoConfiguration.class })的情况下,笔者为了验证事务管理是否正常,在一个声明为 @Transactional(readOnly = true) 的方法里执行 update 数据库的操作,代码如下:

	@Transactional(readOnly = true)
	public Test getTest(int id) {
		
		testDAO.update(1, "hahaha");
		//test rollback
		//int i = 1/0;
		return testDAO.get(id);
		
	}

    但是很奇怪,并没有抛出我预期中的异常,并且成功更新了数据库的数据:

java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

    另外,添加代码 int i = 1/0;  故意在更新操作完成后抛出异常时,结果显示事务管理正常执行了回滚操作,并没有更新数据库中的数据。既然事务管理已经发生作用,为什么「readonly = true」这一禁止更新操作的配置却不起作用呢?如果按照步骤四进行设置,则可以正常抛出上面的异常,同时异常回滚操作也是正常的。

    所以,目前的结论是如果将事务处理交给 HibernateTransactionManager 来处理,就需要在启动类添加@SpringBootApplication(exclude = { HibernateJpaAutoConfiguration.class }),上面提到的异常情况,具体的原因还没有搞清楚,只是猜测是因为 Springboot Jpa 自动配置的事务管理发生了异常,这里只能先记录下来,等继续学习后再来解决了,如果有了解的同学知道具体原因,还希望可以留言指导^_^

参考:

1. https://blog.csdn.net/weixin_44341110/article/details/115208375

2. https://blog.csdn.net/u011930054/article/details/106856750

3. https://www.modb.pro/db/148743

4. https://blog.csdn.net/kimheesunliulu/article/details/99550748

猜你喜欢

转载自blog.csdn.net/crazestone0614/article/details/128138262