SpringBoot集成Spring Data JPA及读写分离
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. 注入entitymanagerfactory有两种方式,一种是直接注入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及读写分离,希望对大家有所帮助