在持久化期间忽略JPA字段的最简单方法是什么?

我实际上是在寻找一个“@Ignore”类型的注释,我可以用它来停止某个特定字段的持久化。如何实现这一目标?

274803 次浏览

@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注释。

为了完成上面的回答,我使用了一个XML映射文件,其中@Transienttransient都不能工作… 我必须将临时信息放在xml文件中:

<attributes>
(...)
<transient name="field" />
</attributes>

要忽略一个字段,用@Transient注释它,这样它就不会被hibernate映射。

但是当转换为JSON时,杰克逊不会连载字段。

如果您需要混合JPA和JSON(被JPA省略,但仍然包含在Jackson中)使用@JsonInclude:

@JsonInclude()
@Transient
private String token;

提示:

你也可以在反序列化时使用JsonInclude.Include.NON_NULL并在JSON中隐藏字段,当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特性:

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表:

The account table .

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
}

基本实体属性被映射到表列,所以像idibancents这样的属性是基本属性。

但是dollarsinterestCentsinterestDollars是计算属性,所以你用@Transient注释它们,以排除它们在SELECT, INSERT, UPDATE和DELETE SQL语句中。

因此,对于基本属性,你需要使用@Transient来排除一个给定的属性被持久化。

协会

假设你有以下postpost_comment表:

The post and post_comment tables

你想要将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

另一种忽略已经被实体标识符映射的关联的方法是使用@MapsId

例如,考虑以下一对一的表关系:

The post and post_details tables

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注释的insertableupdatable属性将指示Hibernate忽略post关联,因为实体标识符负责处理post_id主键列。

有时候你想:

  1. 序列化一个列
  2. 忽略被持久化的列:

使用@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;


}
}