我正在使用Spring Data JPA和Hibernate,并且在@Column
注解上遇到了updatable=false属性的问题。
我为所有@Entity
对象创建了一个基类,并使用UUID定义如下:
@MappedSuperclass
@Getter @Setter @EqualsAndHashCode(of= {"uuid"})
public abstract class AbstractEntity implements Persistable<Long> {
@Id
@GeneratedValue(strategy = AUTO)
@Column(unique = true, updatable = false)
private Long id;
@Column(unique = true, updatable = false)
private UUID uuid = UUID.randomUUID();
}
注意updatable = false
注解。
为了验证UUID字段实际上是不可更新的,我写了这个测试:
@Test
public void testChangeUUID() {
User user = userRepo.findOne(1L);
assertNotNull(user);
assertEquals(USER_UUID, user.getUuid().toString());
final UUID newUuid = UUID.randomUUID();
user.setUuid(newUuid);
user = userRepo.save(user);
assertEquals(newUuid, user.getUuid());
User user2 = userRepo.findOne(1L);
assertNotNull(user2);
assertEquals("UUID should not have changed", USER_UUID, user2.getUuid().toString());
}
我实际上希望在调用userRepo.save(user)
时抛出一个异常,但这并没有发生。相反,最终的assertEquals()
失败了,这意味着UUID实际上正在更新。
这是预期的行为吗?有什么方法可以防止UUID被更改吗?
3条答案
按热度按时间cig3rfwq1#
我遇到了一个类似的问题,我想使用@Column(updatable = false),然后仅在特定情况下手动更新字段。
作为解决方案之一,您可以使用注解,然后在调用userRepo.findOne(1 L)之前尝试清除会话。这将如上所述的工作。
我不太喜欢清除和使用更多的查询,所以我的解决方案是-重写你的保存方法,不使用Hibernate,而是在你的仓库中使用一个原生的SQL脚本。注解只影响Hibernate,但对本机脚本没有影响。Imo,一个更好的方法来做到这一点。
vs91vp4v2#
根据documentation,
updatable
属性决定该列是否是update语句的一部分。这意味着Hibernate在向数据库发送更新时会忽略它。因此,内存中状态和数据库状态将不同。要验证这一点,请尝试在调用
User user2 = userRepo.findOne(1L)
之前清除会话(逐出zzwlnbp83#
虽然最初的问题已经得到了回答,但我想强调一个重要的观点对于那些刚接触Hibernate的人,因为它可能有助于避免一些混乱。
@Column注解的javadoc of Hibernate 5.4表示其可选元素Updatable:
(可选)该列是否包含在持久性提供程序生成的SQL UPDATE语句中。
如果你使用HQL或使用CriteriaUpdate对你用
@Column(updatable = false)
标注的字段发出更新语句,你的更新语句将被执行。当你使用Hibernate的
update
或JPA的merge
方法时,@Column(updatable = false)
可以工作。不会抛出任何异常(与例如:
@Column(nullable= false)
,因为它创建了一个约束...),但是生成的更新语句将不包括标记的字段。要了解更多信息,您必须熟悉JPA的实体生命周期状态和管理它们的方法,如下图所示。
以及我强烈建议阅读这article的详细解释,* 是所附图片的来源 *。