在一些特殊时候,我们会设计到对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的更多相关文章

  1. spring data jpa自定义bean字段映射

    当遇到复杂多表查询时,并且同时还需要确保查询性能,此时则需要使用自定义sql查询,然而spring data jpa对于自定义sql则需使用查询需要在对应的bean中做太多的配置映射,我尝试了一下,最 ...

  2. 【Spring Data 系列学习】Spring Data JPA 自定义查询,分页,排序,条件查询

    Spring Boot Jpa 默认提供 CURD 的方法等方法,在日常中往往时无法满足我们业务的要求,本章节通过自定义简单查询案例进行讲解. 快速上手 项目中的pom.xml.application ...

  3. Spring Data JPA 自定义对象接收查询结果集

    Spring Data JPA 简介 Spring Data JPA 是 Spring 基于 ORM 框架.JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和 ...

  4. Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!

    前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...

  5. 快速搭建springmvc+spring data jpa工程

    一.前言 这里简单讲述一下如何快速使用springmvc和spring data jpa搭建后台开发工程,并提供了一个简单的demo作为参考. 二.创建maven工程 http://www.cnblo ...

  6. Spring Data JPA: 实现自定义Repository

    一.前言 由于项目中的 实体(entity)默认都是继承一个父类(包含一些公共的属性,比如创建时间,修改时间,是否删除,主键id).为了实现逻辑删除,一般会自己实现RepositoryFactoryB ...

  7. Spring data JPA 理解(默认查询 自定义查询 分页查询)及no session 三种处理方法

    简介:Spring Data JPA 其实就是JDK方式(还有一种cglib的方式需要Class)的动态代理 (需要一个接口 有一大堆接口最上边的是Repository接口来自org.springfr ...

  8. Spring Data JPA 查询结果返回至自定义实体

    本人在实际工作中使用Spring Data Jpa框架时,一般查询结果只返回对应的Entity实体.但有时根据实际业务,需要进行一些较复杂的查询,比较棘手.虽然在框架上我们可以使用@Query注解执行 ...

  9. spring data jpa封装specification实现简单风格的动态查询

    github:https://github.com/peterowang/spring-data-jpa-demo 单一实体的动态查询: @Servicepublic class AdvancedUs ...

随机推荐

  1. express使用https

    目录 申请免费ssl证书 修改express的./bin/www 申请免费ssl证书 直接去阿里云申请一个免费的ssl,选择neginx下载证书就可以了,解压得到一个xxx.key.xxx.crt文件 ...

  2. 《c# 实现p2p文件分享与传输系统》 一、 模型

    c#实现P2P文件分享与传输系统 一.模型 P2P的概念大家都不陌生,也就是所谓的“点对点传输”,即不直接通过服务器,在两台或多台客户端之间传输数据,实现信息交流和资源共享.P2P技术已经发展了很多年 ...

  3. dataguard类型转换与模式转化

    修改数据保护模式步骤 前提:是否满足转换模式的配置要求 最大保护(Maximum Protection):Standby Database 必须配置Standby Redo Log,Primary D ...

  4. 并查集基础 模板题 hdu1232 畅通工程

    模板题 引入并查集——一则有趣的故事 为了解释并查集的原理,我将举一个更有趣的例子.话说江湖上散落着各式各样的大侠,有上千个之多.他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的 ...

  5. NancyFX 第一章 NancyFX 简介

    Nancy是.NET 平台的微框架.在受到Ruby社区的Sinatra框架启发下,NancyFx框架提供一个.NET平台下的低门槛.易上手的可用于Web开发工具包. 请注意我说的是可用于Web开发,这 ...

  6. 如何在模板类中使用这些point类型?

    博客转载自:http://www.pclcn.org/study/shownews.php?lang=cn&id=271 由于PCL模块较多,并且是一个模板库,在一个源文件里包含很多PCL算法 ...

  7. Regex Golf 练习记录

    正则表达式的练习网站:https://alf.nu/RegexGolf 共17道题:只能说从第10题开始就很变态了,就是看看答案好了 .Warmup: foo .Anchors: k$ 或 ick$ ...

  8. CodeForces - 828C String Reconstruction 并查集(next跳)

    String Reconstruction Ivan had string s consisting of small English letters. However, his friend Jul ...

  9. iOS内购流程一(协议、税务和银行业务)

    协议.税务和银行业务,这一选项是当你App使用了In-app purchaes时候,你跟苹果签订协议的,需要签订合同和填写你的银行收款等信息 一.填写法人信息 1.登录iTunes Store,点击协 ...

  10. Flask RESTful API搭建笔记

    之前半年时间,来到项目的时候,已经有一些东西,大致就是IIS+MYSQL+PHP. 所以接着做,修修补补,Android/iOS与服务器数据库交换用PHP, Web那边则是JS+PHP,也没有前后端之 ...