最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用springboot aop來實(shí)現(xiàn)讀寫分離和事物配置

 更新時(shí)間:2020年04月26日 14:28:23   作者:Coder_Qiang  
這篇文章主要介紹了使用springboot aop來實(shí)現(xiàn)讀寫分離和事物配置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧

什么事讀寫分離

讀寫分離,基本的原理是讓主數(shù)據(jù)庫處理事務(wù)性增、改、刪操作(INSERT、UPDATE、DELETE),而從數(shù)據(jù)庫處理SELECT查詢操作。數(shù)據(jù)庫復(fù)制被用來把事務(wù)性操作導(dǎo)致的變更同步到集群中的從數(shù)據(jù)庫。

為什么要實(shí)現(xiàn)讀寫分離

增加冗余

增加了機(jī)器的處理能力

對(duì)于讀操作為主的應(yīng)用,使用讀寫分離是最好的場(chǎng)景,因?yàn)榭梢源_保寫的服務(wù)器壓力更小,而讀又可以接受點(diǎn)時(shí)間上的延遲。

實(shí)現(xiàn)

本文介紹利用spring aop來動(dòng)態(tài)切換數(shù)據(jù)源來實(shí)現(xiàn)讀寫分離。

先建一個(gè)maven項(xiàng)目,導(dǎo)入springBoot依賴。

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>1.5.2.RELEASE</version>
</parent>

<dependencies>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>

 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>
 <!--mybatis-->
 <dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>1.3.1</version>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
 </dependency>
 <!-- druid -->
 <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>${druid.version}</version>
 </dependency>
 <!-- mysql connector-->
 <dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>${mysql-connector.version}</version>
 </dependency>

 <!--test-->
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
 </dependency>

</dependencies>

然后在配置文件application.yml中自定義數(shù)據(jù)源配置項(xiàng)

server:
 port: 8080
logging:
 level:
 org.springframework: INFO
 com.qiang: DEBUG
spring:
 output:
 ansi:
  enabled: always
 datasource:
 type: com.alibaba.druid.pool.DruidDataSource
 driver-class-name: com.mysql.cj.jdbc.Driver
 url: jdbc:mysql://localhost:3306/db_area?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
 username: root
 password: root
 db:
 readsize: 2
 read0:
  type: com.alibaba.druid.pool.DruidDataSource
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost:3306/db_area?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
  username: root
  password: root

 read1:
  type: com.alibaba.druid.pool.DruidDataSource
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost:3306/db_area?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
  username: root
  password: root
 aop:
 auto: true
 proxy-target-class: true

配置Druid

package com.qiang.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

/**
 * @author gengqiang
 * @date 2018/5/3
 */
@Configuration
public class DruidConfig {

 private Logger logger = LoggerFactory.getLogger(DruidConfig.class

 /**
  * 主據(jù)源
  * @return
  */
 @Primary
 @Bean(name = "dataSource")
 @ConfigurationProperties(prefix = "spring.datasource")
 public DataSource dataSource() {
  return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();

 }

 /**
  * 從數(shù)據(jù)源1
  * @return
  */
 @Bean(name = "readDataSource0")
 @ConfigurationProperties(prefix = "spring.db.read0")
 public DataSource readDataSource0() {
  return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();

 }
 /**
  * 從數(shù)據(jù)源2
  * @return
  */
 @Bean(name = "readDataSource1")
 @ConfigurationProperties(prefix = "spring.db.read1")
 public DataSource readDataSource1() {
  return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();

 }

} 

配置Mybaits

package com.qiang.config;

import com.qiang.config.db.DataSourceType;
import com.qiang.config.db.RoutingDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author gengqiang
 * @date 2018/5/3
 */
@Configuration
@EnableTransactionManagement(order = 2)
@MapperScan(basePackages = {"com.qiang.demo.mapper"})
public class MybatisConfig implements TransactionManagementConfigurer, ApplicationContextAware {


 private static ApplicationContext context;


 /**
  * 寫庫數(shù)據(jù)源
  */
 @Autowired
 private DataSource dataSource;

 /**
  * 讀數(shù)據(jù)源數(shù)量
  */
 @Value("${spring.db.readsize}")
 private Integer readsize;
 /**
  * 數(shù)據(jù)源路由代理
  *
  * @return
  */

 @Bean
 public AbstractRoutingDataSource routingDataSouceProxy() {
  RoutingDataSource proxy = new RoutingDataSource(readsize);
  Map<Object, Object> targetDataSources = new HashMap<>(readsize + 1);
  targetDataSources.put(DataSourceType.WRITE.getType(), dataSource);
  for (int i = 0; i < readsize; i++) {
   DataSource d = context.getBean("readDataSource" + i, DataSource.class);
   targetDataSources.put(i, d);
  }
  proxy.setDefaultTargetDataSource(dataSource);
  proxy.setTargetDataSources(targetDataSources);
  return proxy;
 }

 @Bean
 @ConditionalOnMissingBean
 public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
  SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  bean.setDataSource(routingDataSouceProxy());
  bean.setVfs(SpringBootVFS.class);
  bean.setTypeAliasesPackage("com.qiang");
  Resource configResource = new ClassPathResource("/mybatis-config.xml");
  bean.setConfigLocation(configResource);
  ResourcePatternResolver mapperResource = new PathMatchingResourcePatternResolver();
  bean.setMapperLocations(mapperResource.getResources("classpath*:mapper/**/*.xml"));
  return bean;
 }

 @Override
 public PlatformTransactionManager annotationDrivenTransactionManager() {
  return new DataSourceTransactionManager(routingDataSouceProxy());
 }

 @Override
 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  if (context == null) {
   context = applicationContext;
  }
 }
}

其中實(shí)現(xiàn)數(shù)據(jù)源切換的功能就是自定義一個(gè)類擴(kuò)展AbstractRoutingDataSource抽象類,就是代碼中的定義的RoutingDataSource,其實(shí)該相當(dāng)于數(shù)據(jù)源DataSourcer的路由中介,可以實(shí)現(xiàn)在項(xiàng)目運(yùn)行時(shí)根據(jù)相應(yīng)key值切換到對(duì)應(yīng)的數(shù)據(jù)源DataSource上。

RoutingDataSource.class

package com.qiang.config.db;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 數(shù)據(jù)源路由
 *
 * @author gengqiang
 */
public class RoutingDataSource extends AbstractRoutingDataSource {

 private AtomicInteger count = new AtomicInteger(0);

 private int readsize;

 public RoutingDataSource(int readsize) {
  this.readsize = readsize;
 }

 @Override
 protected Object determineCurrentLookupKey() {
  String typeKey = DataSourceContextHolder.getJdbcType();
  if (typeKey == null) {
   logger.error("無法確定數(shù)據(jù)源");
  }
  if (typeKey.equals(DataSourceType.WRITE.getType())) {
   return DataSourceType.WRITE.getType();
  }
  //讀庫進(jìn)行負(fù)載均衡
  int a = count.getAndAdd(1);
  int lookupkey = a % readsize;
  return lookupkey;
 }

}

其中用到了2個(gè)輔助類

package com.qiang.config.db;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 全局?jǐn)?shù)據(jù)源
 *
 * @author gengqiang
 */
public class DataSourceContextHolder {

 private static Logger logger = LoggerFactory.getLogger(DataSourceContextHolder.class);

 private final static ThreadLocal<String> local = new ThreadLocal<>();

 public static ThreadLocal<String> getLocal() {
  return local;
 }

 public static void read() {
  logger.debug("切換至[讀]數(shù)據(jù)源");
  local.set(DataSourceType.READ.getType());
 }

 public static void write() {
  logger.debug("切換至[寫]數(shù)據(jù)源");
  local.set(DataSourceType.WRITE.getType());
 }

 public static String getJdbcType() {
  return local.get();
 }

}
package com.qiang.config.db;

/**
 * @author gengqiang
 */
public enum DataSourceType {
 READ("read", "讀庫"), WRITE("write", "寫庫");
 private String type;
 private String name;

 DataSourceType(String type, String name) {
  this.type = type;
  this.name = name;
 }

 public String getType() {
  return type;
 }

 public void setType(String type) {
  this.type = type;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}

最后通過aop設(shè)置切面,攔截讀寫來動(dòng)態(tài)的設(shè)置數(shù)據(jù)源

package com.qiang.config.aop;

import com.qiang.config.db.DataSourceContextHolder;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 攔截?cái)?shù)據(jù)庫讀寫
 *
 * @author gengqiang
 */

@Aspect
@Component
@Order(1)
public class DataSourceAspect {

 Logger logger = LoggerFactory.getLogger(getClass());

 @Before("execution(* com.qiang..*.*ServiceImpl.find*(..)) " +
   "|| execution(* com.qiang..*.*ServiceImpl.count*(..))" +
   "|| execution(* com.qiang..*.*ServiceImpl.sel*(..))" +
   "|| execution(* com.qiang..*.*ServiceImpl.get*(..))"
 )
 public void setReadDataSourceType() {
  logger.debug("攔截[read]方法");
  DataSourceContextHolder.read();
 }

 @Before("execution(* com.qiang..*.*ServiceImpl.insert*(..)) " +
   "|| execution(* com.qiang..*.*ServiceImpl.save*(..))" +
   "|| execution(* com.qiang..*.*ServiceImpl.update*(..))" +
   "|| execution(* com.qiang..*.*ServiceImpl.set*(..))" +
   "|| execution(* com.qiang..*.*ServiceImpl.del*(..))")
 public void setWriteDataSourceType() {
  logger.debug("攔截[write]操作");
  DataSourceContextHolder.write();
 }

}

主要的代碼就寫好了,下面來測(cè)試一下是否讀寫分離。

寫一個(gè)測(cè)試類:

package com.qiang;

import com.qiang.demo.entity.Area;
import com.qiang.demo.service.AreaService;
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;

/**
 * @author gengqiang
 * @date 2018/5/4
 */

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestApplication {

 @Autowired
 private AreaService areaService;


 @Test
 public void test() {
  Area area = new Area();
  area.setDistrictId("0000");
  area.setName("test");
  area.setParentId(0);
  area.setLevel(1);
  areaService.insert(area);
 }

 @Test
 public void test2() {
  areaService.selectByPrimaryKey(1);
 }
}

其中第一個(gè)測(cè)試插入數(shù)據(jù),第二個(gè)測(cè)試查詢。

第一測(cè)試結(jié)果:

第二個(gè)測(cè)結(jié)果:

從結(jié)果看出來第一個(gè)走的寫數(shù)據(jù)源,就是主數(shù)據(jù)源,第二個(gè)的走讀數(shù)據(jù)源,就是從數(shù)據(jù)源。

然后我們?cè)跍y(cè)試一下事物,看遇到異常是否會(huì)滾。

測(cè)試:

 @Test
 public void contextLoads() throws Exception {
  try {
   areaService.insertBack();

  } catch (Exception e) {
//   e.printStackTrace();
  }
  System.out.println(areaService.count(new Area()));

 }

其中service:

@Override
@Transactional(rollbackFor = Exception.class)
public void insertBack() {
 Area area = new Area();
 area.setDistrictId("0000");
 area.setName("test");
 area.setParentId(0);
 area.setLevel(1);
 mapper.insert(area);
 throw new RuntimeException();
}

方法上加@Transactional,聲明一個(gè)事物。

看一下運(yùn)行結(jié)果,雖然運(yùn)行插入的時(shí)候,sql是運(yùn)行了,但最后查詢的時(shí)候數(shù)量為0,說明會(huì)滾了。

配置事物

第一步需要加一個(gè)注解@EnableTransactionManagement,后面的參數(shù)是為了區(qū)分aop和事物執(zhí)行的順序。

然后在需要會(huì)滾的方法上加一個(gè)注解@Transactional。

源代碼地址

以上這篇使用springboot aop來實(shí)現(xiàn)讀寫分離和事物配置就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java使用Tinify實(shí)現(xiàn)圖片無損壓縮(4M無損壓縮到1M)的方法

    Java使用Tinify實(shí)現(xiàn)圖片無損壓縮(4M無損壓縮到1M)的方法

    在當(dāng)今的數(shù)字化時(shí)代,圖片已成為網(wǎng)站、應(yīng)用和社交媒體中不可或缺的元素,然而,大尺寸的圖片不僅會(huì)增加頁面或者客戶端加載時(shí)間,還會(huì)占用大量的存儲(chǔ)空間,本文將詳細(xì)介紹如何利用Tinify壓縮圖片,并將其上傳至OSS,重點(diǎn)介紹圖片壓縮實(shí)現(xiàn)方式,需要的朋友可以參考下
    2024-08-08
  • springboot 熱啟動(dòng)的過程圖解

    springboot 熱啟動(dòng)的過程圖解

    這篇文章主要介紹了springboot 熱啟動(dòng)的過程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • java微信支付功能實(shí)現(xiàn)源碼

    java微信支付功能實(shí)現(xiàn)源碼

    這篇文章主要給大家介紹了關(guān)于java微信支付功能實(shí)現(xiàn)源碼的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Java中BigDecimal類的簡(jiǎn)單用法

    Java中BigDecimal類的簡(jiǎn)單用法

    這篇文章主要介紹了Java中BigDecimal類的簡(jiǎn)單用法,是Java應(yīng)用程序開發(fā)中非常實(shí)用的技巧,本文以實(shí)例形式對(duì)此進(jìn)行了簡(jiǎn)單的分析,需要的朋友可以參考下
    2014-09-09
  • Spring 中使用Quartz實(shí)現(xiàn)任務(wù)調(diào)度

    Spring 中使用Quartz實(shí)現(xiàn)任務(wù)調(diào)度

    這篇文章主要介紹了Spring 中使用Quartz實(shí)現(xiàn)任務(wù)調(diào)度,Spring中使用Quartz 有兩種方式,感興趣的小伙伴們可以參考一下。
    2017-02-02
  • Java CAS操作與Unsafe類詳解

    Java CAS操作與Unsafe類詳解

    這篇文章主要介紹了Java CAS操作與Unsafe類的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-02-02
  • Java 數(shù)組內(nèi)置函數(shù)toArray詳解

    Java 數(shù)組內(nèi)置函數(shù)toArray詳解

    這篇文章主要介紹了Java 數(shù)組內(nèi)置函數(shù)toArray詳解,文本詳細(xì)的講解了toArray底層的代碼和文檔,需要的朋友可以參考下
    2021-06-06
  • Java中使用HashMap改進(jìn)查找性能的步驟

    Java中使用HashMap改進(jìn)查找性能的步驟

    這篇文章主要介紹了Java中使用HashMap改進(jìn)查找性能的步驟,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2021-02-02
  • 手把手教你如何在Idea中下載jar包

    手把手教你如何在Idea中下載jar包

    maven依賴的jar包,很多時(shí)候同一個(gè)jar包會(huì)存在多個(gè)版本,刪除其中一個(gè)后,重新編譯,會(huì)把舊jar由加載回來了,下面這篇文章主要給大家介紹了關(guān)于如何在Idea中下載jar包的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • 一文帶你搞懂Java中Object類和抽象類

    一文帶你搞懂Java中Object類和抽象類

    這篇文章主要為大家詳細(xì)介紹了Java中Object類和抽象類的定義與使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定幫助,需要的可以參考一下
    2022-08-08

最新評(píng)論

惠东县| 平利县| 噶尔县| 彰化市| 松滋市| 宁强县| 胶南市| 巴南区| 寻甸| 陆良县| 石柱| 平远县| 富锦市| 荥阳市| 育儿| 华坪县| 津市市| 呼和浩特市| 临高县| 随州市| 尚义县| 平潭县| 库车县| 辽宁省| 历史| 莱西市| 大渡口区| 阳原县| 青浦区| 横峰县| 科技| 建湖县| 巧家县| 洱源县| 图片| 乌鲁木齐县| 扶余县| 马鞍山市| 葵青区| 临夏县| 河西区|