SpringBoot使用JPA如何實(shí)現(xiàn)讀寫(xiě)分離

今天就跟大家聊聊有關(guān)SpringBoot使用JPA如何實(shí)現(xiàn)讀寫(xiě)分離,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

公司主營(yíng)業(yè)務(wù):做網(wǎng)站、成都網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)推出淮安區(qū)免費(fèi)做網(wǎng)站回饋大家。

JPA是什么

JPA(Java Persistence API)是Sun官方提出的Java持久化規(guī)范,它為Java開(kāi)發(fā)人員提供了一種對(duì)象/關(guān)聯(lián)映射工具 來(lái)管理Java應(yīng)用中的關(guān)系數(shù)據(jù).它包括以下幾方面的內(nèi)容:

1.ORM映射 支持xml和注解方式建立實(shí)體與表之間的映射.

2.Java持久化API 定義了一些常用的CRUD接口,我們只需直接調(diào)用,而不需要考慮底層JDBC和SQL的細(xì)節(jié).

3.JPQL查詢語(yǔ)言 這是持久化操作中很重要的一個(gè)方面,通過(guò)面向?qū)ο蠖敲嫦驍?shù)據(jù)庫(kù)的查詢語(yǔ)言查詢數(shù)據(jù),避免程序的SQL語(yǔ)句緊密耦合.

在工作中,我們都會(huì)用到ORM技術(shù),比如Hibernate,JOOQ等,根據(jù)需求的不同,我們會(huì)采用不同的ORM框架,當(dāng)我們需要 更換ORM框架來(lái)滿足我們的需求時(shí),由于不同ORM框架的實(shí)現(xiàn),使用方式的區(qū)別以及各自為營(yíng),我們往往需要對(duì)代碼進(jìn)行重構(gòu).JPA的 出現(xiàn)就是為了解決這個(gè)問(wèn)題,JPA充分吸收了現(xiàn)有一些ORM框架的優(yōu)點(diǎn),具有易于使用,伸縮性強(qiáng)等優(yōu)點(diǎn),為ORM技術(shù)提供了一套標(biāo)準(zhǔn)的 接口用來(lái)整合不同的ORM框架.

Hibernate對(duì)JPA的實(shí)現(xiàn)

JPA本身并不做具體的實(shí)現(xiàn),而只是定義了一些接口規(guī)范,讓其它ORM來(lái)具體的實(shí)現(xiàn)這些接口,就目前來(lái)說(shuō),對(duì)JPA規(guī)范實(shí)現(xiàn)最好的就是 Hibernate了.這里提一下Mybatis,Mybatis并沒(méi)有實(shí)現(xiàn)JPA規(guī)范,它本身也不能算做一個(gè)真正的ORM框架.

Spring Data JPA是什么

Spring Data JPA只是Spring Data框架的一個(gè)模塊,可以極大的簡(jiǎn)化JPA的使用,Spring Data JPA強(qiáng)大的地方還在于能夠簡(jiǎn)化我們 對(duì)持久層業(yè)務(wù)邏輯的開(kāi)發(fā),通過(guò)規(guī)范持久層方法的名稱,通過(guò)名稱來(lái)判斷需要實(shí)現(xiàn)什么業(yè)務(wù)邏輯,我們機(jī)會(huì)可以在不寫(xiě)一句sql,不做任何dao層 邏輯的情況下完成我們絕大部分的開(kāi)發(fā),當(dāng)然,對(duì)于一些復(fù)雜的,性能要求高的查詢,Spring Data JPA一樣支持我們使用原生的sql.

在這里我們不過(guò)多的去介紹JPA以及Spring Data JPA,主要還是與SpringBoot集成的一些細(xì)節(jié)以及示例.

引入依賴

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

我們引入這個(gè)依賴后,發(fā)現(xiàn)也引入了Hibernate的包,這是現(xiàn)在一種默認(rèn)的做法,Hibernate已經(jīng)被作為JPA規(guī)范的最好實(shí)現(xiàn)了,這里就不介紹Druid數(shù)據(jù)源的 配置了,大家可以看另外一篇XXXX.

配置我們的數(shù)據(jù)源以及JPA(Hibernate)

#配置模板
#https://docs.spring.io/spring-boot/docs/1.4.0.RELEASE/reference/html/common-application-properties.html
#數(shù)據(jù)源
spring.datasource.druid.write.url=jdbc:MySQL://localhost:3306/jpa
spring.datasource.druid.write.username=root
spring.datasource.druid.write.password=1
spring.datasource.druid.write.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.read.url=jdbc:mysql://localhost:3306/jpa
spring.datasource.druid.read.username=root
spring.datasource.druid.read.password=1
spring.datasource.druid.read.driver-class-name=com.mysql.jdbc.Driver
#JPA (JpaBaseConfiguration, HibernateJpaAutoConfiguration)
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.jpa.database=mysql
spring.jpa.generate-ddl=true
#就是hibernate.hbm2ddl.auto,具體說(shuō)明可以看README
spring.jpa.hibernate.ddl-auto=update
#通過(guò)方法名解析sql的策略,具體說(shuō)明可以看README,這里就不配置了
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultComponentSafeNamingStrategy
spring.jpa.show-sql=true
#spring.jpa.properties.*
#spring.jpa.properties.hibernate.hbm2ddl.auto=update
#spring.jpa.properties.hibernate.show_sql=true
#spring.jpa.properties.hibernate.use-new-id-generator-mappings=true

druid數(shù)據(jù)源注入

@Configuration
public class DruidDataSourceConfig {
  /**
   * DataSource 配置
   * @return
   */
  @ConfigurationProperties(prefix = "spring.datasource.druid.read")
  @Bean(name = "readDruidDataSource")
  public DataSource readDruidDataSource() {
    return new DruidDataSource();
  }
  /**
   * DataSource 配置
   * @return
   */
  @ConfigurationProperties(prefix = "spring.datasource.druid.write")
  @Bean(name = "writeDruidDataSource")
  @Primary
  public DataSource writeDruidDataSource() {
    return new DruidDataSource();
  }
}

EntityManagerFactory實(shí)例注入

EntityManagerFactory類似于Hibernate的SessionFactory,mybatis的SqlSessionFactory 總之,在執(zhí)行操作之前,我們總要獲取一個(gè)EntityManager,這就類似于Hibernate的Session, mybatis的sqlSession. 注入EntityManagerFactory有兩種方式,一種是直接注入EntityManagerFactory,另一種是通過(guò) LocalContainerEntityManagerFactoryBean來(lái)間接注入.雖說(shuō)這兩種方法都是基于 LocalContainerEntityManagerFactoryBean的,但是在配置上還是有一些區(qū)別.

1.直接注入EntityManagerFactory

配置:通過(guò)spring.jpa.properties.*來(lái)配置Hibernate的屬性

spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use-new-id-generator-mappings=true
@Configuration
@EnableJpaRepositories(value = "com.lc.springBoot.jpa.repository",
            entityManagerFactoryRef = "writeEntityManagerFactory",
            transactionManagerRef="writeTransactionManager")
public class WriteDataSourceConfig {
  @Autowired
  JpaProperties jpaProperties;
  @Autowired
  @Qualifier("writeDruidDataSource")
  private DataSource writeDruidDataSource;
  /**
   * EntityManagerFactory類似于Hibernate的SessionFactory,mybatis的SqlSessionFactory
   * 總之,在執(zhí)行操作之前,我們總要獲取一個(gè)EntityManager,這就類似于Hibernate的Session,
   * mybatis的sqlSession.
   * @return
   */
  @Bean(name = "writeEntityManagerFactory")
  @Primary
  public EntityManagerFactory writeEntityManagerFactory() {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setPackagesToScan("com.lc.springBoot.jpa.entity");
    factory.setDataSource(writeDruidDataSource);//數(shù)據(jù)源
    factory.setJpaPropertyMap(jpaProperties.getProperties());
    factory.afterPropertiesSet();//在完成了其它所有相關(guān)的配置加載以及屬性設(shè)置后,才初始化
    return factory.getObject();
  }
  /**
   * 配置事物管理器
   * @return
   */
  @Bean(name = "writeTransactionManager")
  @Primary
  public PlatformTransactionManager writeTransactionManager() {
    JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
    jpaTransactionManager.setEntityManagerFactory(this.writeEntityManagerFactory());
    return jpaTransactionManager;
  }
}

2.先注入LocalContainerEntityManagerFactoryBean,再獲取EntityManagerFactory

配置:

spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.jpa.database=mysql
spring.jpa.generate-ddl=true
#就是hibernate.hbm2ddl.auto,具體說(shuō)明可以看README
spring.jpa.hibernate.ddl-auto=update
#通過(guò)方法名解析sql的策略,具體說(shuō)明可以看README,這里就不配置了
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultComponentSafeNamingStrategy
spring.jpa.show-sql=true
@Configuration
@EnableJpaRepositories(value = "com.lc.springBoot.jpa.repository",
    entityManagerFactoryRef = "writeEntityManagerFactory",
    transactionManagerRef = "writeTransactionManager")
public class WriteDataSourceConfig1 {
  @Autowired
  JpaProperties jpaProperties;
  @Autowired
  @Qualifier("writeDruidDataSource")
  private DataSource writeDruidDataSource;
  /**
   * 我們通過(guò)LocalContainerEntityManagerFactoryBean來(lái)獲取EntityManagerFactory實(shí)例
   * @return
   */
  @Bean(name = "writeEntityManagerFactoryBean")
  @Primary
  public LocalContainerEntityManagerFactoryBean writeEntityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
    return builder
        .dataSource(writeDruidDataSource)
        .properties(jpaProperties.getProperties())
        .packages("com.lc.springBoot.jpa.entity") //設(shè)置實(shí)體類所在位置
        .persistenceUnit("writePersistenceUnit")
        .build();
    //.getObject();//不要在這里直接獲取EntityManagerFactory
  }
  /**
   * EntityManagerFactory類似于Hibernate的SessionFactory,mybatis的SqlSessionFactory
   * 總之,在執(zhí)行操作之前,我們總要獲取一個(gè)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));
  }
}

對(duì)于這個(gè)配置

@Bean(name = "writeEntityManagerFactoryBean")
  @Primary
  public LocalContainerEntityManagerFactoryBean writeEntityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
    return builder
        .dataSource(writeDruidDataSource)
        .properties(jpaProperties.getProperties())
        .packages("com.lc.springBoot.jpa.entity") //設(shè)置實(shí)體類所在位置
        .persistenceUnit("writePersistenceUnit")
        .build();
    //.getObject();//不要在這里直接獲取EntityManagerFactory
  }

getObject()方法可以獲取到EntityManagerFactory的實(shí)例,看似跟第一種沒(méi)有什么區(qū)別,但是我們不能直接用 getObject(),不然會(huì)獲取不到,報(bào)空指針異常.

讀寫(xiě)分離配置

自定義注入AbstractRoutingDataSource

@Configuration
public class DataSourceConfig {
  private final static String WRITE_DATASOURCE_KEY = "writeDruidDataSource";
  private final static String READ_DATASOURCE_KEY = "readDruidDataSource";
  /**
   * 注入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();
    targetDataSources.put(WRITE_DATASOURCE_KEY, writeDruidDataSource);
    targetDataSources.put(READ_DATASOURCE_KEY, readDruidDataSource);
    dataSource.setTargetDataSources(targetDataSources);
    dataSource.setDefaultTargetDataSource(writeDruidDataSource);
    return dataSource;
  }
}

自定義注解

  @Target({ElementType.METHOD, ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  public @interface TargetDataSource {
    String dataSource() default "";//數(shù)據(jù)源
  }

使用ThreadLocal使數(shù)據(jù)源與線程綁定

 public class DynamicDataSourceHolder {
    //使用ThreadLocal把數(shù)據(jù)源與當(dāng)前線程綁定
    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() {
      //可以做一個(gè)簡(jiǎn)單的負(fù)載均衡策略
      String lookupKey = DynamicDataSourceHolder.getDataSource();
      System.out.println("------------lookupKey---------"+lookupKey);
      return lookupKey;
    }
  }

定義切面

@Aspect
  @Component
  public class DynamicDataSourceAspect {
    @Around("execution(public * com.lc.springBoot.jpa.service..*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
      MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
      Method targetMethod = methodSignature.getMethod();
      if (targetMethod.isAnnotationPresent(TargetDataSource.class)) {
        String targetDataSource = targetMethod.getAnnotation(TargetDataSource.class).dataSource();
        System.out.println("----------數(shù)據(jù)源是:" + targetDataSource + "------");
        DynamicDataSourceHolder.setDataSource(targetDataSource);
      }
      Object result = pjp.proceed();//執(zhí)行方法
      DynamicDataSourceHolder.clearDataSource();
      return result;
    }
  }

看完上述內(nèi)容,你們對(duì)SpringBoot使用JPA如何實(shí)現(xiàn)讀寫(xiě)分離有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。

本文名稱:SpringBoot使用JPA如何實(shí)現(xiàn)讀寫(xiě)分離
標(biāo)題鏈接:http://bm7419.com/article12/gijhgc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器、品牌網(wǎng)站建設(shè)、全網(wǎng)營(yíng)銷(xiāo)推廣、商城網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)、定制網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司