本篇文章采用 Springboot 整合 Hibernate 实现多数据源模式,数据库采用 postgresql。
# 引入需要的依赖
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-jdbc</artifactId> | |
<version>2.2.6.RELEASE</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-data-jpa</artifactId> | |
<version>2.2.6.RELEASE</version> | |
</dependency> | |
<dependency> | |
<groupId>org.postgresql</groupId> | |
<artifactId>postgresql</artifactId> | |
<version>42.2.14</version> | |
</dependency> |
# 建表
以下两个数据表在不同的数据库中
构建第一数据源表
CREATE TABLE "public"."primary" ( | |
"id" int4 NOT NULL, | |
"code" varchar(255) COLLATE "pg_catalog"."default", | |
"name" varchar(255) COLLATE "pg_catalog"."default", | |
CONSTRAINT "primary_pkey" PRIMARY KEY ("id") | |
) | |
; | |
ALTER TABLE "public"."primary" | |
OWNER TO "postgres"; | |
-- 插入数据 | |
INSERT INTO "public"."primary"("id", "code", "name") VALUES (1, 'PRIMARY', '第一数据源'); |
构建第二数据源表
CREATE TABLE "public"."second" ( | |
"id" int4 NOT NULL, | |
"code" varchar(255) COLLATE "pg_catalog"."default", | |
"name" varchar(255) COLLATE "pg_catalog"."default", | |
CONSTRAINT "second_pkey" PRIMARY KEY ("id") | |
) | |
; | |
ALTER TABLE "public"."second" | |
OWNER TO "postgres"; | |
-- 插入数据 | |
INSERT INTO "public"."second"("id", "code", "name") VALUES (1, 'SECOND', '第二数据源'); |
# 配置文件
server: | |
port: 8080 | |
spring: | |
datasource: | |
primary: | |
driver-class-name: org.postgresql.Driver | |
jdbc-url: jdbc:postgresql://127.0.0.1:5432/primary | |
username: xxx | |
password: xxx | |
hikari: | |
minimum-idle: 2 | |
second: | |
driver-class-name: org.postgresql.Driver | |
jdbc-url: jdbc:postgresql://127.0.0.1:5432/second | |
username: xxx | |
password: xxx | |
hikari: | |
minimum-idle: 2 | |
jpa: | |
hibernate: | |
ddl-auto: none | |
show-sql: true |
# 多数据源配置
第一数据源配置
PrimaryConfig
package com.example.demo.config; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Qualifier; | |
import org.springframework.beans.factory.annotation.Value; | |
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.vendor.HibernateJpaVendorAdapter; | |
import org.springframework.transaction.annotation.EnableTransactionManagement; | |
import javax.persistence.EntityManager; | |
import javax.persistence.EntityManagerFactory; | |
import javax.sql.DataSource; | |
/** | |
* 在 com.example.demo.dao.primary 这个包下 | |
* 都访问第一数据源 | |
*/ | |
@Configuration | |
@EnableJpaRepositories(basePackages = {"com.example.demo.dao.primary"}, | |
transactionManagerRef = "primaryJpaTransactionManager", | |
entityManagerFactoryRef = "primaryLocalContainerEntityManagerFactoryBean") | |
@EnableTransactionManagement | |
public class PrimaryConfig { | |
@Value("${spring.jpa.hibernate.show-sql}") | |
private boolean showSql; | |
@Primary | |
@Bean(destroyMethod = "", name = "primaryEntityManager") | |
public EntityManager primaryEntityManager(@Qualifier(value = "primaryDataSource") DataSource dataSource) { | |
return primaryLocalContainerEntityManagerFactoryBean(dataSource).getObject().createEntityManager(); | |
} | |
@Autowired | |
@Primary | |
@Bean(destroyMethod = "", name = "primaryJpaTransactionManager") | |
public JpaTransactionManager primaryJpaTransactionManager( | |
@Qualifier(value = "primaryDataSource") DataSource dataSource, | |
@Qualifier("primaryLocalContainerEntityManagerFactoryBean") EntityManagerFactory entityManagerFactory) { | |
JpaTransactionManager primaryJpaTransactionManager = new JpaTransactionManager(); | |
primaryJpaTransactionManager.setEntityManagerFactory(entityManagerFactory); | |
primaryJpaTransactionManager.setDataSource(dataSource); | |
return primaryJpaTransactionManager; | |
} | |
@Autowired | |
@Primary | |
@Bean(destroyMethod = "", name = "primaryLocalContainerEntityManagerFactoryBean") | |
LocalContainerEntityManagerFactoryBean primaryLocalContainerEntityManagerFactoryBean( | |
@Qualifier(value = "primaryDataSource") DataSource dataSource) { | |
LocalContainerEntityManagerFactoryBean primaryLocalContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); | |
primaryLocalContainerEntityManagerFactoryBean.setDataSource(dataSource); | |
// 扫描实体包 | |
primaryLocalContainerEntityManagerFactoryBean.setPackagesToScan("com.example.demo.entity"); | |
// 可扫描多个 | |
// primaryLocalContainerEntityManagerFactoryBean.setPackagesToScan("com.example.demo.entity", "com.example.demo.xxx"); | |
primaryLocalContainerEntityManagerFactoryBean.setPersistenceUnitName("primaryPersistenceUnit"); | |
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); | |
jpaVendorAdapter.setGenerateDdl(false); | |
jpaVendorAdapter.setShowSql(showSql); | |
primaryLocalContainerEntityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter); | |
return primaryLocalContainerEntityManagerFactoryBean; | |
} | |
} |
第二数据源配置
SecondConfig
package com.example.demo.config; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Qualifier; | |
import org.springframework.beans.factory.annotation.Value; | |
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.vendor.HibernateJpaVendorAdapter; | |
import org.springframework.transaction.annotation.EnableTransactionManagement; | |
import javax.persistence.EntityManager; | |
import javax.persistence.EntityManagerFactory; | |
import javax.sql.DataSource; | |
/** | |
* 在 com.example.demo.dao.second 包下 | |
* 都访问第二数据源 | |
*/ | |
@Configuration | |
@EnableJpaRepositories(basePackages = "com.example.demo.dao.second", | |
transactionManagerRef = "secondaryJpaTransactionManager", | |
entityManagerFactoryRef = "secondaryLocalContainerEntityManagerFactoryBean") | |
@EnableTransactionManagement | |
public class SecondConfig { | |
@Value("${spring.jpa.hibernate.show-sql}") | |
private boolean showSql; | |
@Bean(destroyMethod = "", name = "secondaryEntityManager") | |
public EntityManager secondaryEntityManager(@Qualifier(value = "secondaryDataSource") DataSource dataSource) { | |
return secondaryLocalContainerEntityManagerFactoryBean(dataSource).getObject().createEntityManager(); | |
} | |
@Autowired | |
@Bean(destroyMethod = "", name = "secondaryJpaTransactionManager") | |
public JpaTransactionManager secondaryJpaTransactionManager( | |
@Qualifier(value = "secondaryDataSource") DataSource dataSource, | |
@Qualifier("secondaryLocalContainerEntityManagerFactoryBean") EntityManagerFactory entityManagerFactory) { | |
JpaTransactionManager secondaryJpaTransactionManager = new JpaTransactionManager(); | |
secondaryJpaTransactionManager.setEntityManagerFactory(entityManagerFactory); | |
secondaryJpaTransactionManager.setDataSource(dataSource); | |
return secondaryJpaTransactionManager; | |
} | |
@Autowired | |
@Bean(destroyMethod = "", name = "secondaryLocalContainerEntityManagerFactoryBean") | |
LocalContainerEntityManagerFactoryBean secondaryLocalContainerEntityManagerFactoryBean( | |
@Qualifier(value = "secondaryDataSource") DataSource dataSource) { | |
LocalContainerEntityManagerFactoryBean secondaryLocalContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); | |
secondaryLocalContainerEntityManagerFactoryBean.setDataSource(dataSource); | |
// 扫描实体包 | |
secondaryLocalContainerEntityManagerFactoryBean.setPackagesToScan("com.example.demo.entity"); | |
secondaryLocalContainerEntityManagerFactoryBean.setPersistenceUnitName("secondaryPersistenceUnit"); | |
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); | |
jpaVendorAdapter.setGenerateDdl(false); | |
jpaVendorAdapter.setShowSql(showSql); | |
secondaryLocalContainerEntityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter); | |
return secondaryLocalContainerEntityManagerFactoryBean; | |
} | |
} |
总数据源配置
DataSourceConfig
package com.example.demo.config; | |
import org.springframework.beans.factory.annotation.Qualifier; | |
import org.springframework.boot.context.properties.ConfigurationProperties; | |
import org.springframework.boot.jdbc.DataSourceBuilder; | |
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 javax.sql.DataSource; | |
/** | |
* 总数据源配置 | |
*/ | |
@Configuration | |
public class DataSourceConfig { | |
@Bean(name = "primaryDataSource") | |
@Qualifier("primaryDataSource") | |
@Primary | |
@ConfigurationProperties(prefix = "spring.datasource.primary") | |
public DataSource primaryDataSource(){ | |
return DataSourceBuilder.create().build(); | |
} | |
@Bean(name = "secondaryDataSource") | |
@Qualifier("secondaryDataSource") | |
@ConfigurationProperties(prefix = "spring.datasource.second") | |
public DataSource secondaryDataSource(){ | |
return DataSourceBuilder.create().build(); | |
} | |
/** | |
* TODO 还可配置多个 | |
*/ | |
@Bean(name = "thirdDataSource") | |
@Qualifier("thirdDataSource") | |
@ConfigurationProperties(prefix = "spring.datasource.third") | |
public DataSource thirdDataSource(){ | |
return DataSourceBuilder.create().build(); | |
} | |
@Bean(name = "secondaryJdbcTemplate") | |
public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource")DataSource secondaryDataSource){ | |
return new JdbcTemplate(secondaryDataSource); | |
} | |
@Bean(name = "thirdJdbcTemplate") | |
public JdbcTemplate thirdJdbcTemplate(@Qualifier("thirdDataSource")DataSource thirdDataSource){ | |
return new JdbcTemplate(thirdDataSource); | |
} | |
} |
# 实体层
第一实体
PrimaryEntity
package com.example.demo.entity; | |
import javax.persistence.Column; | |
import javax.persistence.Entity; | |
import javax.persistence.Id; | |
import javax.persistence.Table; | |
@Table(schema = "public", name = "primary") | |
@Entity | |
public class PrimaryEntity { | |
@Id | |
@Column(name = "id") | |
private String id; | |
@Column(name = "name") | |
private String name; | |
@Column(name = "code") | |
private String code; | |
@Override | |
public String toString() { | |
return "PrimaryEntity{" + | |
"id='" + id + '\'' + | |
", name='" + name + '\'' + | |
", code='" + code + '\'' + | |
'}'; | |
} | |
} |
第二实体
SecondEntity
package com.example.demo.entity; | |
import javax.persistence.Column; | |
import javax.persistence.Entity; | |
import javax.persistence.Id; | |
import javax.persistence.Table; | |
@Table(schema = "public", name = "second") | |
@Entity | |
public class SecondEntity { | |
@Id | |
@Column(name = "id") | |
private String id; | |
@Column(name = "name") | |
private String name; | |
@Column(name = "code") | |
private String code; | |
@Override | |
public String toString() { | |
return "SecondEntity{" + | |
"id='" + id + '\'' + | |
", name='" + name + '\'' + | |
", code='" + code + '\'' + | |
'}'; | |
} | |
} |
# DAO 层
第一数据源访问 DAO 层
在 com.example.demo.dao.primary 包下
PrimaryDao
package com.example.demo.dao.primary; | |
import com.example.demo.entity.PrimaryEntity; | |
import org.springframework.data.jpa.repository.JpaRepository; | |
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; | |
import org.springframework.stereotype.Repository; | |
@Repository | |
public interface PrimaryDao extends JpaSpecificationExecutor<PrimaryEntity>, JpaRepository<PrimaryEntity, String> { | |
} |
第二数据源访问 DAO 层
在 com.example.demo.dao.second 包下
SecondDao
package com.example.demo.dao.second; | |
import com.example.demo.entity.SecondEntity; | |
import org.springframework.data.jpa.repository.JpaRepository; | |
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; | |
import org.springframework.stereotype.Repository; | |
@Repository | |
public interface SecondDao extends JpaSpecificationExecutor<SecondEntity>, JpaRepository<SecondEntity, String> { | |
} |
# 启动类
package com.example.demo; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
import org.springframework.boot.autoconfigure.domain.EntityScan; | |
import org.springframework.context.annotation.ComponentScan; | |
@SpringBootApplication | |
@ComponentScan(value = { | |
"com.example.demo.dao", | |
"com.example.demo.entity", | |
"com.example.demo.config" | |
}) | |
@EntityScan(value = { | |
"com.example.demo.entity" | |
}) | |
public class MainApplication { | |
public static void main(String[] args) { | |
SpringApplication.run(MainApplication.class, args); | |
} | |
} |
# 测试类
package com.example.demo; | |
import com.example.demo.dao.primary.PrimaryDao; | |
import com.example.demo.dao.second.SecondDao; | |
import org.junit.Test; | |
import org.junit.runner.RunWith; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.test.context.SpringBootTest; | |
import org.springframework.test.context.junit4.SpringRunner; | |
@RunWith(SpringRunner.class) | |
@SpringBootTest | |
public class DemoTest { | |
@Autowired | |
private PrimaryDao primaryDao; | |
@Autowired | |
private SecondDao secondDao; | |
@Test | |
public void query(){ | |
//findAll 为 JPA 自带方法,查询所有 | |
System.out.println(primaryDao.findAll()); | |
System.out.println(secondDao.findAll()); | |
} | |
} |
打印结果
Hibernate: select primaryent0_.id as id1_0_, primaryent0_.code as code2_0_, primaryent0_.name as name3_0_ from public.primary primaryent0_
[PrimaryEntity {id='1', name=' 第一数据源 ', code='PRIMARY'}]
Hibernate: select secondenti0_.id as id1_1_, secondenti0_.code as code2_1_, secondenti0_.name as name3_1_ from public.second secondenti0_
[SecondEntity {id='1', name=' 第二数据源 ', code='SECOND'}]