From 3dbdf881240c7fc633edbcaa4fe83ef5bd8a1971 Mon Sep 17 00:00:00 2001 From: jendib Date: Mon, 30 Nov 2015 01:02:54 +0100 Subject: [PATCH 1/3] Closes #49: T_FILE.FIL_IDUSER_C non nullable --- .../src/main/java/com/sismics/docs/core/model/jpa/File.java | 2 +- docs-core/src/main/resources/config.properties | 2 +- docs-core/src/main/resources/db/update/dbupdate-005-0.sql | 2 ++ docs-web/src/dev/resources/config.properties | 2 +- docs-web/src/prod/resources/config.properties | 2 +- docs-web/src/stress/resources/config.properties | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 docs-core/src/main/resources/db/update/dbupdate-005-0.sql diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java index 12062e94..401e1f7e 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java +++ b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java @@ -34,7 +34,7 @@ public class File implements Loggable { /** * User ID. */ - @Column(name = "FIL_IDUSER_C", length = 36) + @Column(name = "FIL_IDUSER_C", length = 36, nullable = false) private String userId; /** diff --git a/docs-core/src/main/resources/config.properties b/docs-core/src/main/resources/config.properties index 03d382c6..a8182ebd 100644 --- a/docs-core/src/main/resources/config.properties +++ b/docs-core/src/main/resources/config.properties @@ -1 +1 @@ -db.version=4 \ No newline at end of file +db.version=5 \ No newline at end of file diff --git a/docs-core/src/main/resources/db/update/dbupdate-005-0.sql b/docs-core/src/main/resources/db/update/dbupdate-005-0.sql new file mode 100644 index 00000000..6d210ced --- /dev/null +++ b/docs-core/src/main/resources/db/update/dbupdate-005-0.sql @@ -0,0 +1,2 @@ +alter table T_FILE alter column FIL_IDUSER_C set not null; +update T_CONFIG set CFG_VALUE_C = '5' where CFG_ID_C = 'DB_VERSION'; diff --git a/docs-web/src/dev/resources/config.properties b/docs-web/src/dev/resources/config.properties index 299540e8..680c36e5 100644 --- a/docs-web/src/dev/resources/config.properties +++ b/docs-web/src/dev/resources/config.properties @@ -1,3 +1,3 @@ api.current_version=${project.version} api.min_version=1.0 -db.version=4 \ No newline at end of file +db.version=5 \ No newline at end of file diff --git a/docs-web/src/prod/resources/config.properties b/docs-web/src/prod/resources/config.properties index 299540e8..680c36e5 100644 --- a/docs-web/src/prod/resources/config.properties +++ b/docs-web/src/prod/resources/config.properties @@ -1,3 +1,3 @@ api.current_version=${project.version} api.min_version=1.0 -db.version=4 \ No newline at end of file +db.version=5 \ No newline at end of file diff --git a/docs-web/src/stress/resources/config.properties b/docs-web/src/stress/resources/config.properties index 299540e8..680c36e5 100644 --- a/docs-web/src/stress/resources/config.properties +++ b/docs-web/src/stress/resources/config.properties @@ -1,3 +1,3 @@ api.current_version=${project.version} api.min_version=1.0 -db.version=4 \ No newline at end of file +db.version=5 \ No newline at end of file From e930ce4d47bc54648549294a96467739b135ffb8 Mon Sep 17 00:00:00 2001 From: jendib Date: Tue, 1 Dec 2015 00:32:57 +0100 Subject: [PATCH 2/3] Closes #48: Delete linked data properly + batch to clean orphan data --- .../docs/core/dao/jpa/DocumentDao.java | 25 ++++++-- .../sismics/docs/core/dao/jpa/FileDao.java | 14 +++++ .../com/sismics/docs/core/dao/jpa/TagDao.java | 7 ++- .../sismics/docs/core/dao/jpa/UserDao.java | 20 ++++++ .../docs/rest/resource/AppResource.java | 63 ++++++++++++++++++- .../docs/rest/resource/UserResource.java | 48 ++++++++++++++ 6 files changed, 167 insertions(+), 10 deletions(-) diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java index 49a70b4c..9be63b68 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java @@ -67,6 +67,20 @@ public class DocumentDao { return q.getResultList(); } + /** + * Returns the list of all documents from a user. + * + * @param userId User ID + * @return List of documents + */ + @SuppressWarnings("unchecked") + public List findByUserId(String userId) { + EntityManager em = ThreadLocalContext.get().getEntityManager(); + Query q = em.createQuery("select d from Document d where d.userId = :userId and d.deleteDate is null"); + q.setParameter("userId", userId); + return q.getResultList(); + } + /** * Returns an active document. * @@ -149,13 +163,12 @@ public class DocumentDao { q.setParameter("dateNow", dateNow); q.executeUpdate(); - // TODO Delete share from deleted ACLs -// q = em.createQuery("update Share s set s.deleteDate = :dateNow where s.documentId = :documentId and s.deleteDate is null"); -// q.setParameter("documentId", id); -// q.setParameter("dateNow", dateNow); -// q.executeUpdate(); + q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :documentId and a.deleteDate is null"); + q.setParameter("documentId", id); + q.setParameter("dateNow", dateNow); + q.executeUpdate(); - q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :documentId"); + q = em.createQuery("update DocumentTag dt set dt.deleteDate = :dateNow where dt.documentId = :documentId and dt.deleteDate is not null"); q.setParameter("documentId", id); q.setParameter("dateNow", dateNow); q.executeUpdate(); diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/FileDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/FileDao.java index 84bb799a..28b7a92c 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/FileDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/FileDao.java @@ -53,6 +53,20 @@ public class FileDao { return q.getResultList(); } + /** + * Returns the list of all files from a user. + * + * @param userId User ID + * @return List of files + */ + @SuppressWarnings("unchecked") + public List findByUserId(String userId) { + EntityManager em = ThreadLocalContext.get().getEntityManager(); + Query q = em.createQuery("select f from File f where f.userId = :userId and f.deleteDate is null"); + q.setParameter("userId", userId); + return q.getResultList(); + } + /** * Returns an active file. * diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java index dbff4438..e12d91aa 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java @@ -240,10 +240,12 @@ public class TagDao { Tag tagDb = (Tag) q.getSingleResult(); // Delete the tag - tagDb.setDeleteDate(new Date()); + Date dateNow = new Date(); + tagDb.setDeleteDate(dateNow); // Delete linked data - q = em.createQuery("delete DocumentTag dt where dt.tagId = :tagId"); + q = em.createQuery("update DocumentTag dt set dt.deleteDate = :dateNow where dt.tagId = :tagId and dt.deleteDate is not null"); + q.setParameter("dateNow", dateNow); q.setParameter("tagId", tagId); q.executeUpdate(); @@ -291,3 +293,4 @@ public class TagDao { return tagFromDb; } } + diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java index a0d20c1c..9d6d154a 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java @@ -204,6 +204,26 @@ public class UserDao { q.setParameter("userId", userFromDb.getId()); q.executeUpdate(); + q = em.createQuery("update Document d set d.deleteDate = :dateNow where d.userId = :userId and d.deleteDate is null"); + q.setParameter("userId", userFromDb.getId()); + q.setParameter("dateNow", dateNow); + q.executeUpdate(); + + q = em.createQuery("update File f set f.deleteDate = :dateNow where f.userId = :userId and f.deleteDate is null"); + q.setParameter("userId", userFromDb.getId()); + q.setParameter("dateNow", dateNow); + q.executeUpdate(); + + q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.targetId = :userId and a.deleteDate is null"); + q.setParameter("userId", userFromDb.getId()); + q.setParameter("dateNow", dateNow); + q.executeUpdate(); + + q = em.createQuery("update Comment c set c.deleteDate = :dateNow where c.userId = :userId and c.deleteDate is null"); + q.setParameter("userId", userFromDb.getId()); + q.setParameter("dateNow", dateNow); + q.executeUpdate(); + // Create audit log AuditLogUtil.create(userFromDb, AuditLogType.DELETE); } diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java index 94a5977d..3269f0f0 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java @@ -11,6 +11,8 @@ import java.util.ResourceBundle; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObjectBuilder; +import javax.persistence.EntityManager; +import javax.persistence.Query; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -19,7 +21,8 @@ import javax.ws.rs.core.Response; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Appender; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.sismics.docs.core.dao.jpa.FileDao; import com.sismics.docs.core.dao.jpa.UserDao; @@ -33,6 +36,7 @@ import com.sismics.docs.core.util.jpa.PaginatedLists; import com.sismics.docs.rest.constant.BaseFunction; import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ServerException; +import com.sismics.util.context.ThreadLocalContext; import com.sismics.util.log4j.LogCriteria; import com.sismics.util.log4j.LogEntry; import com.sismics.util.log4j.MemoryAppender; @@ -44,6 +48,11 @@ import com.sismics.util.log4j.MemoryAppender; */ @Path("/app") public class AppResource extends BaseResource { + /** + * Logger. + */ + private static final Logger log = LoggerFactory.getLogger(AppResource.class); + /** * Return the information about the application. * @@ -92,7 +101,7 @@ public class AppResource extends BaseResource { // TODO Change level by minLevel (returns all logs above) // Get the memory appender - Logger logger = Logger.getRootLogger(); + org.apache.log4j.Logger logger = org.apache.log4j.Logger.getRootLogger(); Appender appender = logger.getAppender("MEMORY"); if (appender == null || !(appender instanceof MemoryAppender)) { throw new ServerException("ServerError", "MEMORY appender not configured"); @@ -168,6 +177,7 @@ public class AppResource extends BaseResource { for (File file : fileList) { fileMap.put(file.getId(), file); } + log.info("Checking {} files", fileMap.size()); // Check if each stored file is valid try (DirectoryStream storedFileList = Files.newDirectoryStream(DirectoryUtil.getStorageDirectory())) { @@ -175,6 +185,7 @@ public class AppResource extends BaseResource { String fileName = storedFile.getFileName().toString(); String[] fileNameArray = fileName.split("_"); if (!fileMap.containsKey(fileNameArray[0])) { + log.info("Deleting orphan files at this location: {}", storedFile); Files.delete(storedFile); } } @@ -182,6 +193,54 @@ public class AppResource extends BaseResource { throw new ServerException("FileError", "Error deleting orphan files", e); } + // Hard delete orphan audit logs + EntityManager em = ThreadLocalContext.get().getEntityManager(); + StringBuilder sb = new StringBuilder("delete from T_AUDIT_LOG al where al.LOG_ID_C in (select al.LOG_ID_C from T_AUDIT_LOG al "); + sb.append(" left join T_DOCUMENT d on d.DOC_ID_C = al.LOG_IDENTITY_C and d.DOC_DELETEDATE_D is null "); + sb.append(" left join T_ACL a on a.ACL_ID_C = al.LOG_IDENTITY_C and a.ACL_DELETEDATE_D is null "); + sb.append(" left join T_COMMENT c on c.COM_ID_C = al.LOG_IDENTITY_C and c.COM_DELETEDATE_D is null "); + sb.append(" left join T_FILE f on f.FIL_ID_C = al.LOG_IDENTITY_C and f.FIL_DELETEDATE_D is null "); + sb.append(" left join T_TAG t on t.TAG_ID_C = al.LOG_IDENTITY_C and t.TAG_DELETEDATE_D is null "); + sb.append(" left join T_USER u on u.USE_ID_C = al.LOG_IDENTITY_C and u.USE_DELETEDATE_D is null "); + sb.append(" where d.DOC_ID_C is null and a.ACL_ID_C is null and c.COM_ID_C is null and f.FIL_ID_C is null and t.TAG_ID_C is null and u.USE_ID_C is null)"); + Query q = em.createNativeQuery(sb.toString()); + log.info("Deleting {} orphan audit logs", q.executeUpdate()); + + // Hard delete orphan ACLs + sb = new StringBuilder("delete from T_ACL a where a.ACL_ID_C in (select a.ACL_ID_C from T_ACL a "); + sb.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C "); + sb.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C "); + sb.append(" left join T_DOCUMENT d on d.DOC_ID_C = a.ACL_SOURCEID_C "); + sb.append(" where s.SHA_ID_C is null and u.USE_ID_C is null or d.DOC_ID_C is null)"); + q = em.createNativeQuery(sb.toString()); + log.info("Deleting {} orphan ACLs", q.executeUpdate()); + + // Hard delete orphan comments + q = em.createNativeQuery("delete from T_COMMENT c where c.COM_ID_C in (select c.COM_ID_C from T_COMMENT c left join T_DOCUMENT d on d.DOC_ID_C = c.COM_IDDOC_C and d.DOC_DELETEDATE_D is null where d.DOC_ID_C is null)"); + log.info("Deleting {} orphan comments", q.executeUpdate()); + + // Hard delete orphan document tag links + q = em.createNativeQuery("delete from T_DOCUMENT_TAG dt where dt.DOT_ID_C in (select dt.DOT_ID_C from T_DOCUMENT_TAG dt left join T_DOCUMENT d on dt.DOT_IDDOCUMENT_C = d.DOC_ID_C and d.DOC_DELETEDATE_D is null left join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C and t.TAG_DELETEDATE_D is null where d.DOC_ID_C is null or t.TAG_ID_C is null)"); + log.info("Deleting {} orphan document tag links", q.executeUpdate()); + + // Hard delete orphan shares + q = em.createNativeQuery("delete from T_SHARE s where s.SHA_ID_C in (select s.SHA_ID_C from T_SHARE s left join T_ACL a on a.ACL_TARGETID_C = s.SHA_ID_C and a.ACL_DELETEDATE_D is null where a.ACL_ID_C is null)"); + log.info("Deleting {} orphan shares", q.executeUpdate()); + + // Hard delete orphan tags + q = em.createNativeQuery("delete from T_TAG t where t.TAG_ID_C in (select t.TAG_ID_C from T_TAG t left join T_USER u on u.USE_ID_C = t.TAG_IDUSER_C and u.USE_DELETEDATE_D is null where u.USE_ID_C is null)"); + log.info("Deleting {} orphan tags", q.executeUpdate()); + + // Hard delete softly deleted data + log.info("Deleting {} soft deleted document tag links", em.createQuery("delete DocumentTag dt where dt.deleteDate is not null").executeUpdate()); + log.info("Deleting {} soft deleted ACLs", em.createQuery("delete Acl a where a.deleteDate is not null").executeUpdate()); + log.info("Deleting {} soft deleted shares", em.createQuery("delete Share s where s.deleteDate is not null").executeUpdate()); + log.info("Deleting {} soft deleted tags", em.createQuery("delete Tag t where t.deleteDate is not null").executeUpdate()); + log.info("Deleting {} soft deleted comments", em.createQuery("delete Comment c where c.deleteDate is not null").executeUpdate()); + log.info("Deleting {} soft deleted files", em.createQuery("delete File f where f.deleteDate is not null").executeUpdate()); + log.info("Deleting {} soft deleted documents", em.createQuery("delete Document d where d.deleteDate is not null").executeUpdate()); + log.info("Deleting {} soft deleted users", em.createQuery("delete User u where u.deleteDate is not null").executeUpdate()); + // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() .add("status", "ok"); diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java index a4da17bb..0460d069 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java @@ -2,6 +2,7 @@ package com.sismics.docs.rest.resource; import java.security.NoSuchAlgorithmException; import java.util.Date; +import java.util.List; import java.util.Set; import javax.json.Json; @@ -26,11 +27,18 @@ import org.apache.commons.lang.StringUtils; import com.google.common.base.Strings; import com.sismics.docs.core.constant.Constants; import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao; +import com.sismics.docs.core.dao.jpa.DocumentDao; +import com.sismics.docs.core.dao.jpa.FileDao; import com.sismics.docs.core.dao.jpa.RoleBaseFunctionDao; import com.sismics.docs.core.dao.jpa.UserDao; import com.sismics.docs.core.dao.jpa.criteria.UserCriteria; import com.sismics.docs.core.dao.jpa.dto.UserDto; +import com.sismics.docs.core.event.DocumentDeletedAsyncEvent; +import com.sismics.docs.core.event.FileDeletedAsyncEvent; +import com.sismics.docs.core.model.context.AppContext; import com.sismics.docs.core.model.jpa.AuthenticationToken; +import com.sismics.docs.core.model.jpa.Document; +import com.sismics.docs.core.model.jpa.File; import com.sismics.docs.core.model.jpa.User; import com.sismics.docs.core.util.EncryptionUtil; import com.sismics.docs.core.util.jpa.PaginatedList; @@ -345,10 +353,30 @@ public class UserResource extends BaseResource { throw new ClientException("ForbiddenError", "The admin user cannot be deleted"); } + // Find linked data + DocumentDao documentDao = new DocumentDao(); + List documentList = documentDao.findByUserId(principal.getId()); + FileDao fileDao = new FileDao(); + List fileList = fileDao.findByUserId(principal.getId()); + // Delete the user UserDao userDao = new UserDao(); userDao.delete(principal.getName()); + // Raise deleted events for documents + for (Document document : documentList) { + DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent(); + documentDeletedAsyncEvent.setDocument(document); + AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent); + } + + // Raise deleted events for files + for (File file : fileList) { + FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent(); + fileDeletedAsyncEvent.setFile(file); + AppContext.getInstance().getAsyncEventBus().post(fileDeletedAsyncEvent); + } + // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() .add("status", "ok"); @@ -383,9 +411,29 @@ public class UserResource extends BaseResource { throw new ClientException("ForbiddenError", "The admin user cannot be deleted"); } + // Find linked data + DocumentDao documentDao = new DocumentDao(); + List documentList = documentDao.findByUserId(user.getId()); + FileDao fileDao = new FileDao(); + List fileList = fileDao.findByUserId(user.getId()); + // Delete the user userDao.delete(user.getUsername()); + // Raise deleted events for documents + for (Document document : documentList) { + DocumentDeletedAsyncEvent documentDeletedAsyncEvent = new DocumentDeletedAsyncEvent(); + documentDeletedAsyncEvent.setDocument(document); + AppContext.getInstance().getAsyncEventBus().post(documentDeletedAsyncEvent); + } + + // Raise deleted events for files + for (File file : fileList) { + FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent(); + fileDeletedAsyncEvent.setFile(file); + AppContext.getInstance().getAsyncEventBus().post(fileDeletedAsyncEvent); + } + // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() .add("status", "ok"); From 5f516047bda4c2e1f10e137e33cd495d756d9746 Mon Sep 17 00:00:00 2001 From: jendib Date: Tue, 1 Dec 2015 01:16:57 +0100 Subject: [PATCH 3/3] #48: Soft delete before hard delete --- .../docs/rest/resource/AppResource.java | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java index 3269f0f0..256a4c16 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java @@ -3,6 +3,7 @@ package com.sismics.docs.rest.resource; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -206,31 +207,46 @@ public class AppResource extends BaseResource { Query q = em.createNativeQuery(sb.toString()); log.info("Deleting {} orphan audit logs", q.executeUpdate()); - // Hard delete orphan ACLs - sb = new StringBuilder("delete from T_ACL a where a.ACL_ID_C in (select a.ACL_ID_C from T_ACL a "); + // Soft delete orphan ACLs + sb = new StringBuilder("update T_ACL a set ACL_DELETEDATE_D = :dateNow where a.ACL_ID_C in (select a.ACL_ID_C from T_ACL a "); sb.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C "); sb.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C "); sb.append(" left join T_DOCUMENT d on d.DOC_ID_C = a.ACL_SOURCEID_C "); sb.append(" where s.SHA_ID_C is null and u.USE_ID_C is null or d.DOC_ID_C is null)"); q = em.createNativeQuery(sb.toString()); + q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan ACLs", q.executeUpdate()); - // Hard delete orphan comments - q = em.createNativeQuery("delete from T_COMMENT c where c.COM_ID_C in (select c.COM_ID_C from T_COMMENT c left join T_DOCUMENT d on d.DOC_ID_C = c.COM_IDDOC_C and d.DOC_DELETEDATE_D is null where d.DOC_ID_C is null)"); + // Soft delete orphan comments + q = em.createNativeQuery("update T_COMMENT c set c.COM_DELETEDATE_D = :dateNow where c.COM_ID_C in (select c.COM_ID_C from T_COMMENT c left join T_DOCUMENT d on d.DOC_ID_C = c.COM_IDDOC_C and d.DOC_DELETEDATE_D is null where d.DOC_ID_C is null)"); + q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan comments", q.executeUpdate()); - // Hard delete orphan document tag links - q = em.createNativeQuery("delete from T_DOCUMENT_TAG dt where dt.DOT_ID_C in (select dt.DOT_ID_C from T_DOCUMENT_TAG dt left join T_DOCUMENT d on dt.DOT_IDDOCUMENT_C = d.DOC_ID_C and d.DOC_DELETEDATE_D is null left join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C and t.TAG_DELETEDATE_D is null where d.DOC_ID_C is null or t.TAG_ID_C is null)"); + // Soft delete orphan document tag links + q = em.createNativeQuery("update T_DOCUMENT_TAG dt set dt.DOT_DELETEDATE_D = :dateNow where dt.DOT_ID_C in (select dt.DOT_ID_C from T_DOCUMENT_TAG dt left join T_DOCUMENT d on dt.DOT_IDDOCUMENT_C = d.DOC_ID_C and d.DOC_DELETEDATE_D is null left join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C and t.TAG_DELETEDATE_D is null where d.DOC_ID_C is null or t.TAG_ID_C is null)"); + q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan document tag links", q.executeUpdate()); - // Hard delete orphan shares - q = em.createNativeQuery("delete from T_SHARE s where s.SHA_ID_C in (select s.SHA_ID_C from T_SHARE s left join T_ACL a on a.ACL_TARGETID_C = s.SHA_ID_C and a.ACL_DELETEDATE_D is null where a.ACL_ID_C is null)"); + // Soft delete orphan shares + q = em.createNativeQuery("update T_SHARE s set s.SHA_DELETEDATE_D = :dateNow where s.SHA_ID_C in (select s.SHA_ID_C from T_SHARE s left join T_ACL a on a.ACL_TARGETID_C = s.SHA_ID_C and a.ACL_DELETEDATE_D is null where a.ACL_ID_C is null)"); + q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan shares", q.executeUpdate()); - // Hard delete orphan tags - q = em.createNativeQuery("delete from T_TAG t where t.TAG_ID_C in (select t.TAG_ID_C from T_TAG t left join T_USER u on u.USE_ID_C = t.TAG_IDUSER_C and u.USE_DELETEDATE_D is null where u.USE_ID_C is null)"); + // Soft delete orphan tags + q = em.createNativeQuery("update T_TAG t set t.TAG_DELETEDATE_D = :dateNow where t.TAG_ID_C in (select t.TAG_ID_C from T_TAG t left join T_USER u on u.USE_ID_C = t.TAG_IDUSER_C and u.USE_DELETEDATE_D is null where u.USE_ID_C is null)"); + q.setParameter("dateNow", new Date()); log.info("Deleting {} orphan tags", q.executeUpdate()); + // Soft delete orphan documents + q = em.createNativeQuery("update T_DOCUMENT d set d.DOC_DELETEDATE_D = :dateNow where d.DOC_ID_C in (select d.DOC_ID_C from T_DOCUMENT d left join T_USER u on u.USE_ID_C = d.DOC_IDUSER_C and u.USE_DELETEDATE_D is null where u.USE_ID_C is null)"); + q.setParameter("dateNow", new Date()); + log.info("Deleting {} orphan documents", q.executeUpdate()); + + // Soft delete orphan files + q = em.createNativeQuery("update T_FILE f set f.FIL_DELETEDATE_D = :dateNow where f.FIL_ID_C in (select f.FIL_ID_C from T_FILE f left join T_USER u on u.USE_ID_C = f.FIL_IDUSER_C and u.USE_DELETEDATE_D is null where u.USE_ID_C is null)"); + q.setParameter("dateNow", new Date()); + log.info("Deleting {} orphan files", q.executeUpdate()); + // Hard delete softly deleted data log.info("Deleting {} soft deleted document tag links", em.createQuery("delete DocumentTag dt where dt.deleteDate is not null").executeUpdate()); log.info("Deleting {} soft deleted ACLs", em.createQuery("delete Acl a where a.deleteDate is not null").executeUpdate());