Spring Data JPA-基礎篇(二)

前置文章:

Spring Data JPA-基礎篇(一)

前言:

前置文章中我們已經介紹了基礎JPA的使用方式,JPA是操作數據庫的一種ORM規范,而文章中使用的Hibernate是其具體的實現。
本文則是使用Spring Data JPA來進行具體的CRUD操作。

零、本文綱要

一、基礎準備

  1. pom.xml配置
  2. applicationContext.xml
  3. Customer實體類
  4. CustomerDao接口

二、基礎CRUD

  1. 測試準備
  2. 測試查詢
  3. 測試保存
  4. 測試刪除
  5. 測試查詢所有
  6. 測試聚合查詢
  7. 判斷是否存在

三、JPQL形式查詢

  1. 條件查詢
  2. 多條件查詢
  3. 更新操作

四、SQL形式查詢

  1. 查詢全部
  2. 模糊匹配

五、方法命名規則查詢

  1. 條件查詢
  2. 模糊匹配
  3. 多條件查詢

一、基礎準備

1. pom.xml配置

① 屬性控制
    <!--屬性控制-->
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring.version>5.1.20.RELEASE</spring.version>
        <hibernate.version>5.0.7.Final</hibernate.version>
        <javax.el.version>2.2.4</javax.el.version>
        <slf4j.version>1.6.6</slf4j.version>
    </properties>
② 依賴管理

此處依賴部分并沒有過多說明,因為現在大家使用的Spring Boot、Spring Cloud都在淡化我們依賴配置的內容,更關注代碼開發本身。

    <!--依賴管理-->
    <dependencies>
        <!--Junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--Spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</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-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.0</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>${spring.version}</version>
        </dependency>
        <!--el-->
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>${javax.el.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>${javax.el.version}</version>
        </dependency>
        <!--hibernate-->
        <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>
        <!--MySQL-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <!--c3p0數據庫連接池-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <!--log-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</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>
    </dependencies>

2. applicationContext.xml

① 基礎約束準備

引入了beans、aop、context、jdbc、tx、jpa這些命名空間。

<?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"
       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">

</beans>
② 數據源配置
    <!-- 1. 創建數據庫連接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql:///test"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>
③ 創建entityManagerFactory對象
    <!-- 2. 創建entityManagerFactory對象 -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!-- ① 配置數據源 -->
        <property name="dataSource" ref="dataSource"/>

        <!-- ② 指定實體類所在的包 -->
        <property name="packagesToScan" value="com.stone.domain"/>

        <!-- ③ 配置JPA的實現廠商(供應商) -->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>

        <!-- ④ JPA的供應商適配器 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!-- a、配置是否自動創建數據庫表 -->
                <property name="generateDdl" value="false"/>
                <!-- b、指定數據庫類型 -->
                <property name="database" value="MYSQL"/>
                <!-- c、數據庫方言:支持的特有語法,MySQL分頁limit,Oracle分頁ROWNUM -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
                <!-- d、是否顯示SQL語句 -->
                <property name="showSql" value="true"/>
            </bean>
        </property>

        <!--⑤ JPA方言:高級特性-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
    </bean>
④ 配置事務管理器
    <!-- 3. 配置事務管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
⑤ 整合Spring Data JPA
    <!-- 4. 整合Spring Data JPA -->
    <jpa:repositories base-package="com.stone.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory"/>
⑥ 聲明式事務

此處非必要

    <!-- txAdvice -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save" propagation="REQUIRED"/>
            <tx:method name="insert" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!-- aop -->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.stone.service.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>
⑦ 配置包掃描
    <context:component-scan base-package="com.stone"/>
⑧ 整合其他配置文件

此處暫時沒有。

3. Customer實體類

/**
 * 客戶實體類
 * → 配置映射關系
 *      1. 實體類和表的映射關系;
 *      2. 實體類中屬性和表中字段的映射關系;
 * → 實體類和表的映射關系
 *      ① @Entity:聲明實體類;
 *      ② @Table:配置實體類和表的映射關系;
 *          → name:配置數據庫表名稱;
 * → 實體類中屬性和表中字段的映射關系
 *      ① @Id:聲明主鍵的配置;
 *      ② @GeneratedValue:配置主鍵生成策略;
 *          → strategy:主鍵策略
 *              a、 GenerationType.IDENTITY  自增,MySQL數據庫;
 *                  底層數據庫必須支持自動增長,采用數據庫自增方式對ID進行自增。
 *              b、 GenerationType.SEQUENCE  序列,Oracle數據庫;
 *                  底層數據庫支持序列。
 *              c、 GenerationType.TABLE
 *                  JPA提供的一種機制,通過一張數據庫表的形式幫助完成主鍵自增。
 *              d、 GenerationType.AUTO
 *                  由程序自動選擇主鍵生成策略。
 *      ③ Column:實體類屬性與表字段映射
 *          → name:數據庫表字段名稱
 */
@Table(name = "cst_customer")
@Entity
public class Customer {

    /*客戶主鍵*/
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id")
    private Long custId;

    /*客戶名稱*/
    @Column(name = "cust_name")
    private String custName;

    /*客戶資源來源*/
    @Column(name = "cust_source")
    private String custSource;

    /*客戶級別*/
    @Column(name = "cust_level")
    private String custLevel;

    /*客戶所屬行業*/
    @Column(name = "cust_industry")
    private String custIndustry;

    /*客戶的聯系方式*/
    @Column(name = "cust_phonoe")
    private String custPhone;

    /*客戶地址*/
    @Column(name = "cust_address")
    private String custAddress;

    ...

}

4. CustomerDao接口

NOTE
JpaRepository<T, ID>,該接口封裝了基礎CRUD操作:
→ T:操作的實體類類型;
→ ID:實體類中主鍵屬性的類型。

JpaSpecificationExecutor<T>,該接口封裝了復雜查詢(分頁):
→ T:操作的實體類類型。

/**
 * 符合SpringDataJpa規范的接口
 * → JpaRepository<T, ID>,該接口封裝了基礎CRUD操作
 * T:操作的實體類類型;
 * ID:實體類中主鍵屬性的類型。
 * → JpaSpecificationExecutor<T>,該接口封裝了復雜查詢(分頁)
 * T:操作的實體類類型。
 */
public interface CustomerDao extends JpaRepository<Customer, Long>, 
JpaSpecificationExecutor<Customer> {
    ...
}

二、基礎CRUD

1. 測試準備

@RunWith(SpringJUnit4ClassRunner.class) //聲明Spring提供的單元測試環境
@ContextConfiguration(locations = "classpath:applicationContext.xml") //指定Spring容器配置信息
public class CustomerDaoTest {

    @Autowired
    private CustomerDao customerDao;

    ...

}

2. 測試查詢

NOTE
findOne → em.find() → 立即加載;
getOne → em.getReference() → 延遲加載;

    /**
     * 測試跟據ID查詢,findOne方法
     */
    @Test
    public void testFindOne() {
        Customer customer = customerDao.findOne(1L);
        System.out.println(customer);
    }

    /**
     * 跟據ID從數據庫查詢,getOne方法需要使用@Transactional注解
     * findOne → em.find():立即加載
     * getOne → em.getReference():延遲加載
     */
    @Test
    @Transactional
    public void testGetOne() {
        Customer customer = customerDao.getOne(5L);
        System.out.println(customer);
    }

3. 測試保存

NOTE
① 實體對象未設置主鍵屬性值:直接保存;
② 實體對象設置主鍵屬性值:
→ a、主鍵存在,進行更新;
→ b、主鍵不存在,跟據主鍵策略生成主鍵進行插入(保存)。

    /**
     * 測試更新|保存,save方法
     * → 先查詢,后更新|保存
     * 如果沒有設置ID屬性,則進行保存操作;如果設置了ID屬性,則會進行更新|保存。
     * ① 如果指定ID數據存在,則進行更新操作;
     * ② 如果指定ID數據不存在,則進行保存操作(注意:保存時主鍵會依據主鍵策略自動生成,而不是指定的主鍵)。
     */
    @Test
    public void testSave() {
        Customer customer = new Customer();
        customer.setCustId(22L);
        customer.setCustName("Stone");
        customerDao.save(customer);
    }

4. 測試刪除

    /**
     * 測試刪除delete
     * → 先查詢,后刪除
     */
    @Test
    public void testDelete() {
        customerDao.delete(21L);
    }

5. 測試查詢所有

    /**
     * 測試查詢所有findAll
     */
    @Test
    public void testFindAll() {
        List<Customer> customers = customerDao.findAll();
        for (Customer customer : customers) {
            System.out.println(customer);
        }
    }

6. 測試聚合查詢

    /**
     * 測試聚合(統計)查詢
     */
    @Test
    public void testCount() {
        long count = customerDao.count();
        System.out.println(count);
    }

7. 判斷是否存在

    /**
     * 測試查詢判斷是否存在
     * 傳統方式:
     * ① 判斷查詢結果是否為空null;
     * ② 判斷查詢結果條數是否大于0;【JPA采用此方式】
     */
    @Test
    public void testExists() {
        boolean exists = customerDao.exists(4L);
        System.out.println(exists);
    }

三、JPQL形式查詢

1. 條件查詢

① 接口方法準備
    /**
     * 跟據客戶名稱查詢客戶信息
     * JPQL:FROM Customer WHERE custName = ?
     * 注意:在@Query注解的value屬性中,需要使用"?1"來指定具體占位。
     *
     * @param custName 客戶名稱
     * @return 客戶信息
     */
    @Query(value = "FROM Customer WHERE custName = ?1")
    public Customer findJpql(String custName);
② 測試
    /**
     * 測試JPQL查詢
     */
    @Test
    public void testJpql() {
        Customer yinRui = customerDao.findJpql("Yin Rui");
        System.out.println(yinRui);
    }

2. 多條件查詢

① 接口方法準備
    /**
     * JPQL:FROM Customer WHERE custName = ? AND custId = ?
     *
     * @param custName 客戶名稱
     * @param custId   客戶ID
     * @return 客戶信息
     */
    @Query(value = "FROM Customer WHERE custName = ?1 AND custId = ?2")
    public Customer findCustNameAndCustId(String custName, Long custId);
② 測試
    /**
     * 測試JPQL多條件查詢
     */
    @Test
    public void testJpqlAnd() {
        Customer nicholasDunn = customerDao.findCustNameAndCustId("Nicholas Dunn", 19L);
        System.out.println(nicholasDunn);
    }

3. 更新操作

① 接口方法準備

NOTE
相比于查詢方法,此處需要配合 @Modifying 注解一起使用。

    /**
     * 更新客戶姓名(@Query:代表進行查詢;@Modifying:代表當前執行方法進行更新操作。)
     * SQL:UPDATE cst_customer SET cust_name = ? WHERE cust_id = ?
     * JPQL:UPDATE Customer SET custName = ? WHERE custId = ?
     *
     * @param custName
     * @param custId
     */
    @Query(value = "UPDATE Customer SET custName = ?1 WHERE custId = ?2")
    @Modifying //(@Modifying+@Query)一起使用,代表更新操作。
    public void updateCustomer(String custName, Long custId);
② 測試
    /**
     * 測試JPQL更新操作
     * → SpringDataJpa中使用jpql完成 更新|刪除操作 需要手動添加事務支持;
     * → 在@Test注解下,默認SpringDataJpa執行完成會進行數據回滾;
     * → 使用@Rollback(value = false),關閉自動回滾。
     */
    @Test
    @Transactional //添加事務支持
    @Rollback(value = false)
    public void testUpdate() {
        customerDao.updateCustomer("Jeff Stone", 1L);
    }

四、SQL形式查詢

1. 查詢全部

① 接口方法準備
    /**
     * SQL:SELECT * FROM cst_customer
     * → 使用@Query注解,配置:
     * ① value屬性:sql;
     * ② nativeQuery屬性:使用本地sql查詢,true;反之,false。
     *
     * @return 查詢對象集合
     */
    @Query(value = "SELECT * FROM cst_customer", nativeQuery = true)
    public List<Object[]> findSql();
② 測試
    /**
     * 測試使用本地SQL查詢全部客戶信息
     */
    @Test
    public void testSql() {
        List<Object[]> objects = customerDao.findSql();
        for (Object[] object : objects) {
            System.out.println(Arrays.toString(object));
        }
    }

2. 模糊匹配

① 接口方法準備
    /**
     * 使用SQL查詢,進行模糊匹配
     * SQL:SELECT * FROM cst_customer WHERE cust_name LIKE ?
     *
     * @param custName 客戶名稱
     * @return 客戶信息
     */
    @Query(value = "SELECT * FROM cst_customer WHERE cust_name LIKE ?1", nativeQuery = true)
    public List<Object[]> findSqlLike(String custName);
② 測試
    /**
     * 測試SQL查詢,模糊匹配
     */
    @Test
    public void testSqlLike() {
        List<Object[]> objects = customerDao.findSqlLike("%Hicks");
        for (Object[] object : objects) {
            System.out.println(Arrays.toString(object));
        }
    }

五、方法命名規則查詢

1. 條件查詢

① 接口方法準備

NOTE
查詢方法寫法:findBy + 屬性名稱(field_name),等值匹配

    /**
     * 方法命名規則查詢,是對JPQL查詢的更深層的封裝,使用該規則可以不用再寫JPQL語句。
     * → 查詢方法寫法:findBy + 屬性名稱(field_name),等值匹配
     * ① 以findBy開頭;
     * ② 拼接對象中的屬性,屬性首字母改為大寫;
     *
     * @param custName 客戶名稱
     * @return 客戶信息
     */
    public Customer findByCustName(String custName);
② 測試
    /**
     * 測試按照方法命名規則查詢
     */
    @Test
    public void testFindByCustName() {
        Customer itoRiku = customerDao.findByCustName("Ito Riku");
        System.out.println(itoRiku);
    }

2. 模糊匹配

① 接口方法準備

NOTE
查詢方法寫法:findBy + 屬性名稱(field_name) + 查詢方式[Like|Isnull]

    /**
     * 跟據方法命名規則,進行模糊匹配
     * → 查詢方法寫法:findBy + 屬性名稱(field_name) + 查詢方式[Like|Isnull]
     *
     * @param custName 客戶名稱
     * @return 客戶信息
     */
    public List<Customer> findByCustNameLike(String custName);
② 測試
    /**
     * 測試模糊匹配查詢(注意:由于時占位符的形式進行填充,"%"的位置信息需要自己判斷。)
     */
    @Test
    public void testFindByCustNameLike() {
        List<Customer> customers = customerDao.findByCustNameLike("%Yuito");
        for (Customer customer : customers) {
            System.out.println(customer);
        }
    }

3. 多條件查詢

① 接口方法準備

NOTE
查詢方法寫法:findBy + 屬性名稱(field_name) + 查詢方式[Like|Isnull] + 多條件連接符(And|Or) + 屬性名稱(field_name) + 查詢方式[Like|Isnull]

    /**
     * 跟據方法命名規則,進行多條件查詢
     * → 查詢方法寫法:findBy + 屬性名稱(field_name) + 查詢方式[Like|Isnull] + 多條件連接符(And|Or)
     * + 屬性名稱(field_name) + 查詢方式[Like|Isnull]
     *
     * @param CustName 客戶名稱
     * @param CustAddress 客戶地址
     * @return 客戶信息
     */
    public List<Customer> findByCustNameLikeAndCustAddress(String CustName, String CustAddress);
② 測試
    /**
     * 測試多條件查詢
     */
    @Test
    public void testFindByCustNameLikeAndCustAddress() {
        List<Customer> customers =
                customerDao.findByCustNameLikeAndCustAddress("Xue%", "536 Wall Street");
        for (Customer customer : customers) {
            System.out.println(customer);
        }
    }

六、結尾

以上即為Spring Data JPA-基礎篇(二)的全部內容,感謝閱讀。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容