continued to add Auditable support

This commit is contained in:
2019-06-20 16:09:09 +02:00
parent 88d2509893
commit a8e0f9bd5f
6 changed files with 64 additions and 166 deletions

View File

@ -16,6 +16,7 @@
package de.muehlencord.shared.db; package de.muehlencord.shared.db;
import de.muehlencord.shared.util.DateUtil; import de.muehlencord.shared.util.DateUtil;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -40,6 +41,7 @@ import javax.persistence.metamodel.IdentifiableType;
import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.SingularAttribute;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
/** /**
@ -47,7 +49,7 @@ import org.apache.commons.lang3.StringUtils;
* @author Joern Muehlencord <joern at muehlencord.de> * @author Joern Muehlencord <joern at muehlencord.de>
* @param <T> * @param <T>
*/ */
public abstract class AbstractController<T> { public abstract class AbstractController<T extends Serializable> {
@Inject @Inject
@ApplicationPU @ApplicationPU
@ -190,14 +192,19 @@ public abstract class AbstractController<T> {
return path; return path;
} }
// public void applyUpdateableChanges(Updateable updateable, boolean onCreate, String updatedBy) throws ControllerException { public Audit applyAuditChanges(Audit audit, boolean onCreate, String changedBy) throws ControllerException {
// if (onCreate) { if (audit == null) {
// updateable.setCreatedBy(updatedBy); audit = new Audit();
// updateable.setCreatedOn(new Date()); }
// } if (onCreate) {
// updateable.setLastUpdatedBy(updatedBy); audit.setCreatedBy(changedBy);
// updateable.setLastUpdatedOn(new Date()); audit.setCreatedOn(DateUtil.getCurrentTimeInUTC());
// } }
audit.setLastUpdatedBy(changedBy);
audit.setLastUpdatedOn(DateUtil.getCurrentTimeInUTC());
return audit;
}
public T attach(T entity) { public T attach(T entity) {
return em.merge(entity); return em.merge(entity);
@ -207,10 +214,10 @@ public abstract class AbstractController<T> {
@Transactional @Transactional
@Lock(LockType.WRITE) @Lock(LockType.WRITE)
public T create(T entity, String createdBy) throws ControllerException { public T create(T entity, String createdBy) throws ControllerException {
// if (Updateable.class.isAssignableFrom(entity.getClass())) { if (Auditable.class.isAssignableFrom(entity.getClass())) {
// Updateable updateable = (Updateable) entity; Audit audit = ((Auditable) entity).getAudit();
// applyUpdateableChanges(updateable, true, createdBy); ((Auditable) entity).setAudit(applyAuditChanges(audit, true, createdBy));
// } }
if (EndDateable.class.isAssignableFrom(entity.getClass())) { if (EndDateable.class.isAssignableFrom(entity.getClass())) {
EndDateable endDateable = (EndDateable) entity; EndDateable endDateable = (EndDateable) entity;
endDateable.setValidFrom(DateUtil.getCurrentTimeInUTC()); endDateable.setValidFrom(DateUtil.getCurrentTimeInUTC());
@ -223,11 +230,26 @@ public abstract class AbstractController<T> {
@Transactional @Transactional
@Lock(LockType.WRITE) @Lock(LockType.WRITE)
public T update(T entity, String updatedBy) throws ControllerException { public T update(T entity, String updatedBy) throws ControllerException {
// if (Updateable.class.isAssignableFrom(entity.getClass())) { T currentEntity = attach(entity);
// Updateable updateable = (Updateable) entity; T newEntity = getClone(currentEntity);
// applyUpdateableChanges(updateable, false, updatedBy); if (Auditable.class.isAssignableFrom(entity.getClass())) {
// } Audit audit = ((Auditable) entity).getAudit();
return em.merge(entity); ((Auditable) entity).setAudit(applyAuditChanges(audit, false, updatedBy));
}
if (EndDateable.class.isAssignableFrom(entity.getClass())) {
// end date existing entity
EndDateable endDateable = (EndDateable) entity;
endDateable.setValidTo(DateUtil.getCurrentTimeInUTC());
em.merge(entity);
// and create new entity instead
return create(newEntity, updatedBy);
} else {
// if it is not enddatable, just update it (already done above)
// and save it
return em.merge(entity);
}
} }
@TransactionAttribute(TransactionAttributeType.REQUIRED) @TransactionAttribute(TransactionAttributeType.REQUIRED)
@ -236,8 +258,6 @@ public abstract class AbstractController<T> {
public void delete(T entity, String deletedBy) throws ControllerException { public void delete(T entity, String deletedBy) throws ControllerException {
// if the entity is endDateable just set the validToDate to now intead of executing the deletion // if the entity is endDateable just set the validToDate to now intead of executing the deletion
if (EndDateable.class.isAssignableFrom(entity.getClass())) { if (EndDateable.class.isAssignableFrom(entity.getClass())) {
EndDateable endDateable = (EndDateable) entity;
endDateable.setValidTo(DateUtil.getCurrentTimeInUTC());
update(entity, deletedBy); update(entity, deletedBy);
} else { } else {
em.remove(attach(entity)); em.remove(attach(entity));
@ -329,4 +349,18 @@ public abstract class AbstractController<T> {
return of.getId(of.getIdType().getJavaType()); return of.getId(of.getIdType().getJavaType());
} }
private T getClone(T entity) {
T newEntity = SerializationUtils.clone(entity);
// remove audit if class is auditable - it is a new clone
if (Auditable.class.isAssignableFrom(newEntity.getClass())) {
((Auditable) newEntity).setAudit(null);
}
// set new valid dates if class is enddateable
if (EndDateable.class.isAssignableFrom(newEntity.getClass())) {
((EndDateable) newEntity).setValidFrom(DateUtil.getCurrentTimeInUTC());
((EndDateable) newEntity).setValidTo(null);
}
return newEntity;
}
} }

View File

@ -1,104 +0,0 @@
/*
* Copyright 2019 Joern Muehlencord <joern at muehlencord.de>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import de.muehlencord.shared.util.DateUtil;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
* @param <T> an entity which needs to extend EndDateable
*/
public abstract class AbstractEnddateableController<T extends EndDateable<T>> extends AbstractController<T> {
private final Class<T> endDateableClass;
public AbstractEnddateableController(Class<T> clazz) {
super(clazz);
this.endDateableClass = clazz;
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Transactional
@Lock(LockType.WRITE)
@Override
public void delete(T entity, String deletedBy) throws ControllerException {
T entityToUpdate = attach(entity);
// if (Updateable.class.isAssignableFrom(entityToUpdate.getClass())) {
// Updateable updateable = (Updateable) entityToUpdate;
// applyUpdateableChanges(updateable, false, deletedBy);
// }
entityToUpdate.setValidTo(DateUtil.getCurrentTimeInUTC());
em.merge(entityToUpdate);
}
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Transactional
@Lock(LockType.WRITE)
public T create(T entity, String createdBy) throws ControllerException {
entity.setValidFrom(DateUtil.getCurrentTimeInUTC());
return super.create(entity, createdBy);
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Transactional
@Lock(LockType.WRITE)
@Override
public T update(T entity, String createdBy) throws ControllerException {
T newEntity = entity.cloneEndDateable();
delete(entity, createdBy);
return create(newEntity, createdBy);
}
@Lock(LockType.READ)
@Override
public List<T> findAll(List<String> orderFields) {
Date now = DateUtil.getCurrentTimeInUTC();
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<T> criteria = cb.createQuery(endDateableClass);
final Root<T> root = criteria.from(endDateableClass);
Predicate alreadyValid = cb.lessThanOrEqualTo(root.get("validFrom"), now);
Predicate validToNotSet = cb.isNull(root.get("validTo"));
Predicate isBeforeValidTo = cb.greaterThanOrEqualTo(root.get("validTo"), now);
Predicate stillValid = cb.or(isBeforeValidTo, validToNotSet);
Predicate isValid = cb.and(alreadyValid, stillValid);
criteria.where(isValid);
List<Order> orderList = new ArrayList<>();
orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(root.get(field))));
final TypedQuery<T> query = em.createQuery(criteria.orderBy(orderList));
return query.getResultList();
}
}

View File

@ -15,6 +15,7 @@
*/ */
package de.muehlencord.shared.db; package de.muehlencord.shared.db;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
import javax.persistence.Basic; import javax.persistence.Basic;
import javax.persistence.Column; import javax.persistence.Column;
@ -26,29 +27,29 @@ import javax.validation.constraints.NotNull;
* *
* @author joern.muehlencord * @author joern.muehlencord
*/ */
public class Audit { public class Audit implements Serializable {
private static final long serialVersionUID = 971294035102346924L; private static final long serialVersionUID = -955765069412891842L;
@Basic(optional = false) @Basic(optional = false)
@NotNull @NotNull
@Column(name = "created_on") @Column(name = "created_on")
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date createdOn; private Date createdOn;
@Basic(optional = false) @Basic(optional = false)
@NotNull @NotNull
@Column(name = "created_by") @Column(name = "created_by")
private String createdBy; private String createdBy;
@Basic(optional = false) @Basic(optional = false)
@NotNull @NotNull
@Column(name = "last_updated_on") @Column(name = "last_updated_on")
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date lastUpdatedOn; private Date lastUpdatedOn;
@Basic(optional = false) @Basic(optional = false)
@NotNull @NotNull
@Column(name = "last_updated_by") @Column(name = "last_updated_by")
private String lastUpdatedBy; private String lastUpdatedBy;

View File

@ -17,20 +17,18 @@ package de.muehlencord.shared.db;
import de.muehlencord.shared.util.DateUtil; import de.muehlencord.shared.util.DateUtil;
import java.util.Date; import java.util.Date;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* * Does not work, cannot Spring independet inject current username - implemented in AbstractController manually
* @author joern.muehlencord * @author joern.muehlencord
*/ */
public class AuditListener { public class AuditListener {
private static final Logger LOGGER = LoggerFactory.getLogger(AuditListener.class); private static final Logger LOGGER = LoggerFactory.getLogger(AuditListener.class);
@PrePersist // @PrePersist
public void prePersist(Auditable auditable) { public void prePersist(Auditable auditable) {
Audit audit = auditable.getAudit(); Audit audit = auditable.getAudit();
@ -50,7 +48,7 @@ public class AuditListener {
} }
@PreUpdate // @PreUpdate
public void preUpdate(Auditable auditable) { public void preUpdate(Auditable auditable) {
Audit audit = auditable.getAudit(); Audit audit = auditable.getAudit();

View File

@ -24,8 +24,6 @@ import java.util.Date;
*/ */
public interface EndDateable<T> { public interface EndDateable<T> {
T cloneEndDateable();
Date getValidFrom(); Date getValidFrom();
Date getValidTo(); Date getValidTo();

View File

@ -1,29 +0,0 @@
package de.muehlencord.shared.db;
import java.util.Date;
/**
* This interface is used for Entities which provide createdOn / createdBy
* lastUpatedBy / lastUpdatedOn fields. The AbstractController uses this interface
* to automatically update the fields on creation / update.
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public interface Updateable {
void setCreatedBy(String createdBy);
String getCreatedBy();
void setCreatedOn(Date createdOn);
Date getCreatedOn();
void setLastUpdatedBy(String lastUpdatedBy);
String getLastUpdatedBy();
void setLastUpdatedOn(Date lastUpdatedOn);
Date getLastUpdatedOn();
}