這篇文章主要介紹了SpringBoot整合Spring Data JPA及讀寫分離的相關知識,需要的朋友可以參考下
相關程式碼: github OSCchina
#JPA是什麼
JPA(Java Persistence API#)是Sun官方提出的Java持久化規範,它為Java開發人員提供了一種物件/關聯映射工具來管理Java應用中的關聯式資料.它包括以下幾個方面的內容:
#1.ORM映射支援xml和註解方式建立實體與表之間的映射.
2.Java持久化API 定義了一些常用的CRUD介面,我們只需直接呼叫,而不需要考慮底層JDBC和SQL的細節.
3.JPQL查詢語言這是持久化操作中很重要的一個方面,透過物件導向而非面向資料庫的查詢語言查詢數據,避免程式的SQL語句緊密耦合.
在工作中,我們都會用到ORM技術,比如Hibernate,JOOQ等,根據需求的不同,我們會採用不同的ORM框架,當我們需要更換ORM框架來滿足我們的需求時,由於不同ORM框架的實現,使用方式的區別以及各自為營,我們往往需要對代碼進行重構.JPA的出現就是為了解決這個問題,JPA充分吸收了現有一些ORM框架的優點,具有易於使用,伸縮性強等優點,為ORM技術提供了一套標準的接口用來整合不同的ORM框架.
Hibernate對JPA的實現
JPA本身並不做具體的實現,而只是定義了一些接口規範,讓其它ORM來具體的實現這些接口,就目前來說,對JPA規範實現最好的就是Hibernate了.這裡提一下Mybatis,Mybatis並沒有實現JPA規範,它本身也不能算做一個真正的ORM框架.
##Spring Data JPA是什麼
Spring Data JPA只是Spring Data框架的一個模組,可以極大的簡化JPA的使用,Spring Data JPA強大的地方還在於能夠簡化我們對持久層業務邏輯的開發,透過規範持久層方法的名稱,透過名稱來判斷需要實現什麼業務邏輯,我們機會可以在不寫一句sql,不做任何dao層邏輯的情況下完成我們絕大部分的開發,當然,對於一些複雜的,性能要求高的查詢,Spring Data JPA一樣支持我們使用原生的sql.在這裡我們不過多的去介紹JPA以及Spring Data JPA,主要還是與SpringBoot集成的一些細節以及範例.引入依賴
<!-- 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>我們引入這個依賴後,發現也引入了Hibernate的套件,這是現在一種預設的做法,Hibernate已經被作為JPA規範的最好實現了,這裡就不介紹Druid資料來源的配置了,大家可以看另外一篇XXXX.
配置我們的資料來源以及JPA(Hibernate)
#配置模板 #https://docs.spring.io/spring-boot/docs/1.4.0.RELEASE/reference/html/common-application-properties.html #数据源 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,具体说明可以看README spring.jpa.hibernate.ddl-auto=update #通过方法名解析sql的策略,具体说明可以看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資料來源注入
@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實例注入
EntityManagerFactory類似Hibernate的SessionFactory,mybatis的SqlSessionFactory 總之,在執行操作之前,我們總是要取得一個EntityManager,這就類似Hibernate的Session, mybatis的sqlSession. 注入一個EntityFactoryy,一種是直接注入EntityFactorManagery,EntityManagerFactory有兩種方式另一種是透過LocalContainerEntityManagerFactoryBean來間接注入.雖然說這兩種方法都是基於LocalContainerEntityManagerFactoryBean的,但是在配置上還是有一些區別.1.直接注入EntityManagerFactory
設定:透過spring.jpa.properties.*來設定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 * 总之,在执行操作之前,我们总要获取一个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);//数据源 factory.setJpaPropertyMap(jpaProperties.getProperties()); factory.afterPropertiesSet();//在完成了其它所有相关的配置加载以及属性设置后,才初始化 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,具体说明可以看README spring.jpa.hibernate.ddl-auto=update #通过方法名解析sql的策略,具体说明可以看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; /** * 我们通过LocalContainerEntityManagerFactoryBean来获取EntityManagerFactory实例 * @return */ @Bean(name = "writeEntityManagerFactoryBean") @Primary public LocalContainerEntityManagerFactoryBean writeEntityManagerFactoryBean(EntityManagerFactoryBuilder builder) { return builder .dataSource(writeDruidDataSource) .properties(jpaProperties.getProperties()) .packages("com.lc.springBoot.jpa.entity") //设置实体类所在位置 .persistenceUnit("writePersistenceUnit") .build(); //.getObject();//不要在这里直接获取EntityManagerFactory } /** * 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)); } }對於這個配置
@Bean(name = "writeEntityManagerFactoryBean") @Primary public LocalContainerEntityManagerFactoryBean writeEntityManagerFactoryBean(EntityManagerFactoryBuilder builder) { return builder .dataSource(writeDruidDataSource) .properties(jpaProperties.getProperties()) .packages("com.lc.springBoot.jpa.entity") //设置实体类所在位置 .persistenceUnit("writePersistenceUnit") .build(); //.getObject();//不要在这里直接获取EntityManagerFactory }getObject()方法可以取得到EntityManagerFactory的實例,看似跟第一種沒有什麼差別,但是我們不能直接用getObject() ,不然會取得不到,報空指標異常.
讀寫分離配置
自訂注入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 "";//数据源 }
使用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; } }定義切面
@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("----------数据源是:" + targetDataSource + "------"); DynamicDataSourceHolder.setDataSource(targetDataSource); } Object result = pjp.proceed();//执行方法 DynamicDataSourceHolder.clearDataSource(); return result; } }
以上是SpringBoot實作Spring Data JPA整合的實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!