Defining Hibernate entities with auditable fields, soft delete field, optimistic locking field, etc
There are certain generic columns that database tables have like
- Auto generated identity columns.
- Auditing columns like creadedDtTm, createdBy, modifiedDtTm, and modifiedBy.
- Soft delete or logical delete flags like inactive 'Y' or 'N'.
- Optimistic locking detection based on columns like Timestamp or version number.
These logic can be mapped in a generic way using hibernate so that all the other entities can reuse. Let's look at some sample code using Hibernate annotations.
1. Identity columns.
package com.myapp.common; import static javax.persistence.GenerationType.IDENTITY; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @SuppressWarnings("serial") @MappedSuperclass public class StandardIdLongBaseEntity extends AuditableBaseEntity<long> { @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "ID", unique = true, nullable = false, precision = 18, scale = 0) // name is almost always overridden @Override public Long getId() { return super.getId(); } }
2. Audit fields and optimistic locking detection using timestamp. Firstly the "MappedSuperclass"
package com.myapp.common; import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.MappedSuperclass; import javax.persistence.Version; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.hibernate.annotations.Generated; import org.hibernate.annotations.GenerationTime; import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDefs; @SuppressWarnings("serial") @MappedSuperclass public abstract class AuditableBaseEntity<pk> extends BaseEntity<pk> { private byte[] timestamp; private StandardAuditFields auditFields = new StandardAuditFields(); @Version @Column(name = "Timestamp", insertable = false, updatable = false, unique = true, nullable = false) @Generated(GenerationTime.ALWAYS) public byte[] getTimestamp() { return timestamp; } public void setTimestamp(byte[] timestamp) { this.timestamp = timestamp; } @Embedded public StandardAuditFields getAuditFields() { if (auditFields == null) { // TODO: Not thread safe. auditFields = new StandardAuditFields(); } return auditFields; } public void setAuditFields(StandardAuditFields auditFields) { this.auditFields = auditFields; } @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE, true); } }
Next, the "Embeddable" class with fields for auditing.
package com.myapp.common; import java.util.Date; import javax.persistence.Column; import javax.persistence.Embeddable; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.hibernate.annotations.Generated; import org.hibernate.annotations.GenerationTime; @Embeddable public class StandardAuditFields { private String createdBy; private Date createdDtTm; private Date modifiedDtTm; private String modifiedBy; @Temporal(TemporalType.TIMESTAMP) @Generated(GenerationTime.INSERT) @Column(name="CreatedDtTm", length=23, insertable=true, updatable=false) public Date getCreatedDtTm() { return createdDtTm; } public void setCreatedDtTm(Date createdDtTm) { this.createdDtTm = createdDtTm; } @Temporal(TemporalType.TIMESTAMP) @Generated(GenerationTime.ALWAYS) @Column(name="ModifiedDtTm", length=23, insertable=false, updatable=false) public Date getModifiedDtTm() { return modifiedDtTm; } public void setModifiedDtTm(Date modifiedDtTm) { this.modifiedDtTm = modifiedDtTm; } @Column(name="ModifiedBy", length=30, insertable=false, updatable=false) public String getModifiedBy() { return modifiedBy; } public void setModifiedBy(String modifiedBy) { this.modifiedBy = modifiedBy; } @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } }3. Mapping soft delete.
package com.myapp.common; import javax.persistence.Column; import javax.persistence.MappedSuperclass; import org.hibernate.annotations.Type; import org.hibernate.annotations.Where; @SuppressWarnings("serial") @MappedSuperclass @Where(clause = "inactiveFlag = 'N'") public class BaseSoftDeleteableLongEntity extends StandardIdLongBaseEntity { private boolean isSoftDeleted; @Column(name = "InactiveFlag", nullable = false, length = 1) @Type(type = "yes_no") public boolean isSoftDeleted() { return isSoftDeleted; } public void setSoftDeleted(boolean isSoftDeleted) { this.isSoftDeleted = isSoftDeleted; } }
4 Finally, defining your entity.
package com.myapp.batch; import com.google.common.base.Objects; import java.util.Date; import javax.persistence.AttributeOverride; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Table; import javax.persistence.Transient; import org.apache.commons.lang.StringUtils; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDefs; import org.hibernate.annotations.Where; @Entity @Table(name = "Tbl_Batch_Run", schema = "dbo", catalog = "sne_kath") @AttributeOverride(name = "id", column = @Column(name = "Tbl_Batch_RunId")) @Where(clause = "inactiveFlag = 'N'") @SQLDelete(sql = "UPDATE Tbl_Batch_Run set inactiveFlag = 'Y' WHERE Tbl_Batch_RunId = ? and timestamp = ?") // optimistic locking @JsonIgnoreProperties( { "auditFields", "id", "softDeleted"}) @TypeDefs( {@TypeDef(name = "BatchTypeCd", typeClass = BatchTypeCdType.class)}) // user defined types public class BatchRun extends BaseSoftDeleteableLongEntity { private String entity; private long batchId; private BatchTypeCd batchTypeCd; private Status status; /** * Required constructor for JSON Marshal */ public BatchRun() { } public BatchRun(BatchRun aBatchRun) { this(aBatchRun.getEntity(), aBatchRun.getBatchId(), aBatchRun.getBatchTypeCd(), aBatchRun.getStatus()); } public BatchRun(String entity, long batchId, BatchTypeCd batchTypeCd, Status status,) { this.entity = entity; this.batchId = batchId; this.batchTypeCd = batchTypeCd; this.status = status; } @Column(name = "Entity", nullable = false) public String getEntity() { return this.entity; } public void setEntity(String entity) { this.entity = entity; } @Column(name = "BatchId", nullable = false) public long getBatchId() { return this.batchId; } public void setBatchId(long batchId) { this.batchId = batchId; } @Enumerated(EnumType.STRING) @Column(name = "Status", nullable = false, length = 30) public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } @Type(type = "BatchTypeCd") @Column(name = "BatchType", nullable = false, length = 30) public BatchTypeCd getBatchTypeCd() { return batchTypeCd; } public void setBatchTypeCd(BatchTypeCd batchTypeCd) { this.batchTypeCd = batchTypeCd; } /** * Business key for this object is the batchid, entity, valuationDate and batchTypeCd * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final BatchRun other = (BatchRun) obj; return Objects.equal(batchId, other.batchId) && Objects.equal(entity, other.entity) && Objects.equal(batchTypeCd, other.batchTypeCd); } @Override public int hashCode() { return Objects.hashCode(batchId, entity, valuationDate, batchTypeCd); } @Override public String toString() { return Objects.toStringHelper(this) .addValue(batchId) .addValue(entity) .addValue(batchTypeCd) .toString(); } }
Labels: Hibernate
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home