continued to add Auditable support
This commit is contained in:
@ -16,6 +16,7 @@
|
||||
package de.muehlencord.shared.db;
|
||||
|
||||
import de.muehlencord.shared.util.DateUtil;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -40,6 +41,7 @@ import javax.persistence.metamodel.IdentifiableType;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.transaction.Transactional;
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
@ -47,7 +49,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class AbstractController<T> {
|
||||
public abstract class AbstractController<T extends Serializable> {
|
||||
|
||||
@Inject
|
||||
@ApplicationPU
|
||||
@ -190,14 +192,19 @@ public abstract class AbstractController<T> {
|
||||
return path;
|
||||
}
|
||||
|
||||
// public void applyUpdateableChanges(Updateable updateable, boolean onCreate, String updatedBy) throws ControllerException {
|
||||
// if (onCreate) {
|
||||
// updateable.setCreatedBy(updatedBy);
|
||||
// updateable.setCreatedOn(new Date());
|
||||
// }
|
||||
// updateable.setLastUpdatedBy(updatedBy);
|
||||
// updateable.setLastUpdatedOn(new Date());
|
||||
// }
|
||||
public Audit applyAuditChanges(Audit audit, boolean onCreate, String changedBy) throws ControllerException {
|
||||
if (audit == null) {
|
||||
audit = new Audit();
|
||||
}
|
||||
if (onCreate) {
|
||||
audit.setCreatedBy(changedBy);
|
||||
audit.setCreatedOn(DateUtil.getCurrentTimeInUTC());
|
||||
}
|
||||
audit.setLastUpdatedBy(changedBy);
|
||||
audit.setLastUpdatedOn(DateUtil.getCurrentTimeInUTC());
|
||||
|
||||
return audit;
|
||||
}
|
||||
|
||||
public T attach(T entity) {
|
||||
return em.merge(entity);
|
||||
@ -207,10 +214,10 @@ public abstract class AbstractController<T> {
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public T create(T entity, String createdBy) throws ControllerException {
|
||||
// if (Updateable.class.isAssignableFrom(entity.getClass())) {
|
||||
// Updateable updateable = (Updateable) entity;
|
||||
// applyUpdateableChanges(updateable, true, createdBy);
|
||||
// }
|
||||
if (Auditable.class.isAssignableFrom(entity.getClass())) {
|
||||
Audit audit = ((Auditable) entity).getAudit();
|
||||
((Auditable) entity).setAudit(applyAuditChanges(audit, true, createdBy));
|
||||
}
|
||||
if (EndDateable.class.isAssignableFrom(entity.getClass())) {
|
||||
EndDateable endDateable = (EndDateable) entity;
|
||||
endDateable.setValidFrom(DateUtil.getCurrentTimeInUTC());
|
||||
@ -223,11 +230,26 @@ public abstract class AbstractController<T> {
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public T update(T entity, String updatedBy) throws ControllerException {
|
||||
// if (Updateable.class.isAssignableFrom(entity.getClass())) {
|
||||
// Updateable updateable = (Updateable) entity;
|
||||
// applyUpdateableChanges(updateable, false, updatedBy);
|
||||
// }
|
||||
return em.merge(entity);
|
||||
T currentEntity = attach(entity);
|
||||
T newEntity = getClone(currentEntity);
|
||||
if (Auditable.class.isAssignableFrom(entity.getClass())) {
|
||||
Audit audit = ((Auditable) entity).getAudit();
|
||||
((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)
|
||||
@ -236,8 +258,6 @@ public abstract class AbstractController<T> {
|
||||
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 (EndDateable.class.isAssignableFrom(entity.getClass())) {
|
||||
EndDateable endDateable = (EndDateable) entity;
|
||||
endDateable.setValidTo(DateUtil.getCurrentTimeInUTC());
|
||||
update(entity, deletedBy);
|
||||
} else {
|
||||
em.remove(attach(entity));
|
||||
@ -329,4 +349,18 @@ public abstract class AbstractController<T> {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package de.muehlencord.shared.db;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
@ -26,9 +27,9 @@ import javax.validation.constraints.NotNull;
|
||||
*
|
||||
* @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)
|
||||
@NotNull
|
||||
|
||||
@ -17,20 +17,18 @@ package de.muehlencord.shared.db;
|
||||
|
||||
import de.muehlencord.shared.util.DateUtil;
|
||||
import java.util.Date;
|
||||
import javax.persistence.PrePersist;
|
||||
import javax.persistence.PreUpdate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* Does not work, cannot Spring independet inject current username - implemented in AbstractController manually
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
public class AuditListener {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AuditListener.class);
|
||||
|
||||
@PrePersist
|
||||
// @PrePersist
|
||||
public void prePersist(Auditable auditable) {
|
||||
Audit audit = auditable.getAudit();
|
||||
|
||||
@ -50,7 +48,7 @@ public class AuditListener {
|
||||
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
// @PreUpdate
|
||||
public void preUpdate(Auditable auditable) {
|
||||
Audit audit = auditable.getAudit();
|
||||
|
||||
|
||||
@ -24,8 +24,6 @@ import java.util.Date;
|
||||
*/
|
||||
public interface EndDateable<T> {
|
||||
|
||||
T cloneEndDateable();
|
||||
|
||||
Date getValidFrom();
|
||||
|
||||
Date getValidTo();
|
||||
|
||||
@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user