Why Does JPA Have a @Transient Annotation

Why does JPA have a @Transient annotation?

Java's transient keyword is used to denote that a field is not to be serialized, whereas JPA's @Transient annotation is used to indicate that a field is not to be persisted in the database, i.e. their semantics are different.

What does @Transient annotation mean for methods?

All field-level JPA annotations can be placed either on fields or on properties, it determines access type of the entity (i.e. how JPA provider will access fields of that entity - directly or using getters/setters).

Default access type is determined by placement of @Id annotation, and it should be consistent for all fields of the entity (or hiererchy of inherited entities), unless explicitly overriden by @Access for some fields.

So, @Transient on getters has the same meaning as @Transient on fields - if default access type for your entity is property access, you need to annotate all getters that don't correspond to persistent properties with @Transient.

Why JPA Transient annotation have method in Target?

In JPA entity you can annotate fields or methods (getters). The @Id annotation dictates this, meaning if you put @Id on a field then all your annotations should go on fields but if you put it on, for example, @Id Long getId() then other annotations should follow. That's why @Transient can be on a method as well.

For example, if you have this

@Id
private Long id;

@Transient
private String someTransientField;

private Long getId() {
return this.id;
}

private String getSomeTransientField() {
return this.someTransientField;
}

then someTransientField would be treated as transient. But if @Id would stay on the field, and you move @Transient to private String getSomeTransientField() then someTransientField would be treated as persistent, since @Id is on the field and therefore all other annotations are expected to be on fields as well.

So the case where @Transient would work on the method is this

private Long id;

private String someTransientField;

@Id
private Long getId() {
return this.id;
}

@Transient
private String getSomeTransientField() {
return this.someTransientField;
}

@Transient annotation, @org.springframework.data.annotation.Transient annotation, transient keyword and password storing

Within the Spring Framework you can use Mapping Framework to convert from one form to another. Say for example your spring java server side application needs send to user information to a client (webpage,mobile app) in JSON format.

@Entity
public class User {

@Id
private long id;

@Column(name = "username")
private String username;

@Column(name = "email")
private String email;

@Column(name = "password")
private String password;

}

Now to map this java entity object to JSON format you can either use a mapping framework (e.g jackson: com.fasterxml.jackson.databind.ObjectMapper) or do it manually.

The JSON format output that you would get when to convert user 2 object to JSON is:

{
"id": 2,
"email": "test03@gmail.com",
"username": "test03",
"password": "$2a$10$UbvmdhfcKxSNr/I4CjOLtOkKGX/j4/xQfFrv3FizxwEVk6D9sAoO"
}

Now if you added :

@org.springframework.data.annotation.Transient
@Column(name = "password")
private String password;

and then used the Mapping Framework to again generate the JSON for the user 2 entity you would get:

{
"id": 2,
"email": "test03@gmail.com",
"username": "test03",
}

Note the password field is missing from you JSON output. Thats because @org.springframework.data.annotation.Transient specifically states to the spring framework that the Object Mapper you are using should not include this value when converting from Java Object to JSON.

Also note if you attempted to persist the above entity into the database, it would still save it to the database because @org.springframework.data.annotation.Transient only applys to Object mapping frameworks not JPA.

So to recap:

transient is for all serializations (over the wire, saving to disk, saving to db)

javax.persistence.Transient is specifically for JPA DB serialization
@org.springframework.data.annotation.Transient is for ObjectMapping Framework serializations used within Spring

JPA Transient annotation

@Transient wont help in your case.

Use @PrePersist on a method in your entity class.

@PrePersist
public void beforePersist() {
this.dateCreated = new Date();
}

beforePersist() method annotated with @PrePersist will help you automatically initilize entity fields before you want to persist a new entity. For your case you can initialize your date property inside this method. You dont have to manually set it everytime you persist a new entity.

Now when you fetch entities from your database you will get your date property.

Also look at @PreUpdate which helps you update entity fields when you update an entity.

Is it safe to assume that @Transient makes JPA provider to leave annotated field always intact?

  • I would like to consider 3 scenarios

    • Calling merge on unmanaged entity where unmanaged entity is populated with a transient field
    • Calling merge on managed entity where managed entity is populated with a transient field after it became managed
    • Calling refresh on managed entity where managed entity is populated with a transient field after it became managed. It is not allowed to call refresh on a unmanaged entity anyway
  • Calling merge on unmanaged entity where unmanaged entity is populated with a transient field.

    • Implementation is free to return a new entity and that means transient field is lost. I have verified the behaviour with hibernate and it loses the value. Even if some implementations make it not lose value, I wouldn't rely it as merge is free to return new entity.
  • Calling merge on managed entity where managed entity is populated with a transient field after it became managed.

    • It is a managed entity so JPA need to provide repeatable read. So even if you perform merge, JPA has to satisfy both guarantees, i.e the object reference you have for the managed entity cannot change and that object should contain latest value from db. There is no reason for JPA to touch your transient field. It will just update any field that are managed and has been updated in the database since. I have verified this behaviour and behaves as expected.
  • Calling refresh on managed entity where managed entity is populated with a transient field after it became managed.

    • Same argument as merging managed entity. JPa cannot lose your transient value. I have verified this behaviour and behaves as expected.

Summary

  • You will lose the transient value of an unmanaged entity when you call merge.

GitRepo

  • https://github.com/kavi-kanap/stackoverflow-63003677. Just import the spring boot project as a maven project and start the app. It will run above 3 scenarios and print the outcome to console

Using @Transient Annotation for persistance storage

The @Transient annotation is used when you need that specific field for certain work but are not willing that field to store in the database.

Here as your question is stated, there are organization and workers, so the relationship is @OneToMany relationship.

In Organization Class:

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="organization")
private List<Workers> workers = new ArrayList<>();

In Workers Class:

@ManyToOne
@JoinColumn(name="id", unique=true)
private Organization organization;

Here, 1 organization can have many workers and one worker can only work on 1 organization. there you have it. :D

What is the lifetime of the JPA @Transient annotated field

@Entity
class Sample {
@Transient
String fieldOne;

transient String otherField;
}

fieldOne is not transient (has not transient keyword), so is serialised (to/from cache, network, file or other sources). But JPA will not store it in database, because annotation denies.

otherField is not seriazable, has transient keyword (i.e. after getting from cache engine, or network can/will be null), but is pesissted in JPA database with default behaviour

This is not academic discussion, sometimes it is useable. Usually values computed from others, or hashed /encrypted /hidden fields.

JPA Transient Annotation and JSON

Contrary to what I was telling you in comments, it seems that Jackson does care about JPA annotations when used to serialize instances of entity classes thanks to the Jackson's Hibernate module.

Within that module, there is an HibernateAnnotationIntrospector that the documentation refers to as a

simple AnnotationIntrospector that adds support for using Transient to
denote ignorable fields (alongside with Jackson and/or JAXB
annotations).

And as you can see here, the default behavior of Jackson is to check for any @Transient annotation it can find.

So in the end, your problem can be solved in either of those 3 ways :

  1. Configure Jackson (using HibernateAnnotationIntrospector's setUseTransient method) to disable the check for @Transient annotations (see this answer for implementation details).
  2. Use another object than PublicationVO as the returned result of your getPublicationDetailsJSON method. You'll have to copy properties from your value object to the object being returned at some point.
  3. Remove the @Transient annotation and persist the property (but I would understand if that is not an option for you since you probably have good reason to have made this property JPA-transient in the first place).

Cheers



Related Topics



Leave a reply



Submit