ChatGPT解决这个技术问题 Extra ChatGPT

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

我本质上是在寻找一个“@Ignore”类型的注释,我可以用它来阻止一个特定的字段被持久化。如何做到这一点?


A
Andrey Atapin

@Transient 符合您的需求。


但是后来jackson在转换成JSON的时候不会序列化字段……怎么解决?
这取决于您的应用程序设计。如果您注释您的实体类 - 它适用于任何地方;但是,如果您注释使用实体的 dao - 这是另一回事。简而言之:当你有多个存储时使用 DAO
不适用于列表字段。对于 Collection 类型的字段,hibernate 实际上会创建一个新的中间表。有什么解决方法吗?
@MobileMon 你可以解决它,检查我的答案stackoverflow.com/a/41850392/3871754
@MobileMon 最好不要将同一实体用于数据库和 API 目的(单一责任)。
K
Kamil Nękanowicz

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

但是杰克逊在转换为 JSON 时不会序列化该字段。

如果您需要将 JPA 与 JSON 混合使用(JPA 省略但仍包含在 Jackson 中)使用 @JsonInclude

@JsonInclude()
@Transient
private String token;

小费:

您还可以在反序列化期间使用 JsonInclude.Include.NON_NULL 并在 token == null 时隐藏 JSON 中的字段:

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

我正在运行 JAX-RS 2.0.1 / Jersey 2.25.1 / Jackson 2.8.7 并且使用该堆栈 @JsonInclude 不是必需的:@Transient 字段仍包含在 JSON 中。 (你仍然得到我的投票:该技术本身在其他情况下可能非常有用)。
我在 Spring Boot 上做到了
嗨,我正在尝试使用它,但它没有被@Transient 字段序列化
在 Spring Boot 2.1.7 中尝试过,但没有奏效。但是你仍然得到我的投票,因为它可能在其他情况下工作。
@Transient 字段仅在 jackson-datatype-hibernate 模块位于类路径上并使用 ObjectMapper 注册时才被排除,这可能是带有 Jackson 和 Hibernate 的 Spring Boot 应用程序的默认设置,以及为什么设置 @JsonInclude 也是可选的为他人。
n
naXa stands with Ukraine

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


O
Olimpiu POP

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

为了避免实体中的字段被持久化在数据库中,可以使用以下两种机制之一:

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

transient 关键字在 java 中。当心 - 使用这个关键字,将阻止该字段与来自 java 的任何序列化机制一起使用。因此,如果必须对字段进行序列化,最好只使用 @Transient 注释。


我只想忽略 get 方法的持久性怎么样?例如: myjparepository.save() 会像往常一样保存模型,而 myjparepository.find(id) 会忽略我想要的字段吗?
@xtiger 您可以创建自定义查询来归档该功能。这是一个示例 link
V
Vlad Mihalcea

根据实体属性类型,有多种解决方案。

基本属性

假设您有以下 account 表:

https://i.stack.imgur.com/5oC40.png

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

https://i.stack.imgur.com/5aNhN.png

您希望将 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

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

https://i.stack.imgur.com/uELvQ.png

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 用于您希望被 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 Primary Key 列。


请注意,id 属性和帖子关联都映射相同的数据库列,即 post_comment 主键列。 例如,在 postpost_details 表的上下文中这是否正确?
感谢您发现它。我修好了它。
我在 DB2 中尝试了所有这些方法,唯一可以使用 DB2 数据库的方法是 @JoinColumn。 PrimaryKeyColumn 也不起作用。大多数生成的生成的 SQL 是不正确的。
O
Obothlale

有时你想:

序列化列 忽略列被持久化:

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

一个好的场景是使用其他列值自动计算某个列


是的,这个解决方案适合场景
这对我来说是最好的解决方案:我想在 EntityManager 操作期间忽略该列,但仍使用它来自动创建架构👍
A
Andrey

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

但! Jackson 也不会序列化该字段。解决只需添加@JsonProperty

一个例子

@Transient
@JsonProperty
private boolean locked;

这对我有用。
V
Vinze

为了完成上述答案,我使用了一个 XML 映射文件,其中 @Transienttransient 都不起作用......我必须将瞬态信息放在 xml 文件中:

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

H
Hodglem

以上答案均不适用于我使用 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 + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false)); 之后,这个解决方案终于奏效了。谢谢!
a
ala_pobs

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

    }
}