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