SpringBoot集成Spring Data JPA及讀寫分離
JPA是什么
JPA(Java Persistence API)是Sun官方提出的Java持久化規(guī)范,它為Java開發(fā)人員提供了一種對象/關聯(lián)映射工具 來管理Java應用中的關系數(shù)據(jù).它包括以下幾方面的內(nèi)容:
1.ORM映射 支持xml和注解方式建立實體與表之間的映射.
2.Java持久化API 定義了一些常用的CRUD接口,我們只需直接調用,而不需要考慮底層JDBC和SQL的細節(jié).
3.JPQL查詢語言 這是持久化操作中很重要的一個方面,通過面向對象而非面向數(shù)據(jù)庫的查詢語言查詢數(shù)據(jù),避免程序的SQL語句緊密耦合.
在工作中,我們都會用到ORM技術,比如Hibernate,JOOQ等,根據(jù)需求的不同,我們會采用不同的ORM框架,當我們需要 更換ORM框架來滿足我們的需求時,由于不同ORM框架的實現(xiàn),使用方式的區(qū)別以及各自為營,我們往往需要對代碼進行重構.JPA的 出現(xiàn)就是為了解決這個問題,JPA充分吸收了現(xiàn)有一些ORM框架的優(yōu)點,具有易于使用,伸縮性強等優(yōu)點,為ORM技術提供了一套標準的 接口用來整合不同的ORM框架.
Hibernate對JPA的實現(xiàn)
JPA本身并不做具體的實現(xiàn),而只是定義了一些接口規(guī)范,讓其它ORM來具體的實現(xiàn)這些接口,就目前來說,對JPA規(guī)范實現(xiàn)最好的就是 Hibernate了.這里提一下Mybatis,Mybatis并沒有實現(xiàn)JPA規(guī)范,它本身也不能算做一個真正的ORM框架.
Spring Data JPA是什么
Spring Data JPA只是Spring Data框架的一個模塊,可以極大的簡化JPA的使用,Spring Data JPA強大的地方還在于能夠簡化我們 對持久層業(yè)務邏輯的開發(fā),通過規(guī)范持久層方法的名稱,通過名稱來判斷需要實現(xiàn)什么業(yè)務邏輯,我們機會可以在不寫一句sql,不做任何dao層 邏輯的情況下完成我們絕大部分的開發(fā),當然,對于一些復雜的,性能要求高的查詢,Spring Data JPA一樣支持我們使用原生的sql.
在這里我們不過多的去介紹JPA以及Spring Data JPA,主要還是與SpringBoot集成的一些細節(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>
我們引入這個依賴后,發(fā)現(xiàn)也引入了Hibernate的包,這是現(xiàn)在一種默認的做法,Hibernate已經(jīng)被作為JPA規(guī)范的最好實現(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,具體說明可以看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數(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實例注入
EntityManagerFactory類似于Hibernate的SessionFactory,mybatis的SqlSessionFactory 總之,在執(zhí)行操作之前,我們總要獲取一個EntityManager,這就類似于Hibernate的Session, mybatis的sqlSession. 注入EntityManagerFactory有兩種方式,一種是直接注入EntityManagerFactory,另一種是通過 LocalContainerEntityManagerFactoryBean來間接注入.雖說這兩種方法都是基于 LocalContainerEntityManagerFactoryBean的,但是在配置上還是有一些區(qū)別.
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
* 總之,在執(zhí)行操作之前,我們總要獲取一個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();//在完成了其它所有相關的配置加載以及屬性設置后,才初始化
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
* 總之,在執(zhí)行操作之前,我們總要獲取一個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的實例,看似跟第一種沒有什么區(qū)別,但是我們不能直接用 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 "";//數(shù)據(jù)源
}
使用ThreadLocal使數(shù)據(jù)源與線程綁定
public class DynamicDataSourceHolder {
//使用ThreadLocal把數(shù)據(jù)源與當前線程綁定
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("----------數(shù)據(jù)源是:" + targetDataSource + "------");
DynamicDataSourceHolder.setDataSource(targetDataSource);
}
Object result = pjp.proceed();//執(zhí)行方法
DynamicDataSourceHolder.clearDataSource();
return result;
}
}
以上所述是小編給大家介紹的SpringBoot集成Spring Data JPA及讀寫分離,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關文章
新手小白學JAVA 日期類Date SimpleDateFormat Calendar(入門)
本文主要介紹了JAVA 日期類Date SimpleDateFormat Calendar,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10
springmvc Controller方法沒有加@ResponseBody導致api訪問404問題
這篇文章主要介紹了springmvc Controller方法沒有加@ResponseBody導致api訪問404問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
java微信公眾號開發(fā)第一步 公眾號接入和access_token管理
這篇文章主要為大家介紹了java微信公眾號開發(fā),主要內(nèi)容包括公眾號接入和access_token管理,感興趣的小伙伴們可以參考一下2016-01-01
IntelliJ?IDEA?2022.2?正式發(fā)布新功能體驗
IntelliJ?IDEA?2022.2為遠程開發(fā)功能帶來了多項質量改進,使其更美觀、更穩(wěn)定,新版本還具有多項值得注意的升級和改進,下面跟隨小編一起看看IDEA?2022.2新版本吧2022-08-08
從JVM的內(nèi)存管理角度分析Java的GC垃圾回收機制
這篇文章主要介紹了從JVM的內(nèi)存管理角度分析Java的GC垃圾回收機制,帶有GC是Java語言的重要特性之一,需要的朋友可以參考下2015-11-11
基于java下載中getContentLength()一直為-1的一些思路
下面小編就為大家?guī)硪黄趈ava下載中getContentLength()一直為-1的一些思路。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
java異常處理執(zhí)行順序詳解try catch finally
try catch語句是java語言用于捕獲異常并進行處理的標準方式,對于try catch及try catch finally執(zhí)行順序必須有深入的了解2021-10-10
SpringMvc3+extjs4實現(xiàn)上傳與下載功能
這篇文章主要為大家詳細介紹了SpringMvc3+extjs4實現(xiàn)上傳與下載功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06

