SpringBoot集成Spring Data JPA及讀寫(xiě)分離
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; } }
以上所述是小編給大家介紹的SpringBoot集成Spring Data JPA及讀寫(xiě)分離,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
新手小白學(xué)JAVA 日期類Date SimpleDateFormat Calendar(入門)
本文主要介紹了JAVA 日期類Date SimpleDateFormat Calendar,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10springmvc Controller方法沒(méi)有加@ResponseBody導(dǎo)致api訪問(wèn)404問(wèn)題
這篇文章主要介紹了springmvc Controller方法沒(méi)有加@ResponseBody導(dǎo)致api訪問(wèn)404問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01java微信公眾號(hào)開(kāi)發(fā)第一步 公眾號(hào)接入和access_token管理
這篇文章主要為大家介紹了java微信公眾號(hào)開(kāi)發(fā),主要內(nèi)容包括公眾號(hào)接入和access_token管理,感興趣的小伙伴們可以參考一下2016-01-01IntelliJ?IDEA?2022.2?正式發(fā)布新功能體驗(yàn)
IntelliJ?IDEA?2022.2為遠(yuǎn)程開(kāi)發(fā)功能帶來(lái)了多項(xiàng)質(zhì)量改進(jìn),使其更美觀、更穩(wěn)定,新版本還具有多項(xiàng)值得注意的升級(jí)和改進(jìn),下面跟隨小編一起看看IDEA?2022.2新版本吧2022-08-08從JVM的內(nèi)存管理角度分析Java的GC垃圾回收機(jī)制
這篇文章主要介紹了從JVM的內(nèi)存管理角度分析Java的GC垃圾回收機(jī)制,帶有GC是Java語(yǔ)言的重要特性之一,需要的朋友可以參考下2015-11-11基于java下載中g(shù)etContentLength()一直為-1的一些思路
下面小編就為大家?guī)?lái)一篇基于java下載中g(shù)etContentLength()一直為-1的一些思路。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06java異常處理執(zhí)行順序詳解try catch finally
try catch語(yǔ)句是java語(yǔ)言用于捕獲異常并進(jìn)行處理的標(biāo)準(zhǔn)方式,對(duì)于try catch及try catch finally執(zhí)行順序必須有深入的了解2021-10-10SpringMvc3+extjs4實(shí)現(xiàn)上傳與下載功能
這篇文章主要為大家詳細(xì)介紹了SpringMvc3+extjs4實(shí)現(xiàn)上傳與下載功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06