【发布时间】:2010-11-19 21:32:43
【问题描述】:
我本质上是在寻找一个“@Ignore”类型的注释,我可以用它来阻止一个特定的字段被持久化。如何实现?
【问题讨论】:
标签: java database hibernate jpa
我本质上是在寻找一个“@Ignore”类型的注释,我可以用它来阻止一个特定的字段被持久化。如何实现?
【问题讨论】:
标签: java database hibernate jpa
显然,使用 Hibernate5Module,如果使用 ObjectMapper,@Transient 将不会被序列化。删除将使其工作。
import javax.persistence.Transient;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TransientFieldTest {
@Test
public void Print_Json() throws JsonProcessingException {
ObjectMapper objectEntityMapper = new ObjectMapper();
//objectEntityMapper.registerModule(new Hibernate5Module());
objectEntityMapper.setSerializationInclusion(Include.NON_NULL);
log.info("object: {}", objectEntityMapper.writeValueAsString( //
SampleTransient.builder()
.id("id")
.transientField("transientField")
.build()));
}
@Getter
@Setter
@Builder
private static class SampleTransient {
private String id;
@Transient
private String transientField;
private String nullField;
}
}
【讨论】:
根据实体属性类型有多种解决方案。
假设您有以下account 表:
account 表映射到Account 实体,如下所示:
@Entity(name = "Account")
public class Account {
@Id
private Long id;
@ManyToOne
private User owner;
private String iban;
private long cents;
private double interestRate;
private Timestamp createdOn;
@Transient
private double dollars;
@Transient
private long interestCents;
@Transient
private double interestDollars;
@PostLoad
private void postLoad() {
this.dollars = cents / 100D;
long months = createdOn.toLocalDateTime()
.until(LocalDateTime.now(), ChronoUnit.MONTHS);
double interestUnrounded = ( ( interestRate / 100D ) * cents * months ) / 12;
this.interestCents = BigDecimal.valueOf(interestUnrounded)
.setScale(0, BigDecimal.ROUND_HALF_EVEN).longValue();
this.interestDollars = interestCents / 100D;
}
//Getters and setters omitted for brevity
}
基本实体属性映射到表列,因此id、iban、cents 等属性是基本属性。
但 dollars、interestCents 和 interestDollars 是计算属性,因此您使用 @Transient 注释它们以将它们从 SELECT、INSERT、UPDATE 和 DELETE SQL 语句中排除。
因此,对于基本属性,您需要使用
@Transient来排除给定属性的持久化。
假设您有以下post 和post_comment 表:
您希望将Post 实体中的latestComment 关联映射到最新添加的PostComment 实体。
为此,您可以使用@JoinFormula 注释:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
private Long id;
private String title;
@ManyToOne(fetch = FetchType.LAZY)
@JoinFormula("(" +
"SELECT pc.id " +
"FROM post_comment pc " +
"WHERE pc.post_id = id " +
"ORDER BY pc.created_on DESC " +
"LIMIT 1" +
")")
private PostComment latestComment;
//Getters and setters omitted for brevity
}
在获取Post实体的时候,可以看到latestComment被获取了,但是如果你想修改它,这个改动会被忽略。
因此,对于关联,您可以使用
@JoinFormula忽略写入操作,同时仍允许读取关联。
另一种忽略已由实体标识符映射的关联的方法是使用@MapsId。
例如,考虑以下一对一的表关系:
PostDetails 实体的映射如下:
@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {
@Id
private Long id;
@Column(name = "created_on")
private Date createdOn;
@Column(name = "created_by")
private String createdBy;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
private Post post;
public PostDetails() {}
public PostDetails(String createdBy) {
createdOn = new Date();
this.createdBy = createdBy;
}
//Getters and setters omitted for brevity
}
请注意id 属性和post 关联都映射同一个数据库列,即post_details 主键列。
要排除 id 属性,@MapsId 注释将告诉 Hibernate post 关联处理表主键列值。
因此,当实体标识符和关联共享同一列时,您可以使用
@MapsId忽略实体标识符属性并使用关联。
insertable = false, updatable = false
另一种选择是使用insertable = false, updatable = false 作为您希望被 Hibernate 忽略的关联。
例如,我们可以像这样映射之前的一对一关联:
@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {
@Id
@Column(name = "post_id")
private Long id;
@Column(name = "created_on")
private Date createdOn;
@Column(name = "created_by")
private String createdBy;
@OneToOne
@JoinColumn(name = "post_id", insertable = false, updatable = false)
private Post post;
//Getters and setters omitted for brevity
public void setPost(Post post) {
this.post = post;
if (post != null) {
this.id = post.getId();
}
}
}
@JoinColumn 注解的insertable 和updatable 属性将指示Hibernate 忽略post 关联,因为实体标识符负责post_id 主键列。
【讨论】:
post 和 @ 的上下文中是否正确987654368@表为例?
使用@Transient 使JPA 忽略该字段。
但是! Jackson 也不会序列化该字段。解决只需添加@JsonProperty
一个例子
@Transient
@JsonProperty
private boolean locked;
【讨论】:
有时你想:
使用@Column(name = "columnName", insertable = false, updatable = false)
一个很好的场景是使用其他列值自动计算某一列时
【讨论】:
要忽略一个字段,用@Transient注释它,这样它就不会被hibernate映射。
但是当转换为 JSON 时,jackson 不会序列化该字段。
如果您需要将 JPA 与 JSON 混合使用(JPA 省略但仍包含在 Jackson 中)请使用 @JsonInclude:
@JsonInclude()
@Transient
private String token;
提示:
token == null时也可以使用JsonInclude.Include.NON_NULL,在反序列化时隐藏JSON中的字段:
@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String token;
【讨论】:
@JsonInclude 不是必需的:@Transient 字段仍包含在 JSON 中。 (你仍然得到我的投票:该技术本身在其他情况下可能非常有用)。
@Transient 字段仅在 jackson-datatype-hibernate 模块位于类路径上并使用 ObjectMapper 注册时才被排除,这可能是带有 Jackson 和 Hibernate 的 Spring Boot 应用程序的默认设置,以及为什么设置@JsonInclude 对其他人也是可选的。
以上答案都不适用于我使用 Hibernate 5.2.10、Jersey 2.25.1 和 Jackson 2.8.9。我终于找到了答案(有点,他们引用 hibernate4module 但它也适用于 5)here。所有 Json 注释都不能与 @Transient 一起使用。显然 Jackson2 足够“聪明”,可以忽略标有@Transient 的东西,除非你明确告诉它不要这样做。关键是添加 hibernate5 模块(我用来处理其他 Hibernate 注释)并在我的 Jersey 应用程序中禁用 USE_TRANSIENT_ANNOTATION 功能:
ObjectMapper jacksonObjectMapper = new ObjectMapper();
Hibernate5Module jacksonHibernateModule = new Hibernate5Module();
jacksonHibernateModule.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
jacksonObjectMapper.registerModule(jacksonHibernateModule);
这里是 Hibernate5Module 的依赖:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.8.9</version>
</dependency>
【讨论】:
@JsonProperty@JsonInclude@JsonSerialize + @JsonDeserializemapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));之后,这个解决方案终于奏效了。谢谢!
要忽略一个字段,用@Transient 注释它,这样它就不会被hibernate 映射。
来源:Hibernate Annotations。
【讨论】:
为了完成上述答案,我使用了一个 XML 映射文件,其中 @Transient 和 transient 都不起作用...
我不得不将瞬态信息放在 xml 文件中:
<attributes>
(...)
<transient name="field" />
</attributes>
【讨论】:
这个答案来得有点晚,但它完成了响应。
为了避免实体中的字段被持久化到数据库中,可以使用以下两种机制之一:
@Transient - 将字段标记为不可持久化的 JPA 注释
transient java 中的关键字。当心 - 使用这个关键字,将阻止该字段与来自 java 的任何序列化机制一起使用。因此,如果必须对字段进行序列化,您最好只使用 @Transient 注释。
【讨论】:
@Transient 符合您的需求。
【讨论】: