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)); } }; } }
成灰之前,抓紧时间做点事!!