成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Hibernate對象管理入門?看這一篇就夠了

開發(fā) 前端
選擇使用 EntityManager? 還是 Session? 取決于我們的項(xiàng)目使用的持久化框架和標(biāo)準(zhǔn)。如果我們在使用 JPA,通常使用 EntityManager?;如果我們在使用 Hibernate,可以使用 Session。在某些情況下,兩者可以混合使用,但要確保它們不會(huì)相互沖突。

Hibernate的實(shí)現(xiàn)原理

Hibernate是一個(gè)用于對象關(guān)系映射(ORM)的Java框架,它允許開發(fā)者將Java對象映射到關(guān)系數(shù)據(jù)庫表,并提供數(shù)據(jù)持久化功能。以下是Hibernate的實(shí)現(xiàn)原理及其如何維護(hù)幾個(gè)關(guān)鍵狀態(tài)的詳細(xì)解釋:

實(shí)現(xiàn)原理

ORM(對象關(guān)系映射)
  • Hibernate通過將Java類映射到數(shù)據(jù)庫表,將Java類的屬性映射到表的列。
  • 使用Hibernate映射文件(XML或注解)定義這種映射關(guān)系。
Session管理
  • Hibernate的核心是Session接口,它是與數(shù)據(jù)庫交互的接口。
  • Session對象負(fù)責(zé)保存、更新、刪除和加載對象。
查詢語言
  • Hibernate提供HQL(Hibernate Query Language)和Criteria API,允許開發(fā)者以面向?qū)ο蟮姆绞讲樵償?shù)據(jù)庫。
緩存機(jī)制
  • Hibernate支持一級(jí)緩存(Session級(jí)別緩存)和二級(jí)緩存(SessionFactory級(jí)別緩存),以提高數(shù)據(jù)訪問性能。

狀態(tài)維護(hù)

Hibernate通過Session對象維護(hù)實(shí)體對象的生命周期狀態(tài)。主要有以下幾個(gè)狀態(tài):

Transient(瞬態(tài))
  • 實(shí)體對象被創(chuàng)建,但沒有與數(shù)據(jù)庫的任何Session關(guān)聯(lián)。
  • 對象沒有被持久化,沒有與數(shù)據(jù)庫記錄對應(yīng)。
  • 例如:Person person = new Person();
Persistent(持久化)
  • 實(shí)體對象被Session管理,與數(shù)據(jù)庫記錄對應(yīng)。
  • 對象的任何變更都會(huì)同步到數(shù)據(jù)庫中。
  • 例如:session.save(person);
Detached(游離)
  • 實(shí)體對象曾經(jīng)是持久化狀態(tài),但當(dāng)前Session已經(jīng)關(guān)閉。
  • 對象仍然存在,但不再與數(shù)據(jù)庫同步。
  • 例如:session.close();之后的對象。
Removed(刪除)
  • 實(shí)體對象在Session中標(biāo)記為刪除。
  • 在事務(wù)提交時(shí),對應(yīng)的數(shù)據(jù)庫記錄會(huì)被刪除。
  • 例如:session.delete(person);

Hibernate狀態(tài)維護(hù)的具體實(shí)現(xiàn)

Transient狀態(tài)
  • 當(dāng)對象被new出來但沒有被Session保存時(shí),它是Transient狀態(tài)。
  • 這些對象沒有數(shù)據(jù)庫標(biāo)識(shí)符,沒有與數(shù)據(jù)庫記錄關(guān)聯(lián)。
Persistent狀態(tài)
  • 當(dāng)對象通過session.save()、session.persist()、session.update()或查詢方法從Session獲取時(shí),它變?yōu)镻ersistent狀態(tài)。
  • Hibernate為這些對象分配數(shù)據(jù)庫標(biāo)識(shí)符,并將其狀態(tài)與數(shù)據(jù)庫同步。
  • 事務(wù)提交時(shí),Hibernate會(huì)將所有持久化對象的變更同步到數(shù)據(jù)庫中。
Detached狀態(tài)
  • 當(dāng)Session關(guān)閉后,持久化對象變?yōu)镈etached狀態(tài)。
  • 可以通過session.evict(object)將對象從Session中移除,使其變?yōu)镈etached狀態(tài)。
  • Detached對象可以通過session.update(object)、session.merge(object)重新關(guān)聯(lián)到Session,變回Persistent狀態(tài)。
Removed狀態(tài)
  • 當(dāng)對象通過session.delete(object)標(biāo)記為刪除時(shí),它變?yōu)镽emoved狀態(tài)。
  • 事務(wù)提交時(shí),Hibernate會(huì)刪除這些對象對應(yīng)的數(shù)據(jù)庫記錄。

事務(wù)管理和緩存機(jī)制

事務(wù)管理
  • Hibernate通常與JDBC或JTA事務(wù)管理器一起使用。
  • 事務(wù)管理確保了一組數(shù)據(jù)庫操作要么全部成功,要么全部回滾。
一級(jí)緩存
  • Hibernate的Session級(jí)別緩存,即一級(jí)緩存。
  • 每個(gè)Session對象都有一個(gè)一級(jí)緩存,緩存當(dāng)前事務(wù)中的所有持久化對象。
  • 當(dāng)Session加載對象時(shí),它首先在一級(jí)緩存中查找,如果找不到才從數(shù)據(jù)庫加載。
二級(jí)緩存
  • SessionFactory級(jí)別的緩存,跨Session共享。
  • 用于緩存被頻繁讀取但不經(jīng)常更改的數(shù)據(jù)。
  • Hibernate支持多種二級(jí)緩存實(shí)現(xiàn),如EHCache、OSCache、Infinispan等。

示例代碼

// Transient狀態(tài)
Person person = new Person();
person.setName("John");
// Persistent狀態(tài)
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(person); // person對象現(xiàn)在是Persistent狀態(tài)
tx.commit();
session.close(); // person對象現(xiàn)在是Detached狀態(tài)
// Removed狀態(tài)
Session newSession = sessionFactory.openSession();
Transaction newTx = newSession.beginTransaction();
newSession.delete(person); // person對象現(xiàn)在是Removed狀態(tài)
newTx.commit();
newSession.close();

通過這種機(jī)制,Hibernate能夠高效地管理對象與數(shù)據(jù)庫記錄之間的映射,并自動(dòng)處理數(shù)據(jù)的持久化和同步工作。

Hibernate的示例代碼

例子構(gòu)建

我們有一個(gè)配置管理的功能,現(xiàn)在定義了一個(gè)系統(tǒng)配置的表:biz_conf

建表語句

-- auto-generated definition
drop sequence if exists seq_seed;
create sequence seq_seed
    maxvalue 65535
    cycle;


alter sequence seq_seed owner to postgres;


drop function if exists snowflake_id;
create function snowflake_id(seed integer) returns bigint
    language plpgsql
as
$$
DECLARE
    start_epoch INT8 := 1546300800000;
    seq_id      INT8 := 0;
    now_millis  INT8 := 0;
    unique_id   INT8 := 0;
    region_val  int  := seed % 1024;
BEGIN
    SELECT nextval('seq_seed') % 4095 INTO seq_id;
    SELECT floor(extract(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
    unique_id := (now_millis - start_epoch) << 22;
    unique_id := unique_id | (region_val << 10);
    unique_id := unique_id | seq_id;
    RETURN unique_id;
END;
$$;


alter function snowflake_id(integer) owner to postgres;


drop function if exists random_id;
create function random_id() returns bigint
    language plpgsql
as
$$
BEGIN
    RETURN snowflake_id(0);
END;
$$;


alter function random_id() owner to postgres;


drop table if exists biz_conf;
create table biz_conf
(
    id           bigint       default random_id()       not null
        constraint biz_conf_pkey
            primary key,
    key_id       varchar(255)                               not null,
    val          varchar(255) default ''::character varying not null,
    created_time timestamp    default now()                 not null
);


-- 添加新增的幾個(gè)配置 sftp的
INSERT INTO biz_conf (key_id, val) VALUES
    ('sftp服務(wù)器地址', ''),
    ('sftp服務(wù)器端口', '22'),
    ('sftp服務(wù)器賬號(hào)', ''),
    ('sftp服務(wù)器密碼', ''),
    ('sftp上傳路徑', './'),
    ('sm2公鑰',''),
    ('sm2私鑰','');

關(guān)系實(shí)體映射

我們在目錄entity下面存放關(guān)系實(shí)體映射

import jakarta.persistence.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;


import java.sql.Timestamp;




@Entity
@Table(name = "biz_conf")
@DynamicInsert
@DynamicUpdate
public class BizConf {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false, unique = true, columnDefinition = "INT8")
    public Long id;


    @Column(name = "key_id", length = 256)
    public String key_id;


    @Column(name = "val",length = 256)
    public String val;


    @Column(name = "created_time", updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    public Timestamp created_time;
}

經(jīng)過我們的實(shí)踐中,在關(guān)系實(shí)體映射中,可以將字段直接置為public,同時(shí)將字段名直接與數(shù)據(jù)庫字段名保持一致。便于后續(xù)映射。(這里可以采用private+駝峰的模式,再使用Lombok,但是代碼直觀性不夠,如果沒有接觸過lombok,將幾乎看不出來原理,同時(shí)我們對各個(gè)反序列化框架對lombok的支持度存有一定的擔(dān)心,故這里我們是建議使用public + 原始字段名)。

構(gòu)建領(lǐng)域?qū)ο髮?shí)體

import com.cqcdi.siem.log_audit_admin.entity.biz.BizConf;
import com.cqcdi.siem.log_audit_admin.maindao.BaseEntityDomain;


/**
 * 類描述: BizConfModel
 *
 **/
public class BizConfModel  extends BaseEntityDomain<BizConf> {


    public BizConfModel() {}


    public BizConfModel(Long id){
        super(id);
    }


    @Override
    protected Class<BizConf> getEntityClass() {
        return BizConf.class;
    }


    @Override
    protected String getEntityClassName() {
        return "BizConf";
    }
}

領(lǐng)域?qū)ο髮?shí)體構(gòu)建完畢之后,我們就可以直接使用。這里的領(lǐng)域?qū)嶓w與我們的dao是有區(qū)別的,dao層一般是一個(gè)spring bean,也就是說基本上是全局單例,不會(huì)有對象狀態(tài)。但是我們可以發(fā)現(xiàn),領(lǐng)域?qū)嶓w實(shí)際上是沒有被標(biāo)記未一個(gè)bean的,所以領(lǐng)域?qū)嶓w實(shí)際上是可以管理對象的狀態(tài)的。

正因?yàn)槿绱耍琱ibernate和領(lǐng)域?qū)ο蟮母拍罱Y(jié)合,我們可以把對象的各種狀態(tài)管理起來。例如,我們可以先查出一個(gè)對象,修改了一些值之后,直接調(diào)用刷新機(jī)制。(這個(gè)與瞬時(shí)態(tài)的寫法其實(shí)是類似的,但是理念不太一樣)

例子

使用領(lǐng)域模型查詢和更新

/**
     * 使用領(lǐng)域模型
     */
    @Test
    public void demo1() {
        BizConfModel bizConfModel = new BizConfModel();
        List<BizConf> bizConfs = bizConfModel.getAll();
        bizConfs.stream().forEach(bizConf -> {
            System.out.println(bizConf.key_id + " " + bizConf.val);
            bizConf.val = "add_"+ DatetimeUtil.getViewStrOfDatetime(LocalDateTime.now()) + "_"+ bizConf.val; // 對象處于被管理的狀態(tài),如果修改了對象,直接調(diào)用flush即可入庫
            bizConfModel.flush();
        });
    }

使用瞬時(shí)態(tài)查詢和更新(更新不會(huì)生效)

/**
     * 使用工具類:所有操作都是瞬時(shí)態(tài)
     */
    @Test
    public void demo2(){
        List<BizConf> bizConfs = BaseEntityUtils.getAll(BizConf.class);
        bizConfs.stream().forEach(bizConf -> {
            System.out.println(bizConf.key_id + " " + bizConf.val);
            bizConf.val = "add_"+ DatetimeUtil.getViewStrOfDatetime(LocalDateTime.now()) + "_" + bizConf.val; // 對象處于瞬時(shí)態(tài)態(tài),如果修改了對象,直接調(diào)用flush,沒有入庫
            BaseEntityUtils.flush(); // 不生效
        });
    }

使用領(lǐng)域模型、瞬時(shí)工具類、原生sql和jpql進(jìn)行分頁查詢

/**
     * 分頁查詢
     */
    @Test
    public void demo3(){
        // 使用領(lǐng)域模型進(jìn)行分頁查詢
        BizConfModel domain = new BizConfModel();
        domain.setDomainEntity(new BizConf());
        domain.getDomainEntity().key_id = "sm2私鑰";
        Page<BizConf> ans = domain.defaultPage(1,10);


        ans.getContent().stream().forEach(bizConf1 -> {
            System.out.println(bizConf1.key_id + " " + bizConf1.val);
        });
        System.out.println(" ------------------ ");


        // 使用工具類進(jìn)行分頁查詢
        BizConf bizConf = new BizConf();
        bizConf.key_id = "sm2私鑰";
        Page<BizConf> bizConfs2 = BaseEntityUtils.defaultPage(bizConf,1,10,BizConf.class);
        bizConfs2.getContent().stream().forEach(bizConf1 -> {
            System.out.println(bizConf1.key_id + " " + bizConf1.val);
        });
        System.out.println(" ------------------ ");


        // 使用sql分頁查詢
        // 使用原生sql查詢
        List<BizConf> bizConfs1 = BaseEntityUtils.executeNativeQuery("select * from biz_conf offset 1 limit 10",BizConf.class);
        bizConfs1.stream().forEach(bizConf1 -> {
            System.out.println(bizConf1.key_id + " " + bizConf1.val);
        });
        // 使用Jpql查詢:把原生sql中的表名替換成對象
        System.out.println(" ------------------ ");
        List<BizConf> bizConfList = BaseEntityUtils.findByConditions("from BizConf",new HashMap<>(),1,10,BizConf.class);
        bizConfList.stream().forEach(bizConf1 -> {
            System.out.println(bizConf1.key_id + " " + bizConf1.val);
        });
    }

Hibernate中的級(jí)聯(lián)操作

Hibernate提供了哪些方法來實(shí)現(xiàn)對象關(guān)系映射(ORM)

@OneToMany

@OneToMany 注解用于描述一對多(One-to-Many)的關(guān)聯(lián)關(guān)系,即一個(gè)實(shí)體對象關(guān)聯(lián)多個(gè)相關(guān)實(shí)體對象。這種關(guān)系通常在數(shù)據(jù)庫中以外鍵來表示。以下是 @OneToMany 注解的基本使用方式

在一的一方(One)
  • 在擁有多個(gè)相關(guān)實(shí)體的一方,使用 @OneToMany 注解。
  • 使用 mappedBy 屬性指定多的一方中關(guān)聯(lián)的屬性名。
@Entity
public class ParentEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields
    @OneToMany(mappedBy = "parentEntity")
    private List<ChildEntity> children;
    // getters and setters
}
在多的一方(Many)
  • 在擁有單個(gè)關(guān)聯(lián)實(shí)體的一方,使用 @ManyToOne 注解。
  • 使用 @JoinColumn 注解指定關(guān)聯(lián)的數(shù)據(jù)庫表列。
@Entity
public class ChildEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields
    @ManyToOne
    @JoinColumn(name = "parent_entity_id")
    private ParentEntity parentEntity;
    // getters and setters
}

在上述示例中,ParentEntity 擁有多個(gè) ChildEntity,因此在 ParentEntity 類上使用了 @OneToMany 注解,并通過 mappedBy 屬性指定了關(guān)聯(lián)關(guān)系的屬性名,這里是 "parentEntity"。在 ChildEntity 類中,使用 @ManyToOne 注解指定了多的一方的類型,并通過 @JoinColumn 注解指定了關(guān)聯(lián)的數(shù)據(jù)庫表列。 請注意,使用 @OneToMany 注解時(shí),通常需要考慮懶加載、級(jí)聯(lián)操作、集合類型等配置。例如,可以使用 fetch 屬性來指定加載策略,使用 cascade 屬性來指定級(jí)聯(lián)操作的行為。配置的詳細(xì)信息會(huì)根據(jù)具體的業(yè)務(wù)需求而變化。

@OneToOne

  • 用于描述一對一關(guān)系。
  • 在關(guān)聯(lián)的兩個(gè)實(shí)體類的其中一個(gè)實(shí)體類的屬性上使用 @OneToOne 注解,表示該屬性與另一個(gè)實(shí)體類建立一對一關(guān)系。
  • 使用 mappedBy 屬性指定另一個(gè)實(shí)體類中關(guān)聯(lián)的屬性名。
```java
@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields
    @OneToOne(mappedBy = "employee")
    private Address address;
    // getters and setters
}
@Entity
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields
    @OneToOne
    @JoinColumn(name = "employee_id")
    private Employee employee;
    // getters and setters
}
```

@ManyToOne

  • 用于描述多對一關(guān)系。
  • 在關(guān)聯(lián)的兩個(gè)實(shí)體類的其中一個(gè)實(shí)體類的屬性上使用 @ManyToOne 注解,表示該屬性與另一個(gè)實(shí)體類建立多對一關(guān)系。
  • 使用 @JoinColumn 注解指定關(guān)聯(lián)的數(shù)據(jù)庫表列,可以使用 name 屬性指定列名。
```java
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields
    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;
    // getters and setters
}
@Entity
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields
    @OneToMany(mappedBy = "customer")
    private List<Order> orders;
    // getters and setters
}
```

@ManyToMany

  • 用于描述多對多關(guān)系。
  • 在關(guān)聯(lián)的兩個(gè)實(shí)體類的其中一個(gè)實(shí)體類的屬性上使用 @ManyToMany 注解,表示該屬性與另一個(gè)實(shí)體類建立多對多關(guān)系。
  • 使用 @JoinTable 注解指定中間表的信息,包括表名、關(guān)聯(lián)列、被關(guān)聯(lián)列等。
```java
@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields
    @ManyToMany
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private List<Course> courses;
    // getters and setters
}
@Entity
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields
    @ManyToMany(mappedBy = "courses")
    private List<Student> students;
    // getters and setters
}
```

這些注解提供了豐富的選項(xiàng),以滿足不同場景下的實(shí)體關(guān)系映射需求。在使用這些注解時(shí),根據(jù)具體的業(yè)務(wù)需求和數(shù)據(jù)庫表結(jié)構(gòu),適當(dāng)?shù)嘏渲藐P(guān)聯(lián)關(guān)系、關(guān)聯(lián)表等信息,以實(shí)現(xiàn)正確而高效的數(shù)據(jù)映射。

級(jí)聯(lián)操作的例子

在Hibernate中,級(jí)聯(lián)操作可以通過在實(shí)體類的映射文件或注解中設(shè)置相應(yīng)的級(jí)聯(lián)關(guān)系來實(shí)現(xiàn)。以下是級(jí)聯(lián)操作的例子:

級(jí)聯(lián)查詢

假設(shè)有兩個(gè)實(shí)體類:ParentEntity 和 ChildEntity,它們之間存在一對多的關(guān)系。可以通過配置級(jí)聯(lián)關(guān)系來在查詢時(shí)同時(shí)獲取關(guān)聯(lián)的子實(shí)體。

@Entity
public class ParentEntity {
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    private List<ChildEntity> children;


    // other fields and methods
}


@Entity
public class ChildEntity {
    @ManyToOne
    @JoinColumn(name = "parent_id")
    private ParentEntity parent;


    // other fields and methods
}

在上述例子中,cascade = CascadeType.ALL 表示在父實(shí)體上的所有操作都將級(jí)聯(lián)到子實(shí)體上,包括查詢。

級(jí)聯(lián)插入

ParentEntity parent = new ParentEntity();
ChildEntity child1 = new ChildEntity();
ChildEntity child2 = new ChildEntity();


// 設(shè)置關(guān)聯(lián)關(guān)系
child1.setParent(parent);
child2.setParent(parent);


// 將子實(shí)體添加到父實(shí)體的集合中
parent.getChildren().add(child1);
parent.getChildren().add(child2);


// 保存父實(shí)體,級(jí)聯(lián)保存子實(shí)體
entityManager.persist(parent);

在上述例子中,當(dāng)保存父實(shí)體時(shí),由于設(shè)置了級(jí)聯(lián)保存 (cascade = CascadeType.ALL),子實(shí)體也會(huì)被保存。

級(jí)聯(lián)更新

ParentEntity parent = entityManager.find(ParentEntity.class, parentId);
parent.setName("Updated Parent");


// 子實(shí)體的相關(guān)屬性也被更新
parent.getChildren().forEach(child -> child.setDescription("Updated Child"));


// 保存父實(shí)體,級(jí)聯(lián)更新子實(shí)體
entityManager.merge(parent);

在上述例子中,由于設(shè)置了級(jí)聯(lián)更新 (cascade = CascadeType.ALL),當(dāng)更新父實(shí)體時(shí),子實(shí)體的相關(guān)屬性也會(huì)被更新。

級(jí)聯(lián)刪除

ParentEntity parent = entityManager.find(ParentEntity.class, parentId);


// 刪除父實(shí)體,級(jí)聯(lián)刪除子實(shí)體
entityManager.remove(parent);

在上述例子中,由于設(shè)置了級(jí)聯(lián)刪除 (cascade = CascadeType.ALL),當(dāng)刪除父實(shí)體時(shí),子實(shí)體也會(huì)被級(jí)聯(lián)刪除。

請注意,級(jí)聯(lián)操作應(yīng)該謹(jǐn)慎使用,確保了解它們的影響,以避免意外的數(shù)據(jù)變更。

級(jí)聯(lián)操作中的cascade

cascade 是 Hibernate 中用于配置級(jí)聯(lián)操作的重要屬性之一。該屬性用于指定在對父實(shí)體進(jìn)行特定操作時(shí),是否要同時(shí)應(yīng)用相同的操作到關(guān)聯(lián)的子實(shí)體上。cascade 屬性通常在關(guān)聯(lián)關(guān)系的注解或映射文件中進(jìn)行配置。 以下是一些常用的 cascade 屬性值

CascadeType.PERSIST (級(jí)聯(lián)保存)

當(dāng)對父實(shí)體調(diào)用 persist() 方法時(shí),子實(shí)體也會(huì)被保存。

CascadeType.MERGE (級(jí)聯(lián)更新)

當(dāng)對父實(shí)體調(diào)用 merge() 方法時(shí),子實(shí)體也會(huì)被更新。

CascadeType.REMOVE (級(jí)聯(lián)刪除)

當(dāng)對父實(shí)體調(diào)用 remove() 方法時(shí),子實(shí)體也會(huì)被刪除。

CascadeType.REFRESH (級(jí)聯(lián)刷新)

當(dāng)對父實(shí)體調(diào)用 refresh() 方法時(shí),子實(shí)體也會(huì)被刷新。

CascadeType.DETACH (級(jí)聯(lián)分離)

當(dāng)對父實(shí)體調(diào)用 detach() 方法時(shí),子實(shí)體也會(huì)被分離。

CascadeType.ALL (全部)

包含所有的級(jí)聯(lián)操作,即包括上述的 PERSIST、MERGE、REMOVE、REFRESH、DETACH。 在實(shí)體類的映射文件或使用注解進(jìn)行配置時(shí),cascade 屬性可以直接指定一個(gè)或多個(gè)級(jí)聯(lián)操作,如下所示

@Entity
public class ParentEntity {
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    private List<ChildEntity> children;
    // other fields and methods
}

在上述例子中,cascade = CascadeType.ALL 表示對 ParentEntity 上的所有操作都會(huì)級(jí)聯(lián)到關(guān)聯(lián)的 ChildEntity 上。我們可以根據(jù)實(shí)際需求選擇適當(dāng)?shù)募?jí)聯(lián)操作。然而,要注意過度使用級(jí)聯(lián)操作可能導(dǎo)致不必要的性能開銷和意外的數(shù)據(jù)變更。

級(jí)聯(lián)操作的好處

級(jí)聯(lián)更新(CascadeType.MERGE)和級(jí)聯(lián)插入(CascadeType.PERSIST)在Hibernate中確實(shí)帶來了一些好處,但同時(shí)也需要謹(jǐn)慎使用以避免潛在的問題。以下是它們的一些優(yōu)勢

級(jí)聯(lián)更新(CascadeType.MERGE)的好處

簡化操作

級(jí)聯(lián)更新允許我們在對父實(shí)體進(jìn)行 merge() 操作時(shí),同時(shí)更新關(guān)聯(lián)的子實(shí)體。這樣,我們不需要手動(dòng)處理每個(gè)關(guān)聯(lián)實(shí)體的更新。

維護(hù)關(guān)聯(lián)關(guān)系

級(jí)聯(lián)更新確保了關(guān)聯(lián)實(shí)體與父實(shí)體的狀態(tài)同步,無需手動(dòng)調(diào)用 merge() 方法來更新關(guān)聯(lián)實(shí)體。

減少代碼冗余

避免了手動(dòng)維護(hù)關(guān)聯(lián)實(shí)體狀態(tài)的繁瑣操作,簡化了代碼邏輯。

級(jí)聯(lián)插入(CascadeType.PERSIST)的好處

原子性

當(dāng)我們持久化一個(gè)新的父實(shí)體時(shí),級(jí)聯(lián)插入可以確保相關(guān)的子實(shí)體也被保存。這確保了相關(guān)實(shí)體的一致性,不需要單獨(dú)保存每個(gè)關(guān)聯(lián)實(shí)體。

簡化操作

通過級(jí)聯(lián)插入,我們不需要為每個(gè)關(guān)聯(lián)實(shí)體顯式調(diào)用 persist() 方法。Hibernate會(huì)自動(dòng)保存相關(guān)的關(guān)聯(lián)實(shí)體。

減少錯(cuò)誤

避免了遺漏保存關(guān)聯(lián)實(shí)體的錯(cuò)誤,提高了代碼的可維護(hù)性。 然而,盡管級(jí)聯(lián)更新和插入帶來了便利,但需要注意以下幾點(diǎn)

性能開銷

過度使用級(jí)聯(lián)操作可能導(dǎo)致性能開銷,尤其是在涉及大量關(guān)聯(lián)實(shí)體的情況下。謹(jǐn)慎選擇使用級(jí)聯(lián),以避免不必要的數(shù)據(jù)庫操作。

數(shù)據(jù)一致性

級(jí)聯(lián)操作可能導(dǎo)致數(shù)據(jù)一致性的問題。例如,級(jí)聯(lián)插入可能會(huì)導(dǎo)致關(guān)聯(lián)實(shí)體被保存,即使它們不是父實(shí)體的直接子元素。

潛在的循環(huán)

避免配置導(dǎo)致循環(huán)級(jí)聯(lián)操作,可能導(dǎo)致無限遞歸或棧溢出的問題。 總體而言,級(jí)聯(lián)操作是強(qiáng)大的工具,但在使用時(shí)需要仔細(xì)考慮,并根據(jù)具體的業(yè)務(wù)需求和性能考慮來選擇適當(dāng)?shù)募?jí)聯(lián)策略。

使用級(jí)聯(lián)操作的策略

使用級(jí)聯(lián)操作時(shí),需要考慮不同的策略、場景和注意事項(xiàng),以確保數(shù)據(jù)的一致性、性能和可維護(hù)性。以下是一些常見的策略、使用場景和注意事項(xiàng)

策略

CascadeType.MERGE(級(jí)聯(lián)更新)

適用場景

當(dāng)我們希望在更新父實(shí)體時(shí)同時(shí)更新關(guān)聯(lián)的子實(shí)體時(shí)。

注意事項(xiàng)

避免循環(huán)級(jí)聯(lián),確保不會(huì)形成無限遞歸的級(jí)聯(lián)更新。

CascadeType.PERSIST(級(jí)聯(lián)插入)

適用場景

當(dāng)我們希望在插入父實(shí)體時(shí)同時(shí)插入關(guān)聯(lián)的子實(shí)體時(shí)。

注意事項(xiàng)

小心處理關(guān)聯(lián)實(shí)體之間的引用,以避免不必要的插入操作。

CascadeType.REMOVE(級(jí)聯(lián)刪除)

適用場景

當(dāng)我們希望在刪除父實(shí)體時(shí)同時(shí)刪除關(guān)聯(lián)的子實(shí)體時(shí)。

注意事項(xiàng)

謹(jǐn)慎使用級(jí)聯(lián)刪除,確保了解刪除操作的影響,避免誤刪關(guān)聯(lián)實(shí)體。

CascadeType.ALL(全部)

適用場景

當(dāng)我們希望對父實(shí)體的所有操作都級(jí)聯(lián)到關(guān)聯(lián)的子實(shí)體時(shí)。

注意事項(xiàng)

盡量避免使用過多的級(jí)聯(lián)操作,只選擇實(shí)際需要的操作,以提高性能和可控性。

使用場景

一對一關(guān)系

當(dāng)兩個(gè)實(shí)體之間是一對一關(guān)系時(shí),可以使用級(jí)聯(lián)來確保它們的狀態(tài)同步。

一對多關(guān)系

在一對多關(guān)系中,級(jí)聯(lián)操作可用于確保在更新或刪除父實(shí)體時(shí),相關(guān)的子實(shí)體也會(huì)被同步更新或刪除。

父子關(guān)系

在父子關(guān)系中,級(jí)聯(lián)插入和更新可簡化操作,確保整個(gè)對象圖的一致性。

注意事項(xiàng)

性能開銷

過度使用級(jí)聯(lián)操作可能導(dǎo)致性能問題。在選擇級(jí)聯(lián)操作時(shí),需要考慮數(shù)據(jù)庫的負(fù)擔(dān)和查詢效率。

循環(huán)級(jí)聯(lián)

避免配置導(dǎo)致循環(huán)級(jí)聯(lián)操作,可能導(dǎo)致無限遞歸或棧溢出的問題。

數(shù)據(jù)一致性

注意級(jí)聯(lián)操作可能導(dǎo)致的數(shù)據(jù)一致性問題,特別是在刪除操作中。

關(guān)聯(lián)實(shí)體引用

確保處理關(guān)聯(lián)實(shí)體之間的引用,以避免不必要的插入和更新操作。

深度級(jí)聯(lián)

謹(jǐn)慎使用深度級(jí)聯(lián),盡量避免將級(jí)聯(lián)操作應(yīng)用到整個(gè)對象圖。

總體而言,級(jí)聯(lián)操作是一個(gè)強(qiáng)大的特性,但需要謹(jǐn)慎使用。根據(jù)具體的業(yè)務(wù)需求和性能要求,選擇適當(dāng)?shù)募?jí)聯(lián)策略,并確保了解每個(gè)級(jí)聯(lián)操作的影響。測試和審查數(shù)據(jù)變更的結(jié)果也是使用級(jí)聯(lián)操作時(shí)的關(guān)鍵步驟。

避免循環(huán)級(jí)聯(lián)的方法

循環(huán)級(jí)聯(lián)

什么是循環(huán)級(jí)聯(lián)

循環(huán)級(jí)聯(lián)(Cascading Cycle)是指在對象關(guān)系映射(ORM)中,特別是在Hibernate等ORM框架中,通過級(jí)聯(lián)操作導(dǎo)致的循環(huán)引用或無限遞歸的情況。 在ORM中,級(jí)聯(lián)操作是指在對一個(gè)實(shí)體進(jìn)行操作時(shí),自動(dòng)對其關(guān)聯(lián)的其他實(shí)體進(jìn)行相應(yīng)的操作,比如保存、更新或刪除。循環(huán)級(jí)聯(lián)則是指這種級(jí)聯(lián)操作形成了一個(gè)循環(huán)鏈,其中實(shí)體之間的級(jí)聯(lián)操作形成一個(gè)循環(huán),可能導(dǎo)致無限遞歸或棧溢出等問題。 例如,考慮兩個(gè)實(shí)體Parent和Child,它們之間是雙向關(guān)聯(lián)的

// Parent Entity
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> children;
// Child Entity
@ManyToOne
private Parent parent;

在上述例子中,當(dāng)我們嘗試保存或更新一個(gè)Parent實(shí)體時(shí),由于配置了CascadeType.ALL,將觸發(fā)對關(guān)聯(lián)的Child實(shí)體進(jìn)行級(jí)聯(lián)操作。如果在Child實(shí)體中也配置了類似的級(jí)聯(lián)操作,就可能形成一個(gè)循環(huán)鏈。 循環(huán)級(jí)聯(lián)可能導(dǎo)致以下問題

無限遞歸

級(jí)聯(lián)操作不斷觸發(fā),導(dǎo)致無限遞歸,最終可能導(dǎo)致棧溢出或應(yīng)用程序無法正常執(zhí)行。

性能問題

無限遞歸會(huì)導(dǎo)致性能下降,因?yàn)橄到y(tǒng)不斷地執(zhí)行相同的級(jí)聯(lián)操作。 為了避免循環(huán)級(jí)聯(lián),需要謹(jǐn)慎配置級(jí)聯(lián)操作,避免形成循環(huán)鏈,或者使用一些技術(shù)手段(如懶加載、手動(dòng)維護(hù)關(guān)聯(lián)關(guān)系等)來規(guī)避潛在的問題。在使用ORM框架時(shí),了解和審查關(guān)聯(lián)關(guān)系、級(jí)聯(lián)配置以及避免雙向關(guān)聯(lián)等是很重要的。

循環(huán)級(jí)聯(lián)的例子

以下是一個(gè)簡單的循環(huán)級(jí)聯(lián)的例子,使用Java中的Hibernate注解。考慮兩個(gè)實(shí)體類Person和Address,它們之間是雙向關(guān)聯(lián)的,并且都配置了級(jí)聯(lián)保存操作。

import javax.persistence.*;
@Entity
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id")
    private Address address;
    // Getter and Setter methods
}
@Entity
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String street;
    @OneToMany(mappedBy = "address", cascade = CascadeType.ALL)
    private List<Person> residents;
    // Getter and Setter methods
}

在上述例子中,Person和Address之間存在雙向關(guān)聯(lián)。一個(gè)Person實(shí)體擁有對應(yīng)的Address實(shí)體,而一個(gè)Address實(shí)體擁有對應(yīng)的List<Person>。此外,配置了級(jí)聯(lián)操作,使得對Person實(shí)體的保存操作會(huì)級(jí)聯(lián)保存關(guān)聯(lián)的Address實(shí)體,而對Address實(shí)體的保存操作也會(huì)級(jí)聯(lián)保存關(guān)聯(lián)的Person實(shí)體。 這種配置可能導(dǎo)致循環(huán)級(jí)聯(lián)的問題。例如,當(dāng)我們嘗試保存一個(gè)Person實(shí)體時(shí),Hibernate會(huì)自動(dòng)保存關(guān)聯(lián)的Address實(shí)體,而在保存Address實(shí)體時(shí),又會(huì)級(jí)聯(lián)保存關(guān)聯(lián)的Person實(shí)體,形成了一個(gè)循環(huán)鏈。 為了避免循環(huán)級(jí)聯(lián),可以通過適當(dāng)配置級(jí)聯(lián)操作、使用mappedBy來指定關(guān)系的擁有方、懶加載等方式來規(guī)避潛在的問題。在實(shí)際應(yīng)用中,需要根據(jù)具體場景來合理設(shè)計(jì)實(shí)體之間的關(guān)系以及級(jí)聯(lián)操作。

如何檢測實(shí)體級(jí)聯(lián)中是否有級(jí)聯(lián)循環(huán)

檢測實(shí)體級(jí)聯(lián)中是否存在級(jí)聯(lián)循環(huán)是一項(xiàng)重要的任務(wù),因?yàn)榧?jí)聯(lián)循環(huán)可能導(dǎo)致無限遞歸或棧溢出等問題。以下是一些方法,我們可以使用它們來檢測實(shí)體級(jí)聯(lián)中是否存在潛在的級(jí)聯(lián)循環(huán)

使用工具或插件:

有一些工具和插件可以幫助檢測級(jí)聯(lián)循環(huán)。例如,Hibernate Envers(Hibernate的審計(jì)插件)可以用于檢測潛在的級(jí)聯(lián)問題。在使用這些工具時(shí),我們需要按照它們的文檔進(jìn)行配置和使用。 也可以生成對象圖或者類圖進(jìn)行分析。

手動(dòng)分析映射關(guān)系:

仔細(xì)分析實(shí)體之間的映射關(guān)系,特別是雙向關(guān)聯(lián)的情況。確保在雙向關(guān)聯(lián)時(shí),通過使用mappedBy等屬性正確指定關(guān)系的擁有方,以避免循環(huán)級(jí)聯(lián)。

測試場景:

創(chuàng)建一些簡單的測試場景,執(zhí)行級(jí)聯(lián)操作,觀察是否會(huì)發(fā)生無限遞歸或棧溢出。這種方法雖然相對簡單,但可能不適用于復(fù)雜的數(shù)據(jù)模型。

審查日志:

在Hibernate的日志中啟用詳細(xì)的日志記錄級(jí)別,觀察是否有關(guān)于級(jí)聯(lián)操作的異常或警告。有時(shí),日志中會(huì)提供有關(guān)級(jí)聯(lián)操作的有用信息,以便我們能夠識(shí)別潛在的循環(huán)問題。

使用斷點(diǎn)調(diào)試:

使用調(diào)試工具在執(zhí)行級(jí)聯(lián)操作時(shí)設(shè)置斷點(diǎn)。觀察在實(shí)體關(guān)系中是否存在循環(huán),以及在何處可能觸發(fā)無限遞歸。

自定義檢測邏輯:

編寫自定義的檢測邏輯,檢查實(shí)體之間的關(guān)聯(lián)關(guān)系。這可能涉及遞歸地檢查實(shí)體的關(guān)聯(lián)關(guān)系,并確保沒有循環(huán)。這種方法可能相對復(fù)雜,但可以提供更精細(xì)的控制。 我們應(yīng)該注意到的是,由于每個(gè)應(yīng)用程序和數(shù)據(jù)模型都是獨(dú)特的,沒有一種通用的方法適用于所有情況。建議使用上述方法的組合,根據(jù)我們的具體場景選擇最適合的方式。在檢測到潛在的級(jí)聯(lián)循環(huán)問題時(shí),及時(shí)調(diào)整映射配置,以避免可能的運(yùn)行時(shí)異常。

解決方案

在Hibernate中,實(shí)體級(jí)聯(lián)操作可能導(dǎo)致循環(huán)級(jí)聯(lián)操作,為了避免這種問題,我們可以采取以下一些方案

使用CascadeType中的subset進(jìn)行限制:

在@OneToMany或@ManyToMany注解中,通過cascade屬性指定級(jí)聯(lián)操作的類型,可以選擇性地配置級(jí)聯(lián),而不是使用CascadeType.ALL。使用CascadeType.PERSIST、CascadeType.MERGE等來限制級(jí)聯(lián)的范圍,以避免不必要的級(jí)聯(lián)操作。

@OneToMany(mappedBy = "parent", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<ChildEntity> children;

手動(dòng)維護(hù)關(guān)聯(lián)關(guān)系:

不使用級(jí)聯(lián),手動(dòng)管理關(guān)聯(lián)關(guān)系。在進(jìn)行操作時(shí),手動(dòng)添加或刪除關(guān)聯(lián)關(guān)系,而不依賴于級(jí)聯(lián)操作。這樣可以更精確地控制實(shí)體之間的關(guān)系。

使用orphanRemoval屬性:

當(dāng)orphanRemoval設(shè)置為true時(shí),如果從父實(shí)體中移除了某個(gè)子實(shí)體,該子實(shí)體將被刪除。這可以避免在刪除父實(shí)體時(shí)引起子實(shí)體的級(jí)聯(lián)刪除,從而防止循環(huán)級(jí)聯(lián)。

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ChildEntity> children;

懶加載:

將關(guān)聯(lián)關(guān)系設(shè)置為懶加載,這樣只有在實(shí)際使用關(guān)聯(lián)實(shí)體時(shí)才會(huì)加載。通過延遲加載,可以減少不必要的級(jí)聯(lián)操作。

@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<ChildEntity> children;

避免雙向關(guān)聯(lián):

雙向關(guān)聯(lián)時(shí),容易出現(xiàn)循環(huán)級(jí)聯(lián)問題。考慮是否真的需要雙向關(guān)聯(lián),如果可以單向關(guān)聯(lián)就單向關(guān)聯(lián),從而避免循環(huán)級(jí)聯(lián)的發(fā)生。

// 避免雙向關(guān)聯(lián)
// ParentEntity
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<ChildEntity> children;
// ChildEntity
@ManyToOne
private ParentEntity parent;

使用@JsonManagedReference和@JsonBackReference (僅在使用Jackson等庫進(jìn)行JSON序列化時(shí)有效):

如果我們使用JSON序列化工具,如Jackson,我們可以使用@JsonManagedReference和@JsonBackReference來解決循環(huán)引用問題。

// ParentEntity
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
@JsonManagedReference
private List<ChildEntity> children;
// ChildEntity
@ManyToOne
@JsonBackReference
private ParentEntity parent;

選擇適合我們場景的方案,根據(jù)需求進(jìn)行靈活配置,以避免循環(huán)級(jí)聯(lián)操作帶來的問題。

Hibernate中關(guān)鍵類的實(shí)現(xiàn)原理

SessionFactory

SessionFactory 是 Hibernate 中的一個(gè)關(guān)鍵接口,它負(fù)責(zé)創(chuàng)建 Session 對象。SessionFactory 的實(shí)現(xiàn)原理涉及到 Hibernate 的啟動(dòng)、配置、元數(shù)據(jù)加載、緩存管理等方面。 以下是 SessionFactory 的主要實(shí)現(xiàn)原理

Hibernate的啟動(dòng)過程

當(dāng)應(yīng)用程序啟動(dòng)時(shí),Hibernate 會(huì)初始化并構(gòu)建 SessionFactory。這通常發(fā)生在應(yīng)用程序啟動(dòng)時(shí)的某個(gè)初始化階段。SessionFactory 的創(chuàng)建是一個(gè)開銷較大的操作,因?yàn)樗枰虞d元數(shù)據(jù)、配置信息以及構(gòu)建內(nèi)部數(shù)據(jù)結(jié)構(gòu)。

配置信息加載

Hibernate 通過讀取配置文件(如 hibernate.cfg.xml)或者通過編程方式配置,獲取數(shù)據(jù)庫連接信息、Hibernate屬性、映射文件的位置等配置信息。這些配置信息被用于創(chuàng)建數(shù)據(jù)庫連接池、指定方言、配置緩存等。

元數(shù)據(jù)加載

Hibernate需要加載應(yīng)用程序中的實(shí)體類和映射文件,以建立 Java 對象與數(shù)據(jù)庫表之間的映射關(guān)系。元數(shù)據(jù)加載的過程包括解析注解或 XML 映射文件,構(gòu)建實(shí)體類的元數(shù)據(jù),然后將這些元數(shù)據(jù)注冊到 SessionFactory 中。

緩存管理

SessionFactory 負(fù)責(zé)管理 Hibernate 的緩存機(jī)制,包括一級(jí)緩存(Session 級(jí)別的緩存)和二級(jí)緩存(SessionFactory 級(jí)別的緩存)。這些緩存用于提高數(shù)據(jù)訪問性能,減少對數(shù)據(jù)庫的頻繁訪問。

SessionFactory 在 Hibernate 中負(fù)責(zé)管理緩存,主要包括一級(jí)緩存(Session 級(jí)別的緩存)和二級(jí)緩存(SessionFactory 級(jí)別的緩存)。緩存的使用旨在提高性能,減少對數(shù)據(jù)庫的頻繁訪問。

一級(jí)緩存(Session 級(jí)別的緩存)

一級(jí)緩存介紹
  • 一級(jí)緩存是與 Session 實(shí)例相關(guān)聯(lián)的緩存,也稱為“Session 緩存”或“對象緩存”。
  • 在 Session 的生命周期內(nèi),通過 get、load、save、update 等方法加載的實(shí)體對象都存儲(chǔ)在一級(jí)緩存中。
緩存的生命周期
  • 一級(jí)緩存的生命周期與 Session 的生命周期相同。當(dāng) Session 關(guān)閉時(shí),一級(jí)緩存中的對象也會(huì)失效。
緩存的管理
  • 一級(jí)緩存是由 Session 實(shí)例負(fù)責(zé)管理的。在同一個(gè) Session 中,如果多次加載相同的實(shí)體對象,第一次加載后的對象會(huì)被存儲(chǔ)在一級(jí)緩存中,后續(xù)加載相同的對象時(shí),直接從緩存中獲取。

二級(jí)緩存(SessionFactory 級(jí)別的緩存)

二級(jí)緩存介紹
  • 二級(jí)緩存是跨 Session 的緩存,也稱為“SessionFactory 緩存”。
  • 二級(jí)緩存存儲(chǔ)的是從數(shù)據(jù)庫中加載的實(shí)體對象,以及查詢的結(jié)果集。
配置二級(jí)緩存
  • 通過配置文件(如hibernate.cfg.xml)啟用二級(jí)緩存
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
支持的緩存提供者
  • Hibernate 支持多種二級(jí)緩存提供者,如 Ehcache、Infinispan、Redis 等。在上述配置中,使用了 Ehcache 作為緩存提供者。
使用緩存
  • 二級(jí)緩存中的對象可以被不同的 Session 共享。當(dāng)某個(gè) Session 加載一個(gè)實(shí)體對象后,該對象會(huì)被存儲(chǔ)在二級(jí)緩存中。當(dāng)另一個(gè) Session 加載相同的實(shí)體對象時(shí),可以直接從二級(jí)緩存中獲取,而不必再次訪問數(shù)據(jù)庫。
緩存策略
  • 通過配置可以設(shè)置緩存的策略,例如緩存的過期時(shí)間、是否讀寫緩存等。 需要注意的是,緩存的使用需要慎重考慮,因?yàn)榫彺婵赡軐?dǎo)致數(shù)據(jù)的不一致性。在某些情況下,需要手動(dòng)清理緩存或者使用合適的緩存策略。此外,緩存的性能影響取決于應(yīng)用程序的訪問模式和數(shù)據(jù)更新頻率。

線程安全性

SessionFactory 是線程安全的,多個(gè)線程可以共享一個(gè) SessionFactory 實(shí)例。SessionFactory 通過多個(gè)線程共享可以提高性能,因?yàn)樗鼈兛梢怨蚕硪恍┰獢?shù)據(jù)和緩存,減少資源開銷。

SessionFactory 在 Hibernate 中是線程安全的。它的線程安全性是通過以下幾個(gè)機(jī)制來保證的

不可變性

在 Hibernate 中,SessionFactory 是一個(gè)不可變的對象。一旦創(chuàng)建,其狀態(tài)不可修改。這意味著在多線程環(huán)境中,多個(gè)線程可以同時(shí)訪問 SessionFactory 實(shí)例,而不會(huì)導(dǎo)致數(shù)據(jù)不一致或競態(tài)條件。

線程本地實(shí)例

Hibernate 通過將 SessionFactory 與線程進(jìn)行綁定,確保每個(gè)線程都擁有獨(dú)立的 Session 實(shí)例。這是通過使用 ThreadLocal 來實(shí)現(xiàn)的。當(dāng)應(yīng)用程序中的一個(gè)線程需要?jiǎng)?chuàng)建或獲取 Session 時(shí),SessionFactory 會(huì)為該線程創(chuàng)建一個(gè)獨(dú)立的 Session 實(shí)例,并將其與線程關(guān)聯(lián)起來。這樣,每個(gè)線程都有自己的 Session 實(shí)例,而不會(huì)互相干擾。

public class HibernateUtil {
    private static final SessionFactory sessionFactory;
    static {
        sessionFactory = new Configuration().configure().buildSessionFactory();
    }
    private static final ThreadLocal<Session> threadLocalSession = new ThreadLocal<>();
    public static Session getSession() {
        Session session = threadLocalSession.get();
        if (session == null) {
            session = sessionFactory.openSession();
            threadLocalSession.set(session);
        }
        return session;
    }
}

上述示例中,HibernateUtil 類的 getSession 方法用于獲取與當(dāng)前線程關(guān)聯(lián)的 Session 實(shí)例。

單例模式和懶加載

通常情況下,SessionFactory 是以單例模式創(chuàng)建的,確保在整個(gè)應(yīng)用程序生命周期內(nèi)只有一個(gè) SessionFactory 實(shí)例。這減少了資源開銷并提高了性能。此外,SessionFactory 的創(chuàng)建通常是在應(yīng)用程序啟動(dòng)時(shí)進(jìn)行的,而不是在每次需要 Session 時(shí)創(chuàng)建。這種懶加載的策略也有助于提高性能。 通過這些機(jī)制,Hibernate 的 SessionFactory 在多線程環(huán)境中可以安全地被多個(gè)線程共享,而不會(huì)引起競爭條件或線程安全問題。注意,盡管 SessionFactory 是線程安全的,但 Session 對象本身并不是線程安全的,因此在多線程環(huán)境中使用 Session 時(shí)需要謹(jǐn)慎處理。

創(chuàng)建 Session

SessionFactory 的主要責(zé)任之一是創(chuàng)建 Session 實(shí)例。Session 是 Hibernate 中用于進(jìn)行數(shù)據(jù)庫操作的主要接口。通過 SessionFactory 創(chuàng)建的 Session 具有與數(shù)據(jù)庫的物理連接,并負(fù)責(zé)管理事務(wù)、執(zhí)行查詢、保存、更新和刪除操作等。 總體而言,SessionFactory 的實(shí)現(xiàn)原理涉及到配置加載、元數(shù)據(jù)加載、緩存管理等多個(gè)方面,其設(shè)計(jì)旨在提高性能、簡化開發(fā)過程,并提供對數(shù)據(jù)庫操作的封裝。 Hibernate 的 SessionFactory 通常是一個(gè)長期存在的對象,負(fù)責(zé)整個(gè)應(yīng)用程序的數(shù)據(jù)庫訪問。

SessionFactory提供了哪些方法

SessionFactory 接口是 Hibernate 中用于創(chuàng)建 Session 實(shí)例的工廠,它定義了一系列用于創(chuàng)建和配置 Session 的方法。以下是 SessionFactory 接口的一些主要方法

openSession()

Session openSession()

該方法用于創(chuàng)建一個(gè)新的 Session 實(shí)例。每個(gè) Session 實(shí)例都表示一個(gè)與數(shù)據(jù)庫的物理連接。

openStatelessSession()

StatelessSession openStatelessSession()

該方法用于創(chuàng)建一個(gè)新的 StatelessSession 實(shí)例。與普通的 Session 不同,StatelessSession 不維護(hù)持久化上下文,對應(yīng)用程序更加透明。

withOptions()

Session openSession(Interceptor interceptor)

該方法用于創(chuàng)建一個(gè)新的 Session 實(shí)例,并允許通過 Interceptor 對象來自定義對數(shù)據(jù)庫操作的攔截。Interceptor 接口允許我們在執(zhí)行 SQL 語句之前和之后執(zhí)行一些額外的邏輯。

getCurrentSession()

Session getCurrentSession() throws HibernateException

該方法用于獲取當(dāng)前線程關(guān)聯(lián)的 Session 實(shí)例。通常與 Hibernate 的事務(wù)管理機(jī)制一起使用。需要在配置文件中啟用事務(wù)管理,并在事務(wù)管理的上下文中才能使用。

openTemporarySession()

Session openTemporarySession()

該方法用于創(chuàng)建一個(gè)新的臨時(shí) Session 實(shí)例。臨時(shí) Session 在處理臨時(shí)數(shù)據(jù)時(shí)可能會(huì)更加高效,但通常情況下,使用 openSession() 是更常見的選擇。

close()

void close()

該方法用于關(guān)閉 SessionFactory,釋放資源。在應(yīng)用程序關(guān)閉時(shí)調(diào)用,或者在不再需要 SessionFactory 時(shí)調(diào)用。通常由應(yīng)用程序的生命周期管理來調(diào)用。 這些方法代表了 SessionFactory 提供的主要功能,其中最常用的是 openSession() 用于創(chuàng)建 Session 實(shí)例。在使用 SessionFactory 時(shí),還需要注意線程安全性和資源管理,以便正確地管理 Session 的生命周期。

EntityManager

在 Hibernate 中,EntityManager 對象是與持久性上下文(Persistence Context)交互的主要接口。Hibernate 提供了兩種主要的持久性上下文管理方式,一種是使用 JPA 規(guī)范定義的 EntityManager,另一種是使用 Hibernate 提供的 Session。EntityManager 主要用于以下作用

實(shí)體管理

EntityManager 負(fù)責(zé)實(shí)體的生命周期管理,包括實(shí)體的持久化、合并、移除以及查詢等操作。

事務(wù)管理

在 JPA 中,EntityManager 通常與事務(wù)一起使用。它可以參與容器管理的事務(wù),或者通過 EntityTransaction 來手動(dòng)管理事務(wù)。

查詢

EntityManager 提供了強(qiáng)大的查詢功能,支持 JPQL(Java Persistence Query Language)和 Criteria 查詢。通過這些查詢方式,可以方便地檢索實(shí)體對象。

緩存管理

EntityManager 負(fù)責(zé)緩存實(shí)體對象,以提高性能。緩存可以在一定程度上避免數(shù)據(jù)庫頻繁的讀寫操作。

在 JPA(Java Persistence API)中,EntityManager 接口提供了一些用于管理緩存的方法。這些方法主要用于實(shí)體對象的加載、存儲(chǔ)以及刷新等操作。以下是一些與緩存管理相關(guān)的 EntityManager 方法

find(Class<T> entityClass, Object primaryKey)
  • 用于通過主鍵查找實(shí)體對象。
  • 在調(diào)用該方法時(shí),EntityManager 會(huì)首先檢查一級(jí)緩存(即持久性上下文)中是否已經(jīng)存在相應(yīng)的實(shí)體對象,如果存在,則直接返回。
  • 如果一級(jí)緩存中不存在該實(shí)體對象,則會(huì)查詢數(shù)據(jù)庫,并將查詢結(jié)果存入一級(jí)緩存,然后返回該實(shí)體對象。
MyEntity entity = entityManager.find(MyEntity.class, primaryKey);
getReference(Class<T> entityClass, Object primaryKey)
  • 類似于 find 方法,但是返回的是實(shí)體的代理對象,不會(huì)立即加載實(shí)體的所有屬性。
  • 也會(huì)首先檢查一級(jí)緩存,如果緩存中存在,則返回代理對象。
  • 如果緩存中不存在,則查詢數(shù)據(jù)庫,返回代理對象。
MyEntity entity = entityManager.getReference(MyEntity.class, primaryKey);
persist(Object entity)
  • 用于將新創(chuàng)建的實(shí)體對象添加到持久性上下文中。
  • 這個(gè)操作會(huì)觸發(fā)實(shí)體的 INSERT 操作,并將實(shí)體對象存入一級(jí)緩存。
entityManager.persist(newEntity);
merge(T entity)
  • 將一個(gè)脫管狀態(tài)(detached)的實(shí)體對象合并到持久性上下文中。
  • 如果合并的實(shí)體對象在一級(jí)緩存中不存在,會(huì)先進(jìn)行數(shù)據(jù)庫查詢,然后將查詢結(jié)果合并到一級(jí)緩存中。
MyEntity managedEntity = entityManager.merge(detachedEntity);
refresh(Object entity)
  • 用于刷新實(shí)體對象的狀態(tài),即重新加載實(shí)體對象的屬性值。
  • 會(huì)先檢查一級(jí)緩存,如果緩存中存在,則重新加載緩存中的值。
  • 如果緩存中不存在,則查詢數(shù)據(jù)庫,然后刷新緩存。
entityManager.refresh(entity);

這些方法都涉及到一級(jí)緩存的管理,一級(jí)緩存是持久性上下文中的緩存,用于存儲(chǔ)已加載的實(shí)體對象。這些方法的使用可以有效地管理緩存,提高應(yīng)用程序的性能。

對象狀態(tài)變更

EntityManager 監(jiān)聽實(shí)體對象的狀態(tài)變更,并在適當(dāng)?shù)臅r(shí)候同步這些變更到數(shù)據(jù)庫。這包括臟檢查(dirty checking)和自動(dòng)更新。 在 Hibernate 中,如果我們使用 JPA 規(guī)范,可以通過 EntityManagerFactory 創(chuàng)建 EntityManager 實(shí)例。如果我們使用 Hibernate 的本地 API,可以使用 SessionFactory 創(chuàng)建 Session。這兩者在實(shí)際使用中有一些細(xì)微的區(qū)別,但本質(zhì)上都是用來管理持久化操作和對象狀態(tài)的工具。

在 Hibernate 中,對象狀態(tài)變更的實(shí)現(xiàn)主要依賴于以下兩個(gè)機(jī)制

臟檢查(Dirty Checking)

Hibernate 使用臟檢查來跟蹤對象狀態(tài)的變化。當(dāng)一個(gè)對象從數(shù)據(jù)庫加載到內(nèi)存時(shí),Hibernate 會(huì)在內(nèi)存中保留一個(gè)快照(snapshot)以記錄對象在加載時(shí)的狀態(tài)。在事務(wù)提交之前,Hibernate 會(huì)再次檢查對象的當(dāng)前狀態(tài)與快照的狀態(tài)是否有變化,如果有變化,則認(rèn)為對象是“臟的”(dirty)。Hibernate 將檢測到的變化同步到數(shù)據(jù)庫中,以確保與數(shù)據(jù)庫的數(shù)據(jù)一致性。

Write-Behind機(jī)制

Hibernate 使用 Write-Behind 機(jī)制來最小化數(shù)據(jù)庫寫操作的次數(shù)。在事務(wù)提交時(shí),Hibernate 不會(huì)立即將所有的變更寫入數(shù)據(jù)庫,而是將變更先記錄到事務(wù)的緩存中。在事務(wù)提交完成后,Hibernate 將所有的變更一次性地批量寫入數(shù)據(jù)庫,以減少與數(shù)據(jù)庫的通信次數(shù)。

Write-Behind 機(jī)制是一種數(shù)據(jù)庫寫入優(yōu)化技術(shù),用于減少對數(shù)據(jù)庫的頻繁寫操作,提高性能。這一機(jī)制的核心思想是延遲(或批量)寫入數(shù)據(jù)庫,而不是在每次數(shù)據(jù)變更時(shí)立即寫入。 在 Hibernate 中,Write-Behind 機(jī)制通常與緩存一起使用,以最小化數(shù)據(jù)庫寫操作的次數(shù)。當(dāng)一個(gè)事務(wù)中的對象發(fā)生變更時(shí),變更不會(huì)立即寫入數(shù)據(jù)庫,而是先被記錄到緩存中。這樣,在事務(wù)提交時(shí),Hibernate 可以收集所有的變更,然后一次性地批量寫入數(shù)據(jù)庫。 Write-Behind 機(jī)制帶來了以下幾個(gè)優(yōu)勢

減少數(shù)據(jù)庫寫操作

通過延遲寫入,可以減少對數(shù)據(jù)庫的頻繁寫操作,提高性能。

批量寫入

將多個(gè)變更一次性批量寫入數(shù)據(jù)庫,減少了與數(shù)據(jù)庫的通信次數(shù),進(jìn)而提高了吞吐量。

異步寫入

在一些情況下,寫入操作可以異步進(jìn)行,不會(huì)阻塞當(dāng)前事務(wù)的執(zhí)行。這有助于提高事務(wù)的響應(yīng)性。

緩解鎖沖突

如果多個(gè)事務(wù)并發(fā)修改同一數(shù)據(jù),Write-Behind 機(jī)制可以減少對數(shù)據(jù)庫的鎖定需求,從而降低鎖沖突的可能性。 要啟用 Write-Behind 機(jī)制,通常需要配置相關(guān)的參數(shù)和策略。Hibernate 提供了一些配置選項(xiàng),以便開發(fā)人員可以根據(jù)應(yīng)用程序的需求進(jìn)行調(diào)整。在使用 Hibernate 緩存和進(jìn)行大規(guī)模數(shù)據(jù)操作時(shí),考慮使用 Write-Behind 機(jī)制是一種常見的優(yōu)化手段。

這兩個(gè)機(jī)制的結(jié)合使得 Hibernate 能夠高效地管理對象狀態(tài)的變更。當(dāng)我們修改了一個(gè)持久化對象的屬性時(shí),Hibernate會(huì)通過臟檢查來檢測對象的狀態(tài)是否發(fā)生變化。如果變化,Hibernate 將更新緩存中的對象狀態(tài),并在事務(wù)提交時(shí)使用 Write-Behind 機(jī)制將變更同步到數(shù)據(jù)庫。 這種機(jī)制不僅減少了數(shù)據(jù)庫的訪問次數(shù),還提高了性能。但需要注意的是,對于大規(guī)模的數(shù)據(jù)操作,仍然需要慎重使用,以免引起性能問題。在一些情況下,我們可能需要手動(dòng)調(diào)整 Hibernate 的配置,或者使用特定的注解和 API 來優(yōu)化數(shù)據(jù)操作的性能。

Session

在Hibernate中,Session 類是與數(shù)據(jù)庫交互的主要接口之一,它代表了一個(gè)與數(shù)據(jù)庫的一次會(huì)話。Session 提供了一系列的方法來執(zhí)行數(shù)據(jù)庫操作,包括保存、更新、刪除、查詢等。以下是Session 類的主要作用和一些常用的 API

主要作用

管理持久化對象

Session 負(fù)責(zé)管理持久化對象的生命周期,包括從數(shù)據(jù)庫中檢索對象、將對象持久化到數(shù)據(jù)庫、更新對象狀態(tài)以及刪除對象。

執(zhí)行數(shù)據(jù)庫操作

通過 Session,可以執(zhí)行各種數(shù)據(jù)庫操作,如保存、更新、刪除和查詢。這使得開發(fā)者能夠直接與數(shù)據(jù)庫交互而無需編寫原始的 SQL 語句。

實(shí)現(xiàn)緩存

Session 包含一個(gè)緩存,用于存儲(chǔ)已加載的對象,以提高性能。這樣,當(dāng)對同一實(shí)體進(jìn)行多次查詢時(shí),Hibernate 可以避免重復(fù)的數(shù)據(jù)庫查詢,從而提升效率。

事務(wù)管理

Session 提供了事務(wù)管理的功能,可以通過 beginTransaction()、commit() 和 rollback() 方法來管理事務(wù),確保數(shù)據(jù)庫操作的一致性。

常用的 API

save(Object obj)

保存一個(gè)對象到數(shù)據(jù)庫。如果對象已經(jīng)存在,會(huì)拋出異常。

update(Object obj)

更新一個(gè)對象到數(shù)據(jù)庫。如果對象不存在,會(huì)拋出異常。

saveOrUpdate(Object obj)

如果對象不存在,則保存它;如果對象已存在,則更新它。

delete(Object obj)

從數(shù)據(jù)庫中刪除一個(gè)對象。

get(Class<?> clazz, Serializable id)

通過主鍵獲取對象。

load(Class<?> clazz, Serializable id)

通過主鍵加載對象。與 get 類似,但 load 返回一個(gè)代理對象,只有在訪問其屬性時(shí)才會(huì)真正加載。

createQuery(String hql)

創(chuàng)建一個(gè) HQL(Hibernate Query Language)查詢對象。

createSQLQuery(String sql)

創(chuàng)建一個(gè)原生 SQL 查詢對象。

beginTransaction()

開啟事務(wù)。

commit()

提交事務(wù)。

rollback()

回滾事務(wù)。

clear()

清空緩存中的所有對象。

flush()

強(qiáng)制將緩存中的數(shù)據(jù)同步到數(shù)據(jù)庫。

這些方法提供了對數(shù)據(jù)庫的常用操作,通過使用Session,開發(fā)者可以更方便地進(jìn)行數(shù)據(jù)庫交互而無需處理底層的數(shù)據(jù)庫連接和SQL語句。需要注意的是,每個(gè)Session實(shí)例通常對應(yīng)一個(gè)數(shù)據(jù)庫連接,因此在使用完畢后應(yīng)該及時(shí)關(guān)閉,以釋放數(shù)據(jù)庫資源。

EntityManager和Session對比

EntityManager 和 Session 是分別來自 JPA(Java Persistence API)和 Hibernate 的兩個(gè)重要接口,用于進(jìn)行持久化操作。以下是它們之間的一些對比:

來源和標(biāo)準(zhǔn)

  • EntityManager: 是 Java Persistence API (JPA) 的一部分,它是在 Java EE 環(huán)境中進(jìn)行對象持久化操作的標(biāo)準(zhǔn)接口。
  • Session: 是 Hibernate 框架中的核心接口,用于在 Java SE 和 Java EE 環(huán)境中進(jìn)行數(shù)據(jù)庫操作。

實(shí)現(xiàn)方

  • EntityManager: 由 JPA 提供,可以由多個(gè) JPA 提供商(如 Hibernate、EclipseLink 等)實(shí)現(xiàn)。
  • Session: 是 Hibernate 框架的一部分,只能由 Hibernate 實(shí)現(xiàn)。

包含的功能

  • EntityManager:

提供了 JPA 標(biāo)準(zhǔn)的 CRUD 操作。

具有查詢語言 JPQL(Java Persistence Query Language)。

支持事務(wù)和事務(wù)控制。

支持持久化上下文和一級(jí)緩存。

  • Session:

提供了豐富的 Hibernate 特有的功能,如 Criteria 查詢、HQL(Hibernate Query Language)查詢等。

具有事務(wù)和事務(wù)控制。

提供了二級(jí)緩存(SessionFactory 級(jí)別的緩存)。

生命周期

  • EntityManager: 生命周期通常與事務(wù)綁定,它可以在一個(gè)事務(wù)內(nèi)持續(xù)存在,也可以在每個(gè)事務(wù)中創(chuàng)建和關(guān)閉。
  • Session: 通常由應(yīng)用程序創(chuàng)建并管理,它可以在應(yīng)用程序運(yùn)行的整個(gè)生命周期內(nèi)存在,也可以在每個(gè)事務(wù)中創(chuàng)建和關(guān)閉。

查詢語言

  • EntityManager: 使用 JPQL(Java Persistence Query Language),是與 JPA 標(biāo)準(zhǔn)相關(guān)的查詢語言。
  • Session: 使用 HQL(Hibernate Query Language),是與 Hibernate 特有的查詢語言,也支持 SQL 原生查詢。

關(guān)聯(lián)

  • EntityManager: 通常與 JPA 的注解一起使用,更關(guān)注 POJO(Plain Old Java Object)的映射。
  • Session: 可以使用 Hibernate 的注解,也支持 XML 配置,提供了更多的靈活性。

適用范圍

  • EntityManager: 更通用,適用于 JPA 提供商提供的多種實(shí)現(xiàn)。
  • Session: 更專注于 Hibernate 框架,適用于需要直接使用 Hibernate 功能的場景。

總的來說,選擇使用 EntityManager 還是 Session 取決于我們的項(xiàng)目使用的持久化框架和標(biāo)準(zhǔn)。如果我們在使用 JPA,通常使用 EntityManager;如果我們在使用 Hibernate,可以使用 Session。在某些情況下,兩者可以混合使用,但要確保它們不會(huì)相互沖突。

責(zé)任編輯:武曉燕 來源: 海燕技術(shù)棧
相關(guān)推薦

2023-09-11 08:13:03

分布式跟蹤工具

2020-10-18 07:32:06

SD-WAN網(wǎng)絡(luò)傳統(tǒng)廣域網(wǎng)

2020-02-18 16:20:03

Redis ANSI C語言日志型

2023-02-10 09:04:27

2022-06-20 09:01:23

Git插件項(xiàng)目

2022-08-01 11:33:09

用戶分析標(biāo)簽策略

2021-04-08 07:37:39

隊(duì)列數(shù)據(jù)結(jié)構(gòu)算法

2018-11-14 11:57:28

2023-10-30 07:12:04

2020-07-03 08:21:57

Java集合框架

2019-05-14 09:31:16

架構(gòu)整潔軟件編程范式

2023-10-17 08:15:28

API前后端分離

2018-05-22 08:24:50

PythonPyMongoMongoDB

2024-09-23 08:00:00

消息隊(duì)列MQ分布式系統(tǒng)

2017-03-11 22:19:09

深度學(xué)習(xí)

2022-04-07 10:39:21

反射Java安全

2023-11-18 09:30:42

模型AI

2022-05-19 08:28:19

索引數(shù)據(jù)庫

2019-04-01 10:43:59

Linux問題故障

2022-07-06 12:07:06

Python函數(shù)式編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品美女久久久 | 亚洲在线电影 | 日韩男人天堂 | 欧美综合久久 | 一级做a爰片久久毛片免费看 | 色狠狠一区 | 欧美精品欧美精品系列 | 亚洲欧美日本在线 | 97日日碰人人模人人澡分享吧 | 亚洲一区国产 | 亚洲欧美中文日韩在线v日本 | 国产9 9在线 | 中文 | 久久一| 亚洲精品白浆高清久久久久久 | 99精品网 | 一区二区三区日本 | 亚洲美女网站 | 在线观看日本网站 | 国产在线麻豆精品入口 | 日日操操 | 国产欧美精品 | 精品久久久一区 | 精品国产一区二区在线 | 国产精品久久久久免费 | 中文字幕日韩欧美一区二区三区 | 久久精品亚洲一区 | 国产日韩欧美精品一区二区三区 | 日韩精品一区二区三区中文字幕 | 日韩一区二区三区视频在线播放 | 色婷婷精品 | 日韩av免费在线观看 | 日韩高清国产一区在线 | 精精国产xxxx视频在线播放 | 日本三级黄视频 | 黄视频网站免费观看 | 成人在线h| 欧美成人视屏 | 成人精品国产 | 亚洲一区二区三区观看 | 日本免费视频在线观看 | 亚洲精品区 |