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));
            }
        };

    }
}

 

posted @ 2019-08-28 22:52  李鹏飞ONLINE  阅读(1983)  评论(1编辑  收藏  举报