为什么JPA有一个@Transient注解?

377
Java有一个关键字叫做transient。为什么JPA不直接使用已经存在的Java关键字,而是使用@Transient呢?
8个回答

590

Java中的transient关键字用于表示某个字段不需要被序列化,而JPA中的@Transient注解则表示某个字段不需要在数据库中持久化存储,也就是说它们的语义是不同的。


3
是的,语义不同。但是为什么JPA被设计成这样呢? - Dilum Ranatunga
1
不确定我是否理解你的意思,但可以看一下“Pascal Thivent”的回答 ;) - Jawher
45
这很方便,因为您可能不想将数据存储在数据库中,但是您确实希望将其存储在使用序列化进行实体存储/恢复的JPA缓存系统中。 - Kdeveloper
1
“JPA缓存系统”是使用序列化来存储/恢复实体的吗? JPA实现可以以任何希望的方式缓存对象,而序列化并不涉及其中。 - DataNucleus
@Jawher,这里的瞬态不是持久化任何值,否则它将插入该属性的默认值。 - Satish Sharma
显示剩余2条评论

144
因为它们有不同的含义。@Transient 注释告诉 JPA 提供程序不要持久化任何(非瞬态)属性。而另一个注释告诉序列化框架不要序列化属性。您可能希望拥有一个@Transient 属性,但仍要对其进行序列化。

3
感谢你的回答Pascal。根据你的评论,我想指出:“您可能希望拥有一个@Transient属性并仍然对其进行序列化。”(这就是我要找的)我还想补充说,反之则不成立。如果将变量设置为瞬态,则无法持久化它。 - jfajunior

134

正如其他人所说,@Transient 用于标记不应该被持久化的字段。考虑这个简短的例子:

public enum Gender { MALE, FEMALE, UNKNOWN }

@Entity
public Person {
    private Gender g;
    private long id;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    public long getId() { return id; }
    public void setId(long id) { this.id = id; }

    public Gender getGender() { return g; }    
    public void setGender(Gender g) { this.g = g; }

    @Transient
    public boolean isMale() {
        return Gender.MALE.equals(g);
    }

    @Transient
    public boolean isFemale() {
        return Gender.FEMALE.equals(g);
    }
}
当将这个类传递给JPA时,它会持久化genderid,但不会尝试持久化辅助布尔方法 - 如果没有使用@Transient注解,底层系统将抱怨实体类Person缺少setMale()setFemale()方法,因此根本不会持久化Person

@psp,你能详细解释一下为什么/如何会导致未指定的行为吗?谢谢! - 40pro
@40绘制规范中如此说明。 - psp
8
我认为这应该是被接受的答案,因为它比当前被接受的答案更加详细地解释了... - Honza Zidek
你真的会使用 equals() 来比较枚举吗?这就像写 new Integer(3).equals(new Integer(3)) 来比较数字一样。 - Jack
@Jack,别这样,你在回复一个12年前的答案。我自2016年以来就没有碰过Java了,现在我是一名全栈Clojurist。 - Esko
1
万一你又回到了Java的怀抱 :P - Jack

79

目的不同:

transient关键字和@Transient注解有两个不同的目的:一个处理序列化,另一个处理持久化。作为程序员,我们经常将这两个概念合并成一个,但总体上这是不准确的。持久化指的是状态特征,它超出了创建它的进程的生命周期。Java中的序列化指的是将对象的状态编码/解码为字节流的过程。

transient关键字比@Transient注解更强:

如果一个字段使用了transient关键字,在对象转换为字节流时,该字段将不会被序列化。此外,由于JPA将带有transient关键字标记的字段视为具有@Transient注解,因此该字段也不会被JPA持久化。

另一方面,仅带有@Transient注解的字段在对象序列化时转换为字节流,但不会被JPA持久化。因此,transient关键字比@Transient注解更强。

示例

这引出了一个问题:为什么有人想要序列化一个不被持久化到应用程序数据库的字段呢? 事实是,序列化用于更多的内容而不仅仅是持久化。在企业Java应用程序中,需要一种在分布式组件之间交换对象的机制;序列化提供了一个常见的通信协议来处理这个问题。因此,一个字段可能保存了用于组件间通信的关键信息;但是从持久性的角度来看,该字段可能没有价值。

例如,假设在服务器上运行了一个优化算法,并且假设该算法需要几个小时才能完成。对于客户端而言,拥有最新的解决方案集很重要。因此,客户端可以订阅服务器并在算法执行阶段定期接收更新。这些更新使用ProgressReport对象提供:

@Entity
public class ProgressReport implements Serializable{

    private static final long serialVersionUID = 1L;

    @Transient
    long estimatedMinutesRemaining;
    String statusMessage;
    Solution currentBestSolution;

}

Solution类可能看起来像这样:

@Entity
public class Solution implements Serializable{

    private static final long serialVersionUID = 1L;

    double[][] dataArray;
    Properties properties;
}
服务器将每个ProgressReport保存到其数据库中。服务器不关心是否持久化estimatedMinutesRemaining,但客户端肯定关心这些信息。因此,使用@TransientestimatedMinutesRemaining进行了注释。当算法找到最终的Solution时,JPA会直接将其持久化,而不使用ProgressReport

2
如果它们确实是不同的问题,那么肯定存在一个能捕捉到细微差别的不同词语。为什么要重载术语呢?作为一个起始建议,可以使用@Unpersisted - Dilum Ranatunga
4
我个人喜欢@Ephemeral。根据韦氏词典:当“ephemeral”在17世纪首次印刷成英语时,“它是一个科学术语,用于短期发热,后来用于寿命非常短暂的生物(如昆虫和花卉)。不久之后,它获得了引申意义,指任何短暂而短暂的事物(如“ephemeral pleasures”)。” - Austin
1
我喜欢这个答案的另一个原因是它提到JPA将transient字段视为隐含具有@Transient注释。因此,如果您使用transient关键字来防止字段序列化,则该字段也不会出现在数据库中。 - neXus
1
这是最准确的答案。 - Sibulele

25

如果你只希望某个字段不被持久化,使用transient@Transient都可以。但是问题在于,既然已经有transient了,为什么还要使用@Transient

因为@Transient字段仍会被序列化!

假设你创建了一个实体,在其中进行一些计算以获得结果,但是这个结果不会保存在数据库中。但是你想将该实体发送给其他使用JMS的Java应用程序,那么你应该使用@Transient,而不是JavaSE关键字transient。这样运行在其他VM上的接收者就可以节省时间,避免重新计算。


请您能否提供一个示例以使其更加清晰? - Harsh Kanakhara
有没有一种注解,JPA会将其视为瞬态的,但Jackson不会? - Kalpesh Soni

7
简单来说,如果你在实体的属性上使用@Transient注释:这个属性将被单独处理,并不会保存到数据库中。实体对象中其他属性仍将被保存。

example:

我正在使用内置的JPA存储库保存对象到数据库中,方法如下:

userRoleJoinRepository.save(user2);


4
这并没有回答问题。问题不是“@Transient是做什么用的?”,而是“为什么不使用关键字。” - E-Riz
@E-Riz 不是啊。它的作用正如我在解释和截图中所阐述的那样,展示了为什么不要使用它,正如其他6个人也同意的那样。谢谢。 - Llama
@E-Riz如此生气,竟然在将近一年后还给我点了一个踩。 - Llama

5
对于Kotlin开发人员而言,需要记住Java中的`transient`关键字已经成为了Kotlin内置的`@Transient`注解。因此,如果您在实体中使用了JPA的`@Transient`,请确保导入了JPA。
import javax.persistence.Transient

2
我将尝试回答“为什么”的问题。想象一下这样的情况:您有一个包含许多列的大型数据库表,并且您的项目/系统使用工具从数据库生成实体(如Hibernate等)。现在,假设根据您的业务逻辑,您需要某个特定字段不被持久化。您必须以特定方式“配置”实体。虽然Transient关键字对对象起作用 - 就Java语言而言,但@Transient仅设计用于响应与持久性任务相关的任务。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接