Spring Data JPA——Specifications動態查詢

一、Specifications動態查詢

有時我們在查詢某個實體的時候,給定的條件是不固定的,這時就需要動態構建相應的查詢語句,在Spring Data JPA中可以通過JpaSpecificationExecutor接口查詢。相比JPQL,其優勢是類型安全,更加的面向對象。
在這裏插入圖片描述
對於JpaSpecificationExecutor,這個接口基本是圍繞着Specification接口來定義的。我們可以簡單的理解爲,Specification構造的就是查詢條件

在這裏插入圖片描述

參數:
root :Root接口,代表查詢的根對象,可以通過root獲取實體中的屬性
query :代表一個頂層查詢對象,用來自定義查詢
cb :用來構建查詢,此對象裏有很多條件方法

1、pom文件和applicationContext.xml文件

    <properties>
        <spring.version>4.2.4.RELEASE</spring.version>
        <hibernate.version>5.0.7.Final</hibernate.version>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
        <c3p0.version>0.9.1.2</c3p0.version>
        <mysql.version>5.1.6</mysql.version>
    </properties>

    <dependencies>
        <!-- junit單元測試 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.9</version>
            <scope>test</scope>
        </dependency>

        <!-- spring beg -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.8</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- spring end -->

        <!-- hibernate beg -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.2.1.Final</version>
        </dependency>
        <!-- hibernate end -->

        <!-- c3p0 beg -->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>${c3p0.version}</version>
        </dependency>
        <!-- c3p0 end -->

        <!-- log end -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <!-- log end -->


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.9.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>

        <!-- el beg 使用spring data jpa 必須引入 -->
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>2.2.4</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.4</version>
        </dependency>
        <!-- el end -->
    </dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/data/jpa
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">


    <!-- Spring 和 Spring data jpa的配置 -->

    <!-- 1. 創建entityManagerFactory對象交給spring容器管理-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置要掃描的實體類包 -->
        <property name="packagesToScan" value="com.zy.domain"/>
        <!-- jpa的實現廠家 -->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>

        <!--JPA的供應商適配器-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!-- 配置是否自動創建數據庫表 -->
                <property name="generateDdl" value="false"/>
                <!-- 指定數據庫類型 -->
                <property name="database" value="MYSQL"/>
                <!-- 數據庫方言 -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
                <!-- 是否顯示sql -->
                <property name="showSql" value="true"/>
            </bean>
        </property>

        <!-- jpa的方言,高級的特性 -->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>

    </bean>

    <!-- 2. 創建數據庫連接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/jpa?useUnicode=true&amp;characterEncoding=UTF-8" />
        <property name="user" value="root" />
        <property name="password" value="1111" />
    </bean>

    <!-- 3. 整合spring data Jpa -->
    <jpa:repositories base-package="com.zy.dao" transaction-manager-ref="transactionManager"
        entity-manager-factory-ref="entityManagerFactory">
    </jpa:repositories>

    <!-- 4. 配置事務管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <!-- 5. 配置聲明式事務(這裏先不配置) -->

    <!-- 6. 配置包掃描 -->
    <context:component-scan base-package="com.zy"/>


</beans>

dao

public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
}

domain

/**
 * 1.實體類和表的映射關係
 *      @Eitity
 *      @Table
 * 2.類中屬性和表中字段的映射關係
 *      @Id
 *      @GeneratedValue
 *      @Column
 */
@Entity
@Table(name = "cst_customer")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="cust_id")
    private Long custId;
    
    @Column(name="cust_address")
    private String custAddress;
    
    @Column(name="cust_industry")
    private String custIndustry;
    
    @Column(name="cust_level")
    private String custLevel;
    
    @Column(name="cust_name")
    private String custName;
    
    @Column(name="cust_phone")
    private String custPhone;
    
    @Column(name="cust_source")
    private String custSource;
}

2、單條件、多條件、模糊、排序、分頁等查詢實現

/**
 * Description:
 *
 * @author zygui
 * @date 2020/5/6 08:40
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpecTest {

    @Autowired
    private CustomerDao customerDao;

    /**
     * 根據條件, 查詢單個對象
     */
    @Test
    public void testSpec () {
        // 匿名內部類
        /**
         * 自定義查詢條件
         *      1.實現Specification接口(提供泛型:查詢的對象類型)
         *      2.實現toPredicate方法(構造查詢條件)
         *      3.需要藉助方法參數中的兩個參數(
         *          root:獲取需要查詢的對象屬性
         *          CriteriaBuilder:構造查詢條件的,內部封裝了很多的查詢條件(模糊匹配,精準匹配)
         *       )
         *  案例:根據客戶名稱查詢,查詢客戶名爲傳智播客的客戶
         *          查詢條件
         *              1.查詢方式
         *                  cb對象
         *              2.比較的屬性名稱
         *                  root對象
         *
         */
        Specification<Customer> spec = new Specification<Customer>() {
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //1. 獲取比較的屬性
                Path<Object> custName = root.get("custName");
                //2.構造查詢條件  :    select * from cst_customer where cust_name = '桂朝陽'
                /**
                 * 第一個參數:需要比較的屬性(path對象)
                 * 第二個參數:當前需要比較的取值
                 */
                Predicate predicate = criteriaBuilder.equal(custName, "桂朝陽");
                return predicate;
            }
        };
        Customer customer = customerDao.findOne(spec);
        System.out.println("customer = " + customer);
    }

    /**
     * 多條件查詢
     *      案例:根據客戶名(cust_name)和客戶所屬行業查詢(xxx)
     *
     */
    @Test
    public void testSpec1() {
        /**
         *  root:獲取屬性
         *      客戶名
         *      所屬行業
         *  cb:構造查詢
         *      1.構造客戶名的精準匹配查詢
         *      2.構造所屬行業的精準匹配查詢
         *      3.將以上兩個查詢聯繫起來
         */
        Specification<Customer> spec = new Specification<Customer>() {
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Path<Object> custName = root.get("custName"); // 客戶名
                Path<Object> custIndustry = root.get("custIndustry"); // 所屬行業

                // 構造查詢
                //1. 構造客戶名的精準匹配查詢
                Predicate p1 = criteriaBuilder.equal(custName, "老虎");
                //2..構造所屬行業的精準匹配查詢
                Predicate p2 = criteriaBuilder.equal(custIndustry, "animal");
                //3. 將多個查詢條件組合起來: 組合; And Or
                Predicate p3 = criteriaBuilder.and(p1, p2); // 以`與`的形式拼接多個查詢條件
                return p3;
            }
        };
        Customer customer = customerDao.findOne(spec);
        System.out.println("customer = " + customer);
    }


    /**
     * 案例:完成根據客戶名稱的模糊匹配,返回客戶列表
     *      客戶名稱以 ’桂‘ 開頭
     *
     * equal :直接的到path對象(屬性),然後進行比較即可
     * gt,lt,ge,le,like : 得到path對象,根據path指定比較的參數類型,再去進行比較
     *      指定參數類型:path.as(類型的字節碼對象)
     */
    @Test
    public void testSpec3() {
        // 構造查詢條件
        Specification<Customer> spec = new Specification<Customer>() {
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //查詢屬性:客戶名
                Path<Object> custName = root.get("custName");
                //查詢方式:模糊匹配
                Predicate predicate = criteriaBuilder.like(custName.as(String.class), "桂%");
                return predicate;
            }
        };
        List<Customer> list = customerDao.findAll(spec);
        for (Customer customer : list) {
            System.out.println("customer = " + customer);
        }
    }

    @Test
    public void testSpec10() {
        // 構造查詢條件
        Specification<Customer> spec = new Specification<Customer>() {
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //查詢屬性:客戶名
                Path<Object> custName = root.get("custName");
                //查詢方式:模糊匹配
                Predicate predicate = criteriaBuilder.like(custName.as(String.class), "桂%");
                return predicate;
            }
        };
        // 添加排序
        // 創建排序對象, 需要調用構造方法實例化sort對象
        //第一個參數:排序的順序(倒序,正序)
        //   Sort.Direction.DESC:倒序
        //   Sort.Direction.ASC : 升序
        //第二個參數:排序的屬性名稱
        Sort sort = new Sort(Sort.Direction.DESC, "custId");
        List<Customer> list = customerDao.findAll(spec, sort);
        for (Customer customer : list) {
            System.out.println("customer = " + customer);
        }
    }

    /**
     * 分頁查詢
     *      Specification: 查詢條件
     *      Pageable:分頁參數
     *          分頁參數:查詢的頁碼,每頁查詢的條數
     *          findAll(Specification,Pageable):帶有條件的分頁
     *          findAll(Pageable):沒有條件的分頁
     *  返回:Page(springDataJpa爲我們封裝好的pageBean對象,數據列表,共條數)
     */
    @Test
    public void testSpec4() {
        Specification<Customer> spec = null;
        //PageRequest對象是Pageable接口的實現類
        /**
         * 創建PageRequest的過程中,需要調用他的構造方法傳入兩個參數
         *      第一個參數:當前查詢的頁數(從0開始)
         *      第二個參數:每頁查詢的數量
         */
        Pageable pageable = new PageRequest(0, 2);
        // 分頁查詢
        Page<Customer> page = customerDao.findAll(null, pageable);
        System.out.println(page.getContent()); //得到數據集合列表
        System.out.println(page.getTotalElements());//得到總條數
        System.out.println(page.getTotalPages());//得到總頁數
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章