代码完整,可以正常启动,功能也已经实现,已经用于生产
但不敢保证百分百文章内容正确,如果存在错误之处欢迎各位大佬指出
git地址,可以直接clone启动
业务介绍
业务需要,需要根据业务判断访问不同的数据源,问了gpt也参考了github上的一些例子,gpt给的例子一开始有问题,github上的也不够简洁,融合了半天,搞出来了个最简单的版本,和大家分享下
核心逻辑
使用spring提供的AbstractRoutingDataSource实现动态数据源,根据入参动态设置 DataSourceContextHolder中的数据源名称
注意
因为使用了动态数据源,yml中的数据源默认配置不再生效,需要使用LocalContainerEntityManagerFactoryBean手动设置jpa相关配置
核心配置
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("app.datasource.main")
public DataSource mainDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean
@ConfigurationProperties("app.datasource.child")
public DataSource childDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean
public AbstractRoutingDataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
DataSource mainDataSource = mainDataSource();
DataSource childDataSource = childDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("main", mainDataSource);
targetDataSources.put("child", childDataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(mainDataSource);
return dynamicDataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.example.demo");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
properties.put("hibernate.implicit_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
em.setJpaPropertyMap(properties);
return em;
}
@Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
package com.example.demo.config;
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
其他全代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>demo</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties
server.port=8090
#Datasource -main
app.datasource.main.jdbc-url=jdbc:mysql://localhost:3306/main?characterEncoding=utf-8&useSSL=false
app.datasource.main.username=root
app.datasource.main.password=root
#Datasource -child
app.datasource.child.jdbc-url=jdbc:mysql://localhost:3306/child?characterEncoding=utf-8&useSSL=false
app.datasource.child.username=root
app.datasource.child.password=root
uses.java
package com.example.demo.user;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Data
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String userName;
// Getters and Setters
}
UserRepository.java
package com.example.demo.user;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
// 可以添加自定义查询方法
}
TestController.java
package com.example.demo.controller;
import com.example.demo.config.DataSourceContextHolder;
import com.example.demo.user.User;
import com.example.demo.user.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@Slf4j
public class TestController {
@Autowired
private UserRepository userRepository;
//http://localhost:8090/test?type=main
//http://localhost:8090/test?type=child
@GetMapping("/test")
public String getAllUsers(@RequestParam(name = "type") String type) {
DataSourceContextHolder.setDataSourceType(type);
User user = userRepository.findById(1).orElse(null);
if (user == null) {
log.error("is null, please add user");
return "is null, please add user";
}
return user.getUserName();
}
}