SpringBoot应用多数据源支持[嗡汤圆的小笔记]

在某些应用场景中,SpringBoot应用可能需要同时连接多个数据源(同类型或不同类型数据库)进行数据处理和写入操作。下文将配置多数据源(两个Postgres数据库)为例进行说明。
其中:
* 数据库-1维护表1,表3
* 数据库-2维护表2,表3
* 分别说明如何分别往表1,表2(各库独占表)写数据,以及分别往数据库-1的表3、数据库-2的表3写入不同的数据。

1、项目搭建

基础步骤,在start.spring.io中勾选JPA, JDBC, POSTGRESSQL组件,下载基础MAVEN项目。并导入IDE即可。
已经建好的项目和样例代码请见链接:SpringBootMultipleDataSource

1.1、基础配置

刚构建好的项目包含spring-data相关组件,因此无初始配置时,会提示缺少默认配置而无法启动。以下演示配置过程。

1.1.1、Properties文件配置

在application.properties声明数据源配置,分别包括数据库one,数据库two,和通用jpa设置。

spring.datasource.one.url=jdbc:postgresql://localhost:5432/testdb1
spring.datasource.one.username=postgres
spring.datasource.one.password=password
spring.datasource.one.driverClassName=org.postgresql.Driver

spring.datasource.two.url=jdbc:postgresql://localhost:5432/testdb2
spring.datasource.two.username=postgres
spring.datasource.two.password=password
spring.datasource.two.driverClassName=org.postgresql.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.show-sql=true

1.1.2、Bean声明

由于存在两个数据源,因此不能使用Springboot的默认数据配置方式,而是自行声明bean。
SpirngBoot的数据源组件(包含jpa)声明层级如下:
DataSource -> EntityManager -> TransactionManager
这些组件声明都可以写在一个类中,方便管理。

(1) Datasource

设置了各数据库的连接方式与参数,结合Spring的配置文件自动注入方式来分别声明两个数据源。

/*通过不同的配置参数前缀来分别为不同datasource赋予参数*/
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.one")
public DataSource primaryDataSource() {
    return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.two")
public DataSource secondaryDataSource() {
    return DataSourceBuilder.create().build();
}
(2) EntityManager

通过EntityManagerFactoryBuild -> EntityManagerFactory,用于以后声明EntityManager使用。EntityManager定义了所使用的数据源,和所扫描的Entity包路径(packages)。EntityManager仅管理包路径下的Entity,以达到在不同数据源管理不同表的目的。
例子中的表1位于tbdsone包下,表3位于tbdsboth包下,表2位于tdbstwo包下。而两个EntityManager均管理tbdsboth,因此表3在两个数据源中都存在。

@Bean(name = "primaryEMFB")
@Primary
public LocalContainerEntityManagerFactoryBean 
  primaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
    return builder.dataSource(primaryDataSource())
            .packages("com.rails.demoapp.core.module.tbdsboth",
                         "com.rails.demoapp.core.module.tbdsone")
            .persistenceUnit("primary")
            .properties(buildProperties()).build();
} 
@Bean(name = "secondaryEMFB")
public LocalContainerEntityManagerFactoryBean 
  secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
    return builder.dataSource(primaryDataSource())
            .packages("com.rails.demoapp.core.module.tbdsboth",
                         "com.rails.demoapp.core.module.tbdstwo")
            .persistenceUnit("secondary")
            .properties(buildProperties()).build();
}

// 公共jpa设置
@Value("${spring.jpa.hibernate.ddl-auto}")
String dll;
@Value("${spring.jpa.properties.hibernate.dialect}")
String dialect;
@Value("${spring.jpa.show-sql}")
String showSql;
private Map<String, Object> buildProperties() {
    Map<String, Object> properties = new HashMap<String, Object>();
    properties.put("hibernate.ejb.naming_strategy", ImprovedNamingStrategy.class.getName());
    properties.put("hibernate.hbm2ddl.auto", dll);
    properties.put("hibernate.dialect", dialect);
    properties.put("hibernate.show_sql", showSql);
    return properties;
}
(3) TransactionManager

TransactionManager用于事务管理,基于EntityManager创建。

@Bean(name="primaryTM")
@Autowired
public PlatformTransactionManager primaryTransactionManager(EntityManagerFactoryBuilder builder) {
    return new JpaTransactionManager(primaryEntityManagerFactory(builder).getObject());
}
@Bean(name="secondaryTM")
@Autowired
public PlatformTransactionManager 
secondaryTransactionManager(EntityManagerFactoryBuilder builder) {
    return new JpaTransactionManager(primaryEntityManagerFactory(builder).getObject());
}

1.2、启动效果

可以在两个Postgres数据库中看到:testdb1中包含table1,table3;testdb2中包含table2,table3。即可说明两个EntityManager分别在不同数据库中创建了自己所管理的表。

2、应用开发

2.1、Dao层

首先创建公共Dao层用于编写基础的增删改查操作代码。传统的单数据源代码结构大致如下:

 /*通过接口定义操作,以及实现类编写具体代码*/
 public Interface CommonDao<T> {
    T findById(Object id) throws Exception;
    List<T> query(String sql) throws Exception;
    T save(Object t) throws Exception;
    T update(Object t) throws Exception;
    void delete(Object t) throws Exception;
 }

 public class CommonDaoImpl<T> implements CommonDao<T> {
    //由于只有一个entityManager,因此用Autowired注入默认即可
    @Autowired
    EntityManager em;

    @Override
    T findById(Object id) throws Exception{
        return (T) em.find(this.getPersistentClass(), id);
    }
    @Override
    List<T> query(String sql) throws Exception{
        Query query =  em.createNativeQuery(sql);
        List<T> results = query.getResultList(); 
        return results;
    }
    // ......
    // 以下省略......
 }

但是在多数据源场景下,需要调用不同的EntityManager来操作不同数据库,因此需要在提取出一个虚类来,代码结构如下:

/*通过接口定义操作,以及抽象类编写具体代码,在实现类中注入各自的entityManager*/
public Interface CommonDao<T> {
    T findById(Object id) throws Exception;
    List<T> query(String sql) throws Exception;
    T save(Object t) throws Exception;
    T update(Object t) throws Exception;
    void delete(Object t) throws Exception;
 }
 /*统一的实现代码虚类*/
 public abstract class AbsCommonDaoImpl<T>  implements CommonDao<T>{
    // 引入的EntityManager由具体的实现类引入,上例代码中的em变量统一通过getEntityManager()方法获取。
    abstract EntityManager getEntityManager();

    @Override
    T findById(Object id) throws Exception{
        return (T) getEntityManager().find(this.getPersistentClass(), id);
    }
    @Override
    List<T> query(String sql) throws Exception{
        Query query =  getEntityManager().createNativeQuery(sql);
        List<T> results = query.getResultList(); 
        return results;
    }
    // ......
    // 以下省略......
 }
 /*各数据源对应的实现类如下*/
// 数据源-1
@Repository("PrimaryCommonDaoImpl")
public class PrimaryCommonDaoImpl<T> extends AbsCommonDaoImpl<T>{
    // 注入数据库-1的EntityManager
    @Autowired
    @Qualifier("primaryEMFB") 
    protected EntityManager em;

    @Override
    EntityManager getEntityManager() {
        return em;
    }
}
// 数据源-2
@Repository("ScondaryCommonDaoImpl")
public class ScondaryCommonDaoImpl<T> extends AbsCommonDaoImpl<T>{
    // 注入数据库-2的EntityManager
    @Autowired
    @Qualifier("secondaryEMFB") 
    protected EntityManager em;

    @Override
    EntityManager getEntityManager() {
        return em;
    }
}

具体到某张表的Dao时,则通过继承实体类来实现基本功能如有张表为TableOne,它仅在数据源-1中使用,则Dao如下:

public Interface TableOneDao extends CommonDao<TableOne> {
    // 这里写针对TableOne的操作
}
@Repository("TableOneDaoImpl")
public Class TableOneDaoImpl extends PrimaryCommonDaoImpl<TableOne> implements TableOneDao {
    // 针对TableOneDao接口声明方法的实现代码
}

2.2、Service层

Service层用于实现复杂业务逻辑,以及事务管理。通用Service设计如下:

public Interface CommonService<T> {
    T findById(Object id) throws Exception;
    List<T> query(String sql) throws Exception;
    T save(Object t) throws Exception;
    T update(Object t) throws Exception;
    void delete(Object t) throws Exception;
}
// 由于具体表的实现需要制定具体的Dao,因此在这里无需实现公用的ServiceImpl,仅对接口做一下定义即可。

某张具体表的实现类如下:

 @Service("TableOneServiceImpl ")
 public TableOneServiceImpl extends CommonService<TableOne> {
    // tableOneDaoImpl已经指定了使用数据源-1
    @Resource(name = "TableOneDaoImpl")
    private TableOneDao tableOneDao;

    // 以实现save方法为例说明Transactional的声明方法
    // !!! 这里的@Transactional注解不是原先单数据源时的Transactional
    // 而是org.springframework.transaction.annotation.Transactional
    // 这点需要注意,否则不支持下边的参数
    @Transactional("primaryTM")//通过bean name制定具体的transactionManager
    @Override
    TableOne save(Object t) throws Exception {
        return tableOneDao.save(t);
    }
 }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值