Example #1
0
 /**
  * saving and cloning records
  *
  * @param <T> POJOs must extend DefaultRecord
  * @param recordClass class of the records to clone
  * @param records records to clone
  * @return list of cloned and saved records
  */
 public <T extends Record> List<T> clone(Class<T> recordClass, T... records) {
   RecordWrapper cloneWrapper = new RecordWrapper();
   List<T> list = new ArrayList<>();
   mc.transaction(
       () -> {
         // saving records
         save(records);
         // cloning
         for (T record : records) {
           RecordWrapper wrapper =
               mc.getTransactionEntityManager().find(RecordWrapper.class, record.getId());
           cloneWrapper.setRecordType(wrapper.getRecordType());
           cloneWrapper.setTenant(wrapper.getTenant());
           if (wrapper.getData() != null) {
             cloneWrapper.setData(wrapper.getData());
           }
           ////                if (wrapper.getDocument() != null) {
           ////                    cloneWrapper.setDocument(wrapper.getDocument());
           ////                }
           // performing database operations
           mc.getTransactionEntityManager().persist(cloneWrapper);
           T recordClone = mc.getRecord(recordClass, wrapper);
           indexRecord(true, recordClone);
           auditor.logCreateRecord(cloneWrapper);
           list.add(recordClone);
         }
       });
   return list;
 }
Example #2
0
  /**
   * listing "distinct" and sorted values from an index key
   *
   * @param key index key to query as defined in the FieldIndexing annotation (if not empty)
   * @return list of string of available values for the index
   */
  public List<String> getIndexList(String key) {
    List selectedList =
        mc.getTransactionEntityManager()
            .createNamedQuery("RecordIndex.findValueFromKey")
            .setParameter("key", key)
            .getResultList();
    if (selectedList.isEmpty()) {
      selectedList =
          mc.getTransactionEntityManager()
              .createNamedQuery("RecordIndex.findDateFromKey")
              .setParameter("key", key)
              .getResultList();
    }
    if (selectedList.isEmpty()) {
      selectedList =
          mc.getTransactionEntityManager()
              .createNamedQuery("RecordIndex.findNumericFromKey")
              .setParameter("key", key)
              .getResultList();
    }

    List<String> list = new ArrayList();
    for (Object o : selectedList) {
      list.add(o.toString());
    }
    return list;
  }
Example #3
0
 /**
  * Saving records to the database. JPA handles bath writing, so parameter paging can be ignored
  * (vs remove).
  *
  * @param <T> POJOs must extend DefaultRecord
  * @param list list of records to save in the database
  */
 public <T extends Record> void save(List<T> list) {
   if (list != null) {
     mc.transaction(
         () -> {
           list.stream()
               .filter((record) -> (record != null))
               .forEach(
                   (record) -> {
                     Long recordId = record.getId();
                     if (recordId == null) {
                       // persisting new record
                       RecordWrapper wrapper = createRecordWrapper(record);
                       indexRecord(true, record);
                       auditor.logCreateRecord(wrapper);
                     } else {
                       // using reference as only SET is necesssary
                       RecordWrapper wrapper =
                           mc.getTransactionEntityManager()
                               .getReference(
                                   RecordWrapper.class,
                                   recordId); // getReference creating two queries including
                                              // RecordType
                       wrapper.setData(mc.toWrapper(record));
                       ////                       if (record.isDocumentChanged()) {
                       ////                           wrapper.setDocument(record.getDocument());
                       ////                       }
                       indexRecord(false, record);
                       auditor.logUpdateRecord(wrapper, false); // //record.isDocumentChanged());
                     }
                   });
         });
   }
 }
Example #4
0
 /**
  * Gives the type of a record from its id. Main purpose is to check if the manipulated id has the
  * correct type.
  *
  * @param id record id
  * @return type name of the record
  */
 public String getRecordType(Long id) {
   RecordWrapper record = mc.getTransactionEntityManager().find(RecordWrapper.class, id);
   if (record != null) {
     return mc.getTransactionEntityManager()
         .find(RecordType.class, record.getRecordType())
         .getCode();
   }
   return null;
 }
Example #5
0
 /**
  * deleting records from the database
  *
  * @param <T> POJOs must extend DefaultRecord
  * @param records list of records to remove from the database
  */
 public <T extends Record> void remove(List<T> records) {
   if (!records.isEmpty()) {
     // -- removing records by max batch of PARAMETER_PAGING which is the max number of paramaters
     // supported by some databases (ex PostgreSQL)
     // -- single transaction for all deletes to preserve integrity and faster processing (batch
     // SQL).
     mc.transaction(
         () -> {
           int start = 0;
           int end = Math.min(records.size(), PARAMETER_PAGING);
           while (start < records.size()) {
             removeTransaction(records.subList(start, end));
             start = end;
             end = Math.min(records.size(), end + PARAMETER_PAGING);
           }
         });
     // -- if transaction is successfull unvalidating IDs
     records
         .stream()
         .forEach(
             (record) -> {
               record.setId(-1L);
             });
   }
 }
Example #6
0
 /**
  * parsing record to extract indexed fields
  *
  * @param isNew record has never been parsed
  * @param record record to parse
  */
 private void indexRecord(boolean isNew, Record record) {
   for (Field field : mc.getIndexingField(record.getClass())) {
     try {
       // indexes can be shared by providing common key, or default key using field path is used
       String key = field.getAnnotation(FieldIndexing.class).value();
       String name = field.getName();
       if (key.length() == 0) {
         key = record.getClass().getName() + "." + name;
       }
       // reading field value to add to index list
       boolean status = field.isAccessible();
       field.setAccessible(true);
       Object fieldValue = field.get(record);
       field.setAccessible(status);
       // database access
       EntityManager em = mc.getTransactionEntityManager();
       RecordIndexPK pk = new RecordIndexPK(key, record.getId(), name);
       // forcing indexe deletion to preserve record batch save (npreveting select for each save)
       // Note : make sure the ModelController provides an entity manager with
       // setShouldPerformDeletesFirst(true)
       if (!isNew) {
         em.remove(em.getReference(RecordIndex.class, pk));
       }
       // mapping the index to the database
       Comparable converted = pm.getConverted(fieldValue);
       if (converted != null) {
         RecordIndex ri = new RecordIndex(pk);
         switch (pm.getType(field.getType())) {
           case DATE:
             ri.setDate((Date) converted);
             break;
           case STRING:
             ri.setValue((String) converted);
             break;
           case NUMERIC:
             ri.setNumeric((BigDecimal) converted);
             break;
         }
         // saving
         em.persist(ri);
       }
     } catch (IllegalArgumentException | IllegalAccessException ex) {
       // convet to runtime exception
       throw new RuntimeException(ex);
     }
   }
 }
Example #7
0
 /**
  * creating a new record
  *
  * @param <T> POJOs must extend DefaultRecord
  * @param record record to save
  * @return the entity encapsulating the record in the database
  */
 private <T extends Record> RecordWrapper createRecordWrapper(T record) {
   RecordWrapper wrapper = new RecordWrapper();
   RecordType recordType = mc.getType(record.getClass(), false);
   wrapper.setRecordType(recordType.getId());
   wrapper.setData(mc.toWrapper(record));
   ////        if (record.isDocumentChanged()) {
   ////            wrapper.setDocument(record.getDocument());
   ////        }
   // -- tenant management
   Object tenant =
       mc.getTransactionEntityManager()
           .getProperties()
           .get(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT);
   if (tenant != null) {
     wrapper.setTenant(tenant.toString());
   }
   // -- getting the id from the JPA
   mc.getTransactionEntityManager().persist(wrapper);
   Long recordId = wrapper.getId();
   record.setId(recordId);
   return wrapper;
 }
Example #8
0
 /**
  * removing parents and children
  *
  * @param parentId record id
  * @param childId record id
  */
 private void removePath(Long parentId, Long childId) {
   EntityManager em = mc.getTransactionEntityManager();
   // -- building branch to remove from below hierarchy >> using parent to use only this parent
   // path if more than one exists
   List<Long> pathList =
       em.createQuery(
               "SELECT rp.recordPK.path FROM RecordPath rp WHERE rp.recordPK.child = :child")
           .setParameter("child", parentId)
           .getResultList();
   pathList.add(parentId);
   // -- retrieving hierarchies to complete with new branch >> finding all children of reference
   List<RecordPathPK> pkList =
       em.createQuery("SELECT rp.recordPK FROM RecordPath rp WHERE rp.recordPK.path = :path")
           .setParameter("path", childId)
           .getResultList();
   pkList.add(new RecordPathPK(parentId, childId, parentId));
   // -- applying path branch to childrens
   for (RecordPathPK pk : pkList) {
     for (Long path : pathList) {
       pk.setPath(path);
       em.remove(em.getReference(RecordPath.class, pk));
     }
   }
 }
Example #9
0
 /**
  * Spawn transaction to group multiple writing operations. Otherwise a transaction is
  * encapsulating each operation.
  *
  * @param runnable for Lambda
  */
 public void transaction(Runnable runnable) {
   mc.transaction(runnable);
 }
Example #10
0
  /**
   * Logic removing the record as well as the links. Multiple trials are preserved in comments just
   * for reminder on current reference implementation.
   *
   * @param <T> POJOs must extend DefaultRecord
   * @param recordList list of records to remove
   */
  private <T extends Record> void removeTransaction(List<T> recordList) {
    // String recordType = records.get(0).getClass().getName();
    // -- extracting IDs for batch processing
    ArrayList<Long> idList = new ArrayList<>();
    for (Record record : recordList) {
      idList.add(record.getId());
      // -- POSTGRES won't perform correctly with multiple delete query
      // mc.getEntityManager().createNamedQuery("RecordLink.deleteByRecordId").setParameter("record", record.getId()).executeUpdate();
      // mc.getEntityManager().createNamedQuery("RecordWrapper.deleteByRecordId").setParameter("record", record.getId()).executeUpdate();
      // record.setId(null);
      // ------------------------------------------------------------------
    }
    // -- DELETE query less efficient than JPA remove (= batch) by x3
    // mc.getEntityManager().createNamedQuery("RecordLink.deleteByRecordIdList").setParameter("list", idList).executeUpdate();
    // mc.getEntityManager().createNamedQuery("RecordWrapper.deleteByRecordIdList").setParameter("list", idList).executeUpdate();
    // ----------------------------------------------------------------------
    EntityManager em = mc.getTransactionEntityManager();
    // -- removing links
    // -- two queries to fetch links is more efficient than a single with OR by x10
    // -- CursoredStream is effective in memory management and helps to get better performances than
    // EAGER fetch
    CursoredStream pkList1 =
        (CursoredStream)
            em.createNamedQuery("RecordLink.findByReferenceIdList")
                .setParameter("list", idList)
                .setHint("eclipselink.cursor", true)
                .getSingleResult();
    CursoredStream pkList2 =
        (CursoredStream)
            em.createNamedQuery("RecordLink.findByLinkIdList")
                .setParameter("list", idList)
                .setHint("eclipselink.cursor", true)
                .getSingleResult();

    // -- using reference to avoid complete loading of obsolete objects
    while (!pkList1.atEnd()) {
      for (Object pkO : pkList1.next(100)) {
        RecordLinkPK pk = (RecordLinkPK) pkO;
        em.remove(em.getReference(RecordLink.class, pk));
        auditor.logRemoveLink(pk.getReference(), pk.getLink());
      }
    }
    pkList1.close();
    while (!pkList2.atEnd()) {
      for (Object pkO : pkList2.next(100)) {
        RecordLinkPK pk = (RecordLinkPK) pkO;
        em.remove(em.getReference(RecordLink.class, pk));
        // no log here as duplicated
      }
    }
    pkList2.close();

    // -- removing paths
    List<RecordPathPK> childList =
        em.createNamedQuery("RecordPath.findChildFromIdList")
            .setParameter("list", idList)
            .getResultList();
    for (RecordPathPK path : childList) {
      removePath(path.getParent(), path.getChild());
      auditor.logRemovePath(path.getParent(), path.getChild());
    }
    List<RecordPathPK> parentList =
        em.createNamedQuery("RecordPath.findParentFromIdList")
            .setParameter("list", idList)
            .getResultList();
    for (RecordPathPK path : parentList) {
      removePath(path.getParent(), path.getChild());
      auditor.logRemovePath(path.getParent(), path.getChild());
    }

    // -- removing indexes
    List<RecordIndexPK> indexList =
        em.createNamedQuery("RecordIndex.findIndexFromIdList")
            .setParameter("list", idList)
            .getResultList();
    indexList
        .stream()
        .forEach(
            (index) -> {
              em.remove(em.getReference(RecordIndex.class, index));
            });

    // -- removing records
    for (Long id : idList) {
      RecordWrapper rw = em.getReference(RecordWrapper.class, id);
      em.remove(rw);
      auditor.logRemoveRecord(rw);
    }
  }