JPA——CascadeType.PERSIST

今天我们来学习一种简单的级联关系——CascadeType.PERSIST。

业务分析

首先,我们使用一个具体的业务场景,每个Post内容都有一个PostDetails细节,它们之间是一对一的关系,按照业务原则,我们来看看PostPostDetails之间的关系是组合还是聚合,没有了Post,那么PostDetails没有存在的必要,因此PostDetails没有单独存在的可能性。因此它们之间是组合关系。

 

代码展示

我们再来看看模型Post的代码:

@Entity
public class Post {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    private String name;
 
    @OneToOne(mappedBy = "post",
        cascade = CascadeType.ALL, orphanRemoval = true)
    private PostDetails details;
 
    public Long getId() {
        return id;
    }
 
    public PostDetails getDetails() {
        return details;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void addDetails(PostDetails details) {
        this.details = details;
        details.setPost(this);
    }
 
    public void removeDetails(PostDetails details) {
        if (details != null) {
            details.setPost(null);
        }
        this.details = null;
    }
}

模型PostDetails的代码:

@Entity
public class PostDetails {
    @Id
    private Long id;
    @Column(name = "created_on")
    @Temporal(TemporalType.TIMESTAMP)
    private Date createdOn = new Date();
    private boolean visible;
    @OneToOne
    @MapsId
    private Post post;
    public Long getId() {
        return id;
    }
    public void setVisible(boolean visible) {
        this.visible = visible;
    }
    public void setPost(Post post) {
        this.post = post;
    }
}

再来看看调用方:

public void savePostAndDetail() {
    Post post = new Post();
    post.setName("Hibernate Master Class");

    PostDetails details = new PostDetails();

    post.addDetails(details);
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction tx = entityManager.getTransaction();
    tx.begin();

    entityManager.persist(post);
    tx.commit();
    entityManager.close();
}

日志分析

执行调用方的代码后,我们看到的日志信息如下:

create table Post (
    id bigint not null,
    name varchar(255),
    primary key (id)
)
Hibernate: 
    
create table PostDetails (
    created_on timestamp,
    visible boolean not null,
    post_id bigint not null,
    primary key (post_id)
)
alter table PostDetails 
    add constraint FKxtdvogk47ve80had52g3dm8s 
    foreign key (post_id) 
    references Post

insert 
    into
        Post
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        PostDetails
        (created_on, visible, post_id) 
    values
        (?, ?, ?)

可以看到,因为在建模的时候使用的是双向关联,并且在PostDetails中使用的共享主键的方式,因此JPA在创建外键的时候是映射的@MapId映射出来的值,即post+Id,因此PostDetails的主键变成了post_id。

其次需要注意的是为什么说这里使用的是CascadeType.ALL 这是因为Post和PostDetails之间还存在另外一些关系,比如删除Post的时候,必定删除PostDetails。如果我配置成CascadeType.PERSIST这个例子在这里还是没有问题的,但是如果删除的时候就不能级联删除了,另外如果需要更新的时候,也不能级联更新。这破坏了模型的一些约定。

再次Post实体扮演Parent角色,PostDetailsChild。在这种特殊情况下,CascadeType.ALL 和orphan removal是有意义的,因为 PostDetails 生命周期绑定到其 Post Parent 实体的生命周期。

双向关联应始终在两侧更新,因此父端应包含 addChild 和 removeChild 组合。这些方法确保我们始终同步关联的双方,以避免对象或关系数据损坏问题。

总结如下

1、在JPA中配置级联的时候需要具体根据业务模型的业务来进行配置,通常情况下配置成CascadeType.ALL一般不会存在什么问题。但是如果有些业务只有新增的话,那么配置成CascadeType.PERSIST会是一个更好的选择

2、这里JPA会生成相应的外键,而这个外键并不是在PostDetails中配置的id,而是通过映射关系生成的。


 

 

版权声明:著作权归作者所有。

thumb_up 0 | star_outline 0 | textsms 0