英文:
Hibernate: @Column(updatable = false) not working for @MappedSuperclass
问题
使用Spring Data JPA与Hibernate时,我在@Column注解的updatable=false属性上遇到了问题。
我有一个基类,用于所有的@Entity对象,其中包含一个像这样定义的createdDate:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Setter
@Getter
@ToString
public abstract class BaseEntity {
@CreatedBy
@Column(name = "CREATE_USER_ID", nullable = false, updatable = false)
protected String createdBy;
@CreationTimestamp
@Column(name = "CREATE_TS", columnDefinition = "TIMESTAMP WITH TIME ZONE", nullable = false, updatable = false)
protected Instant createdDate; //用于数据复制冲突解决的审计(Oracle Golden Gate)
@LastModifiedBy
@Column(name = "LAST_UPDATE_USER_ID")
protected String lastModifiedBy;
@UpdateTimestamp
@Column(name = "LAST_UPDATE_TS", columnDefinition = "TIMESTAMP WITH TIME ZONE")
protected Instant lastModifiedDate;
}
以下是我的子实体:
@Entity
@Table(name = "CUSTOMER")
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = {})
public class Customer extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_ID_SEQ")
@SequenceGenerator(sequenceName = "Customer_ID_SEQ", allocationSize = 1, name = "CUSTOMER_ID_SEQ")
@Column(name = "CUSTOMER_ID")
private Long CustomerId; //主要标识符
@Column(name = "CUSTOMER_NAME")
private String customerName; //客户姓名
@Column(name = "COUNTRY")
private String customerCountry; //客户国家
@Column(name = "REGION")
private String customerRegion; //客户地区
}
来自CustomerService.java的代码片段:
public CustomerDetails updateCustomer(CustomerDetails customerDetails, Long customerId) {
Customer customer = customerEntityToDTOMapper.dtoToEntity(customerDetails);
/**
某种业务逻辑
**/
Customer updatedCustomer = customerRepository.saveAndFlush(customer);
return customerEntityToDTOMapper.entityToDTO(updatedCustomer);
}
现在,当我从Postman调用updateCustomer API时,如果我在请求体中更改createdDate的值,它会在数据库中进行更新(数据库将使用请求体中传递的新值替换createdDate的实际值)。这实际上不应该发生。无论我在请求体中传递什么有效值,数据库中的createdDate在updateCustomer API调用时都不应该被更新。
另外,在createCustomer请求中,如果createdDate=null,JPA会根据预期设置createdDate,并且我在repository.saveAndFlush()方法的响应中收到正确的日期。但是在updateCustomer中,如果我在实体中将createdDate=null,那么即使它在数据库中存在,repository.saveAndFlush()方法的响应中createdDate也会变为null。
有人可以帮我看看我错在哪里吗?
英文:
I am using Spring Data JPA with Hibernate and am having trouble with the updatable=false property on the @Column annotation.
I have a base class for all of my @Entity objects with a createdDate defined like this:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Setter
@Getter
@ToString
public abstract class BaseEntity {
@CreatedBy
@Column(name = "CREATE_USER_ID", nullable = false, updatable = false)
protected String createdBy;
@CreationTimestamp
@Column(name = "CREATE_TS", columnDefinition = "TIMESTAMP WITH TIME ZONE", nullable = false, updatable = false)
protected Instant createdDate; //Audit for data use replication conflict resolution (Oracle Golden Gate)
@LastModifiedBy
@Column(name = "LAST_UPDATE_USER_ID")
protected String lastModifiedBy;
@UpdateTimestamp
@Column(name = "LAST_UPDATE_TS", columnDefinition = "TIMESTAMP WITH TIME ZONE")
protected Instant lastModifiedDate;
Following is my child entity
@Entity
@Table(name = "CUSTOMER")
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = {""})
public class Customer extends BaseEntity
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_ID_SEQ")
@SequenceGenerator(sequenceName = "Customer_ID_SEQ", allocationSize = 1, name = "CUSTOMER_ID_SEQ")
@Column(name = "CUSTOMER_ID")
private Long CustomerId; //Primary identifier
@Column(name = "CUSTOMER_NAME")
private String customerName; //Customer Name
@Column(name = "COUNTRY")
private String customerCountry; //Customer Country
@Column(name = "REGION")
private String customerRegion; //Customer Region
}
Code snippet from CustomerService.java
public CustomerDetails updateCustomer(CustomerDetails customerDetails, Long customerId) {
Customer customer = customerEntityToDTOMapper.dtoToEntity(customerDetails);
/**
Some sort of business logic
**/
Customer updatedCustomer = customerRepository.saveAndFlush(customer);
return customerEntityToDTOMapper.entityToDTO(updatedCustomer);
}
Now when I call updateCustomer API from the postman, if I change the value of createdDate in request body, it is getting updated in database (the database will replace actual value of createdDate by the new value passed in the request body). This should ideally not happen. Irrespective of whatever valid value I pass in requestBody, createdDate in the database should not be used on updateCustomer API call.
Also in createCustomer request, if createdDate=null, JPA will set the createdDate as per expectation and I receive proper date in the response of repository.saveAndFlush() method. But in updateCustomer if I set createdDate=null in the entity, then createdDate comes as null in the response of repository.saveAndFlush() method even though it exists in the database.
Can someone help me with where am I going wrong?
答案1
得分: 1
具有讽刺意味的是,简单地移除“updatable = false”属性就为我解决了这个问题。现在,日期/时间戳在初始插入后不会再被更新。
对于那些使用.hbm.xml文件的人:
之前:
<property name="createdOn" column="CREATED_ON" update="false"/>
之后:
<property name="createdOn" column="CREATED_ON"/>
英文:
Ironically, simply removing the "updatable = false" attribute fixed the issue for me. The date / timestamp no longer gets updated once its been initially inserted now.
And for those who use a .hbm.xml file:
Before:
<property name="createdOn" column="CREATED_ON" update="false"/>
After:
<property name="createdOn" column="CREATED_ON"/>
答案2
得分: 0
@Column
注解属性会被处理以从实体创建/更新/验证等操作中生成/更新/定义表结构,并根据实体来定义表上的约束(取决于hbm2ddl.auto属性)。
现在,insertable
和updatable
被@persistenceContext
用于发出查询,如果未明确提供并且设置为false,则查询中将忽略其字段。
由于在您的情况下,您没有提供代码,也没有说明您如何设置这些值,我假设在“update”情况下,您将createdDate
设置为null,这将无法正常工作,因为您已经这样定义了该字段:
@CreationTimestamp // 仅在实体为新时添加时间戳
@Column(name = "CREATE_TS", columnDefinition = "TIMESTAMP WITH TIME ZONE", nullable = false, updatable = false)
protected Instant createdDate;
因此,在更新时,您明确地将createdDate
更新为null,这会覆盖updatable=false
,据我所知,updatable=false
存在一些奇怪的行为,会在实体中返回null(在保存后检索时),并且当再次保存实体时,该null值会被持久化(无论updatable=false如何,因为Hibernate将看到该值已更改为null)。
我建议移除updatable=false
,只使用@CreationTimeStamp
属性,它将不会更新该值参考链接,并且数据库约束应该防止为null
。
英文:
The @Column
annotation attributes are processed to create/update/validate etc the schema and define the constraints on your table from entities (depends on hbm2ddl.auto property).
Now insertable
and updatable
are used by @persistenceContext
to issue the query and it will ignore the fields in its query if not provided explicitly and set to false.
Since in your case, you have not provided the code, how you are setting the values, I am assuming in case of update
, you are setting createdDate
null, which will not work because you have defined the field like
@CreationTimestamp // This will add the timestamp if null when entity is new only
@Column(name = "CREATE_TS", columnDefinition = "TIMESTAMP WITH TIME ZONE", nullable = false, updatable = false)
protected Instant createdDate;
So, your update is explicitly updating the createdDate
as null, which is overriding the updatable=false
and AFAIK there is some weird behaviour with updatable=false
which returns null in entity (when you fetched after saving it) and when entity is saved again that null value is persisted (which will be saved irrespective of updatable=false, because hibernate will see the value is changed to null)
I would suggest remove the updatable=false
and use only @CreationTimeStamp
property only, which would not update the value See this and database constraint should be there to prevent null
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论