zoukankan      html  css  js  c++  java
  • spring boot 自定义repository

    spring boot 提供的默认repository 适合大多场景,对于一些特殊场景,需要特殊的方法,除了使用@NAMEQUERY @QUERY 自定义sql语句外,还可以自定义基础repository

    这里记录一些踩到的坑或者说遇到的需要注意的地方

    1、自定义查询条件,使用specification自定义查询条件

    注意的地方root 获取属性,criteriaBuilder自定义条件

    package com.duoke.demo.bean;
    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 CustomerSpecs {
    
        /**
         * 自定义查询
         * @param manager
         * @param example
         * @param <T>
         * @return
         */
        public static <T>Specification<T> byAuto(final EntityManager manager,final T example){
    
            // 获取泛型类别
            final Class<T> type = (Class<T>) example.getClass();
    
            return new Specification<T>() {
                @Override
                public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                    List<Predicate> predicates = new ArrayList<>();
    
                    // 获取实体类的entitytype
                    EntityType<T> entity = manager.getMetamodel().entity(type);
    
                    // 对实体类的所有属性循环
                    for (Attribute<T,?> attr:entity.getDeclaredAttributes()) {
                        Object attrValue = getValue(example,attr);
                        if(attrValue !=null){
                            if(attr.getJavaType() == String.class){
                                if(!StringUtils.isEmpty(attrValue)){
                                    predicates.add(criteriaBuilder.like(root.get(attribute(entity,attr.getName(),String.class)),pattern((String) attrValue)));
                                }
                            }else{
                                predicates.add(criteriaBuilder.equal(root.get(attribute(entity,attr.getName(),attrValue.getClass())),attrValue));
                            }
                        }
                    }
                    return predicates.isEmpty() ? criteriaBuilder.conjunction() : criteriaBuilder.and(toArray(predicates,Predicate.class));
                }
    
    
                /**
                 * 获取field属性
                 * @param example
                 * @param attr
                 * @param <T>
                 * @return
                 */
                private  <T>Object getValue(T example,Attribute<T,?> attr){
                    return ReflectionUtils.getField((Field) attr.getJavaMember(),example);
                }
    
                /**
                 * 获取实体类的当前属性
                 * @param entity
                 * @param fieldName
                 * @param fieldClass
                 * @param <T>
                 * @param <E>
                 * @return
                 */
                private <T,E> SingularAttribute<T,E> attribute(EntityType<T>entity,String fieldName,Class<E> fieldClass){
                    return entity.getDeclaredSingularAttribute(fieldName,fieldClass);
                }
            };
    
        }
    
        static private  String pattern(String str){
            return "%"+str+"%";
        }
    
    }

    2、自定义领repository(1-5步弄完后,很重要的一步最容易忘记的一步是第六步,注意,否则启动项目会报错,诸如找不到 实体type,not suitable 缺少构造函数等等错误)

    1)、定义repository接口类,添加自定义接口

    package com.duoke.demo.service;
    
    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;
    
    //指明当前接口不是领域类模型(service)
    @NoRepositoryBean
    //继承JPARES 具有基础的方法比如findALL
    //继承spec 具有使用自定义条件的基础
    public interface CustomRepo<T,ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
        Page<T> findByAuto(T example, Pageable pageable);
    }

    2).定义实现类

    package com.duoke.demo.service;
    
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
    
    import javax.persistence.EntityManager;
    import java.io.Serializable;
    
    import static com.duoke.demo.bean.CustomerSpecs.*;
    
    public class CustomRepoImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements CustomRepo<T,ID>{
        private EntityManager entityManager;
    
        public CustomRepoImpl(Class<T> domainClass, EntityManager entityManager) {
            super(domainClass, entityManager);
            this.entityManager = entityManager;
        }
    
        @Override
        public Page<T> findByAuto(T example, Pageable pageable) {
            return findAll(byAuto(entityManager,example),pageable);
        }
    }

    3)定义repository 工厂bean

    package com.duoke.demo.service;
    
    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.JpaRepositoryImplementation;
    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;
    
    /**
     * 自定义jpa 工厂bean
     * @param <T>
     * @param <S>
     * @param <ID>
     */
    public class CustomRepoFactoryBean<T extends JpaRepository<S, ID>,S,ID extends Serializable> extends JpaRepositoryFactoryBean<T,S,ID> {
        /**
         * Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface.
         *
         * @param repositoryInterface must not be {@literal null}.
         */
        public CustomRepoFactoryBean(Class<? extends T> repositoryInterface) {
            super(repositoryInterface);
        }
    
        // 工厂生产repo 方法
        @Override
        protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
            return new CustomRepoFactory(entityManager);
        }
    
        private class CustomRepoFactory extends JpaRepositoryFactory{
    
            private final EntityManager entityManager;
            /**
             * Creates a new {@link JpaRepositoryFactory}.
             *
             * @param entityManager must not be {@literal null}
             */
            public CustomRepoFactory(EntityManager entityManager) {
                super(entityManager);
                this.entityManager = entityManager;
            }
    
            @Override
            // 重写获取实现类
            protected JpaRepositoryImplementation<?, ?> getTargetRepository(RepositoryInformation information, EntityManager entityManager) {
                return  new CustomRepoImpl<T,ID>((Class<T>) information.getDomainType(),entityManager);
            }
    
            @Override
            protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
                return CustomRepoImpl.class;
            }
        }
    }

    4)改变之前的res继承关系

    package com.duoke.demo.service;
    
    import com.duoke.demo.bean.Person;
    import com.sun.xml.internal.bind.v2.model.core.ID;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.query.Param;
    
    import java.util.List;
    
    /**
     * JPA 数据访问接口
     */
    public interface IPersonRepository  extends CustomRepo<Person,String> {
        // 定义访问接口
    //    List<Person> findByName(String name);
    
    //    List<Person> findByAge(Integer age);
    
    //
    //    List<Person> findByNameLike(String name);
    //
    //    List<Person> findByNameAndAddress(String name,String address);
    //
    //    @Query("select p from Person p where p.address = :address and p.name = :name")
    //    List<Person> withNameAndAddress(@Param("address") String address,@Param("name") String name);
    //
    //    @Modifying
    //    @Transactional
    //    @Query("update person set name = ?1")
    //    int setName(String name);
    
    }

    5)controller 注入repository后可直接使用

    @RequestMapping("custom")
        public List<Person> findByCustom(String name){
            Person person = new Person();
            person.setName(name);
            Page<Person> peoples = iPersonRepository.findByAuto(person,buildPage(1,2));
            return peoples.getContent();
        }

    6)* 启动类指定加载的bean工厂类

    @SpringBootApplication(scanBasePackages = "com.duoke")
    @EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepoFactoryBean.class)
    public class DemoApplication {
    //    项目启动main方法
        public static void main(String[] args) {
    //        关闭banner
            SpringApplication app = new SpringApplication(DemoApplication.class);
            app.setBannerMode(Banner.Mode.OFF);
            app.run();
    //        SpringApplication.run(DemoApplication.class, args);
        }
    }

    另一种查询条件的方式使用jdk 提供的原生反射接口获取值

    package com.duoke.demo.bean;
    
    import org.springframework.data.jpa.domain.Specification;
    
    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.EntityType;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;
    
    public class PersonSpec {
    
        public static<T> Specification<T> findByEquealAddress(T example){
            // 获取实例类
            Class<T> type = (Class<T>) example.getClass();
    
            return new Specification<T>() {
                @Override
                public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                    List<Predicate> predicates = new ArrayList<>();for (Field field:type.getDeclaredFields()) {
                        field.setAccessible(true);
                        try {
                            System.out.println(field.get(example));
                            Object value = field.get(example);
                            if(value!=null){
                                if (value.getClass() == String.class){
                                    predicates.add(criteriaBuilder.equal(root.get(field.getName()),value));
                                }
                            }
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                    Predicate predicate[] = new Predicate[predicates.size()];
    
                    return criteriaBuilder.and(predicates.toArray(predicate));
                }
            };
    
        }
    }
  • 相关阅读:
    Android自定义控件 -Canvas绘制折线图(实现动态报表效果)
    Android事件分发机制详解(2)----分析ViewGruop的事件分发
    Android事件分发机制详解(1)----探究View的事件分发
    Java单例模式(转载)
    排序算法练习--JAVA(:内部排序:插入、选择、冒泡、快速排序)
    javaIO-学习笔记
    java多线程基础
    java二分查找举例讨论
    tomcat安装部署完整过程
    mariadb安装部署完整过程
  • 原文地址:https://www.cnblogs.com/jony-it/p/11427051.html
Copyright © 2011-2022 走看看