在一些特殊时候,我们会设计到对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. python爬虫知识点总结(六)BeautifulSoup库详解

    官方学习文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/ 一.什么时BeautifulSoup? 答:灵活又方便的网页解析库,处 ...

  2. POJ2253(djkstra求最长最短边)

    Frogger Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 32257   Accepted: 10396 Descrip ...

  3. jQuery中attr()与prop()区别介绍

    .attr() : 获取匹配的元素集合中的第一个元素的属性的值 或 设置每一个匹配元素的一个或多个属性. •.attr( attributeName ) •.attr( attributeName ) ...

  4. java&nbsp;POP3

    package com.skyzoo.Jutil; import java.io.BufferedReader; import java.io.IOException; import java.io. ...

  5. Hadoop中Partition解析

    1.解析Partition Map的结果,会通过partition分发到Reducer上,Reducer做完Reduce操作后,通过OutputFormat,进行输出,下面我们就来分析参与这个过程的类 ...

  6. day8 服务器

    XML约束 XML约束要求:大家能够看懂约束内容,根据约束内容写出符合规则的xml文件. 2.1 引入 XML语法: 规范的xml文件的基本编写规则.(由w3c组织制定的) XML约束: 规范XML文 ...

  7. 如何在VS2015中使用Git命令提示符

    本文转载自 http://qkxue.net/info/176223/Visual-Studio-Git VS2015自带了Git插件,但有时候觉得Git控制台命令更方便些.VS中本身不能把Git B ...

  8. “MVC+Nhibernate+Jquery-EasyUI”信息发布系统 第一篇

    一.第一篇内容中必须得简单的介绍一下MVC(在大学时用的是WebForm,拖控件感觉很爽,但是工作后,技术总监让一定要放弃这种 想法!) 1.MVC是什么?:(1) ASP.NET MVC是微软官方提 ...

  9. HDU - 5534 Partial Tree(每种都装的完全背包)

    Partial Tree In mathematics, and more specifically in graph theory, a tree is an undirected graph in ...

  10. nginx是如何处理一个请求的(包含https配置)

    配置https首先要有ssl证书,这个证书目前阿里有免费的,但如果自己做实验,也是可以自签证书,只不过不受信 openssl genrsa -des3 -out server.key 1024     ...