/** * 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); } } }
/** * 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); } }