博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Javaweb——Spring Boot 系列(11)自定义 Repository访问数据库
阅读量:3933 次
发布时间:2019-05-23

本文共 10281 字,大约阅读时间需要 34 分钟。

自定义Repository访问数据库

  • JPA 除了可以直接调用相关接口来访问和操作数据库,还支持通过继承接口自定义 Repository 实现访问数据库,自定义实现主要工作为自定义编辑一个 Specification,一个继承 JpaRepository 的 Interface 以及相应的实现,定义一个 repositoryFactoryBean。

1、Specification 自定义

  • 在项目目录下新建一个 specs 文件夹,新建一个 CustomSpecs 类,编辑如下代码:
    package com.pyc.springjpa.specs;import static com.google.common.collect.Iterables.toArray;import org.springframework.data.jpa.domain.Specification;import org.springframework.util.ReflectionUtils;import org.springframework.util.StringUtils;import javax.persistence.EntityManager;import javax.persistence.criteria.CriteriaBuilder;import javax.persistence.criteria.CriteriaQuery;import javax.persistence.criteria.Predicate;import javax.persistence.criteria.Root;import javax.persistence.metamodel.Attribute;import javax.persistence.metamodel.EntityType;import javax.persistence.metamodel.SingularAttribute;import java.lang.reflect.Field;import java.util.ArrayList;import java.util.List;public class CustomSpecs {
    // Define a method that returns a Specification and named byAuto, use generic as return type of this method // so that this method can affect any entity class,the parameter of this method is entityManager // and a query condition with current parameter value. // 定义一个返回值为 Specification 名称为byAuto的方法,使用泛型 T,表明这个Specification 可用于 // 任何实体类;参数为 entityManager 和包含当前值的查询条件 public static
    Specification
    byAuto(final EntityManager entityManager, final T example) {
    //获得当前实体类对象的类型 final Class
    type = (Class
    ) example.getClass(); return new Specification
    () {
    @Override public Predicate toPredicate(Root
    root, CriteriaQuery
    criteriaQuery, CriteriaBuilder criteriaBuilder) { // create a Predicate list to stores the constructed query conditions List
    predicates = new ArrayList<>(); //新建 Predicate 列表存储构造的查询条件 // get the type of entity so,and then can get the attributes of entity by using entity type //获得实体类的 EntityType,从而获得实体类的属性 EntityType
    entity = entityManager.getMetamodel().entity(type); // Doing loop on all attributes of entity // 对实体类的所有属性做循环 for (Attribute
    attr : entity.getDeclaredAttributes()) { // get the value of an attribute of entity // 获得实体类对象某一个属性的值 Object attrValue = getValue(example, attr); if (attrValue != null) { // if the value type of current attribute is character type //当前属性值为字符类型的时候 if (attr.getJavaType() == String.class) { // if current character value not null // 当前字符不为空 if (!StringUtils.isEmpty(attrValue)) { // construct a query condition of current attribute by using 'like' expression // and add to condition list // 构造当前属性 like(前后%)属性值查询条件,并添加到条件列表 predicates.add(criteriaBuilder.like(root.get(attribute(entity, attr.getName(), String.class)), pattern((String) attrValue))); } } else { // other happening,construct a query condition of current attribute by using 'equal' expressing // 其他条件,构造属性和属性值 equal 查询条件并添加到条件列表 predicates.add(criteriaBuilder.equal(root.get(attribute(entity, attr.getName(), attrValue.getClass())), attrValue)); } } } // Transform the query condition list to Predicate //将条件列表转换成 Predicate return predicates.isEmpty() ? criteriaBuilder .conjunction() : criteriaBuilder.and(toArray(predicates, Predicate.class)); } private
    Object getValue(T example, Attribute
    attr) { // get the attribute value of the corresponding attribute of entity object by reflect function // 通过反射获得实体类对象对应属性的属性值 return ReflectionUtils.getField((Field) attr.getJavaMember(), example); } // Get the SingularAttribute of the current attribute of the entity class, //the SingularAttribute contains a single attribute of the entity class // 获得实体类的当前属性的 SingularAttribute,SingularAttribute 包含实体类某个单独属性 private
    SingularAttribute
    attribute(EntityType
    entity, String fieldName, Class
    fieldClass) { return entity.getDeclaredSingularAttribute(fieldName, fieldClass); } }; } // 构造 like 查询模式 static private String pattern(String str) { return "%" + str + "%"; }}

2、自定义接口

  • 新建一个 support 文件夹,用于存放虚接口类,代码如下:
    package com.pyc.springjpa.support;import org.springframework.data.domain.Page;import org.springframework.data.domain.Pageable;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.JpaSpecificationExecutor;import org.springframework.data.repository.NoRepositoryBean;import java.io.Serializable;@NoRepositoryBeanpublic interface CustomRepository
    extends JpaRepository
    ,JpaSpecificationExecutor
    {
    Page
    findByAuto(T example,Pageable pageable);}
  • 继承 J怕Repository,从而继承我们需要的方法,而继承 JpaSpecificationExecutor 则可以是该接口的实现类有使用 Specification 的能力。

3、接口实现类

  • 新建一个类 CustomRepositoryImpl 用于实现刚刚新建的接口 CustomRepository,代码如下:
    package com.pyc.springjpa.support;import java.io.Serializable;import javax.persistence.EntityManager;import org.springframework.data.domain.Page;import org.springframework.data.domain.Pageable;import org.springframework.data.jpa.repository.support.SimpleJpaRepository;import static com.pyc.springjpa.specs.CustomSpecs.*;public class CustomRepositoryImpl 
    extends SimpleJpaRepository
    implements CustomRepository
    {
    private final EntityManager entityManager; public CustomRepositoryImpl(Class
    domainClass, EntityManager entityManager) {
    super(domainClass, entityManager); this.entityManager = entityManager; } public Page
    findByAuto(T example, Pageable pageable){
    return findAll(byAuto(entityManager,example),pageable); }}
  • 继承自定义接口的同时,也继承了 SimpleJpaRepository 中的方法。

4、定义 RepositoryFactoryBean

  • 代码如下:
    package com.pyc.springjpa.support;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;import org.springframework.data.jpa.repository.support.SimpleJpaRepository;import org.springframework.data.repository.core.RepositoryInformation;import org.springframework.data.repository.core.RepositoryMetadata;import org.springframework.data.repository.core.support.RepositoryFactorySupport;import javax.persistence.EntityManager;import java.io.Serializable;public class CustomRepositoryFactoryBean
    ,S,ID extends Serializable>extends JpaRepositoryFactoryBean
    {
    // 重写 createRepositoryFactory // rewrite method createRepositoryFactory @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
    return new CustomRepositoryFactory(entityManager); } // create inner class CustomRepository and extend JpaRepositoryFactory private static class CustomRepositoryFactory extends JpaRepositoryFactory {
    public CustomRepositoryFactory(EntityManager entityManager) {
    super(entityManager); } // rewrite method getTargetRepository and using CustomRepository @Override @SuppressWarnings({
    "unchecked"}) protected
    SimpleJpaRepository
    getTargetRepository( RepositoryInformation information, EntityManager entityManager) {
    return new CustomRepositoryImpl
    ((Class
    ) information.getDomainType(), entityManager); } @Override protected Class
    getRepositoryBaseClass(RepositoryMetadata metadata) {
    return CustomRepositoryImpl.class; } }}
  • 以上三个类文件位于同个目录下。

5、修改 PersonRepository

  • 修改上一篇博文所使用的 PersonRepository,将其继承的接口从 JpaRepository 改为自定义的 CustomRepository 接口,代码如下:
    package com.pyc.springjpa.dao;import com.pyc.springjpa.domain.Person;import com.pyc.springjpa.support.CustomRepository;import org.springframework.data.repository.query.Param;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.Query;import java.util.List;public interface PersonRepository extends CustomRepository
    {
    // 根据地址查询,返回值为列表 List
    findByAddress(String address); // 根据姓名和地址查询,返回值为单个对象 Person findByNameAndAddress(String name, String address); // 使用 @Query 查询,参数按照名称绑定 @Query("select p from Person p where p.name= :name and p.address= :address") Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address); //使用 @NamedQuery 查询,在实体类中已注解 Person withNameAndAddressNamedQuery(String name, String address);}

6、添加 URL

  • 在上一篇中的 DataController 中增加一个 @RequestMapping 注解的方法,代码如下:
    @RequestMapping("/auto")public Page
    auto(Person person){
    Page
    pagePeople; pagePeople = personRepository.findByAuto(person,new PageRequest(0,10)); return pagePeople;}

7、配置入口类

  • 在入口类上增加一个 @EnableRepositories 注解,以及 @Autowired PersonRepository,代码如下:
    package com.pyc.springjpa;import com.pyc.springjpa.dao.PersonRepository;import com.pyc.springjpa.support.CustomRepositoryFactoryBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.data.jpa.repository.config.EnableJpaRepositories;@SpringBootApplication@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)public class SpringjpaApplication {
    @Autowired PersonRepository personRepository; public static void main(String[] args) {
    SpringApplication.run(SpringjpaApplication.class, args); }}

8、运行测试

  • 运行项目,打开浏览器,首先访问 localhost:8080/auto,无构造查询条件,查询所有的数据记录,浏览器获得后台返回的数据,如下:
    在这里插入图片描述
  • 再测试 localhost:8080/auto?address=海,构造 address 的 like 查询,即如下查询语句:
    select * from PERSON where address like '%海%';
  • 查询结果如下:
    在这里插入图片描述
  • 使用 URL=localhost:8080/auto?address=海&name=y&age=22,该 url 等价于如下的 SQL 语句,
    select * from PERSON where address like '%海%' and name like '%y%' and age=22
  • 返回结果如下:
    在这里插入图片描述
  • 在测试的时候,一开始忘记给入口类增加新注解 @EnableJpaRepository 导致无法执行,一度以为自定义接口写错了,改了好几遍才发现是入口类忘记配置了,这说明配置的重要性,该用的注解一个都不能少了。

转载地址:http://wlqgn.baihongyu.com/

你可能感兴趣的文章
CMake Useful Variables/Logging Useful Variables
查看>>
使用cmake建立工程链接OPENNI2
查看>>
ubuntu下解决csdn网页打不开的问题
查看>>
uninstall software on ubuntu
查看>>
install kinnect senor on ubuntu
查看>>
calibrate kinnect v1 on ubuntu
查看>>
flann中关于数据的stride
查看>>
cv::Mat ptr 和 at 注意事项
查看>>
cuda更新过后, findcuda找不到怎么办?
查看>>
cast shared_ptr to shared_ptr
查看>>
Elastic Job入门示例-实现DataflowJob接口
查看>>
Elastic Job入门示例-Console控制台
查看>>
Elastic Job入门示例-实现原理介绍
查看>>
HTTP状态码对照表
查看>>
Spring Cloud Feign 服务间调用 -超时
查看>>
MySQL 中事务、事务隔离级别详解
查看>>
Telnet 命令在Windows与Linux/Unix下的区别
查看>>
Java传统IO / NIO基础知识
查看>>
Netty3- 入门示例
查看>>
Netty3 - 多连接的客户端示例
查看>>