I'm essentially looking for a "@Ignore" type annotation with which I can stop a particular field from being persisted. How can this be achieved?
10 Answers
To ignore a field, annotate it with @Transient
so it will not be mapped by hibernate.
but then jackson will not serialize the field when converting to JSON.
If you need mix JPA with JSON(omit by JPA but still include in Jackson) use @JsonInclude
:
@JsonInclude()
@Transient
private String token;
TIP:
You can also use JsonInclude.Include.NON_NULL and hide fields in JSON during deserialization when token == null
:
@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String token;
To ignore a field, annotate it with @Transient
so it will not be mapped by hibernate.
Source: Hibernate Annotations.
This answer comes a little late, but it completes the response.
In order to avoid a field from an entity to be persisted in DB one can use one of the two mechanisms:
@Transient - the JPA annotation marking a field as not persistable
transient keyword in java. Beware - using this keyword, will prevent the field to be used with any serialization mechanism from java. So, if the field must be serialized you'd better use just the @Transient annotation.
There are multiple solutions depending on the entity attribute type.
Basic attributes
Consider you have the following account
table:
The account
table is mapped to the Account
entity like this:
@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
}
The basic entity attributes are mapped to table columns, so properties like id
, iban
, cents
are basic attributes.
But the dollars
, interestCents
, and interestDollars
are computed properties, so you annotate them with @Transient
to exclude them from SELECT, INSERT, UPDATE, and DELETE SQL statements.
So, for basic attributes, you need to use
@Transient
in order to exclude a given property from being persisted.
Associations
Assuming you have the following post
and post_comment
tables:
You want to map the latestComment
association in the Post
entity to the latest PostComment
entity that was added.
To do that, you can use the @JoinFormula
annotation:
@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
}
When fetching the Post
entity, you can see that the latestComment
is fetched, but if you want to modify it, the change is going to be ignored.
So, for associations, you can use
@JoinFormula
to ignore the write operations while still allowing reading the association.
@MapsId
Another way to ignore associations that are already mapped by the entity identifier is to use @MapsId
.
For instance, consider the following one-to-one table relationship:
The PostDetails
entity is mapped like this:
@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
}
Notice that both the id
attribute and the post
association map the same database column, which is the post_details
Primary Key column.
To exclude the id
attribute, the @MapsId
annotation will tell Hibernate that the post
association takes care of the table Primary Key column value.
So, when the entity identifier and an association share the same column, you can use
@MapsId
to ignore the entity identifier attribute and use the association instead.
Using insertable = false, updatable = false
Another option is to use insertable = false, updatable = false
for the association which you want to be ignored by Hibernate.
For instance, we can map the previous one-to-one association like this:
@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();
}
}
}
The insertable
and updatable
attributes of the @JoinColumn
annotation will instruct Hibernate to ignore the post
association since the entity identifier takes care of the post_id
Primary Key column.
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);
Here is the dependency for the Hibernate5Module:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.8.9</version>
</dependency>
Apparently, using Hibernate5Module, the @Transient will not be serialize if using ObjectMapper. Removing will make it work.
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;
}
}