JPA + SpringData 操作數據庫原來可以這么簡單 ---- 深入了解 JPA - 3


原創播客,如需轉載請注明出處。原文地址:http://www.cnblogs.com/crawl/p/7718741.html 

----------------------------------------------------------------------------------------------------------------------------------------------------

筆記中提供了大量的代碼示例,需要說明的是,大部分代碼示例都是本人所敲代碼並進行測試,不足之處,請大家指正~

本博客中所有言論僅代表博主本人觀點,若有疑惑或者需要本系列分享中的資料工具,敬請聯系 qingqing_crawl@163.com

-----------------------------------------------------------------------------------------------------------------------------------------------------------

前言:今天是 10.24,傳說中的程序員節,先祝大家節日快樂,不知道大家今天有沒有加班,哈哈。

   經過了前兩篇的詳細介紹,終於迎來了 JPA 的終結篇,樓主認為如果僅僅了解了 JPA 的話,大家可能感覺與 Hibernate 幾乎差不多,沒有什么亮點,但是等大家了解了 SpringData 后,JPA 與 SpringData 相結合,便會發揮出它巨大的優勢,極大的簡化了我們操作數據庫的步驟,使我們的代碼具有很強的可維護性,樓主隨后的博客也將繼續介紹。

六、JPA 的二級緩存

1. 大家對一級緩存比較熟悉,即若查詢一條同樣的記錄,因為一級緩存的存在只發送一條 SQL 語句。那么 JPA 的二級緩存又體現在哪呢?樓主給大家解釋為:查詢一條同樣的記錄,在第一次查詢后關閉 EntityManager、提交事務后,再重新獲取 EntityManager 並開啟事務再查詢同樣的記錄,因為有二級緩存的存在也會只發送一條記錄。如下:

 1    //測試 JPA 的二級緩存
 2     @Test
 3     public void testSecondLevelCache() {
 4         Customer customer1 = entityManager.find(Customer.class, 1);
 5         
 6         transaction.commit();
 7         entityManager.close();
 8         
 9         entityManager = entityManagerFactory.createEntityManager();
10         transaction = entityManager.getTransaction();
11         transaction.begin();
12         
13         Customer customer2 = entityManager.find(Customer.class, 1);
14     }

大家可以看到,4 行和 13 行的查詢語句一樣,6 行,7 行 提交了事務關閉了 EntityManager。若不進行二級緩存的配置,這樣的操作會發送兩次一模一樣的 SQL 語句,結果就不貼上了,大家可以試一試。若配置了二級緩存,同樣的操作便只會發送一條 SQL ,這樣可以減小服務器的壓力,減少訪問數據庫的次數。那么如何來配置二級緩存呢?

2. 如何配置二級緩存:

1)persistence.xml 文件中配置二級緩存相關

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="jpa-1" transaction-type="RESOURCE_LOCAL">
        <!-- 配置使用什么 ORM 產品來作為 JPA 的實現 1. 實際上配置的是 javax.persistence.spi.PersistenceProvider 
            接口的實現類 2. 若 JPA 項目中只有一個 JPA 的實現產品,則可以不配置該節點 -->
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <!-- 添加持久化類 -->
        <class>com.software.jpa.helloworld.Customer</class>
        <class>com.software.jpa.helloworld.Order</class>

        <class>com.software.jpa.helloworld.Manager</class>
        <class>com.software.jpa.helloworld.Department</class>

        <class>com.software.jpa.helloworld.Category</class>
        <class>com.software.jpa.helloworld.Item</class>

        <!-- 配置二級緩存的策略 
                ALL:所有的實體類都被緩存
                NONE:所有的實體類都不被緩存. 
                ENABLE_SELECTIVE:標識 @Cacheable(true) 注解的實體類將被緩存 
                DISABLE_SELECTIVE:緩存除標識 @Cacheable(false) 以外的所有實體類
                UNSPECIFIED:默認值,JPA 產品默認值將被使用 -->
        <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>

        <properties>
            <!-- 連接數據庫的基本信息 -->
            <!-- 在 Connection 選項中配置后會自動生成如下信息 -->
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="qiqingqing" />

            <!-- 配置 JPA 實現產品的基本屬性,即配置 Hibernate 的基本屬性 -->
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="update" />

            <!-- 二級緩存相關 -->
            <property name="hibernate.cache.use_second_level_cache"
                value="true" />
            <property name="hibernate.cache.region.factory_class"
                value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />
            <property name="hibernate.cache.use_query_cache" value="true" />
        </properties>
    </persistence-unit>
</persistence>

2)導入 ehcache 的 jar 包和配置文件 ehcache.xml 

jar 包:

配置文件:對二級緩存參數的配置

<ehcache>

    <!-- Sets the path to the directory where cache .data files are created.

         If the path is a Java System Property it is replaced by
         its value in the running VM.

         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - Default temp file path -->
    <diskStore path="java.io.tmpdir"/>


    <!--Default Cache configuration. These will applied to caches programmatically created through
        the CacheManager.

        The following attributes are required for defaultCache:

        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

    <!--Predefined caches.  Add your cache configuration settings here.
        If you do not have a configuration for your cache a WARNING will be issued when the
        CacheManager starts

        The following attributes are required for defaultCache:

        name              - Sets the name of the cache. This is used to identify the cache. It must be unique.
        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->

    <!-- Sample cache named sampleCache1
        This cache contains a maximum in memory of 10000 elements, and will expire
        an element if it is idle for more than 5 minutes and lives for more than
        10 minutes.

        If there are more than 10000 elements it will overflow to the
        disk cache, which in this configuration will go to wherever java.io.tmp is
        defined on your system. On a standard Linux system this will be /tmp"
        -->
    <cache name="sampleCache1"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

    <!-- Sample cache named sampleCache2
        This cache contains 1000 elements. Elements will always be held in memory.
        They are not expired. -->
    <cache name="sampleCache2"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        /> -->

    <!-- Place configuration for your caches following -->

</ehcache>

3)給需要緩存的類添加 @Cacheable(true) 注解,有前面的代碼可知,樓主獲取的是 Customer 對象

二級緩存就給大家介紹到這里。

七、JPQL

1.什么是 JPQL:JPQL語言,即 Java Persistence Query Language 的簡稱。

2.然后來看一個 JPQL 的 Helloworld:

 1    //JPQL 的 HelloWorld
 2     @Test
 3     public void testHelloJPQL() {
 4         String jpql = "FROM Customer c WHERE c.age > ?";
 5         Query query = entityManager.createQuery(jpql);
 6         
 7         //占位符的索引是從 1 開始
 8         query.setParameter(1, 21);
 9         
10         List<Customer> lists = query.getResultList();
11         System.out.println(lists.size());
12         
13     }

乍一看,大家可能感覺 JPQL 像極了 Hibernate 的 HQL 查詢,沒錯,這兩種查詢的相似度極高。需要注意的是,使用 Query 的 setParameter() 的方法填占位符是,索引是從 1

開始的。

3. 查詢部分屬性:

 1    @Test
 2     public void testPartlyProperties() {
 3         String jpql = "SELECT new Customer(c.lastName, c.age) FROM Customer c WHERE c.id > ?";
 4         Query query = entityManager.createQuery(jpql);
 5         
 6         query.setParameter(1, 1);
 7         
 8         List lists = query.getResultList();
 9         System.out.println(lists);
10     }

默認情況下若只查詢部分屬性,則將返回 Object[] 類型的結果或 Object[] 類型的 List,可以在實體類中創建對應的構造器,然后在 jpql 中利用對應的構造器返回實體類對應的對象,這樣得到的結果可以很令人滿意,也很方便我們來操作。

 4.命名查詢 NamedQuery:

1)在需要查詢的對象類上添加 @NamedQuery 注解:

2)創建測試方法:

5. 本地 SQL 查詢使用 EntityManager 的 createNativeQuery() 方法:

1    //本地 SQL 查詢
2     @Test
3     public void testNativeQuery() {
4         String sql = "SELECT age FROM jpa_customer WHERE id = ?";
5         Query query = entityManager.createNativeQuery(sql).setParameter(1, 1);
6         Object result = query.getSingleResult();
7         System.out.println(result);
8     }

6. 可以使用 Order By 字句:

 1    // jpql 中的 Order By 子句
 2     @Test
 3     public void testOrderBy() {
 4         String jpql = "FROM Customer c WHERE c.age > ? ORDER BY c.age DESC";
 5         Query query = entityManager.createQuery(jpql);
 6         
 7         //占位符的索引是從 1 開始
 8         query.setParameter(1, 21);
 9         
10         List<Customer> lists = query.getResultList();
11         System.out.println(lists.size());
12     }

7.還可以使用 Group By 子句:

1    //查詢 order 數量大於 2 的那些 Customer
2     @Test
3     public void testGroupBy() {
4         String jpql = "SELECT o.customer FROM Order o GROUP BY o.customer HAVING count(o.id) >= 2";
5         List<Customer> lists = entityManager.createQuery(jpql).getResultList();
6         System.out.println(lists);
7     }

8.也可以使用子查詢

1    //子查詢
2     @Test
3     public void testSubQuery() {
4         //查詢所有 Customer 的 lastName 為 YY 的 Order
5         String jpql = "SELECT o FROM Order o"
6                 + " WHERE o.customer = (SELECT c FROM Customer c WHERE c.lastName = ?)";
7         List<Order> orders = entityManager.createQuery(jpql).setParameter(1, "YY").getResultList();
8         System.out.println(orders.size());
9     }

八、Spring 整合 JPA

JPA 的一些 API 也可以放到 Spring 的 IOC 容器中,交由 Spring 容器管理,那么如何用 Spring 來整合 JPA 呢?

1.新建 JPA 工程,導入所需的 jar包(Hibernate、JPA、c3p0、Spring、MySQL 驅動)

2.類路徑下創建 db.properties 數據庫配置文件,配置數據庫的鏈接信息(樓主在這只配置了必須屬性)

1 jdbc.user=root
2 jdbc.password=qiqingqing
3 jdbc.driverClass=com.mysql.jdbc.Driver
4 jdbc.jdbcUrl=jdbc:mysql://localhost:3306/jpa

3.類路徑下創建 Spring 的配置文件 applicationContext.xml,配置自動掃描的包,將 db.propertiest 文件導入,並配置 c3p0 數據源

 1    <!-- 配置自動掃描的包 -->
 2     <context:component-scan base-package="com.software.jpa"></context:component-scan>
 3 
 4     <!-- 配置數據源 -->
 5     <context:property-placeholder location="classpath:db.properties"/>
 6     
 7     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
 8         <property name="user" value="${jdbc.user}"></property>
 9         <property name="password" value="${jdbc.password}"></property>
10         <property name="driverClass" value="${jdbc.driverClass}"></property>
11         <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
12     </bean>

4.在 applicationContext.xml 中配置 JPA 的 EntityManagerFactory

 1    <!-- 配置 EntityManagerFactory -->
 2     <bean id="entityManagerFactory"
 3                  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
 4         <!-- 配置數據源 -->
 5         <property name="dataSource" ref="dataSource"></property>
 6         <!-- 配置 JPA 提供商的適配器,可以通過內部 bean 的方式來配置 -->
 7         <property name="jpaVendorAdapter">
 8             <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
 9         </property>
10         <!-- 配置實體類所在的包 -->
11         <property name="packagesToScan" value="com.software.jpa.spring.entities"></property>
12         <!-- 配置 JPA 的基本屬性,比如,JPA 實現產品的屬性 -->
13         <property name="jpaProperties">
14             <props>
15                 <prop key="hibernate.show_sql">true</prop>
16                 <prop key="hibernate.format_sql">true</prop>
17                 <prop key="hibernate.hbm2ddl.auto">update</prop>
18             </props>
19         </property>
20     </bean>

5.配置 JPA 使用的事務管理器及配置支持基於注解的事務配置

1    <!-- 配置  JPA 使用的事務管理器 -->
2     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
3         <property name="entityManagerFactory" ref="entityManagerFactory"></property>
4     </bean>
5     
6     <!-- 配置支持基於注解的事務配置 -->
7     <tx:annotation-driven transaction-manager="transactionManager"/>

6.為了測試創建實體類 Person,添加相應的 JPA 注解,生成對應的數據表

 1 package com.software.jpa.spring.entities;
 2 
 3 import javax.persistence.Column;
 4 import javax.persistence.Entity;
 5 import javax.persistence.GeneratedValue;
 6 import javax.persistence.Id;
 7 import javax.persistence.Table;
 8 
 9 @Table(name="JPA_PERSONS")
10 @Entity
11 public class Person {
12     
13     private Integer id;
14     
15     private String lastName;
16     
17     private String email;
18     
19     private Integer age;
20 
21     @GeneratedValue
22     @Id
23     public Integer getId() {
24         return id;
25     }
26 
27     public void setId(Integer id) {
28         this.id = id;
29     }
30 
31     @Column(name="LAST_NAME")
32     public String getLastName() {
33         return lastName;
34     }
35 
36     public void setLastName(String lastName) {
37         this.lastName = lastName;
38     }
39 
40     public String getEmail() {
41         return email;
42     }
43 
44     public void setEmail(String email) {
45         this.email = email;
46     }
47 
48     public Integer getAge() {
49         return age;
50     }
51 
52     public void setAge(Integer age) {
53         this.age = age;
54     }
55     
56 }

7.創建 PersonDao 使用 @PersistenceContext 獲取和當前事務關聯的 EntityManager 對象

 1 package com.software.jpa.dao;
 2 
 3 import javax.persistence.EntityManager;
 4 import javax.persistence.PersistenceContext;
 5 
 6 import org.springframework.stereotype.Repository;
 7 
 8 import com.software.jpa.spring.entities.Person;
 9 
10 @Repository
11 public class PersonDao {
12     
13     //使用 @PersistenceContext 獲取和當前事務關聯的 EntityManager 對象
14     @PersistenceContext
15     private EntityManager entityManager;
16     
17     public void save(Person p) {
18         entityManager.persist(p);
19     }
20 
21 }

8.創建 PersonService ,模擬事務操作,20 行樓主設計了一個算數異常,若整合成功,因為添加了事務操作,所以 18 行和 22 行的兩條記錄都沒有插入進數據庫。

 1 package com.software.jpa.service;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.stereotype.Service;
 5 import org.springframework.transaction.annotation.Transactional;
 6 
 7 import com.software.jpa.dao.PersonDao;
 8 import com.software.jpa.spring.entities.Person;
 9 
10 @Service
11 public class PersonService {
12     
13     @Autowired
14     private PersonDao dao;
15 
16     @Transactional
17     public void save(Person p1, Person p2) {
18         dao.save(p1);
19         
20         int i = 10/0;
21         
22         dao.save(p2);
23     }
24     
25 }

9.創建測試方法,並執行

 1 package com.software.jpa.spring;
 2 
 3 import javax.sql.DataSource;
 4 
 5 import org.junit.Test;
 6 import org.springframework.context.ApplicationContext;
 7 import org.springframework.context.support.ClassPathXmlApplicationContext;
 8 
 9 import com.software.jpa.service.PersonService;
10 import com.software.jpa.spring.entities.Person;
11 
12 public class JPATest {
13     
14     private ApplicationContext ctx = null;
15     
16     private PersonService personService = null;
17     
18     {
19         ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
20         
21         personService = ctx.getBean(PersonService.class);
22     }
23     
24     @Test
25     public void testSave() {
26         Person p1 = new Person();
27         p1.setAge(11);
28         p1.setEmail("aa@163.com");
29         p1.setLastName("AA");
30         
31         Person p2 = new Person();
32         p2.setAge(12);
33         p2.setEmail("bb@163.com");
34         p2.setLastName("BB");
35         
36         System.out.println(personService.getClass().getName());
37         personService.save(p1, p2);
38     }
39 
40     @Test
41     public void testDataSourct() throws Exception {
42         DataSource dataSource = ctx.getBean(DataSource.class);
43         System.out.println(dataSource.getConnection());
44     }
45 
46 }

JPA 的知識介紹到此就完全結束了,樓主整理了不短的時間,希望可以幫助到需要的朋友。

相關鏈接:

JPA + SpringData 操作數據庫原來可以這么簡單 ---- 深入了解 JPA - 1

JPA + SpringData 操作數據庫原來可以這么簡單 ---- 深入了解 JPA - 2

JPA + SpringData 操作數據庫 ---- 深入了解 SpringData

手把手教你解決無法創建 JPA 工程的問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM