spring boot 自定義repository

spring boot 提供的默認repository 適合大多場景,對於一些特殊場景,須要特殊的方法,除了使用@NAMEQUERY @QUERY 自定義sql語句外,還能夠自定義基礎repositoryjava

這裏記錄一些踩到的坑或者說遇到的須要注意的地方spring

 

一、自定義查詢條件,使用specification自定義查詢條件sql

注意的地方root 獲取屬性,criteriaBuilder自定義條件app

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+"%";
    }

}

二、自定義領repository(1-5步弄完後,很重要的一步最容易忘記的一步是第六步,注意,不然啓動項目會報錯,諸如找不到 實體type,not suitable 缺乏構造函數等等錯誤)dom

1)、定義repository接口類,添加自定義接口ide

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 工廠beanui

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繼承關係this

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後可直接使用google

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

    }
}
相關文章
相關標籤/搜索