我实际上是在寻找一个“@Ignore”类型的注释,我可以用它来停止某个特定字段的持久化。如何实现这一目标?
@Transient符合您的需要。
@Transient
忽略一个字段,用@Transient注释它,这样它就不会被hibernate映射 来源:Hibernate注释。< / p >
这个回答来的有点晚,但它完成了响应。
为了避免来自实体的字段被持久化到DB中,可以使用以下两种机制之一:
@Transient -标记字段不可持久的JPA注释
java中的< a href = " https://docs.oracle.com/javase/specs/jls/se8/html/jls - 8. - html # jls-8.3.1.3 " >瞬态< / >关键字。注意-使用此关键字将阻止该字段与java中的任何序列化机制一起使用。因此,如果字段必须序列化,最好只使用@Transient注释。
transient
<attributes> (...) <transient name="field" /> </attributes>
要忽略一个字段,用@Transient注释它,这样它就不会被hibernate映射。
但是当转换为JSON时,杰克逊不会连载字段。
如果您需要混合JPA和JSON(被JPA省略,但仍然包含在Jackson中)使用@JsonInclude:
@JsonInclude
@JsonInclude() @Transient private String token;
提示:
你也可以在反序列化时使用JsonInclude.Include.NON_NULL并在JSON中隐藏字段,当token == null:
token == null
@JsonInclude(JsonInclude.Include.NON_NULL) @Transient private String token;
在使用Hibernate 5.2.10、Jersey 2.25.1和Jackson 2.8.9时,上述答案都不适用。我终于找到了答案(某种程度上,他们引用hibernate4module,但它也适用于5)在这里。没有一个Json注释在@Transient中起作用。显然,Jackson2足够“聪明”,可以友好地忽略标有@Transient的东西,除非你明确地告诉它不要这样做。关键是添加hibernate5模块(我用它来处理其他Hibernate注释),并在我的Jersey应用程序中禁用USE_TRANSIENT_ANNOTATION特性:
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>
根据实体属性类型,有多种解决方案。
假设你有下面的account表:
account
.
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这样的属性是基本属性。
id
iban
cents
但是dollars, interestCents和interestDollars是计算属性,所以你用@Transient注释它们,以排除它们在SELECT, INSERT, UPDATE和DELETE SQL语句中。
dollars
interestCents
interestDollars
因此,对于基本属性,你需要使用@Transient来排除一个给定的属性被持久化。
假设你有以下post和post_comment表:
post
post_comment
你想要将Post实体中的latestComment关联映射到最新添加的PostComment实体。
Post
latestComment
PostComment
为此,你可以使用@JoinFormula注释:
@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。
@MapsId
例如,考虑以下一对一的表关系:
PostDetails实体是这样映射的:
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主键列。
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主键列。
@JoinColumn
insertable
updatable
post_id
有时候你想:
使用@Column(name = "columnName", insertable = false, updatable = false)
@Column(name = "columnName", insertable = false, updatable = false)
一个好的场景是通过使用其他列值自动计算某个列
使用@Transient使JPA忽略字段。
但是!Jackson也不会序列化该字段。要解决这个问题,只需添加@JsonProperty
一个例子
@Transient @JsonProperty private boolean locked;
显然,使用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; } }