JPA在持久化期间忽略字段的最快方法?

时间:2009-08-15 13:54:32

标签: java database hibernate jpa

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

10 个答案:

答案 0 :(得分:379)

@Transient符合您的需求。

答案 1 :(得分:80)

要忽略某个字段,请使用@Transient对其进行注释,以便hibernate不会对其进行映射。

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

如果您需要将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;

答案 2 :(得分:21)

要忽略某个字段,请使用@Transient对其进行注释,以便hibernate不会对其进行映射。
来源:Hibernate Annotations

答案 3 :(得分:15)

这个答案有点晚了,但它完成了回复。

为了避免实体中的字段被保留在DB中,可以使用以下两种机制之一:

@Transient - 将字段标记为不可持久的JPA注释

java中的

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

答案 4 :(得分:10)

有多种解决方案,具体取决于实体属性类型。

基本属性

请考虑以下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才能排除给定属性的持久性。

有关计算的实体属性的更多详细信息,请查看this article

协会

假设您具有以下postpost_comment表:

The post and post_comment tables

您要将latestComment实体中的Post关联映射到最新添加的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忽略写操作,同时仍允许读取关联。

有关计算的关联的更多详细信息,请查看this article

@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关联将照顾表的Primary Key列值。

因此,当实体标识符和关联共享同一列时,可以使用@MapsId来忽略实体标识符属性,而使用关联。

有关@MapsId的更多详细信息,请查看this article

使用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();
        }
    }
}

insertable批注的updatable@JoinColumn属性将指示Hibernate忽略post关联,因为实体标识符负责post_id主对象关键列。

答案 5 :(得分:4)

有时候您想:

  1. 序列化列
  2. 忽略列被保留:

使用@Column(name = "columnName", insertable = false, updatable = false)

一种好的情况是,使用其他列值自动计算出某列

答案 6 :(得分:2)

为了完成上述答案,我遇到了使用XML映射文件的案例,@Transienttransient都没有... 我不得不将瞬态信息放在xml文件中:

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

答案 7 :(得分:1)

上述答案都不适用于我使用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>

答案 8 :(得分:1)

使用@Transient使JPA忽略该字段。

但是!杰克逊也不会序列化该字段。要解决,只需添加@JsonProperty

一个例子

@Transient
@JsonProperty
private boolean locked;

答案 9 :(得分:1)

显然,使用 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;

    }
}