zoukankan      html  css  js  c++  java
  • spring data jpa自定义baseRepository

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

    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
  • 相关阅读:
    CSS3 -- 透明色(rgba)
    CSS3 -- 多背景(backgrounds)
    CSS3 -- 背景原点(background-origin)
    CSS3 -- 背景裁剪(background-clip)
    CSS3 -- 背景尺寸(background-size)
    CSS3 -- 图片边框(border-image)
    CSS3 -- 边框颜色(border-color)
    CSS3 -- 盒模型(box-sizing)
    CSS -- 字体单位(px、em、rem)
    课程总结
  • 原文地址:https://www.cnblogs.com/wangjing666/p/7374846.html
Copyright © 2011-2022 走看看