JPA快速入門(一)

作者:鐘昕靈,叩丁狼教育高級講師。原創(chuàng)文章,轉(zhuǎn)載請注明出處。

JPA簡介

JPA是Java Persistence API的簡稱,中文名Java持久層API,是JDK 5.0注解或XML描述對象-關(guān)系表的映射關(guān)系,并將運行期的實體對象持久化到數(shù)據(jù)庫中。

Sun引入新的JPA ORM規(guī)范出于兩個原因:
其一,簡化現(xiàn)有Java EE和Java SE應(yīng)用開發(fā)工作;
其二,Sun希望整合ORM技術(shù),實現(xiàn)天下歸一。

JPA的宗旨是為POJO提供持久化標(biāo)準(zhǔn)規(guī)范,由此可見,經(jīng)過這幾年的實踐探索,能夠脫離容器獨立運行,方便開發(fā)和測試的理念已經(jīng)深入人心了。Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的實現(xiàn)。

JPA的總體思想和現(xiàn)有Hibernate、TopLink、JDO等ORM框架大體一致??偟膩碚f,JPA包括以下3方面的技術(shù):

ORM映射元數(shù)據(jù)

JPA支持XML和JDK5.0注解兩種元數(shù)據(jù)的形式,元數(shù)據(jù)描述對象和表之間的映射關(guān)系,框架據(jù)此將實體對象持久化到數(shù)據(jù)庫表中;

API

用來操作實體對象,執(zhí)行CRUD操作,框架在后臺替代我們完成所有的事情,開發(fā)者從繁瑣的JDBC和SQL代碼中解脫出來。

查詢語言

這是持久化操作中很重要的一個方面,通過面向?qū)ο蠖敲嫦驍?shù)據(jù)庫的查詢語言查詢數(shù)據(jù),避免程序的SQL語句緊密耦合。


image.png
JPA開發(fā)環(huán)境搭建
  • jar包的依賴
    如果是maven項目,將下面的配置添加到pom.xml文件中
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <target>1.8</target>
                <source>1.8</source>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>
<dependencies>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.3.5.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>4.3.5.Final</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.21</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.6</version>
    </dependency>
</dependencies>

如果是普通的java項目,將下面的jar包添加到項目的lib目錄中


image.png
  • persistence.xml文件
    如果是maven項目,在src/main/resources下創(chuàng)建META-INF文件夾,將persistence.xml文件放在該目錄下
    如果是普通的java項目,在src下創(chuàng)建META-INF文件夾,將persistence.xml文件夾放在該目錄下

在persistence.xml文件中做如下配置

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <!--
    JPA根據(jù)下面的配置信息創(chuàng)建EntityManagerFactory,一個項目中可以配置多個持久單元
    name:為當(dāng)前持久單元命名,可以通過該名稱指定加載對應(yīng)的配置信息
-->
    <persistence-unit name="myPersistence">
        <!--指定掃描貼Entity實體類所在的jar包-->
        <properties>
    <!--數(shù)據(jù)庫的方言,告訴JPA當(dāng)前應(yīng)用使用的數(shù)據(jù)庫-->
            <property name="hibernate.dialect"  value="org.hibernate.dialect.MySQL5Dialect"/>
            <!--jpa的相關(guān)的配置信息-->
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
            <property name="javax.persistence.jdbc.driver"  value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="admin"/>
    <!--是否在控制臺打印執(zhí)行的sql語句-->
            <property name="hibernate.show_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

到此,開發(fā)JPA應(yīng)用的環(huán)境就搭建完成,接下來,在此基礎(chǔ)上來完成基本的CRUD操作吧

基于JPA的CRUD
  • 實體類及映射配置
//getter/setter和toString方法
@Getter@Setter@ToString

//JPA會掃描到貼了Entity注解的類,將其作為需要持久化的類
@Entity
//根據(jù)需求,對類和表做相關(guān)映射(如:表名)
@Table(name="user")
public class User {
    //標(biāo)識該字段為主鍵列對應(yīng)的字段
    @Id
    //指定主鍵的生成策略
    @GeneratedValue(strategy = GenerationType.AUTO)
    //為當(dāng)前字段和對應(yīng)的列做映射(如:列名,列的長度等)
    @Column(name = "id")
    private Long id;
    @Column(name = "name",length = 20)
    private String name;
    @Column(name = "sn",nullable = false)
    private String sn;
    //對日期類型做映射
    @Temporal(TemporalType.DATE)
    private Date hiredate;
}
  • EntityManagerFactory和EntityManager對象的創(chuàng)建
  1. EntityManagerFactory:JPA通過加載META-INF/persistence.xml文件中配置的persistence-unit創(chuàng)建EntityManagerFactory對象,該對象相當(dāng)于一個連接池對象,用來創(chuàng)建EntityManager,是線程安全的,多線程可以共用同一個EntityManagerFactory,創(chuàng)建該對象需要消耗較多的資源,所以通常一個項目只需要創(chuàng)建一個EntityManagerFactory對象
  2. EntityManager:相當(dāng)于一個連接對象,該對象線程不安全,所以,每次對象數(shù)據(jù)庫的訪問應(yīng)該創(chuàng)建一個新的EntityManager對象
public class JPAUtil {

    private static EntityManagerFactory emf;

    private JPAUtil() {}

    static {
        //加載persistence.xml文件中的persistence-util中的配置信息創(chuàng)建EntityManagerFactory對象
        emf = Persistence.createEntityManagerFactory("myPersistence");
    }

    //使用EntityManager創(chuàng)建EntityManager對象
    public static EntityManager getEntityManager() {
        return emf.createEntityManager();
    }
}
  • 保存操作
@Test
public void testSave() throws Exception {
    //封裝需要持久化的數(shù)據(jù)
    User u = new User();
    u.setName("Neld");
    u.setSn("sn");
    u.setHiredate(new Date());

    EntityManager em = JPAUtil.getEntityManager();
    //開啟事務(wù)
    em.getTransaction().begin();
    //執(zhí)行保存
    em.persist(u);
    //提交事務(wù)
    em.getTransaction().commit();
    //釋放資源
    em.close();
}
  • 刪除操作
@Test
public void testDelete() throws Exception {
    EntityManager em = JPAUtil.getEntityManager();
    em.getTransaction().begin();
    User u = em.getReference(User.class, 1L);
//執(zhí)行刪除,將持久化狀態(tài)的對象從數(shù)據(jù)庫中刪除
    em.remove(u);
    em.getTransaction().commit();
    em.close();
}
  • 修改操作
@Test
public void testUpdate() throws Exception {
    EntityManager em = JPAUtil.getEntityManager();
    em.getTransaction().begin();
    User u = em.find(User.class, 1L);
    u.setName("xxxx");
    em.merge(u);
    em.getTransaction().commit();
    em.close();
}
  • 查詢操作
@Test
public void testGet() throws Exception {
    EntityManager em = JPAUtil.getEntityManager();
    //查詢指定類型和OID的用戶信息
    User u = em.find(User.class, 1L);
    em.close();
    System.out.println(u);
}
  • CRUD小結(jié)
  1. persistence.xml文件的配置
    配置連接數(shù)據(jù)庫的基本信息
    JPA的基本行為配置

  2. 實體類的基本映射
    @Entity:標(biāo)注該類為持久化類
    JPA掃描到類上的注解,會將當(dāng)前類作為持久化類
    @Table:配置當(dāng)前類和表的相關(guān)映射
    下面的注解可以貼在字段或者是get方法上,
    如果選定了一個位置,那么所有的屬性相關(guān)的注解都應(yīng)該貼在這個位置,意思是說,不能一部分在字段上,一部分在get方法上
    @Id:主鍵屬性的映射---和表中的主鍵映射
    @GeneratedValue:主鍵生成策略(指定生成主鍵的方式:自增長/手動設(shè)置)
    @Column:配置當(dāng)前屬性和列的映射
    @Temporal:對日期類型的屬性映射(Date/DateTime/TimeStemp)

  3. 完成CRUD的步驟
    加載persistence.xml文件,使用指定的<persistence-unit>配置創(chuàng)建EntityManagerFactory對象,相當(dāng)于根據(jù)配置信息創(chuàng)建一個連接池對象
    創(chuàng)建EntityManager對象,相當(dāng)于獲取到一個連接對象
    開啟事務(wù)
    執(zhí)行crud相關(guān)的方法(persist/merge/remove/find),查詢所有調(diào)用Query中的getResultList方法
    Persist:保存數(shù)據(jù)
    Merge:保存或者更新,當(dāng)對象有OID的時候,更新,反之,保存
    Remove:刪除數(shù)據(jù)
    Find:根據(jù)主鍵查詢數(shù)據(jù)
    Query:其他的查詢需要使用該對象,傳入對應(yīng)的JPQL(相當(dāng)于SQL),調(diào)用getResultList方法執(zhí)行查詢,返回對應(yīng)的List集合
    提交事務(wù)
    釋放資源

hbm2ddl工具的使用

在持久層應(yīng)用的開發(fā)過程中,我們發(fā)現(xiàn),實體類和表結(jié)構(gòu)是一一對應(yīng)的,所以,我們會想,是否可以讓JPA根據(jù)實體類和對應(yīng)的映射信息的配置,為我們自動的生成對應(yīng)的表結(jié)構(gòu)呢?

答案是肯定的,又因為我們現(xiàn)在講的是hibernate對JPA的實現(xiàn),所以我們應(yīng)用hibernate中提供的hbm2ddl工具來實現(xiàn),配置很簡單,在persistence.xml文件中作如下配置即可

<property name="hibernate.hbm2ddl.auto" value="create"/>

接下來,我們來解釋一下每種策略的含義及使用場景

  • hibernate.hbm2ddl.auto=create
    在啟動的時候先刪除被管理的實體對應(yīng)的表,然后再創(chuàng)建jpa管理的實體類對應(yīng)的表
  • hibernate.hbm2ddl.auto=create-drop
    和create一致,只是在關(guān)閉系統(tǒng)之前會刪除jpa管理的所有的表
  • hibernate.hbm2ddl.auto=update
    在啟動的時候,檢查實體類和表結(jié)構(gòu)是否有變化,如果有,執(zhí)行更新表結(jié)構(gòu)相關(guān)的sql
    如果添加一個屬性,JPA可以幫我們在表中添加對應(yīng)的列
    如果刪除一個屬性,JPA不會幫我們?nèi)ケ碇袆h除對應(yīng)的列
    如果修改一個屬性(類型),JPA不會幫我們?nèi)ケ碇袆h除對應(yīng)的列
  • hibernate.hbm2ddl.auto=validate
    在啟動的時候,檢查實體類和表結(jié)構(gòu)是否有變化,如果有,啟動失敗,拋出異常
    Caused by: org.hibernate.HibernateException: Missing column: sn in jpa.user
選擇:
  • 在開發(fā)階段,我們通常使用create或者create-drop,可以快速的創(chuàng)建對應(yīng)的表結(jié)構(gòu)
  • 在測試階段,不要使用create或者create-drop,因為這樣會將我們辛苦錄入的測試數(shù)據(jù)刪除,所以,我們使用update,在實體類修改的時候,更新表結(jié)構(gòu)即可
  • 在生產(chǎn)環(huán)境中,我們通常使用validate,這樣可以在啟動階段發(fā)現(xiàn)表結(jié)構(gòu)相關(guān)的問題,至于表結(jié)構(gòu)的修改,交給我們的DBA去完成吧.

單對象映射中常用的注解

  • 對象映射相關(guān)
  1. @Entity:
    對實體類的映射,默認(rèn)使用當(dāng)前類的簡單名稱作為類名,如在使用JPQL做查詢的時候,使用該名字實現(xiàn)數(shù)據(jù)的查詢
    JPQL語句:SELECT u FROM User u;
    User:為默認(rèn)使用的類名,可以通過Entity中的name屬性修改
    @Entity(name=”UserInfo”):將類的名稱修改為UserInfo,那么上面的JPQL中的User修改為UserInfo即可

  2. @Table:
    指定實體類映射的表的相關(guān)信息,如:表名,默認(rèn)和類名一致
    @Table(name=”t_user”):將映射的表名修改為t_user

  3. persistence.xml文件中的相關(guān)元素的配置說明
    <class>:指定需要掃描的實體類
    <exclude-unlisted-classes>:設(shè)置為true的時候,表示不掃描這里沒有列出來的類
    <jar-file>:指定對項目中引入的jar包中的類進(jìn)行掃描

  • 屬性相關(guān):
  1. @GeneratedValue,主鍵生成策略
    在一張表中,主鍵列的信息通常需要受到程序員的特殊關(guān)照,這里我們需要探討一下主鍵的生成方式(自動生成/手動設(shè)值)
    首先,我們需要在主鍵屬性上使用@GeneratedValue注解中的strategy屬性來設(shè)值主鍵的生成方式

    1. strategy=GenerationType.AUTO
      把主鍵生成策略交給JPA廠商(Persistence Provider),由它根據(jù)具體的數(shù)據(jù)庫選擇合適的策略,可以是Table/Sequence/Identity中的一種。假如數(shù)據(jù)庫是Oracle,則選擇Sequence。
      如果不做特別指定,默認(rèn)是使用這種方式生成主鍵

    2. strategy=GenerationType.IDENTITY
      多數(shù)數(shù)據(jù)庫支持IDENTITY,數(shù)據(jù)庫會在新行插入時自動給ID賦值,這也叫做ID自增長列,比如MySQL中可以在創(chuàng)建表時聲明“AUTO_INCREMENT”,該策略在Oracle數(shù)據(jù)庫中不支持

    3. strategy=GenerationType.TABLE
      有時候為了不依賴于數(shù)據(jù)庫的具體實現(xiàn),在不同數(shù)據(jù)庫之間更好的移植,可以在數(shù)據(jù)庫中新建序列表來生成主鍵,序列表一般包含兩個字段:第一個字段引用不 同的關(guān)系表,第二個字段是該關(guān)系表的最大序號。這樣,只需要一張序列就可以用于多張表的主鍵生成。
      如果不指定表生成器,JPA廠商會使用默認(rèn)的表,比如Hibernate在Oracle數(shù)據(jù)庫上會默認(rèn)使用表hibernate_sequence。
      這種方式雖然通用性最好,所有的關(guān)系型數(shù)據(jù)庫都支持,但是由于不能充分利用具體數(shù)據(jù)庫的特性,建議不要優(yōu)先使用。

    4. strategy=GenerationType.SEQUENCE
      Oracle不支持ID自增長列而是使用序列的機(jī)制生成主鍵ID,對此,可以選用序列作為主鍵生成策略:
      如果不指定序列生成器的名稱,則使用廠商提供的默認(rèn)序列生成器,比如Hibernate默認(rèn)提供的序列名稱為hibernate_sequence。
      支持的數(shù)據(jù)庫: Oracle、PostgreSQL、DB2
      屬性映射
      @Column:
      使用該注解可以對屬性和列進(jìn)行相關(guān)映射

該注解可以貼在字段上,也可貼在getter方法上,但是必須是統(tǒng)一的,不能一部分在字段上,一部分在getter方法上

  • @Access
    在實際開發(fā)中,也可以告訴JPA只去掃描哪個位置上的@Column注解,如果沒有就不在去其他地方掃描
    @Access(AccessType.PROPERTY):屬性,對應(yīng)著get方法
    @Access(AccessType.FIELD):字段:對應(yīng)字段

  • @Column
    name:列名,通常,屬性名和列名一直的時候,不需要指定,默認(rèn)使用屬性名作為列名
    unique:唯一性約束
    nullable:非空約束
    insertable:false,表示在生成insert語句的時候不插入這一列的值
    updatable:false,表示在生成update語句的時候不更新這一列的值
    length:指定該列的長度
    columnDefination:自定義列的類型,默認(rèn)是JPA根據(jù)屬性的類型自動生成
    precision:在使用decimal類型的時候指定總長度
    scale:在使用decimal類型的時候指定小數(shù)位數(shù)

  • @Temporal:
    日期類型的映射
    指定日期類型的屬性對應(yīng)的列的類型(date/datatime/timestamp)

  • @Transient:
    非持久化類型的映射
    JPA在做對象關(guān)系映射的時候,默認(rèn)是對實體類中的所有屬性進(jìn)行映射的,如果有不需要映射的屬性,可以使用該注解完成

  • @Lob:
    大數(shù)據(jù)類型的映射
    對象如果是String類型的,默認(rèn)情況下載表中映射的是VARCHAR類型
    該注解可以對應(yīng)text/blob/clob類型進(jìn)行映射,如:

@Lob
private String content;

一級緩存

在EntityManager中存在一個緩存區(qū)域,稱之為一級緩存

在該緩存區(qū)中,會將查詢到的對象緩存到該區(qū)域中

如果在同一個EntityManager中,查詢相同OID的數(shù)據(jù),那么只需要發(fā)送一條sql

在事務(wù)提交/關(guān)閉EntityManager之后,一級緩存會清空,所以在不同的EntityManager中使用不
同的一級緩存

一級緩存也可以使用下面的方法手動清除緩存數(shù)據(jù)
detach:清除一級緩存中指定的對象
clear:清除一級緩存中的所有的緩存數(shù)據(jù)

image.png

但是一級緩存的緩存能力是非常有限的,因為我們不會經(jīng)常在一個EntityManager中查詢相同的數(shù)據(jù)
延遲加載

JPA中,根據(jù)主鍵查詢數(shù)據(jù)可以使用下面兩個方法完成:
<T> T find(Class<T> type, Object oid);
<T> T getReference(Class<T> type, Object oid);
相同點:都是根據(jù)主鍵查詢指定類型的數(shù)據(jù)

不同點: getReference方法是在真實使用該對象的時候才會發(fā)送查詢的sql語句,如

public void testGetReference() throws Exception {
    EntityManager em = JPAUtil.getEntityManager();
    //這里不會立即發(fā)送sql查詢
    User u = em.getReference(User.class, 1L);
    System.out.println("-------------");
    //在訪問User對象中的屬性值的時候表示真正使用該對象
    System.out.println(u.getName());
    em.close();
}

執(zhí)行結(jié)果:

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

Hibernate: select user0_.id as id1_0_0_, user0_.hiredate as hiredate2_0_0_, user0_.name             as  name3_0_0_, user0_.sn as sn4_0_0_ from User user0_ where user0_.id=?

Neld

根據(jù)執(zhí)行的打印結(jié)果可以看到,是我們在真正使用該對象的時候才會執(zhí)行查詢的sql,而在這之前是不會發(fā)送SQL執(zhí)行數(shù)據(jù)的查詢

延遲加載

getReference方法查詢數(shù)據(jù)的方式我們稱之為延遲加載

什么是延遲加載? 就是不會立即執(zhí)行查詢的sql,而是延遲到真正使用的時候再執(zhí)行,上面的例子已經(jīng)證明了這一點

再觀察:
find方法查詢到的結(jié)果,如果查詢到了對應(yīng)的數(shù)據(jù),返回查詢到的結(jié)果即可,反之,返回null,所以可以使用ifnull判斷是否有數(shù)據(jù)
getReference方法查詢到的結(jié)果,無論是否查詢到了數(shù)據(jù),結(jié)果都不會是null,所以不能使用ifnull判斷是否有對應(yīng)的數(shù)據(jù)
如果在表中沒有對應(yīng)的數(shù)據(jù),拋出異常
javax.persistence.EntityNotFoundException: Unable to find cn.wolfcode._01_hello.User with id 2

原理:
JPA使用動態(tài)代理機(jī)制實現(xiàn)延遲加載,覆寫該對象中的所有的getter方法,在getter方法中執(zhí)行查詢當(dāng)前對象的sql

延遲加載需要搞懂的問題:
1.延遲加載什么時候發(fā)送SQL執(zhí)行數(shù)據(jù)?
2.為什么需要在關(guān)閉EntityManager對象之前初始化延遲加載對象?
3.為什么在訪問對象的get方法的時候,會去初始化當(dāng)前對象(發(fā)送SQL執(zhí)行查詢)呢?
4.使用find方法沒有查詢到數(shù)據(jù)的時候,返回值是什么?使用getReference方法沒有查詢到數(shù)據(jù)的時候,返回值是什么?

對象狀態(tài)

對象的狀態(tài)是JPA中非常重要的概念,描述了實體對象從瞬時到持久、從刪除到游離的狀態(tài)變換。對實體的操作其實就是對象實體狀態(tài)的改變, 這對于我們分析SQL的執(zhí)行情況有很大的幫助。

  • 瞬時狀態(tài)(Transient)
    使用new關(guān)鍵字創(chuàng)建出來的新對象,沒有OID,不在一級緩存中
  • 持久狀態(tài)(Persistent)
    調(diào)用持久化方法之后,將對象保存到數(shù)據(jù)庫中,對象狀態(tài)轉(zhuǎn)化成持久狀態(tài)
  • 游離狀態(tài)(Detached)
    對象存在于數(shù)據(jù)庫中,但是不在一級緩存中
  • 刪除狀態(tài)(Removed)
    事務(wù)一旦提交,對象就會被從數(shù)據(jù)庫中刪除,是介于持久狀態(tài)和被刪除之間的一個臨界狀態(tài)

我們可以通過下面的表格了解到各個狀態(tài)的特點:

狀態(tài) 是否在一級緩存 是否有OID
瞬時狀態(tài)(Transient)
持久狀態(tài)(Persistent)
游離狀態(tài)(Detached)
刪除狀態(tài)(Removed)

EntityManager提供一系列的方法管理實體對象的狀態(tài),包括:

  • persist, 將新創(chuàng)建的或已刪除的實體轉(zhuǎn)變?yōu)镻ersistent狀態(tài),數(shù)據(jù)存入數(shù)據(jù)庫。
  • remove,刪除持久狀態(tài)的實體
  • merge,將游離實體轉(zhuǎn)變?yōu)镻ersistent狀態(tài),數(shù)據(jù)存入數(shù)據(jù)庫。

如果使用了事務(wù)管理,則事務(wù)的commit/rollback也會改變實體的狀態(tài)。
如圖:


image.png

有了對對象狀態(tài)的了解之后,我們來分析面的案例中sql的發(fā)送

@Test
public void test() throws  Exception{
    EntityManager em = JPAUtil.getEntityManager();
    em.getTransaction().begin();
    //通過find方法查詢到處于持久狀態(tài)的User對象
    User u = em.find(User.class, 1L);
    u.setName("Lucy");//①
    em.getTransaction().commit();
    em.close();
}

執(zhí)行結(jié)果:
Hibernate: select user0_.id as id1_0_0_, user0_.hiredate as hiredate2_0_0_, user0_.name as name3_0_0_, user0_.sn as sn4_0_0_ from User user0_ where user0_.id=?

Hibernate: update User set hiredate=?, name=?, sn=? where id=?

  • 分析:
    ①:在這里,我們修改了查詢出來處于持久狀態(tài)的User對象的name屬性的值

我們并沒有調(diào)用merge方法去更新User對象,為什么會發(fā)送update語句呢?

  • 原因:
    首先,將數(shù)據(jù)從數(shù)據(jù)庫中查詢出來后,在內(nèi)存中會有兩份數(shù)據(jù),一份在EntityManager一級緩存區(qū)域,一份在EntityManager的快照區(qū),兩份數(shù)據(jù)完全一樣

然后,修改User的name屬性時,其實是修改的緩存區(qū)的數(shù)據(jù)

最后,在提交事務(wù)的時候,會清理一級緩存,此時會對比兩份數(shù)據(jù)是否一致,如果不一致,發(fā)送對應(yīng)的update語句將緩存中的臟數(shù)據(jù)(和數(shù)據(jù)庫中的數(shù)據(jù)不一致)同步到數(shù)據(jù)庫中

所以,在上面的例子中,我們看到執(zhí)行了一條更新語句,這樣相信大家就能夠理解了,這也是在我們了解了對象的狀態(tài)之后對SQL的發(fā)送有了更深入的認(rèn)識

WechatIMG9.jpeg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。