Spring Data JPA项目实战(下)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chengqiuming/article/details/82558218

一 项目说明

结合Specification和自定义Repository实现来定制一个自动模糊查询。即对于任意实体类型进行查询,对象里有几个值就查几个值,当值为字符型时,就通过like查询,其余的类型使用自动等于查询,没有值就查询全部。

二 实战

1 定义Specification

package com.wisely.specs;

import static com.google.common.collect.Iterables.toArray;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

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 org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

public class CustomerSpecs {
    //定义一个返回值为Specification的方法byAuto,这里使用的是泛型T,所以这个Specification是可以用于任意的实体类
    //它接受的参数是entityManager和当前包含值作为查询条件的实体类对象
    public static <T> Specification<T> byAuto(final EntityManager entityManager, 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 cb) {
                List<Predicate> predicates = new ArrayList<>(); //新建Predicate列表存储构造的查询条件
                //获得实体类的EntityType,可以从中获得实体类的属性
                EntityType<T> entity = entityManager.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(cb.like(root.get(attribute(entity, attr.getName(), String.class)),
                                        pattern((String) attrValue))); //构造当前属性like属性值查询条件,并添加到条件列表中
                            }
                        } else {
                            predicates.add(cb.equal(root.get(attribute(entity, attr.getName(), attrValue.getClass())),
                                    attrValue)); //构造属性和属性值equal查询条件,并添加到条件列表中
                        }
                    }

                }
                //将条件列表转换成Predicate
                return predicates.isEmpty() ? cb.conjunction() : cb.and(toArray(predicates, Predicate.class));
            }

            /**
             * 通过反射获得实体对象对应属性的属性值
             */
            private <T> Object getValue(T example, Attribute<T, ?> attr) {
                return ReflectionUtils.getField((Field) attr.getJavaMember(), example);
            }
            
            /**
             * 获得实体类的当前属性的SingularAttribute,SingularAttribute包含的是实体类的某个单独属性。
             */
            private <E, T> SingularAttribute<T, E> attribute(EntityType<T> entity, String fieldName,
                    Class<E> fieldClass) {
                return entity.getDeclaredSingularAttribute(fieldName, fieldClass);
            }

        };

    }
    
    /**
     * 构造like查询条件模式,即前后添加%
     */
    static private String pattern(String str) {
        return "%" + str + "%";
    }

}

2 定义接口

//此例中接口继承了JpaRepository,具备了JpaRepository所提供的方法,继承了JpaSpecificationExecutor,让我们具备使用Specification的能力。
package com.wisely.support;

import java.io.Serializable;

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;

@NoRepositoryBean
public interface CustomRepository<T, ID extends Serializable>extends JpaRepository<T, ID> ,JpaSpecificationExecutor<T>{
    
    Page<T> findByAuto(T example,Pageable pageable);
    

}

3 定义CustomRepositoryImpl实现

package com.wisely.support;

import java.io.Serializable;

import javax.persistence.EntityManager;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import static com.wisely.specs.CustomerSpecs.*;
//此类继承JpaRepository的实现类SimpleJpaRepository,让我们可以使用SimpleJpaRepository的方法
//此类当然还要实现我们自定义的接口CustomRepository
public class CustomRepositoryImpl <T, ID extends Serializable>
                    extends SimpleJpaRepository<T, ID>  implements CustomRepository<T,ID> {
    
    private final EntityManager entityManager;
    
    public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
        super(domainClass, entityManager);
        this.entityManager = entityManager;
    }

    //findByAuto方法使用byAuto Specification构造的条件查询,并提供分页功能
    @Override
    public Page<T> findByAuto(T example, Pageable pageable) {
        return findAll(byAuto(entityManager, example),pageable);
    }


}

4 定义repositoryFactoryBean

package com.wisely.support;

import java.io.Serializable;

import javax.persistence.EntityManager;

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.SimpleJpaRepository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
//自定义CustomRepositoryFactoryBean,继承JpaRepositoryFactoryBean
public class CustomRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable>
        extends JpaRepositoryFactoryBean<T, S, ID> {

    @Override
    //重写createRepositoryFactory方法,用当前的CustomRepositoryFactory创建实例
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new CustomRepositoryFactory(entityManager);  //该类见下面的定义
    }
     //定义一个私有的静态类,并继承JpaRepositoryFactory
    private static class CustomRepositoryFactory extends JpaRepositoryFactory {

        //构造函数
        public CustomRepositoryFactory(EntityManager entityManager) {
            super(entityManager);
        }

        @Override
        @SuppressWarnings({"unchecked"})
        protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(
                RepositoryInformation information, EntityManager entityManager) {// 获得当前自定义类的实现
            return new CustomRepositoryImpl<T, ID>((Class<T>) information.getDomainType(), entityManager);

        }

        @Override
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {// 获得当前自定义类的类型
            return CustomRepositoryImpl.class;
        }
    }
}

5 定义PersonRepository

package com.wisely.dao;

import java.util.List;

import com.wisely.support.CustomRepository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.wisely.domain.Person;
//只需PersonRepository继承我们自定义的Repository接口,即可使我们在自定义PersonRepository实现功能
public interface PersonRepository extends CustomRepository<Person, Long> {
    List<Person> findByAddress(String address);   //使用方法名查询,接收一个name参数,返回值为列表
    
    Person findByNameAndAddress(String name,String address); //使用方法名查询,接受name和address,返回值为单个对象
    
    //使用@Query查询,参数按照名称绑定
    @Query("select p from Person p where p.name= :name and p.address= :address")
    Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);
    //使用@NamedQuery查询,该查询在实体类中有定义
    Person withNameAndAddressNamedQuery(String name,String address);

}

6 控制器

package com.wisely.web;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.wisely.dao.PersonRepository;
import com.wisely.domain.Person;

@RestController
public class DataController {
    // Spring Data JPA已自动为你注册bean,所以可自动注入
    @Autowired
    PersonRepository personRepository;
    
    //控制器接受一个Person对象,当Person的name有值时,会自动对name进行like查询;当age有值时,会进行等于查询
    //当Person中有多个值不为空时,会自动构造多个查询条件;当Person所有值为空时,默认查询出所有记录    
    @RequestMapping("/auto")
    public Page<Person> auto(Person person){

        Page<Person> pagePeople = personRepository.findByAuto(person, new PageRequest(0, 10));

        return pagePeople;

    }
    
}

7 主类

package com.wisely;

import com.wisely.domain.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

import com.wisely.dao.PersonRepository;
import com.wisely.support.CustomRepositoryFactoryBean;
import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManagerFactory;
import java.util.List;


@SpringBootApplication
//该注解指定repositoryFactoryBeanClass,并让自定义的Repsitory实现生效。
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class Ch82Application {
    @Autowired
    PersonRepository personRepository;
    
    
    public static void main(String[] args) {
        SpringApplication.run(Ch82Application.class, args);

    }

}

三 运行

1 输入http://localhost:8080/auto

2 输入http://localhost:8080/auto?address=肥,构造address的like查询

3 输入http://localhost:8080/auto?address=肥&name=云&age=32,构造address的like查询,name的like查询以及age的equal查询

猜你喜欢

转载自blog.csdn.net/chengqiuming/article/details/82558218