package uk.nhs.careconnect.ri.stu3.dao; import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hibernate.Session; import org.hibernate.Transaction; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.Duration; import org.hl7.fhir.dstu3.model.Quantity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import uk.nhs.careconnect.fhir.OperationOutcomeException; import uk.nhs.careconnect.ri.database.daointerface.CodeSystemRepository; import uk.nhs.careconnect.ri.database.daointerface.ConceptRepository; import uk.nhs.careconnect.ri.database.entity.codeSystem.CodeSystemEntity; import uk.nhs.careconnect.ri.database.entity.codeSystem.ConceptDesignation; import uk.nhs.careconnect.ri.database.entity.codeSystem.ConceptEntity; import uk.nhs.careconnect.ri.database.entity.codeSystem.ConceptParentChildLink; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import javax.persistence.criteria.*; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @Repository @Transactional public class ConceptDao implements ConceptRepository { @PersistenceContext EntityManager em; private EntityManager sessionEntityManager; @Autowired private PlatformTransactionManager myTransactionMgr; @Autowired private TransactionTemplate transactionTemplate; @Autowired CodeSystemRepository codeSystemRepository; private static final Logger log = LoggerFactory.getLogger(ConceptDao.class); public Session getSession(){ sessionEntityManager = em.getEntityManagerFactory().createEntityManager(); Session session = (Session) sessionEntityManager.unwrap(Session.class); return session; } @Override public Transaction getTransaction(Session session) { return session.beginTransaction(); } public void beginTransaction(Transaction tx) { // tx.begin(); } public void commitTransaction(Transaction tx) { tx.commit(); } @Override public void save(ConceptParentChildLink conceptParentChildLink) throws OperationOutcomeException { em.persist(conceptParentChildLink); //sessionEntityManager.flush(); /* TransactionTemplate tt = new TransactionTemplate(myTransactionMgr); tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); tt.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { em.persist(object); em.flush(); log.info("Saved ConceptParentChildLink.Id = "+object.getId()); } }); */ } @Override public ConceptEntity save(ConceptEntity conceptEntity) throws OperationOutcomeException { em.persist(conceptEntity); for (ConceptParentChildLink child: conceptEntity.getChildren()) { child.setParent(conceptEntity); } return conceptEntity; } /* @Override public ConceptEntity saveTransactional(ConceptEntity conceptEntity){ sessionEntityManager.persist(conceptEntity); for (ConceptParentChildLink child: conceptEntity.getChildren()) { child.setParent(conceptEntity); } sessionEntityManager.flush(); return conceptEntity; } */ @Override public ConceptDesignation save(ConceptDesignation conceptDesignation) throws OperationOutcomeException{ sessionEntityManager.persist(conceptDesignation); sessionEntityManager.flush(); return conceptDesignation; } @Override public void persistLinks(ConceptEntity conceptEntity) throws OperationOutcomeException { for (ConceptParentChildLink childLink : conceptEntity.getChildren()) { if (childLink.getId() == null) { childLink.setCodeSystem(childLink.getChild().getCodeSystem()); save(childLink); } } for (ConceptParentChildLink childLink : conceptEntity.getChildren()) { persistLinks(childLink.getChild()); } } @Override @Transactional public void storeNewCodeSystemVersion(CodeSystemEntity theCodeSystem, RequestDetails theRequestDetails) throws OperationOutcomeException { log.info("Starting Code Processing CodeSystem.id = "+theCodeSystem.getId()); log.info("Adding Concepts - Number of Concepts CodeSystem.id = "+theCodeSystem.getConcepts().size()); for (ConceptEntity conceptEntity : theCodeSystem.getConcepts()) { save(conceptEntity); saveChildConcepts(conceptEntity); } for (ConceptEntity conceptEntity : theCodeSystem.getConcepts()) { persistLinks(conceptEntity); } log.info("Finished Code Processing"); } private void saveChildConcepts(ConceptEntity conceptEntity) throws OperationOutcomeException { for (ConceptParentChildLink childLink : conceptEntity.getChildren()) { save(childLink.getChild()); saveChildConcepts(childLink.getChild()); } } @Override public CodeSystemEntity findBySystem(String system) { CriteriaBuilder builder = sessionEntityManager.getCriteriaBuilder(); CodeSystemEntity codeSystemEntity = null; CriteriaQuery<CodeSystemEntity> criteria = builder.createQuery(CodeSystemEntity.class); Root<CodeSystemEntity> root = criteria.from(CodeSystemEntity.class); List<Predicate> predList = new LinkedList<Predicate>(); Predicate p = builder.equal(root.<String>get("codeSystemUri"),system); predList.add(p); Predicate[] predArray = new Predicate[predList.size()]; predList.toArray(predArray); if (predList.size()>0) { log.debug("Found CodeSystem "+system); criteria.select(root).where(predArray); List<CodeSystemEntity> qryResults = sessionEntityManager.createQuery(criteria).getResultList(); for (CodeSystemEntity cme : qryResults) { codeSystemEntity = cme; break; } } if (codeSystemEntity == null) { log.info("Not found adding CodeSystem = "+system); codeSystemEntity = new CodeSystemEntity(); codeSystemEntity.setCodeSystemUri(system); sessionEntityManager.persist(codeSystemEntity); } return codeSystemEntity; } @Override public ConceptEntity findCode(CodeSystemEntity codeSystem, String code) { ConceptEntity conceptEntity = null; CriteriaBuilder builder = sessionEntityManager.getCriteriaBuilder(); CriteriaQuery<ConceptEntity> criteria = builder.createQuery(ConceptEntity.class); Root<ConceptEntity> root = criteria.from(ConceptEntity.class); List<Predicate> predList = new LinkedList<Predicate>(); List<ConceptEntity> results = new ArrayList<ConceptEntity>(); log.debug("Looking for code ="+code+" in "+codeSystem.getCodeSystemUri()); Predicate pcode = builder.equal(root.get("code"), code); predList.add(pcode); Predicate psystem = builder.equal(root.get("codeSystemEntity"), codeSystem.getId()); predList.add(psystem); Predicate[] predArray = new Predicate[predList.size()]; predList.toArray(predArray); criteria.select(root).where(predArray); TypedQuery<ConceptEntity> qry = sessionEntityManager.createQuery(criteria); List<ConceptEntity> qryResults = qry.getResultList(); for (ConceptEntity concept : qryResults) { conceptEntity = concept; log.debug("Found for code="+code+" ConceptEntity.Id="+conceptEntity.getId()); break; } return conceptEntity; } @Override public ConceptEntity findCode(Coding coding) { ConceptEntity conceptEntity = null; CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<ConceptEntity> criteria = builder.createQuery(ConceptEntity.class); Root<ConceptEntity> root = criteria.from(ConceptEntity.class); List<Predicate> predList = new LinkedList<Predicate>(); List<ConceptEntity> results = new ArrayList<ConceptEntity>(); Join<ConceptEntity,CodeSystemRepository> join = root.join("codeSystemEntity"); log.debug("Looking for code ="+coding.getCode()+" in "+coding.getSystem()); Predicate pcode = builder.equal(root.get("code"), coding.getCode()); predList.add(pcode); Predicate psystem = builder.equal(join.get("codeSystemUri"), coding.getSystem()); predList.add(psystem); Predicate[] predArray = new Predicate[predList.size()]; predList.toArray(predArray); criteria.select(root).where(predArray); TypedQuery<ConceptEntity> qry = em.createQuery(criteria); qry.setHint("javax.persistence.cache.storeMode", "REFRESH"); List<ConceptEntity> qryResults = qry.getResultList(); for (ConceptEntity concept : qryResults) { conceptEntity = concept; log.debug("Found for code="+coding.getCode()+" ConceptEntity.Id="+conceptEntity.getId()); break; } return conceptEntity; } @Override public ConceptEntity findCode(Duration duration) { Coding code = new Coding().setCode(duration.getCode()).setSystem(duration.getSystem()); ConceptEntity conceptEntity = findCode(code); return conceptEntity; } @Override public ConceptEntity findAddCode(org.hl7.fhir.r4.model.Coding code) { Coding coding = new Coding().setCode(code.getCode()).setSystem(code.getSystem()).setDisplay(code.getDisplay()); return findAddCode(coding); } public ConceptEntity findAddCode(String codeSystemUri, CodeSystem.ConceptDefinitionComponent concept) { // KGM removed from CodeSystem Coding coding = new Coding().setCode(concept.getCode()).setSystem(codeSystemUri).setDisplay(concept.getDisplay()); return findAddCode(coding); } @Override public ConceptEntity findAddCode(Coding coding) { ConceptEntity conceptEntity = findCode(coding); // 12/Jan/2018 KGM to cope with LOINC codes and depreciated SNOMED codes. if (conceptEntity == null) { CodeSystemEntity system = codeSystemRepository.findBySystem(coding.getSystem()); if (system !=null) { conceptEntity = new ConceptEntity(); conceptEntity.setCode(coding.getCode()); conceptEntity.setDescription(coding.getDisplay()); conceptEntity.setDisplay(coding.getDisplay()); conceptEntity.setCodeSystem(system); em.persist(conceptEntity); } else { throw new IllegalArgumentException("Unsupported System "+coding.getSystem()); } } else { // TODO look at disabling this in the future // Pick up descriptions from incoming resources - should correct LOINC issues if (coding.getDisplay() != null && !coding.getDisplay().isEmpty() && (conceptEntity.getDisplay() == null || conceptEntity.getDisplay().isEmpty())) { conceptEntity.setDisplay(coding.getDisplay()); conceptEntity.setDescription(coding.getDisplay()); em.persist(conceptEntity); } } return conceptEntity; } @Override public ConceptEntity findAddCode(Quantity quantity) { Coding code = new Coding().setCode(quantity.getCode()).setSystem(quantity.getSystem()); ConceptEntity conceptEntity = findCode(code); // 12/Jan/2018 KGM to cope with LOINC codes and depreciated SNOMED codes. if (conceptEntity == null) { CodeSystemEntity system = codeSystemRepository.findBySystem(quantity.getSystem()); if (system !=null) { conceptEntity = new ConceptEntity(); conceptEntity.setCode(quantity.getCode()); conceptEntity.setDescription(quantity.getUnit()); conceptEntity.setDisplay(quantity.getUnit()); conceptEntity.setCodeSystem(system); em.persist(conceptEntity); } else { throw new IllegalArgumentException("Unsupported system "+quantity.getSystem()); } } return conceptEntity; } }