关于JPA @Column注解中的insertable = false和updatable = false,请进行解释。

204
如果一个字段被注释为insertable=false, updatable=false,这不意味着你既不能插入值也不能修改现有的值吗?你为什么要这样做呢?
@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(mappedBy="person", cascade=CascadeType.ALL)
    private List<Address> addresses;
}

@Entity
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne
    @JoinColumn(name="ADDRESS_FK")
    @Column(insertable=false, updatable=false)
    private Person person;
}
8个回答

162

在另一个实体中负责创建/更新所引用列的实体时,您将这样做。


5
你的意思是,当在Person实体上设置updatable=false时,在更新地址时将禁用对Person.name的更新(我不同意,因为这是级联的目的)。你还说,在@Column定义中,当它是外键(Person)时,会发生不同的事情,而当它不是外键时,没有引用实体去禁用更新。根据updatable的Javadoc,我认为它只会禁止在Address已被持久化后更改Person。请问你能解释一下吗? - Flowy
2
这不是已经由级联选项充分定义了吗?我看到它被用来避免尝试写入生成的列而遇到麻烦。(例如,一个体积列 GENERATED ALWAYS AS (height * width * length) VIRTUAL - Zyl

150

当你需要在实体中映射一个字段多次时,定义 insertable=false, updatable=false 是有用的,通常包括以下情况:

我认为这不是语义上的问题,但肯定是技术上的问题。


30
我强烈认为这个答案比被采纳的答案更好。被采纳的答案让人感觉插入/更新属性与相关实体的创建/更新有关,而这些属性背后的真正意图是防止在当前实体中插入/更新列。相关实体的创建/更新是通过映射注解的级联属性来处理的。 - Jayant

33

我想要补充BalusCPascal Thivent的答案,介绍一个insertable=false, updatable=false的另一个常见用法:

考虑一个不是id但是某种序列号的列。计算序列号的责任可能并不属于应用程序。

例如,序列号从1000开始,每个新实体应递增1。这很容易且非常合适在数据库中完成,在这种情况下,这些配置就有意义了。


2
序列也被JPA支持,因此您也可以使用JPA注释来定义您的序列。 - eis

23

另一个例子是在“created_on”列上,您希望让数据库处理日期创建。


2
Hibernate是否应该根据updatable=false注释阻止更新?在我的JPA存储库测试中,具有此注释的created_on列可以接受更新而不报错。 - chrisinmtown
2
@chrisinmtown Eclipselink 不会在 SQL 中包含该列。我认为 Hibernate 也是一样的。 - Jaqen H'ghar
1
实际上这就是我要回复的内容。 - Daniel Pop

4

最好的答案,说实话。 - Mike Doe

2
根据Javax的持久性文档:
无论列是否包含在持久化提供程序生成的SQL UPDATE语句中。
最好从官方文档这里了解。

这帮助我理解了为什么一个使用@CreatedDate和/或@UpdateTimestamp注解的列,如果还有@Column(...insertable=false, updatable=false)注解,就不会被更新。谢谢! - GlauberMD

2
另一个可能的原因是您的属性映射到视图的列(例如,您的Hibernate实体是表和视图的融合)。因此,您的列不能被插入(也不能更新),这是没有意义的。
@Entity
@Table(name = "THE_VIEW")
@SecondaryTable(name = "THE_TABLE", pkJoinColumns = @PrimaryKeyJoinColumn(name = "THE_ID"))
public class MyEntity {

    @Id
    @Column(name = "THE_ID")
    private Integer id;

    @Column(name = "VIEW_COLUMN", updatable = false, insertable = false)
    private String viewColumn

    @Column(name = "TABLE_COLUMN", table = "THE_TABLE")
    private String tableColumn;

(我这里不讨论可更新的视图)


2
除了之前的答案,insertable=false, updatable=false 的常见用途是为了节省冗余的数据库查询,从而提高性能。
假设有一个客户端类,其中包含一个父实体。如果您只想检查客户端是否有父级,则只需检查其parent_id列中是否存在值即可。没有必要请求Hibernate获取父实体及其可能的所有其他关联,从而导致额外的查询数量:
public class Client {
    @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Parent parent;

    @Column(name = "parent_id", insertable = false, updatable = false)
    private UUID parentId;
}

通过上述设置,parentId字段将简单地获取存储在parent_id列中的任何值,该列仅由父实体编辑/更新。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接