Spring Boot 3 + JPA多模块系统对MySQL和DORIS进行多数据源集成实战(荣耀典藏版)

Spring Boot 3 + JPA多模块系统对MySQL和DORIS进行多数据源集成实战(荣耀典藏版) 前言大家好我是月夜枫没错这个不是凑数的。背景最近公司要求系统内使用DORIS做数据分析同时需要保留原来MySQL部分因此需要从配置上对多数据源进行集成。主模块配置spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://ip:port/database username: username password: password type: com.alibaba.druid.pool.DruidDataSource druid: # https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter # 连接池的配置信息 # 初始化大小最小最大 initial-size: 2 min-idle: 2 # 最大连接数建议 CPU核核数 * 2 1 max-active: 5 validation-query: SELECT 1 FROM DUAL validation-query-timeout: 1 # 设置从连接池获取连接时是否检查连接有效性true时如果连接空闲时间超过manEvictableIdleTimeMillis进行检查否则不检查;false时不检查 test-while-idle: true # 指明是否在从池中取出连接时进行检查,每次都检查 validation-query 不能为空 test-on-borrow: false # 指明是否在归还到池中前进行检查 test-on-return: false # 打开后增强timeBetweenEvictionRunsMillis的周期性连接检查minIdle内的空闲连接每次检查强制验证连接有效性. 参考https://github.com/alibaba/druid/wiki/KeepAlive_cn keep-alive: true # 打开PSCacheOracle等支持游标的数据库打开此开关会以数量级提升性能具体查阅PSCache相关资料 pool-prepared-statements: true # 指定每个连接上PSCache的大小 max-pool-prepared-statement-per-connection-size: 20 #filters: stat,wall,slf4j #配置监控统计拦截的filters去掉后监控界面sql无法统计wall用于防火墙 filters: stat,slf4j # 配置DruidStatFilter web-stat-filter: enabled: true url-pattern: /druid/* exclusions: /druid/*,*.html,*.htm,*.js,*.css,*.gif,*.jpg,*.bmp,*.png,*.ico,*.svg,*.ttf,*.woff,*.woff2 # 配置 DruidStatViewServlet stat-view-servlet: enabled: true url-pattern: /druid/* # 禁用HTML页面上的“Reset All”功能 reset-enable: false # 登录名 login-username: druid # 登录密码 login-password: druid filter: # 数据库监控统计 StatFilter stat: # 记录慢 sql 配置 enabled: true db-type: mysql log-slow-sql: true merge-sql: true # 慢 sql 标准 slow-sql-millis: 5000 # 防火墙防 sql 注入 wall: db-type: mysql slf4j: enabled: true >if (StringUtils.hasText(ddlAuto) !none.equals(ddlAuto)) { result.put(hibernate.hbm2ddl.auto, ddlAuto); } else { result.remove(hibernate.hbm2ddl.auto); }在org.springframework.boot.autoconfigure.orm.jpa包下的HibernateProperties中determineHibernateProperties方法内有上述逻辑判断当ddl-auto设置为none时Hibernate不会再对表结构做处理。import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.annotation.Resource; import jakarta.persistence.EntityManager; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.SharedEntityManagerCreator; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.Map; import java.util.Objects; Configuration EnableJpaRepositories(basePackages com.mysqlPackages.**.dao.jpa, entityManagerFactoryRef mySQLEntityManagerFactory, transactionManagerRef mySQLTransactionManager) public class MySQLConfiguration { Resource private JpaProperties jpaProperties; Resource private HibernateProperties hibernateProperties; Bean Primary ConfigurationProperties(spring.datasource) public DataSourceProperties mySQLDataSourceProperties() { return new DataSourceProperties(); } Bean Primary ConfigurationProperties(spring.datasource) public DataSource mySQLDataSource() { return mySQLDataSourceProperties().initializeDataSourceBuilder().build(); } Bean Primary public LocalContainerEntityManagerFactoryBean mySQLEntityManagerFactory(EntityManagerFactoryBuilder builder) { MapString, Object properties hibernateProperties.determineHibernateProperties( this.jpaProperties.getProperties(), new HibernateSettings() ); return builder .dataSource(mySQLDataSource()) .packages(com.mysqlPackages.**.entity, com.core.**)//可配置多个扫描路径包括Entity和Converter等类的扫描 .properties(properties) .persistenceUnit(mySQLPersistenceUnit) .build(); } Bean Primary public PlatformTransactionManager mySQLTransactionManager(EntityManagerFactoryBuilder builder) { JpaTransactionManager transactionManager new JpaTransactionManager(); transactionManager.setEntityManagerFactory(mySQLEntityManagerFactory(builder).getObject()); return transactionManager; } Bean public EntityManager mySQLEntityManager(EntityManagerFactoryBuilder builder) { return SharedEntityManagerCreator.createSharedEntityManager(Objects.requireNonNull(mySQLEntityManagerFactory(builder).getObject())); } Bean public JPAQueryFactory jpaQueryFactory(EntityManagerFactoryBuilder builder) { return new JPAQueryFactory(mySQLEntityManager(builder)); } }配置类中首先注意通过EnableJpaRepositories配置扫描dao包路径要与DORIS的路径做区分避免同时加载到容器中。添加了注解Primary标注作为最高优先级的bean该注解是Spring Boot 3引入的。引入了DataSourceProperties在源码上它默认获取的是spring.datasource下的配置因此实际ConfigurationProperties注解是不需要进行标注的这里是为了提供代码可读性依旧标注在配置bean上ConfigurationProperties( prefix spring.datasource ) public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean { // 内容 }同样的在org.springframework.boot.autoconfigure.orm.jpa包下JPA的配置引入也是有源码支持的ConfigurationProperties( prefix spring.jpa ) public class JpaProperties { // 内容 }EntityManagerFactory的properties将JPA和Hibernate的配置通过HibernateProperties的determineHibernateProperties方法注入到Map中。由之前提到的HibernateProperties源码可知假设需要在配置类中的properties内单独加入ddl-auto的配置在目前的版本就不能使用spring.jpa.hibernate.ddl-auto而应该使用hibernate.hbm2ddl.auto。DORIS模块配置spring: datasource: doris: driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://ip:port/database username: username password: password type: com.alibaba.druid.pool.DruidDataSource druid: # https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter # 连接池的配置信息 # 初始化大小最小最大 initial-size: 2 min-idle: 2 # 最大连接数建议 CPU核核数 * 2 1 max-active: 5 validation-query: SELECT 1 FROM DUAL validation-query-timeout: 1 # 设置从连接池获取连接时是否检查连接有效性true时如果连接空闲时间超过manEvictableIdleTimeMillis进行检查否则不检查;false时不检查 test-while-idle: true # 指明是否在从池中取出连接时进行检查,每次都检查 validation-query 不能为空 test-on-borrow: false # 指明是否在归还到池中前进行检查 test-on-return: false # 打开后增强timeBetweenEvictionRunsMillis的周期性连接检查minIdle内的空闲连接每次检查强制验证连接有效性. 参考https://github.com/alibaba/druid/wiki/KeepAlive_cn keep-alive: true # 打开PSCacheOracle等支持游标的数据库打开此开关会以数量级提升性能具体查阅PSCache相关资料 pool-prepared-statements: true # 指定每个连接上PSCache的大小 max-pool-prepared-statement-per-connection-size: 20 #filters: stat,wall,slf4j #配置监控统计拦截的filters去掉后监控界面sql无法统计wall用于防火墙 filters: stat,slf4j # 配置DruidStatFilter web-stat-filter: enabled: true url-pattern: /druid/* exclusions: /druid/*,*.html,*.htm,*.js,*.css,*.gif,*.jpg,*.bmp,*.png,*.ico,*.svg,*.ttf,*.woff,*.woff2 # 配置 DruidStatViewServlet stat-view-servlet: enabled: true url-pattern: /druid/* # 禁用HTML页面上的“Reset All”功能 reset-enable: false # 登录名 login-username: druid # 登录密码 login-password: druid filter: # 数据库监控统计 StatFilter stat: # 记录慢 sql 配置 enabled: true db-type: mysql log-slow-sql: true merge-sql: true # 慢 sql 标准 slow-sql-millis: 5000 # 防火墙防 sql 注入 wall: db-type: mysql slf4j: enabled: true >import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.annotation.Resource; import jakarta.persistence.EntityManager; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.SharedEntityManagerCreator; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.Map; import java.util.Objects; Configuration EnableJpaRepositories(basePackages com.dorisPackadges.**.dao.jpa, entityManagerFactoryRef dorisEntityManagerFactory, transactionManagerRef dorisTransactionManager) public class DorisConfiguration { Resource private JpaProperties jpaProperties; Resource private HibernateProperties hibernateProperties; Bean ConfigurationProperties(spring.datasource.doris) public DataSourceProperties dorisDataSourceProperties() { return new DataSourceProperties(); } Bean ConfigurationProperties(spring.datasource.doris) public DataSource dorisDataSource() { return dorisDataSourceProperties().initializeDataSourceBuilder().build(); } Bean public LocalContainerEntityManagerFactoryBean dorisEntityManagerFactory(EntityManagerFactoryBuilder builder) { MapString, Object properties hibernateProperties.determineHibernateProperties( this.jpaProperties.getProperties(), new HibernateSettings() ); LocalContainerEntityManagerFactoryBean entityManagerFactoryBean builder .dataSource(dorisDataSource()) .properties(properties) .packages(com.dorisPackadges.entity.doris, com.core.**)//可配置多个扫描路径包括Entity和Converter等类的扫描 .persistenceUnit(dorisPersistenceUnit) .build(); return entityManagerFactoryBean; } Bean public PlatformTransactionManager dorisTransactionManager(EntityManagerFactoryBuilder builder) { JpaTransactionManager transactionManager new JpaTransactionManager(); transactionManager.setEntityManagerFactory(dorisEntityManagerFactory(builder).getObject()); return transactionManager; } Bean public EntityManager dorisEntityManager(EntityManagerFactoryBuilder builder) { return SharedEntityManagerCreator.createSharedEntityManager(Objects.requireNonNull(dorisEntityManagerFactory(builder).getObject())); } Bean public JPAQueryFactory dorisJpaQueryFactory(EntityManagerFactoryBuilder builder) { return new JPAQueryFactory(dorisEntityManager(builder)); } }DORIS的配置类变化也不是很大主要有以下几点扫描包路径更改为DORIS对应路径DataSourceProperties需要指定配置ConfigurationProperties(spring.datasource.doris)JPA的配置可共用与MySQL差别不大。其他配置由于使用到了多模块的配置除了不同环境的yml配置文件集成外不同模块的配置文件也需要集成因此我的application.yml设计如下spring: profiles: # 动态环境 active: dev # env # 采坑记录使用 获取 pom.xml 环境变量时必须指定 build resources resource filteringtrue/filtering include: dorisinclude实际指向的是DORIS模块下的application-doris.yml配置文件。Configuration public class JpaConfiguration { public static JPAQueryFactory getJpaQueryFactory() { return BeanUtils.getBean(jpaQueryFactory); } }如果用到了QueryDSL那么不同数据源对应的不同的JpaQueryFactory需要做不同的引用这里就只能用beanName在容器中做bean的指定了。最后说一句(求关注别白嫖我)如果这篇文章对您有所帮助或者有所启发的话帮忙关注一下您的支持是我坚持写作最大的动力。求一键三连点赞、转发、在看。我从清晨走过也拥抱夜晚的星辰人生没有捷径你我皆平凡你好陌生人一起共勉。