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


@瞬态符合您的需求。


要忽略一个字段,请使用@Transient注释它,这样它就不会被hibernate映射。 来源:Hibernate注解。


这个回答来的有点晚,但它完成了响应。

为了避免来自实体的字段被持久化到DB中,可以使用以下两种机制之一:

@Transient—标记字段不可持久的JPA注释

java中的Transient关键字。注意-使用此关键字将阻止该字段与java中的任何序列化机制一起使用。因此,如果字段必须序列化,最好只使用@Transient注释。


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

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

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

但是jackson在转换为JSON时不会序列化字段。

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

@JsonInclude()
@Transient
private String token;

TIP:

你也可以使用JsonInclude.Include。当token == null时,在反序列化期间在JSON中隐藏NON_NULL字段:

@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String token;

None of the above answers worked for me using Hibernate 5.2.10, Jersey 2.25.1 and Jackson 2.8.9. I finally found the answer (sort of, they reference hibernate4module but it works for 5 too) here. None of the Json annotations worked at all with @Transient. Apparently Jackson2 is 'smart' enough to kindly ignore stuff marked with @Transient unless you explicitly tell it not to. The key was to add the hibernate5 module (which I was using to deal with other Hibernate annotations) and disable the USE_TRANSIENT_ANNOTATION feature in my Jersey Application:

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实体,如下所示:

@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

另一种忽略已经由实体标识符映射的关联的方法是使用@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关联负责处理表的Primary Key列值。

因此,当实体标识符和关联共享同一列时,可以使用@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注释的可插入和可更新属性将指示Hibernate忽略post关联,因为实体标识符负责处理post_id主键列。


有时候你想:

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

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

    }
}