国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

從零開始 Spring Boot 49:Hibernate Entity Lifecycle

這篇具有很好參考價值的文章主要介紹了從零開始 Spring Boot 49:Hibernate Entity Lifecycle。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

從零開始 Spring Boot 49:Hibernate Entity Lifecycle

從零開始 Spring Boot 49:Hibernate Entity Lifecycle

圖源:簡書 (jianshu.com)

本文將介紹 Hibernate 的 Session 接口,以及如何用 Session 的相關 API 轉換實體(Entity)的生命周期狀態(tài)。

如果缺少的 JPA 和 Hibernate 的基本認識,可以閱讀前篇文章。

概念

持久化上下文

在 JPA 的相關概念中,存在一個持久化上下文(Persistence Context)。

持久化上下文處于代碼端與數(shù)據(jù)庫之間,充當一個容器或一級緩存的作用,負責管理運行時的實體(Entity),它可以在合適的時間從數(shù)據(jù)庫中加載數(shù)據(jù)到實體對象,也可以將實體對象“回寫”到數(shù)據(jù)庫。

在 Hibernate 中,持久化上下文由 org.hibernate.Session實例表示,在標準的 JPA 中,表現(xiàn)為jakarta. persistence. EntityManager。在使用 Hibernate 的時候,這兩者都可以使用,但相比EntityManager,Session是一個更豐富的接口,有時候可能會更有用。

實體狀態(tài)

與持久化上下文(本文特指Session)關聯(lián)的實體實例是存在狀態(tài)的,它們必然處于以下三種狀態(tài)之一:

  • transient,此實例從來沒有附加到 Session,且數(shù)據(jù)庫中也不存在對應的行數(shù)據(jù),這只是一個為保存數(shù)據(jù)到數(shù)據(jù)庫創(chuàng)建的新對象。
  • persistent,實例與唯一的 Session 對象關聯(lián),并對應數(shù)據(jù)庫中的一條記錄。Session 刷新后,將檢查數(shù)據(jù)一致性,并在不一致的情況下更新數(shù)據(jù)庫中的數(shù)據(jù)。
  • detached,實例曾經(jīng)與 Session 關聯(lián)(處于 persistent 狀態(tài)),但當前已經(jīng)不是。將實例從 Session 逐出(Session.evict)、關閉 Session 或將實例序列化/反序列化都會讓實例進入這個狀態(tài)。

可以通過 Session 的 API 將實例的狀態(tài)進行轉換:

從零開始 Spring Boot 49:Hibernate Entity Lifecycle

圖源:Baeldung

圖中的一些方法調用已經(jīng)作廢,比如save()。

持久的實體是最為關鍵的,處于這種狀態(tài)的實體實例會被 Session 管理和監(jiān)控,對這些實體的任何改變都會被記錄,且在事務提交或 Session 關閉時回寫到數(shù)據(jù)庫,且不需要我們調用任何其它方法。

持久實體也被稱作“被管理的實體”(Managed Entity)。實際上這些實體都有一個唯一的數(shù)據(jù)庫標識(database identifier),所以對這些實體的任何更改都會被傳播到數(shù)據(jù)庫。

  • 這也是為什么在實體類中要用@Id定義一個主鍵字段。
  • 要牢記,只有在事務提交后才會真正向數(shù)據(jù)庫中插入數(shù)據(jù),但在此之前,也會為持久實體分配數(shù)據(jù)庫標識。

Session API

下面我們看相關的 Session API。

在介紹相關 API 之前,需要對一些用到的工具類進行簡要說明。

@Component
public class HibernateLifecycleUtil {
    @SneakyThrows
    public List<EntityEntry> getManagedEntities(Session session) {
        // 傳入的參數(shù) session 不能是一個代理對象,否則會報類型轉換錯誤
        Map.Entry<Object, EntityEntry>[] entries = ((SessionImplementor) session).getPersistenceContext().reentrantSafeEntityEntries();
        return Arrays.stream(entries).map(e -> e.getValue()).collect(Collectors.toList());
    }
}

HibernateLifecycleUtil.getManagedEntities方法可以返回 Session 管理的實體實例(persistent)。

public class DirtyDataRecorderInterceptor implements Interceptor, Serializable {
    private static final List<Object> dirtyEntities = Collections.synchronizedList(new ArrayList<>());

    @Override
    public boolean onFlushDirty(Object entity, Object id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) throws CallbackException {
        boolean b = Interceptor.super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types);
        dirtyEntities.add(entity);
        return b;
    }

    public static List<Object> getDirtyEntities() {
        return dirtyEntities;
    }

    public static void clearDirtyEntitites() {
        dirtyEntities.clear();
    }
}

DirtyDataRecorderInterceptor 是一個 Hiberante 的攔截器,在這里用于記錄變成“臟數(shù)據(jù)”的實體實例(被修改過的 persistent 實體實例)。

之前的 Hibernate 攔截器往往通過擴展 EmptyInterceptor 實現(xiàn),該類已經(jīng)作廢。

persist

使用persist方法可以將一個瞬時(transient)的實體實例變成持久的(關聯(lián)到 Session):

Session session = sessionFactory.withOptions().interceptor(new DirtyDataRecorderInterceptor()).openSession();
Transaction transaction = session.beginTransaction();
var student = new Student("icexmoon", LocalDate.of(1990, 1, 1), Gender.MALE);
session.persist(student);
transaction.commit();
session.close();

事務提交后就能看到數(shù)據(jù)庫中多了一條新紀錄。

這里只展示測試用例中的關鍵代碼,可以從這里查看完整代碼。

特別的,如果實體實例已經(jīng)是持久的,那么調用persist方法不會有任何影響。如果實體實例是分離的,則會產(chǎn)生一個異常

// 添加實體,實體變成持久化的
session.persist(student);
// 刪除實體,實體變成已分離的
session.evict(student);
// 嘗試添加已分離的實體,會拋出一個PersistentObjectException異常
Assertions.assertThrows(PersistentObjectException.class, () -> {
session.persist(student);
});

merge

使用merge方法,可以用一個實體實例“更新” Session 中的對應實體實例。

一個典型的用法是在實體實例被分離后,改變其數(shù)據(jù),并合并(merge)回 Session:

List<Student> students = session.createQuery("from user_student", Student.class)
.getResultList();
var icexmoon = students.stream().filter(student -> student.getName().equals("icexmoon"))
.findFirst().get();
// 分離實體,分離后可以對實體進行序列化/反序列化等操作
session.evict(icexmoon);
icexmoon.setBirthDay(LocalDate.of(2000, 5, 1));
Student mergedIcexmoon = session.merge(icexmoon);
// 合并后的 entity 與原始 entity 不是同一個對象,但內容一致
Assertions.assertNotSame(mergedIcexmoon, icexmoon);
Assertions.assertEquals(mergedIcexmoon, icexmoon);

要注意的是,merge方法被調用后會返回“被管理的實體實例”,且該實例與用于合并的實例并不是同一個對象。前者是持久的,后者依然是分離的。

如果實體實例是瞬時的,調用merge方法會新建一個持久的實體實例并返回

// 用一個新的 Entity 添加
Student lalala = new Student("lalala", LocalDate.of(2001, 1, 1), Gender.MALE);
Student mergedLalala = session.merge(lalala);
// 合并后的 entity 與原始 entity 不是同一個對象,但內容一致
Assertions.assertNotSame(mergedLalala, lalala);
Assertions.assertEquals(mergedLalala, lalala);

如果實體實例已經(jīng)是持久的,調用這個方法不會有任何影響。

evict & remove

前邊已經(jīng)多次演示了evict的用途——將持久實體從 Session 中“驅逐”(變成分離實體)。

要注意的是,evict僅改變了實體的狀態(tài),并不會影響數(shù)據(jù)庫(不會將對應數(shù)據(jù)刪除):

List<Student> students = session.createQuery("from user_student", Student.class)
.getResultList();
var icexmoon = students.stream().filter(s -> s.getName().equals("icexmoon")).findFirst().get();
session.evict(icexmoon);
transaction.commit();
session.close();
var modifiedStudents = studentRepository.findAll();
Assertions.assertEquals(this.students.size() , modifiedStudents.size());

remove會將持久實體從 Session 中移除,變成瞬時實體。換言之,會刪除數(shù)據(jù)庫中對應的數(shù)據(jù):

List<Student> students = session.createQuery("from user_student", Student.class)
.getResultList();
var icexmoon = students.stream().filter(s -> s.getName().equals("icexmoon")).findFirst().get();
session.remove(icexmoon);
transaction.commit();
session.close();
var modifiedStudents = studentRepository.findAll();
Assertions.assertEquals(this.students.size() - 1, modifiedStudents.size());

因為remove后的實體狀態(tài)是瞬時的(transient),所以可以用persist再次添加:

var icexmoon = students.stream().filter(s -> s.getName().equals("icexmoon")).findFirst().get();
session.remove(icexmoon);
session.persist(icexmoon);

身份域

通常我們并不會直接修改(或添加)實體對象中的身份域(Indentity field),身份域都是由 Session 賦予和管理的。但如果我們愿意,完全可以通過修改和指定身份域去更新指定的持久實體:

var icexmoon = students.stream().filter(s -> s.getName().equals("icexmoon")).findFirst().get();
var student = new Student("icexmoon", LocalDate.of(2002, 1, 1), Gender.MALE);
student.setId(icexmoon.getId());
session.merge(student);

在這個示例中,并沒有像常見的那樣直接修改持久實體icexmoon中的屬性來變更數(shù)據(jù),而是創(chuàng)建了一個新的瞬時實體student,并且通過setId方法為其指定了和持久實體相同的身份域,此時調用merge方法,就不再是添加一個新的持久實體,而是修改了已有的持久實體(因為它們的 id 相同),并最終修改了數(shù)據(jù)庫中的數(shù)據(jù)。

一般而言實體類不需要 id 的 Setter,這里僅為了實現(xiàn)測試用例而添加。

The End,謝謝閱讀。

可以從這里獲取本文的完整示例代碼。文章來源地址http://www.zghlxwxcb.cn/news/detail-515120.html

參考資料

  • Hibernate Entity Lifecycle | Baeldung
  • Hibernate: save,persist, update, merge | Baeldung
  • 從零開始 Spring Boot 48:JPA & Hibernate - 紅茶的個人站點 (icexmoon.cn)
  • What Is the JDK com.sun.proxy.$Proxy Class? | Baeldung
  • Dynamic Proxies in Java | Baeldung
  • spring boot 中如何設置hibernate Interceptor
  • Hibernate Interceptors | Baeldung
  • Guide to the Hibernate EntityManager | Baeldung

到了這里,關于從零開始 Spring Boot 49:Hibernate Entity Lifecycle的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 從零開始學Spring Boot系列-集成Kafka

    Apache Kafka是一個開源的分布式流處理平臺,由LinkedIn公司開發(fā)和維護,后來捐贈給了Apache軟件基金會。Kafka主要用于構建實時數(shù)據(jù)管道和流應用。它類似于一個分布式、高吞吐量的發(fā)布-訂閱消息系統(tǒng),可以處理消費者網(wǎng)站的所有動作流數(shù)據(jù)。這種動作流數(shù)據(jù)包括頁面瀏覽、搜

    2024年03月21日
    瀏覽(23)
  • 從零開始學Spring Boot系列-集成mybatis

    在Spring Boot的應用開發(fā)中,MyBatis是一個非常流行的持久層框架,它支持定制化SQL、存儲過程以及高級映射。在本篇文章中,我們將學習如何在Spring Boot項目中集成MyBatis,以便通過MyBatis進行數(shù)據(jù)庫操作。 首先,我們需要在項目中添加MyBatis的依賴。在Spring Boot中,我們通常會使

    2024年03月10日
    瀏覽(24)
  • Spring Boot(04):讓你的Spring Boot應用“火力全開”,從零開始學習starter

    Spring Boot(04):讓你的Spring Boot應用“火力全開”,從零開始學習starter

    ????????Spring Boot是一款非常流行的Java開發(fā)框架,其具有快速開發(fā)、自動化配置、內嵌服務器、易于擴展等特點,因此備受開發(fā)者歡迎。在日常開發(fā)中,我們經(jīng)常需要在不同的環(huán)境中進行測試和部署,此時,如何實現(xiàn)開發(fā)、測試、生產(chǎn)環(huán)境的快速切換,成為了我們需要解決

    2024年04月13日
    瀏覽(22)
  • 從零開始 Spring Boot 37:初始化 ApplicationContext

    從零開始 Spring Boot 37:初始化 ApplicationContext

    圖源:簡書 (jianshu.com) 從前文可以知道,作為 Ioc 容器的 ApplicationContext,需要進行一系列步驟來初始化以最終就緒(對于 Web 應用來說就是可以提供Http服務)。 這些步驟大概可以分為以下內容: 準備上下文關聯(lián)的 Environment 。 初始化 ApplicationContext( ApplicationContextInitializers

    2024年02月08日
    瀏覽(26)
  • 從零開始 Spring Boot 38:Lombok 與依賴注入

    從零開始 Spring Boot 38:Lombok 與依賴注入

    圖源:簡書 (jianshu.com) 在之前的文章中,我詳細介紹了 Lombok 的用法,考慮到在 Spring 中使用依賴注入(DI)是如此的頻繁,因此有必要討論使用 Lombok 時可能對依賴注入造成的影響。 我們都知道,Spring 中的依賴注入分為三種情況: 通過屬性進行依賴注入。 通過構造器進行依

    2024年02月08日
    瀏覽(37)
  • 從零開始 Spring Boot 52:@Embedded 和 @Embeddable

    從零開始 Spring Boot 52:@Embedded 和 @Embeddable

    圖源:簡書 (jianshu.com) 這篇文章會介紹 @Embedded 和 @Embeddable 兩個注解在 JPA 中的用法。 先看一個示例: 這里使用了 Lombok 相關注解(比如 @Builder )幫助構建實體類,詳細內容可以閱讀我的相關文章。 user_student 是一個學生表,其中的 contacts_ 開頭的字段保存聯(lián)系人信息,這體

    2024年02月12日
    瀏覽(17)
  • 從零開始學Spring Boot系列-外部化配置

    從零開始學Spring Boot系列-外部化配置

    Spring Boot 允許你將配置外部化,以便可以在不同的環(huán)境中使用相同的應用程序代碼。可以使用屬性文件、YAML文件、環(huán)境變量和命令行參數(shù)將配置外部化。屬性值可以通過使用 @Value 注解直接注入 bean,可以通過 Spring 的 Environment 抽象訪問,也可以通過 @ConfigurationProperties。 Sp

    2024年04月10日
    瀏覽(27)
  • 從零開始 Spring Boot 51:JPA 中的默認列值

    從零開始 Spring Boot 51:JPA 中的默認列值

    圖源:簡書 (jianshu.com) JPA 是一個 ORM 框架,因此,通常我們需要在實體類中定義表結構,這其中就包含可能的字段默認值。 本文介紹如何在 Hibernate(JPA)中設置默認列值(Default Column Value)。 最簡單的方式是對實體類指定一個默認的屬性值,比如: 測試用例: 這樣做的缺點

    2024年02月11日
    瀏覽(23)
  • 從零開始 Spring Boot 57:JPA中的一對多關系

    從零開始 Spring Boot 57:JPA中的一對多關系

    圖源:簡書 (jianshu.com) 在上篇文章中我們介紹了如何在 JPA 中實現(xiàn)實體的一對一關系,在關系型數(shù)據(jù)庫設計中,除了一對一關系,還存在一對多關系。本篇文章介紹如何在 JPA 中實現(xiàn)一對多關系。 假設我們有兩張表,學生表和電子郵件賬號表,一個學生可以有多個電子郵件賬

    2024年02月12日
    瀏覽(25)
  • Spring Boot日志系統(tǒng)大揭秘:從零開始學習Spring Boot日志:常見問題解答和最佳實踐

    Spring Boot日志系統(tǒng)大揭秘:從零開始學習Spring Boot日志:常見問題解答和最佳實踐

    Spring Boot 日志機制和工具用于記錄應用程序的日志信息和追蹤應用程序的執(zhí)行過程。它集成了常用的日志框架,如 Log4j、logback、Java Util Logging等,并提供簡單易用的配置方式,讓開發(fā)人員可以方便地監(jiān)控應用程序的運行狀態(tài)和性能。在項目啟動時,日志已經(jīng)開始輸出,但尚未

    2024年02月08日
    瀏覽(48)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包