spring data jpa自定义baseRepository
在一些特殊时候,我们会设计到对Spring Data JPA中的方法进行重新实现,这将会面临一个问题,如果我们新创建一个实现类。如果这个实现类实现了JpaRepository接口,这样我们不得不实现该接口中的所有方法,如果不实现该接口,那意味着我们就无法使用Spring Data JPA中给我们提供的那些好用的方法。所以在扩展的时候我们需要按照如下方法进行。

这些需要注意的是,接口和实现类的名称必须遵循spring data jpa的命名规范,如果要为接口StudentBaseRepository写自定义的接口,首先需要创建一个接口名称为StudentBaseRepositoryCustom,这表示是自定义接口,实现类的名称必须是StudentBaseRepositoryImpl,此时当StudentBaseRepository实现StudentBaseRepositoryCustom之后就可以使用我们自己实现的方法了,同理StudentBaseRepository也可以继承JpaRepository来获取Spring Data Jpa 给我们的方法。
StudentBaseRepositoryCustom代码如下
public interface StudentBaseRepositoryCustom {
//基于原生态的sql进行查询
List<Object[]> groupByStudentAsSql();
//基于Hibernate的HQL进行查询
List<Object[]> groupByStudentAsHql();
//基于Specification的方式进行查询,使用的是CriteriaQuery进行查询
List<Object[]> groupByStudentAsSpecification();
}
以上代码中定义了三个方法,第一个是基于原始的SQL来进行分组查询,第二个是基于hibernate的HQL进行查询,最后一个是用Specification中的CriteriaQuery来进行处理,首先要解决的问题是StudentBaseRepositoryCustom没有实现Repository,该如何来执行SQL语句呢,我们可以给实现类注入另一个EntityManger,通过EntityManager来执行SQL语句。以下是StudentBaseRepositoryImpl的实现代码
public class StudentBaseRepositoryImpl implements StudentBaseRepositoryCustom {
@Autowired
@PersistenceContext
private EntityManager entityManager;
@Override
public List<Object[]> groupByStudentAsSql() {
List<Object[]> list = entityManager
.createNativeQuery("select address,count(*) from t_student group by address")
.getResultList();
return list;
}
@Override
public List<Object[]> groupByStudentAsHql() {
List<Object[]> list = entityManager
.createQuery("select address,count(*) from Student group by address")
.getResultList();
return list;
}
@Override
public List<Object[]> groupByStudentAsSpecification() {
//根据地址分组查询,并且学生数量大于3的所有地址
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = builder.createQuery(Object[].class);
Root<Student> root = query.from(Student.class);
query.multiselect(root.get("address"),builder.count(root.get("id")))
.groupBy(root.get("address")).having(builder.gt(builder.count(root.get("id")),3));
return entityManager.createQuery(query).getResultList();
}
}
前面两个方法的实现都非常容易理解,就是创建一个查询语句,执行完成之后会返回一组Object[]的投影,第三个方法稍微有些复杂,这是CriteriaQuery的标准写法。
到这里我们解决了扩展类的问题,但仍然有些疑问,如果每个类都有自己独立的方法,那么是不是每一个类都得按照上面的方面来写接口和实现类,以上做法虽然可以很好的解决自定义类的扩展问题,但是仍然稍显麻烦,我们可以定义一个基类来覆盖一些比较通用的方法,如通用的SQL查询等。下面我们就来创建这个BaseRepository,整个创建的过程有些复杂,可以参照项目的源代码
接下来我们使用自定义baseRepository
创建的第一步定义一个BaseRepository的接口
/**
* 通用repository
* 我们使用它来简化我们的一些repository的通用CRUD
* 不需要在每一个repository中写CRUD,只需在需要的repository上继承就好。
* @NoRepositoryBean,这个表示该接口不会创建这个接口的实例,像UserInfoRepository等,
* 只要是在jpaConfig里配置的基础名里的接口全会被实例化。
*/
@NoRepositoryBean
public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID>{
//自定义sql查询
List<T> listBySql(String sql);
//自定义多条件动态查询
List<T> listByOwn(Map<String,Object> map1,Map<String,Object> map2);
}
之后我们编写实现类BaseRepositoryImpl
/**
*这个实现类比较的简单,首先我们需要继承SimpleJpaRepository,
* SimpleJpaRepository帮助我们实现了JpaRepository中的方法。
* 然后实现BaseRepository接口。listBySQL方法非常的简单,具体的作用就是执行一条sql返回一组投影的列表。
* Created by BFD-593 on 2017/8/16.
*/
public class BaseRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID>
implements BaseRepository<T,ID>{
private final EntityManager entityManager; //父类没有不带参数的构造方法,这里手动构造父类
public BaseRepositoryImpl(Class<T> domainClass,EntityManager entityManager){
super(domainClass, entityManager);
this.entityManager = entityManager;
}
//通过EntityManager来完成查询,指定sql来查询
@Override
public List<T> listBySql(String sql) {
return entityManager.createNativeQuery(sql).getResultList();
} @Override
public List<T> listByOwn(final Map<String,Object> map1, final Map<String,Object> map2) {
Specification<T> sf = new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> list = new ArrayList<>();
String key1 = (String) map1.keySet().toArray()[0];
String key2 = (String) map2.keySet().toArray()[0];
if (StringUtils.isNotEmpty(key1)) {
list.add(criteriaBuilder.like(root.get(key1).as(String.class),"%"+map1.get(key1)+"%"));
}
if(StringUtils.isNotEmpty(key2)){
list.add(criteriaBuilder.greaterThanOrEqualTo(root.get(key2).as(Integer.class), (Integer) map2.get(key2)));
}
Predicate[] pre = new Predicate[list.size()];
criteriaQuery.where(list.toArray(pre));
return criteriaQuery.getRestriction();
}
};
return findAll(sf);
}
}
下一步我们需要创建一个自定义的工厂,在这个工厂中注册我们自己定义的BaseRepositoryImpl的实现。
/**
* 我们需要创建一个自定义的工厂,
* 在这个工厂中注册我们自己定义的BaseRepositoryImpl的实现。
* 这个工厂的写法具体参照Spring Data的JpaRepositoryFactoryBean和JpaRepositoryFactory。
* 这个类上面一堆的泛型,我们不用考虑,只要按照相同的方式来写即可。
* Created by BFD-593 on 2017/8/16.
*/
public class BaseRepositoryFactoryBean<R extends JpaRepository<T, I>, T,
I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
public BaseRepositoryFactoryBean(Class<? extends R> repositoryInterface) {
super(repositoryInterface);
} /**
* 此方法是JpaRepositoryFactoryBean中的,
* 目的是返回一个工厂,我们调用它来反回我们自己的工厂
* @param entityManager
* @return
*/
@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new BaseRepositoryFactory(entityManager);
}
//创建一个内部类,该类不用在外部访问
private static class BaseRepositoryFactory<T,I extends Serializable> extends JpaRepositoryFactory{
private final EntityManager entityManager;
public BaseRepositoryFactory(EntityManager entityManager){
super(entityManager);
this.entityManager=entityManager;
} /**
* 通过这两个方法来确定具体的实现类,JpaRepositoryFactory中的方法
* 也就是Spring Data Jpa具体实例化一个接口的时候会去创建的实现类。
* Spring Data JPA都是调用SimpleJpaRepository来创建实例。以下是我们自己的工厂实现的代码
* @param information
* @return
*/
//设置具体的实现类是BaseRepositoryImpl
@Override
protected Object getTargetRepository(RepositoryInformation information) {
return new BaseRepositoryImpl<T, I>((Class<T>)information.getDomainType(), entityManager);
}
//设置具体的实现类的class
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseRepositoryImpl.class;
}
}
}
接着我们需要让spring在加载的时候找到我们自定义的BaseRepository的工厂,当我们使用了SpringBoot之后一切都变得简单了,只要在入口类中加入@EnableJpaRepositories即可,代码如下
/**
* 我们使用通用repository时
* 我们需要让spring在加载的时候找到我们自定义的BaseRepositoryFactoryBean的工厂,
* 只要在入口类中加入@EnableJpaRepositories即可,代码如下
*/
@EnableJpaRepositories(basePackages = {"com.example.demo.dao"},
repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class)//我们自己的工厂
@SpringBootApplication
public class SpringBootJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootJpaApplication.class, args);
}
}
到这里我们的整个自定义工厂的流程就结束了,我们写一个接口实现BaseRepository即可。
具体代码请看https://github.com/peterowang/spring-data-jpa-demo
spring data jpa自定义baseRepository的更多相关文章
- spring data jpa自定义bean字段映射
当遇到复杂多表查询时,并且同时还需要确保查询性能,此时则需要使用自定义sql查询,然而spring data jpa对于自定义sql则需使用查询需要在对应的bean中做太多的配置映射,我尝试了一下,最 ...
- 【Spring Data 系列学习】Spring Data JPA 自定义查询,分页,排序,条件查询
Spring Boot Jpa 默认提供 CURD 的方法等方法,在日常中往往时无法满足我们业务的要求,本章节通过自定义简单查询案例进行讲解. 快速上手 项目中的pom.xml.application ...
- Spring Data JPA 自定义对象接收查询结果集
Spring Data JPA 简介 Spring Data JPA 是 Spring 基于 ORM 框架.JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和 ...
- Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!
前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...
- 快速搭建springmvc+spring data jpa工程
一.前言 这里简单讲述一下如何快速使用springmvc和spring data jpa搭建后台开发工程,并提供了一个简单的demo作为参考. 二.创建maven工程 http://www.cnblo ...
- Spring Data JPA: 实现自定义Repository
一.前言 由于项目中的 实体(entity)默认都是继承一个父类(包含一些公共的属性,比如创建时间,修改时间,是否删除,主键id).为了实现逻辑删除,一般会自己实现RepositoryFactoryB ...
- Spring data JPA 理解(默认查询 自定义查询 分页查询)及no session 三种处理方法
简介:Spring Data JPA 其实就是JDK方式(还有一种cglib的方式需要Class)的动态代理 (需要一个接口 有一大堆接口最上边的是Repository接口来自org.springfr ...
- Spring Data JPA 查询结果返回至自定义实体
本人在实际工作中使用Spring Data Jpa框架时,一般查询结果只返回对应的Entity实体.但有时根据实际业务,需要进行一些较复杂的查询,比较棘手.虽然在框架上我们可以使用@Query注解执行 ...
- spring data jpa封装specification实现简单风格的动态查询
github:https://github.com/peterowang/spring-data-jpa-demo 单一实体的动态查询: @Servicepublic class AdvancedUs ...
随机推荐
- 结合Django+celery二次开发定时周期任务
需求: 前端时间由于开发新上线一大批系统,上完之后没有配套的报表系统.监控,于是乎开发.测试.产品.运营.业务部.财务等等各个部门就跟那饥渴的饿狼一样需要 各种各样的系统数据满足他们.刚开始一天一个还 ...
- RT-Thread RTOS
RT-ThreadRTOS是一款来自中国的开源实时操作系统,由RT-Thread工作室的专业开发人员开发.维护. 起初RT-Thread是一个实时的内核(全抢占优先级调度,调度器时间复杂度O(1)), ...
- Oracle字段增删改方法总结
一.修改字段的语法:alter table tablename modify (字段名 类型 [default value][null/not null],….);有一个表名为tb,字段段名为name ...
- mysql基础itcast笔记
1. 课程回顾 mysql基础 1)mysql存储结构: 数据库 -> 表 -> 数据 sql语句 2)管理数据库: 增加: create database 数据库 default c ...
- win7 64位搭建Mantis 缺陷管理系统(2)
建立Bug数据库 1. 右键Windows托盘的图标,选择“Local Web”,(或者在IE地址中输入“http://127.0.0.1/”)可看到如下页面: 2. 点击选择“mantis”,进入页 ...
- C# 使用 MemoryStream 将数据写入内存
转自:http://blog.csdn.net/andrew_wx/article/details/6629951 常用的MemoryStream构造函数有以下3种. 1:MemoryStream() ...
- caffe c++
http://blog.csdn.net/yao_zhuang/article/details/1853625
- linux工具————fish shell
1.说明 fish is a fully-equipped command line shell (like bash or zsh) that is smart and user-friendly. ...
- 反射设置当前窗体所有控件的Text
在我们编程的时候,有时需要动态的获取当前窗体控件的Text,但是又不能一个一个控件的设置,这个时候可以通过反射来动态设置. 第一步:先建立一个类来保存控件的Text信息. public class C ...
- 洛谷P3688/uoj#291. [ZJOI2017]树状数组
传送门(uoj) 传送门(洛谷) 这里是题解以及我的卡常数历程 话说后面那几组数据莫不是lxl出的这么毒 首先不难发现这个东西把查询前缀和变成了查询后缀和,结果就是查了\([l-1,r-1]\)的区间 ...