SpringBoot + JPA + MySQL多数据源配置,附带常见问题处理

SpringBoot + JPA + MySQL多数据源配置,附带常见问题处理

1、配置文件(此处我使用的是properties文件,yml也一样的)

spring.datasource.type=com.zaxxer.hikari.HikariDataSource
#数据源1
spring.datasource.operation.url = jdbc:mysql://你自己的数据源1
spring.datasource.operation.username = 数据源1的用户名
spring.datasource.operation.password = 数据源1的密码
spring.datasource.operation.driverClassName = com.mysql.jdbc.Driver
#数据源2
spring.datasource.zhiyi.url = jdbc:mysql://你自己的数据源2
spring.datasource.zhiyi.username = 数据源2的用户名
spring.datasource.zhiyi.password = 数据源2的密码
spring.datasource.zhiyi.driverClassName = com.mysql.jdbc.Driver



spring.datasource.max-active=20
spring.datasource.max-idle=8
spring.datasource.min-idle=8
spring.datasource.initial-size=10

spring.main.allow-circular-references=true

spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update,validate,none)
spring.jpa.hibernate.ddl-auto = none
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

这里要改成none否则启动的时候默认是主数据源,会报错表缺失,因为有些表是在另一个数据源
spring.jpa.hibernate.ddl-auto = none

2、新建数据源属性类(读取多个数据源属性)

我习惯新建一个package叫做config,来存放启动时候需要加载的配置

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class DataSourceProperties {

    @Value("${spring.datasource.operation.url}")
    private String operationUrl;
    @Value("${spring.datasource.operation.username}")
    private String operationUserName;
    @Value("${spring.datasource.operation.password}")
    private String operationPassword;
    @Value("${spring.datasource.operation.driverClassName}")
    private String operationDriverClass;

    @Value("${spring.datasource.zhiyi.url}")
    private String zhiyiUrl;
    @Value("${spring.datasource.zhiyi.username}")
    private String zhiyiUserName;
    @Value("${spring.datasource.zhiyi.password}")
    private String zhiyiPassword;
    @Value("${spring.datasource.zhiyi.driverClassName}")
    private String zhiyiDriverClass;


    public String getOperationUrl() {
        return operationUrl;
    }

    public void setOperationUrl(String operationUrl) {
        this.operationUrl = operationUrl;
    }

    public String getOperationUserName() {
        return operationUserName;
    }

    public void setOperationUserName(String operationUserName) {
        this.operationUserName = operationUserName;
    }

    public String getOperationPassword() {
        return operationPassword;
    }

    public void setOperationPassword(String operationPassword) {
        this.operationPassword = operationPassword;
    }

    public String getOperationDriverClass() {
        return operationDriverClass;
    }

    public void setOperationDriverClass(String operationDriverClass) {
        this.operationDriverClass = operationDriverClass;
    }

    public String getZhiyiUrl() {
        return zhiyiUrl;
    }

    public void setZhiyiUrl(String zhiyiUrl) {
        this.zhiyiUrl = zhiyiUrl;
    }

    public String getZhiyiUserName() {
        return zhiyiUserName;
    }

    public void setZhiyiUserName(String zhiyiUserName) {
        this.zhiyiUserName = zhiyiUserName;
    }

    public String getZhiyiPassword() {
        return zhiyiPassword;
    }

    public void setZhiyiPassword(String zhiyiPassword) {
        this.zhiyiPassword = zhiyiPassword;
    }

    public String getZhiyiDriverClass() {
        return zhiyiDriverClass;
    }

    public void setZhiyiDriverClass(String zhiyiDriverClass) {
        this.zhiyiDriverClass = zhiyiDriverClass;
    }
}

3、新建动态数据源上下文管理类

import java.util.ArrayList;
import java.util.List;

public class DataSourceContextHolder {

    // 存放当前线程使用的数据源类型
    private static final ThreadLocal<DataSourceTypeEnum> contextHolder = new ThreadLocal<>();

    //存放数据源id
    public static List<String> dataSourceIds = new ArrayList<String>();


    // 设置数据源
    public static void setDataSource(DataSourceTypeEnum type) {
        contextHolder.set(type);
    }

    // 获取数据源
    public static DataSourceTypeEnum getDataSource() {
        return contextHolder.get();
    }

    // 清除数据源
    public static void clearDataSource() {
        contextHolder.remove();
    }

    //判断当前数据源是否存在
    public static boolean isContainsDataSource(String dataSourceId) {
        return dataSourceIds.contains(dataSourceId);
    }

}

4、新建动态数据源类

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

5、新建动态数据源配置类

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DataSourceConfig {
    @Autowired
    DataSourceProperties dataSourceProperties;

    @Bean(name = "operation")
    public DataSource operation() {
        HikariDataSource operation = new HikariDataSource();
        operation.setJdbcUrl(dataSourceProperties.getOperationUrl());
        operation.setDriverClassName(dataSourceProperties.getOperationDriverClass());
        operation.setUsername(dataSourceProperties.getOperationUserName());
        operation.setPassword(dataSourceProperties.getOperationPassword());
        operation.setPoolName("HikariPool-operation");
        operation.setAutoCommit(true);
        operation.setReadOnly(false);
        operation.setConnectionTestQuery("SELECT 1;");
        return operation;
    }

    @Bean(name = "zhiyi")
    public DataSource zhiyi() {
        HikariDataSource zhiyi = new HikariDataSource();
        zhiyi.setJdbcUrl(dataSourceProperties.getZhiyiUrl());
        zhiyi.setDriverClassName(dataSourceProperties.getZhiyiDriverClass());
        zhiyi.setUsername(dataSourceProperties.getZhiyiUserName());
        zhiyi.setPassword(dataSourceProperties.getZhiyiPassword());
        zhiyi.setPoolName("HikariPool-zhiyi");
        zhiyi.setAutoCommit(true);
        zhiyi.setReadOnly(false);
        zhiyi.setConnectionTestQuery("SELECT 1;");
        return zhiyi;
    }

    @Bean(name = "operationJdbcTemplate")
    public JdbcTemplate operationJdbcTemplate(@Qualifier("operation") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean(name = "zhiyiJdbcTemplate")
    public JdbcTemplate zhiyiJdbcTemplate(@Qualifier("zhiyi") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }


    @Primary
    @Bean("dynamicDataSource")
    public DataSource dynamicDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put(DataSourceTypeEnum.operation, operation());
        targetDataSources.put(DataSourceTypeEnum.zhiyi, zhiyi());
        // 添加数据源名称到列表
        DataSourceContextHolder.dataSourceIds.add(DataSourceTypeEnum.operation.name());
        DataSourceContextHolder.dataSourceIds.add(DataSourceTypeEnum.zhiyi.name());

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 如果没有指定数据源自动切换主数据源
        dynamicDataSource.setDefaultTargetDataSource(operation());
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }


    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(entityManagerFactory);
        jpaTransactionManager.setDataSource(dynamicDataSource());
        return jpaTransactionManager;
    }


}

6、自定义DS注解(标注DataSource)

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })

public @interface DS {

    DataSourceTypeEnum value() default DataSourceTypeEnum.operation;
}

7、使用AOP的切面来切换数据源

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Order(-10) // 保证该AOP在@Transactional之前执行
@Component
public class DynamicDataSourceAspect {

    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);

    @Before(value = "@annotation(source)")
    public void changeDataSource(JoinPoint point, DS source) throws Exception {
        DataSourceTypeEnum currentSource = source.value();
        logger.info("Change DataSource To:[" + currentSource + "]");
        DataSourceContextHolder.setDataSource(currentSource);
    }

    @After(value = "@annotation(source)")
    public void restoreDataSource(JoinPoint point, DS source) {
        // 方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。
        DataSourceContextHolder.clearDataSource();
        logger.info("Clear Change DataSource...");
    }

}

8、在Service实现类上标注@DS来实现数据源的切换

    @DS(DataSourceTypeEnum.operation)
    @Override
    public OrderDiagnoseModel saveOrUpdate(OrderDiagnoseModel orderDiagnoseModel) {
        return diagnoseDao.save(orderDiagnoseModel);
    }

9、配置数据源的名称枚举

public enum DataSourceTypeEnum {
    operation, zhiyi
}

常见问题

1、bean注入失败,名称冲突

两个数据源都有订单表t_order 实体类名称都叫OrderModel,service都叫OrderService,控制器都叫OrderController 项目启动的时候会报错,bean的名称冲突
(1)、修改实体类
operation数据源:

@Entity
@Table(name = "t_order")

zhiyi数据源:

@Entity(name = "zhiyiOrderModel")
@Table(name = "t_order")

(2)、修改DAO层
operation数据源:

@Repository

zhiyi数据源:

@Repository("zhiyiOrderDao")

(3)、修改Service层,修改Servie的实现注解
operation数据源:

@Service

zhiyi数据源:

@Service("zhiyiOrderService")

(4)、修改Controller层
operation数据源:

@RestController
@RequestMapping("/operation/order")

zhiyi数据源:

@RestController("zhiyiOrderController")
@RequestMapping("/zhiyi/order")

在注入Dao 、注入Service的地方加上@Qualifier注解,例如

    @Autowired
    @Qualifier("zhiyiOrderService")
    private OrderService orderService;

2、dao层查询方法失败

原因:可能有多个名称相同的实体类,所以Dao层在查询结果时候不知道是哪个同名的entity,解决方案是写出实体的完整路径

@Query("select h from com.dyt.operationappoint.model.zhiyi.HospitalModel h where h.status=1 and h.index is null order by h.sort desc")
    List<HospitalModel> getAllHospital();

鸣谢:大部分内容参考自CSDN博主:隔壁的老司机 的博客,原文链接:https://blog.csdn.net/chenxing1990/article/details/122808525

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot是一个用于构建独立的、可执行的Spring应用程序的框架,简化了Spring应用程序的配置和部署。JPA(Java Persistence API)是一种用于管理Java对象和关系数据库之间映射的规范。Druid是阿里巴巴开源的关系型数据库连接池。 在Spring Boot配置多数据源需要以下几步: 1. 引入相关依赖:需要引入Spring BootSpring Data JPA和Druid的相关依赖。 2. 配置数据源:在application.properties或application.yml文件中配置多个数据源的连接信息,并指定每个数据源的名称和相关属性。 3. 配置数据源连接池:使用@ConfigurationProperties注解创建多个数据源的连接池对象,并指定数据源的名称以及相关属性。 4. 配置实体管理器工厂:为每个数据源配置对应的实体管理器工厂,用于处理JPA实体与数据库之间的映射关系。 5. 配置事务管理器:为每个数据源配置对应的事务管理器,用于处理事务操作。 6. 配置数据源路由:创建动态数据源,根据传入的数据源名称选择对应的数据源进行操作。 7. 配置JPA的Repository:创建接口继承JpaRepository,用于定义数据访问方法。 通过以上步骤配置多数据源后,就可以在Spring Boot应用程序中使用多个数据源进行数据库的操作。可以根据需要在Service或Controller中使用@PersistenceContext注解指定具体的数据源,或者使用@Primary注解指定默认的数据源。 总结:通过Spring Boot的自动配置和Druid的连接池,可以很方便地实现多数据源配置。使用JPA进行数据操作,能够有效地减少开发人员编写SQL语句的工作量,提高开发效率。通过合理的配置,可以根据需要选择不同的数据源进行操作,实现灵活的数据访问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值