轻松搞定spring boot+jpa实现多数据源读取

多数据源配置使用注解和yml,只需要四步,亲测可用。

环境配置不提版本简直是耍流氓。
所以声明下,工程是在SpringBoot 2.1.8.RELEASE, JDK8环境下调通的。

多数据源的需求:笔者这个微服务负责数据分析,所以微服务除了自己的直连数据库,还需要连接其他的数据库进行合并查询。从下图可以看到服务目前支持连接user数据库和analysis数据库。

工程目录一栏

如果需求相同、版本相似,下面代码毫不犹豫拿去修改就行,坑已趟平。如果想要看数据库原理的,欢迎看笔者的其他博客。

四步走起

1. 配置DataSource

注释掉的是非必须的配置

@Configuration
@Slf4j
public class DruidDataSourceConfig {

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Value("${spring.datasource.initial-size}")
    private int initialSize;

    @Value("${spring.datasource.min-idle}")
    private int minIdle;

    @Value("${spring.datasource.max-active}")
    private int maxActive;

    @Value("${spring.datasource.max-wait}")
    private int maxWait;

    @Value("${spring.datasource.connectionInitSqls}")
    private String connectionInitSqls;
//
//    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
//    private int timeBetweenEvictionRunsMillis;
//
//    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
//    private int minEvictableIdleTimeMillis;
//
//    @Value("${spring.datasource.validationQuery}")
//    private String validationQuery;
//
//    @Value("${spring.datasource.testWhileIdle}")
//    private boolean testWhileIdle;
//
//    @Value("${spring.datasource.testOnBorrow}")
//    private boolean testOnBorrow;
//
//    @Value("${spring.datasource.testOnReturn}")
//    private boolean testOnReturn;
//
//    @Value("${spring.datasource.poolPreparedStatements}")
//    private boolean poolPreparedStatements;
//
//    @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
//    private int maxPoolPreparedStatementPerConnectionSize;
//
//    @Value("${spring.datasource.filters}")
//    private String filters;
//
//    @Value("{spring.datasource.connectionProperties}")
//    private String connectionProperties;

    /* #####################基础公共配置##################### */

    /* #####################analysisdb配置##################### */
    @Value("${spring.datasource.analysisdb.url}")
    private String analysisUrl;

    @Value("${spring.datasource.analysisdb.username}")
    private String analysisUsername;

    @Value("${spring.datasource.analysisdb.password}")
    private String analysisPassword;

    @Bean(name = "analysisDataSource")
    @Primary // 确定主数据源
    public DataSource analysisDataSource() {
        log.info("创建analysisDataSource数据源");
        return createDataSource(analysisUrl, analysisUsername, analysisPassword);
    }

    /* #####################userdb配置##################### */

    @Value("${spring.datasource.userdb.url}")
    private String userUrl;

    @Value("${spring.datasource.userdb.username}")
    private String userUsername;

    @Value("${spring.datasource.userdb.password}")
    private String userPassword;

    @Bean(name = "userDataSource")
    public DataSource userDataSource() {
        log.info("创建userDataSource数据源");
        return createDataSource(userUrl, userUsername, userPassword);
    }

    /* #####################DataSource配置##################### */

    private DataSource createDataSource(String url, String username, String password) {
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(url);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        // configuration
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
//        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
//        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
//        datasource.setValidationQuery(validationQuery);
//        datasource.setTestWhileIdle(testWhileIdle);
//        datasource.setTestOnBorrow(testOnBorrow);
//        datasource.setTestOnReturn(testOnReturn);
//        datasource.setPoolPreparedStatements(poolPreparedStatements);
//        datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
//        try {
//            datasource.setFilters(filters);
//        } catch (SQLException e) {
//            e.printStackTrace();
//        }
//        datasource.setConnectionProperties(connectionProperties);

        List<String> connectInitSqls = new ArrayList<String>(){{
            add(connectionInitSqls);
        }};
        datasource.setConnectionInitSqls(connectInitSqls);
        return datasource;
    }

}

2. 配置多数据源yml

server:
  port: 9007
spring:
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      show_sql: true
      format_sql: true
      hibernate:
        ejb:
#          interceptor: 此处是打印sql的配置,非必须
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
#  自定义数据库配置
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    connectionInitSqls: set names utf8mb4;
    initial-size: 10
    max-active: 60
    min-idle: 5
    max-wait: 60
    analysisdb:
      url: jdbc:mysql://host:3306/analysis?useUnicode=true&characterEncoding=utf-8&useSSL=false
      username: username
      password: password
    userdb:
      url: jdbc:mysql://host:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false
      username: username
      password: password

3. 配置每个数据源的初始化信息

第一个数据库:

@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "analysisEntityManagerFactory", // 实体类工厂依赖
        transactionManagerRef = "analysisTransactionManager", // 事务依赖
        basePackages = "com.xxx.analysis.dao.analysis") // repository类所在的包
@EnableTransactionManagement
@Slf4j
public class AnalysisDBConfig {

    @Autowired
    private JpaProperties jpaProperties;

    @Autowired
    @Qualifier("analysisDataSource")
    private DataSource dataSource;

    @Autowired
    private HibernateProperties hibernateProperties;
    
    /*
     * 通过LocalContainerEntityManagerFactoryBean来获取EntityManagerFactory实例
     */
    @Bean(name = "analysisEntityManagerFactoryBean")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(
            EntityManagerFactoryBuilder builder) {
        Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
                jpaProperties.getProperties(), new HibernateSettings());
        return builder.dataSource(dataSource).properties(properties)
                .packages("com.xxx.analysis.domain.analysis").build();
    }

    /*
     * EntityManagerFactory类似于Hibernate的SessionFactory,mybatis的SqlSessionFactory
     * 总之,在执行操作之前,我们总要获取一个EntityManager,这就类似于Hibernate的Session,
     * mybatis的sqlSession.
     */
    @Bean(name = "analysisEntityManagerFactory")
    @Primary
    public EntityManagerFactory entityManagerFactory(EntityManagerFactoryBuilder builder) {
        log.info("创建analysisEntityManagerFactory");
        return this.entityManagerFactoryBean(builder).getObject();
    }

    /*
     * 配置事务管理器
     */
    @Bean(name = "analysisTransactionManager")
    @Primary
    public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
        log.info("创建analysisTransactionManager");
        return new JpaTransactionManager(this.entityManagerFactory(builder));
    }
}

第二个数据库:

@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "userEntityManagerFactory",
        transactionManagerRef = "userTransactionManager",
        basePackages = "com.xxx.analysis.dao.user")
@EnableTransactionManagement
@Slf4j
public class UserDBConfig {

    @Autowired
    private JpaProperties jpaProperties;

    @Autowired
    @Qualifier("userDataSource")
    private DataSource dataSource;

    @Autowired
    private HibernateProperties hibernateProperties;

    @Bean(name = "userEntityManagerFactoryBean")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(
            EntityManagerFactoryBuilder builder) {
        Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
                jpaProperties.getProperties(), new HibernateSettings());
        return builder.dataSource(dataSource).properties(properties)
                .packages("com.xxx.analysis.domain.user").build();
    }

    @Bean(name = "userEntityManagerFactory")
    public EntityManagerFactory entityManagerFactory(EntityManagerFactoryBuilder builder) {
        log.info("创建userEntityManagerFactory");
        return this.entityManagerFactoryBean(builder).getObject();
    }

    @Bean(name = "userTransactionManager")
    @Primary
    public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
        log.info("创建userTransactionManager");
        return new JpaTransactionManager(this.entityManagerFactory(builder));
    }
}

4. 增加domain和dao层代码,补充测试代码。

简单贴一下代码,毕竟不是每个人都熟悉jpa。
下面为analysis的domain层和dao层的代码。

注意,domain和dao的包名要与第三步配置的包名是一致的。

domain:

@Data
@Entity
@Table(name = "test_info")
@Where(clause = "valid = 1")
public class TestInfo extends BaseEntity {
    @Column
    private String content;
}

dao:

@Repository
public interface TestRepository extends BaseRepository<TestInfo, Long> {

}

测试代码,笔者觉得这个很随意,当时随手写了个定时来debug

@Component
@Slf4j
public class DataSchedule {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private TestRepository testRepository;

    @Scheduled(cron = "0 * * * * *")
    public void autoCalData() {
        log.info("start task autoCalData");
        TestInfo testInfo = testRepository.findById(1L).get();
        UserInfo userInfo = userRepository.findById(680L).get();
        log.info("task  finish autoCalData");
    }
}

最后,限于笔者经验水平有限,欢迎读者就文中的观点提出宝贵的建议和意见。如果想获得更多的学习资源或者想和更多的是技术爱好者一起交流,可以关注我的公众号『全菜工程师小辉』后台回复关键词领取学习资料、进入后端技术交流群和程序员副业群。同时也可以加入程序员副业群Q群:735764906 一起交流。

哎呀,如果我的名片丢了。微信搜索“全菜工程师小辉”,依然可以找到我

发布了88 篇原创文章 · 获赞 434 · 访问量 47万+

猜你喜欢

转载自blog.csdn.net/y277an/article/details/104643245
今日推荐