Spring Boot 2.0.4整合Spring Data JPA和Druid,双数据源

最近Team开始尝试使用Spring Boot + Spring Data JPA作为数据层的解决方案,在网上逛了几圈之后发现大家并不待见JPA,理由是(1)MyBatis简单直观够用,(2)以Hibernate为底层的Spring Data JPA复杂且性能一般。

但是当我们来到Spring Boot的世界后发现,相较于Spring Data JPA,MyBatis对Spring Boot的支持有限,Spring Data JPA与Spring Boot结合可以让dao变得非常简单,比如(1)JPA自带分页对象,无需设置插件;(2)一个空接口搞定所有基本CRUD。

本着虚心学习的态度,我决定将Spring Boot、Spring Data JPA和Druid三者整合在一起,并分别对SQL Server和MySQL进行支持,希望本文能够帮助到需要相关技术的同学。

1. 程序和版本

Spring Boot 2.0.4

mssql-jdbc 6.2.2.jre8

mysql-connector-java 5.1.46

druid-spring-boot-starter 1.1.10

2. properties配置文件

我们把主程序配置文件application.properties和数据库配置文件分开,这样可使application.properties不至于臃肿。

(1) application.properties

1 server.port=9006
2 spring.application.name=spring-data-jpa
3 
4 #Serialize JPA entity to Json string.
5 spring.jackson.serialization.fail-on-empty-beans=false

第5行的作用是避免com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer,该配置只对MSSQL数据源有效。

(2) db.properties

 1 #Data source 1
 2 db1.sqlserver.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
 3 db1.sqlserver.url=${DB1_URL:jdbc:sqlserver://127.0.0.1:1433;DatabaseName=MyTestDb1}
 4 db1.sqlserver.username=${DB1_UID:tester}
 5 db1.sqlserver.password=${DB1_PWD:tester}
 
6
db1.sqlserver.initial-size=1 7 db1.sqlserver.min-idle=1 8 db1.sqlserver.max-active=20 9 db1.sqlserver.max-wait=60000 10 db1.sqlserver.time-between-eviction-runs-millis=60000 11 db1.sqlserver.min-evictable-idle-time-millis=300000 12 db1.sqlserver.validation-query=select 1 13 db1.sqlserver.test-on-borrow=true 14 db1.sqlserver.test-While-Idle=true 15 db1.sqlserver.test-on-return=false 16 db1.sqlserver.pool-prepared-statements=false 17 db1.sqlserver.max-pool-prepared-statement-per-connection-size=20 18 19 db1.sqlserver.filter.stat.enabled=true 20 db1.sqlserver.filter.stat.db-type=mssql 21 db1.sqlserver.filter.stat.log-slow-sql=true 22 db1.sqlserver.filter.stat.slow-sql-millis=2000 23 24 db1.sqlserver.jpa.hibernate.dialect=org.hibernate.dialect.SQLServerDialect 25 db1.sqlserver.jpa.hibernate.show_sql=true 26 db1.sqlserver.jpa.hibernate.format_sql=true 27 28 #Data source 2 29 db2.mysql.driver-class-name=com.mysql.jdbc.Driver 30 db2.mysql.url=${DB2_URL:jdbc:mysql://127.0.0.1:3306/Test}?useUnicode=true&useSSL=false 31 db2.mysql.username=${DB2_UID:tester} 32 db2.mysql.password=${DB2_PWD:tester} 33 db2.mysql.initial-size=1 34 db2.mysql.min-idle=1 35 db2.mysql.max-active=20 36 db2.mysql.max-wait=60000 37 db2.mysql.time-between-eviction-runs-millis=60000 38 db2.mysql.min-evictable-idle-time-millis=300000 39 db2.mysql.validation-query=select 1 40 db2.mysql.test-on-borrow=true 41 db2.mysql.test-While-Idle=true 42 db2.mysql.test-on-return=false 43 db2.mysql.pool-prepared-statements=false 44 db2.mysql.max-pool-prepared-statement-per-connection-size=20 45 46 db2.mysql.filter.stat.enabled=true 47 db2.mysql.filter.stat.db-type=mysql 48 db2.mysql.filter.stat.log-slow-sql=true 49 db2.mysql.filter.stat.slow-sql-millis=2000 50 51 db2.mysql.jpa.hibernate.dialect=org.hibernate.dialect.MySQLDialect 52 db2.mysql.jpa.hibernate.show_sql=true 53 db2.mysql.jpa.hibernate.format_sql=true 54 db2.mysql.jpa.hibernate.enable_lazy_load_no_trans=true

该配置文件可分为三部分:一是JPA的数据源基本信息配置(行5之前);二是JPA的数据库连接池配置(行6-行17);三是Druid连接池的特殊配置(行19-行22);四是自定义配置(行24-行26)。

需要注意行54的配置,加这一行是为了解决由Hibernate懒加载引起的异常org.hibernate.LazyInitializationException: could not initialize proxy [devutility.test.database.springdatajpa.dao.mysql.entity.Customer#100000123] - no Session

但是让enable_lazy_load_no_trans=true会带来一定的性能问题,具体参考https://vladmihalcea.com/the-hibernate-enable_lazy_load_no_trans-anti-pattern/

此外,解决org.hibernate.LazyInitializationException异常还有另外一种方法,在每个Entity类型上添加@Proxy(lazy = false)注解,经测试有效。

3. Java Config

为便于管理,每个数据源一个配置类,此处只列出一个数据源:

 1 import java.util.Properties;
 2 
 3 import javax.sql.DataSource;
 4 
 5 import org.springframework.boot.context.properties.ConfigurationProperties;
 6 import org.springframework.context.annotation.Bean;
 7 import org.springframework.context.annotation.Configuration;
 8 import org.springframework.context.annotation.Primary;
 9 import org.springframework.context.annotation.PropertySource;
10 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
11 import org.springframework.orm.jpa.JpaTransactionManager;
12 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
13 import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
14 import org.springframework.transaction.PlatformTransactionManager;
15 
16 import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
17 
18 import devutility.internal.util.PropertiesUtils;
19 
20 @Configuration
21 @PropertySource("classpath:db.properties")
22 @EnableJpaRepositories(basePackages = "devutility.test.database.springdatajpa.dao.mssql", entityManagerFactoryRef = "entityManagerFactory1", transactionManagerRef = "transactionManager1")
23 public class DataSource1Configuration {
24     @Primary
25     @Bean
26     @ConfigurationProperties("db1.sqlserver")
27     public DataSource dataSource1() {
28         return DruidDataSourceBuilder.create().build();
29     }
30 
31     @Bean
32     @ConfigurationProperties("db1.sqlserver.jpa")
33     public Properties jpaProperties1() {
34         return new Properties();
35     }
36 
37     @Primary
38     @Bean
39     public LocalContainerEntityManagerFactoryBean entityManagerFactory1() {
40         LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
41         localContainerEntityManagerFactoryBean.setDataSource(dataSource1());
42         localContainerEntityManagerFactoryBean.setPackagesToScan(new String[] { "devutility.test.database.springdatajpa.dao.mssql.entity" });
43         localContainerEntityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
44         localContainerEntityManagerFactoryBean.setJpaPropertyMap(PropertiesUtils.toMap(jpaProperties1()));
45         return localContainerEntityManagerFactoryBean;
46     }
47 
48     @Bean
49     public PlatformTransactionManager transactionManager1() {
50         JpaTransactionManager transactionManager = new JpaTransactionManager();
51         transactionManager.setEntityManagerFactory(entityManagerFactory1().getObject());
52         return transactionManager;
53     }
54 }

4. Druid控制台页面配置

Druid的详细配置见Druid官网

如果你不想对Druid控制台的访问加以限制可以忽略此节,如果你希望通过用户名和密码访问Druid控制台,有如下两种配置方式:

(1)Java Config

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;

@Configuration
public class DruidConfiguration {
    @Bean
    public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
        ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        servletRegistrationBean.addInitParameter("loginUsername", "admin");
        servletRegistrationBean.addInitParameter("loginPassword", "admin");
        servletRegistrationBean.addInitParameter("resetEnable", "false");
        return servletRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean<WebStatFilter> druidStatFilter() {
        FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
        filterRegistrationBean.setName("DruidWebStatFilter");
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
}

(2). 在application.properties文件中添加

#Configuration for druid
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin

Demo代码

猜你喜欢

转载自www.cnblogs.com/eagle6688/p/9557620.html