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?
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?
There are multiple solutions depending on the entity attribute type.
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
@Transientin order to exclude a given property from being persisted.
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
@JoinFormulato ignore the write operations while still allowing reading the association.
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
@MapsIdto ignore the entity identifier attribute and use the association instead.
insertable = false, updatable = falseAnother 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.
Sometimes you want to:
Use @Column(name = "columnName", insertable = false, updatable = false)
A good scenario is when a certain column is automatically calculated by using other column values
use @Transient to make JPA ignoring the field.
but! Jackson will not serialize that field as well. to solve just add @JsonProperty
an example
@Transient
@JsonProperty
private boolean locked;
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;
}
}