Spring boot實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離的方法
背景
數(shù)據(jù)庫配置主從之后,如何在代碼層面實(shí)現(xiàn)讀寫分離?
用戶自定義設(shè)置數(shù)據(jù)庫路由
Spring boot提供了AbstractRoutingDataSource根據(jù)用戶定義的規(guī)則選擇當(dāng)前的數(shù)據(jù)庫,這樣我們可以在執(zhí)行查詢之前,設(shè)置讀取從庫,在執(zhí)行完成后,恢復(fù)到主庫。
實(shí)現(xiàn)可動(dòng)態(tài)路由的數(shù)據(jù)源,在每次數(shù)據(jù)庫查詢操作前執(zhí)行
ReadWriteSplitRoutingDataSource.java
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * @author songrgg * @since 1.0 */ public class ReadWriteSplitRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DbContextHolder.getDbType(); } }
線程私有路由配置,用于ReadWriteSplitRoutingDataSource動(dòng)態(tài)讀取配置
DbContextHolder.java
/** * @author songrgg * @since 1.0 */ public class DbContextHolder { public enum DbType { MASTER, SLAVE } private static final ThreadLocal<DbType> contextHolder = new ThreadLocal<>(); public static void setDbType(DbType dbType) { if(dbType == null){ throw new NullPointerException(); } contextHolder.set(dbType); } public static DbType getDbType() { return contextHolder.get() == null ? DbType.MASTER : contextHolder.get(); } public static void clearDbType() { contextHolder.remove(); } }
AOP優(yōu)化代碼
利用AOP將設(shè)置數(shù)據(jù)庫的操作從代碼中抽離,這里的粒度控制在方法級(jí)別,所以利用注解的形式標(biāo)注這個(gè)方法涉及的數(shù)據(jù)庫事務(wù)只讀,走從庫。
只讀注解,用于標(biāo)注方法的數(shù)據(jù)庫操作只走從庫。
ReadOnlyConnection.java
package com.wallstreetcn.hatano.config; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Indicates the database operations is bound to the slave database. * AOP interceptor will set the database to the slave with this interface. * @author songrgg * @since 1.0 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ReadOnlyConnection { }
ReadOnlyConnectionInterceptor.java
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; /** * Intercept the database operations, bind database to read-only database as this annotation * is applied. * @author songrgg * @since 1.0 */ @Aspect @Component public class ReadOnlyConnectionInterceptor implements Ordered { private static final Logger logger = LoggerFactory.getLogger(ReadOnlyConnectionInterceptor.class); @Around("@annotation(readOnlyConnection)") public Object proceed(ProceedingJoinPoint proceedingJoinPoint, ReadOnlyConnection readOnlyConnection) throws Throwable { try { logger.info("set database connection to read only"); DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE); Object result = proceedingJoinPoint.proceed(); return result; } finally { DbContextHolder.clearDbType(); logger.info("restore database connection"); } } @Override public int getOrder() { return 0; } }
UserService.java
@ReadOnlyConnection public List<User> getUsers(Integer page, Integer limit) { return repository.findAll(new PageRequest(page, limit)); }
配置Druid數(shù)據(jù)庫連接池
build.gradle
compile("com.alibaba:druid:1.0.18")
groovy依賴注入
配置dataSource為可路由數(shù)據(jù)源
context.groovy
import com.alibaba.druid.pool.DruidDataSource import DbContextHolder import ReadWriteSplitRoutingDataSource ** SOME INITIALIZED CODE LOAD PROPERTIES ** def dataSourceMaster = new DruidDataSource() dataSourceMaster.url = properties.get('datasource.master.url') println("master set to " + dataSourceMaster.url) dataSourceMaster.username = properties.get('datasource.master.username') dataSourceMaster.password = properties.get('datasource.master.password') def dataSourceSlave = new DruidDataSource() dataSourceSlave.url = properties.get('datasource.slave.url') println("slave set to " + dataSourceSlave.url) dataSourceSlave.username = properties.get('datasource.slave.username') dataSourceSlave.password = properties.get('datasource.slave.password') beans { dataSource(ReadWriteSplitRoutingDataSource) { bean -> targetDataSources = [ (DbContextHolder.DbType.MASTER): dataSourceMaster, (DbContextHolder.DbType.SLAVE): dataSourceSlave ] } }
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 使用springboot aop來實(shí)現(xiàn)讀寫分離和事物配置
- SpringBoot自定義注解使用讀寫分離Mysql數(shù)據(jù)庫的實(shí)例教程
- SpringBoot整合MyCat實(shí)現(xiàn)讀寫分離的方法
- Springboot + Mysql8實(shí)現(xiàn)讀寫分離功能
- spring boot + mybatis如何實(shí)現(xiàn)數(shù)據(jù)庫的讀寫分離
- springboot基于Mybatis mysql實(shí)現(xiàn)讀寫分離
- 詳解Spring Boot中整合Sharding-JDBC讀寫分離示例
- spring boot配置讀寫分離的完整實(shí)現(xiàn)步驟
- SpringBoot集成Spring Data JPA及讀寫分離
- Spring Boot 結(jié)合 aop 實(shí)現(xiàn)讀寫分離
相關(guān)文章
Mybatis-plus多條件篩選分頁的實(shí)現(xiàn)
本文主要介紹了Mybatis-plus多條件篩選分頁,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09SpringBoot FreeWorker模板技術(shù)解析
這篇文章主要介紹了SpringBoot FreeWorker模板技術(shù)解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11詳解@ConditionalOnMissingBean注解的作用
這篇文章主要介紹了詳解@ConditionalOnMissingBean注解的作用,@ConditionalOnMissingBean,它是修飾bean的一個(gè)注解,主要實(shí)現(xiàn)的是,當(dāng)你的bean被注冊(cè)之后,如果而注冊(cè)相同類型的bean,就不會(huì)成功,它會(huì)保證你的bean只有一個(gè),需要的朋友可以參考下2023-10-10Java使用JNDI連接數(shù)據(jù)庫的實(shí)現(xiàn)方法
本文主要介紹了Java使用JNDI連接數(shù)據(jù)庫的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12springboot + rabbitmq 如何實(shí)現(xiàn)消息確認(rèn)機(jī)制(踩坑經(jīng)驗(yàn))
這篇文章主要介紹了springboot + rabbitmq 如何實(shí)現(xiàn)消息確認(rèn)機(jī)制,本文給大家分享小編實(shí)際開發(fā)中的一點(diǎn)踩坑經(jīng)驗(yàn),內(nèi)容簡單易懂,需要的朋友可以參考下2020-07-07