MySQL-SpringBoot整合JPA實現資料讀寫分離
在上篇部落格《MySQL-主從複製之同步主從資料》中,我們實現了讀庫和寫庫的資料同步。今天,我們繼續學習SpringBoot整合JPA如何實現資料讀寫分離。廢話不多話直接上程式碼。
一、配置資料來源
# 資料來源
spring.datasource.druid.write.url=jdbc:mysql://localhost:3380/test
spring.datasource.druid.write.username=root
spring.datasource.druid.write.password=Anbang713
spring.datasource.druid.write.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.read.url=jdbc:mysql://localhost:3381/test
spring.datasource.druid.read.username=root
spring.datasource.druid.read.password=Anbang713
spring.datasource.druid.read.driver-class-name=com.mysql.jdbc.Driver
# JPA
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database=mysql
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultComponentSafeNamingStrategy
spring.jpa.show-sql=false
二、資料來源配置類
/**
* 資料來源配置
*
* @author Administrator
*
*/
@Configuration
public class DataSourceConfig {
public final static String WRITE_DATASOURCE_KEY = "writeDruidDataSource";
public final static String READ_DATASOURCE_KEY = "readDruidDataSource";
@ConfigurationProperties(prefix = "spring.datasource.druid.read")
@Bean(name = READ_DATASOURCE_KEY)
public DataSource readDruidDataSource() {
return new DruidDataSource();
}
@ConfigurationProperties(prefix = "spring.datasource.druid.write")
@Bean(name = WRITE_DATASOURCE_KEY)
@Primary
public DataSource writeDruidDataSource() {
return new DruidDataSource();
}
/**
* 注入AbstractRoutingDataSource
*
* @param readDruidDataSource
* @param writeDruidDataSource
* @return
* @throws Exception
*/
@Bean
public AbstractRoutingDataSource routingDataSource(
@Qualifier(READ_DATASOURCE_KEY) DataSource readDruidDataSource,
@Qualifier(WRITE_DATASOURCE_KEY) DataSource writeDruidDataSource) throws Exception {
DynamicDataSource dataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
targetDataSources.put(WRITE_DATASOURCE_KEY, writeDruidDataSource);
targetDataSources.put(READ_DATASOURCE_KEY, readDruidDataSource);
dataSource.setTargetDataSources(targetDataSources);// 配置資料來源
dataSource.setDefaultTargetDataSource(writeDruidDataSource);// 預設為主庫用於寫資料
return dataSource;
}
}
三、使用ThreadLocal使資料來源與執行緒繫結
public class DynamicDataSourceHolder {
// 使用ThreadLocal把資料來源與當前執行緒繫結
private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
public static void setDataSource(String dataSourceName) {
dataSources.set(dataSourceName);
}
public static String getDataSource() {
return (String) dataSources.get();
}
public static void clearDataSource() {
dataSources.remove();
}
}
四、動態資料來源配置
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 可以做一個簡單的負載均衡策略
String lookupKey = DynamicDataSourceHolder.getDataSource();
System.out.println("------------lookupKey---------" + lookupKey);
return lookupKey;
}
}
五、寫資料來源配置類
@Configuration
@EnableJpaRepositories(value = "com.study.mysql.jpa.dao",
entityManagerFactoryRef = "writeEntityManagerFactory",
transactionManagerRef = "writeTransactionManager")
public class WriteDataSourceConfig {
@Autowired
JpaProperties jpaProperties;
@Autowired
@Qualifier("writeDruidDataSource")
private DataSource writeDruidDataSource;
/**
* 我們通過LocalContainerEntityManagerFactoryBean來獲取EntityManagerFactory例項
*
* @return
*/
@Bean(name = "writeEntityManagerFactoryBean")
public LocalContainerEntityManagerFactoryBean writeEntityManagerFactoryBean(
EntityManagerFactoryBuilder builder) {
return builder.dataSource(writeDruidDataSource).properties(jpaProperties.getProperties())
.packages("com.study.mysql.jpa.api") // 設定實體類所在位置
.persistenceUnit("writePersistenceUnit").build();
}
/**
* EntityManagerFactory類似於Hibernate的SessionFactory,mybatis的SqlSessionFactory
* 總之在執行操作之前我們總要獲取一個EntityManager,這就類似於Hibernate的Session,mybatis的sqlSession。
*
* @param builder
* @return
*/
@Bean(name = "writeEntityManagerFactory")
@Primary
public EntityManagerFactory writeEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return this.writeEntityManagerFactoryBean(builder).getObject();
}
/**
* 配置事物管理器
*
* @return
*/
@Bean(name = "writeTransactionManager")
@Primary
public PlatformTransactionManager writeTransactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(writeEntityManagerFactory(builder));
}
}
六、自定義註解
@Target({
ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDateSource {
String dataSource() default "";// 資料來源
}
七、定義切面,實現資料來源切換
@Aspect
@Component
public class DynamicDataSourceAspect {
@Around("execution(public * com.study.mysql.jpa.core..*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method targetMethod = methodSignature.getMethod();
if (targetMethod.isAnnotationPresent(TargetDateSource.class)) {
String targetDataSource = targetMethod.getAnnotation(TargetDateSource.class).dataSource();
System.out.println("----------資料來源是:" + targetDataSource + "------");
DynamicDataSourceHolder.setDataSource(targetDataSource);
}
// 執行方法
Object result = pjp.proceed();
DynamicDataSourceHolder.clearDataSource();
return result;
}
}
在完成上面的相關配置後,我們寫個簡單的學生增刪改查介面做測試。至此,我們的專案結構是這樣的:
當然在這裡,我們有必要看一下業務層實現類的程式碼,通過註解@TargetDataSource註解實現讀寫分離。
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentDao studentDao;
@Override
@TargetDateSource(dataSource = DataSourceConfig.READ_DATASOURCE_KEY)
public List<Student> findAll() {
return studentDao.findAll();
}
@Override
@TargetDateSource(dataSource = DataSourceConfig.READ_DATASOURCE_KEY)
public Student findById(Integer id) {
Optional<Student> students = studentDao.findById(id);
if (students.isPresent() && students.get() != null) {
return students.get();
}
return null;
}
@Override
@Transactional
@TargetDateSource(dataSource = DataSourceConfig.WRITE_DATASOURCE_KEY)
public Integer save(Student entity) throws Exception {
if (entity.getId() != null) {
Student perz = studentDao.saveAndFlush(entity);
return perz.getId();
}
Student perz = studentDao.save(entity);
return perz.getId();
}
}
八、測試
啟動SpringBoot啟動類,並通過http://localhost:8080/swagger-ui.html訪問我們的學生類介面。在測試之前,我們現在看下資料庫的資料。可以看到我們的主從資料庫資料是一樣的。(MySQL5.6-3380為主資料庫,用於寫資料;MySQL5.6-3381為從資料庫,用於讀資料)
那麼我們現在往資料庫插入一條資料,執行save介面:
首先可以看到,在切面類中列印的日誌,已經實現資料來源的自動切換了。
然後我們看下資料庫的資料,可以看到兩邊的資料是一模一樣的。
最後,我們測試一下讀的時候是從哪個資料來源讀的。
可以看到,在讀請求的時候,是從從資料庫讀的資料。至此,我們使用SpringBoot整合JPA實現讀寫分離的目的已經達到。
原始碼地址: https://gitee.com/chengab/MySQL
參考部落格:https://www.jb51.net/article/111588.htm
相關文章
- shardingjdbc + jpa 完成讀寫分離配置及資料分片JDBC
- Spring Aop實現資料庫讀寫分離Spring資料庫
- ShardingSphere + Mysql,實現分庫分表、讀寫分離,並整合 SpringBootMySqlSpring Boot
- 資料庫讀寫分離,主從同步實現方法資料庫主從同步
- 資料庫讀寫分離資料庫
- Sharding-JDBC基本使用,整合Springboot實現分庫分表,讀寫分離JDBCSpring Boot
- ProxySQL實現MySQL讀寫分離MySql
- 資料讀寫壓力大,讀寫分離
- ShardingSphere(七) 讀寫分離配置,實現分庫讀寫操作
- Spring Boot + Mybatis 多資料來源配置實現讀寫分離Spring BootMyBatis
- 資料庫中介軟體sharding-jdbc實現讀寫分離資料庫JDBC
- 【Mongo】Mongo讀寫分離的實現Go
- Docker實現Mariadb分庫分表、讀寫分離Docker
- 大資料資料庫讀寫分離分庫分表大資料資料庫
- (7)資料庫讀寫分離,主從同步實現方法(資料庫設定)資料庫主從同步
- PostgreSQL+Pgpool實現HA讀寫分離SQL
- docker+atlas+mysql實現讀寫分離DockerMySql
- Kubernetes 中實現 MySQL 的讀寫分離MySql
- 位元組面試:什麼是讀寫分離?讀寫分離的底層如何實現?面試
- 直播賣貨系統,如何實現mysql資料庫的讀寫分離MySql資料庫
- ssm讀寫分離多資料來源SSM
- MySQL 中讀寫分離資料延遲MySql
- 資料庫讀寫分離Master-Slave資料庫AST
- ProxySQL實現Mysql讀寫分離 - 部署手冊MySql
- SpringBoot 專案優雅實現讀寫分離Spring Boot
- 搭建MySQL主從實現Django讀寫分離MySqlDjango
- MHA+ProxySQL實現讀寫分離高可用SQL
- 搭建基於springmvc,ibatis的工程實現讀寫分離,配置分離SpringMVCBAT
- 基於Sharding-Jdbc 實現的讀寫分離實現JDBC
- springboot多資料來源配合docker部署mysql主從實現讀寫分離Spring BootDockerMySql
- 資料庫治理利器:動態讀寫分離資料庫
- 分庫分表(6)--- SpringBoot+ShardingSphere實現分表+ 讀寫分離Spring Boot
- Mycat中介軟體實現Percona Cluster讀寫分離
- redis客戶端實現高可用讀寫分離Redis客戶端
- Mycat實現mysql的負載均衡讀寫分離MySql負載
- StoneDB 讀寫分離實踐方案
- hibernate及SpringBoot整合Jpa實現對資料庫操作Spring Boot資料庫
- golang saas框架,資料庫級別隔離、讀寫分離Golang框架資料庫