More JPA work

This commit is contained in:
jamesagnew 2014-05-23 08:37:40 -04:00
parent 78d712de25
commit 03bf040ef9
13 changed files with 815 additions and 537 deletions

View File

@ -20,6 +20,8 @@ package ca.uhn.fhir.model.primitive;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.math.BigDecimal;
import org.apache.commons.lang3.StringUtils;
@ -99,10 +101,11 @@ public class IdDt extends BasePrimitive<String> {
* If the value is not a valid BigDecimal
*/
public BigDecimal asBigDecimal() {
if (getValue() == null) {
String val = getUnqualifiedId();
if (isBlank(val)) {
return null;
}
return new BigDecimal(getValueAsString());
return new BigDecimal(val);
}
/**
@ -112,10 +115,11 @@ public class IdDt extends BasePrimitive<String> {
* If the value is not a valid Long
*/
public Long asLong() {
if (getValue() == null) {
String val = getUnqualifiedId();
if (isBlank(val)) {
return null;
}
return Long.parseLong(getValueAsString());
return Long.parseLong(val);
}
/**

View File

@ -26,11 +26,16 @@ import java.util.List;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.primitive.StringDt;
public class FhirTerser {
@ -47,23 +52,22 @@ public class FhirTerser {
BaseRuntimeElementCompositeDefinition<?> currentDef = def;
List<String> parts = Arrays.asList(thePath.split("\\."));
List<String> subList = parts.subList(1, parts.size() );
if (subList.size()< 1) {
List<String> subList = parts.subList(1, parts.size());
if (subList.size() < 1) {
throw new ConfigurationException("Invalid path: " + thePath);
}
return getDefinition(currentDef, subList);
}
private BaseRuntimeChildDefinition getDefinition(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, List<String> theSubList) {
BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(theSubList.get(0));
if (theSubList.size() == 1) {
return nextDef;
} else {
BaseRuntimeElementCompositeDefinition<?> cmp=(BaseRuntimeElementCompositeDefinition<?>) nextDef.getChildByName(theSubList.get(0));
return getDefinition(cmp, theSubList.subList(1, theSubList.size() ));
BaseRuntimeElementCompositeDefinition<?> cmp = (BaseRuntimeElementCompositeDefinition<?>) nextDef.getChildByName(theSubList.get(0));
return getDefinition(cmp, theSubList.subList(1, theSubList.size()));
}
}
@ -74,8 +78,8 @@ public class FhirTerser {
Object currentObj = theResource;
List<String> parts = Arrays.asList(thePath.split("\\."));
List<String> subList = parts.subList(1, parts.size() );
if (subList.size()< 1) {
List<String> subList = parts.subList(1, parts.size());
if (subList.size() < 1) {
throw new ConfigurationException("Invalid path: " + thePath);
}
return getValues(currentDef, currentObj, subList);
@ -93,11 +97,94 @@ public class FhirTerser {
} else {
for (IElement nextElement : values) {
BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextElement.getClass());
List<?> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size() ));
List<?> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()));
retVal.addAll(foundValues);
}
}
return retVal;
}
/**
* Returns a list containing all child elements (including the resource itself) which are <b>non-empty</b>
* and are either of the exact type specified, or are a subclass of that type.
* <p>
* For example, specifying a type of {@link StringDt} would return all non-empty string instances within
* the message. Specifying a type of {@link IResource} would return the resource itself, as well as any contained resources.
* </p>
* @param theResourceT The resource instance to search. Must not be null.
* @param theType The type to search for. Must not be null.
* @return
*/
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(IResource theResource, Class<T> theType) {
ArrayList<T> retVal = new ArrayList<T>();
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
getAllChildElementsOfType(theResource, def, theType, retVal);
return retVal;
}
private <T extends IElement> void getAllChildElementsOfType(IElement theElement, BaseRuntimeElementDefinition<?> theDefinition, Class<T> theType, ArrayList<T> theList) {
if (theElement.isEmpty()) {
return;
}
addIfCorrectType(theElement, theType, theList);
addUndeclaredExtensions(theElement, theType, theList);
switch (theDefinition.getChildType()) {
case PRIMITIVE_XHTML:
case PRIMITIVE_DATATYPE:
case RESOURCE_REF:
// These are primitive types
break;
case RESOURCE_BLOCK:
case COMPOSITE_DATATYPE:
case RESOURCE: {
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) theDefinition;
for (BaseRuntimeChildDefinition nextChild : childDef.getChildren()) {
List<? extends IElement> values = nextChild.getAccessor().getValues(theElement);
if (values != null) {
for (IElement nextValue : values) {
if (nextValue == null) {
continue;
}
BaseRuntimeElementDefinition<?> childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
getAllChildElementsOfType(nextValue, childElementDef, theType, theList);
}
}
}
break;
}
case CONTAINED_RESOURCES: {
ContainedDt value = (ContainedDt) theElement;
for (IResource next : value.getContainedResources()) {
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(next);
getAllChildElementsOfType(next, def, theType, theList);
}
break;
}
case EXTENSION_DECLARED:
case UNDECL_EXT: {
throw new IllegalStateException("state should not happen: " + theDefinition.getChildType());
}
}
}
private <T extends IElement> void addUndeclaredExtensions(IElement theElement, Class<T> theType, ArrayList<T> theList) {
if (theElement instanceof ISupportsUndeclaredExtensions) {
ISupportsUndeclaredExtensions elem = (ISupportsUndeclaredExtensions) theElement;
for (ExtensionDt nextExt : elem.getUndeclaredExtensions()) {
addIfCorrectType(nextExt, theType, theList);
addIfCorrectType(nextExt.getValue(), theType, theList);
addUndeclaredExtensions(nextExt, theType, theList);
}
}
}
@SuppressWarnings("unchecked")
private <T extends IElement> void addIfCorrectType(IElement theElement, Class<T> theType, ArrayList<T> theList) {
if (theElement != null && theType.isAssignableFrom(theElement.getClass())) {
theList.add((T) theElement);
}
}
}

View File

@ -0,0 +1,34 @@
package ca.uhn.fhir.test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.util.FhirTerser;
public class FhirTerserTest {
@Test
public void testGetAllPopulatedChildElementsOfType() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:foo");
p.addAddress().addLine("Line1");
p.addAddress().addLine("Line2");
p.addName().addFamily("Line3");
FhirTerser t = new FhirContext().newTerser();
List<StringDt> strings = t.getAllPopulatedChildElementsOfType(p, StringDt.class);
assertEquals(3, strings.size());
assertThat(strings, containsInAnyOrder(new StringDt("Line1"), new StringDt("Line2"), new StringDt("Line3")));
}
}

View File

@ -0,0 +1,431 @@
package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.*;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.AddressDt;
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.ContactDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.FhirTerser;
public abstract class BaseFhirDao {
private FhirContext myContext=new FhirContext();
@PersistenceContext(name = "FHIR_UT", type = PersistenceContextType.TRANSACTION, unitName = "FHIR_UT")
private EntityManager myEntityManager;
@Autowired
private List<IFhirResourceDao<?>> myResourceDaos;
private Map<Class<? extends IResource>, IFhirResourceDao<?>> myResourceTypeToDao;
public FhirContext getContext() {
return myContext;
}
public void setContext(FhirContext theContext) {
myContext = theContext;
}
protected List<ResourceLink> extractResourceLinks(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceLink> retVal = new ArrayList<ResourceLink>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
FhirTerser t = getContext().newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.REFERENCE) {
continue;
}
String nextPath = nextSpDef.getPath();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
if (nextObject == null) {
continue;
}
ResourceLink nextEntity;
if (nextObject instanceof ResourceReferenceDt) {
ResourceReferenceDt nextValue = (ResourceReferenceDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
String typeString = nextValue.getResourceId().getResourceType();
if (isBlank(typeString)) {
continue;
}
Class<? extends IResource> type = getContext().getResourceDefinition(typeString).getImplementingClass();
String id = nextValue.getResourceId().getUnqualifiedId();
if (StringUtils.isBlank(id)) {
continue;
}
IFhirResourceDao<?> dao = getDao(type);
if (dao == null) {
throw new InvalidRequestException("This server is not able to handle resources of type: " + nextValue.getResourceId().getResourceType());
}
Long valueOf;
try {
valueOf = Long.valueOf(id);
} catch (Exception e) {
String resName = getContext().getResourceDefinition(type).getName();
throw new InvalidRequestException("Resource ID " + resName + "/" + id + " is invalid (must be numeric), specified in path: " + nextPath);
}
ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf);
if (target == null) {
String resName = getContext().getResourceDefinition(type).getName();
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPath);
}
nextEntity = new ResourceLink(nextPath, theEntity, target);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
if (nextEntity != null) {
retVal.add(nextEntity);
}
}
}
theEntity.setHasLinks(retVal.size() > 0);
return retVal;
}
protected List<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamDate> retVal = new ArrayList<ResourceIndexedSearchParamDate>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
FhirTerser t = getContext().newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.DATE) {
continue;
}
String nextPath = nextSpDef.getPath();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
if (nextObject == null) {
continue;
}
ResourceIndexedSearchParamDate nextEntity;
if (nextObject instanceof BaseDateTimeDt) {
BaseDateTimeDt nextValue = (BaseDateTimeDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue());
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
if (nextEntity != null) {
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
}
theEntity.setParamsDatePopulated(retVal.size() > 0);
return retVal;
}
protected ArrayList<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamNumber> retVal = new ArrayList<ResourceIndexedSearchParamNumber>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
FhirTerser t = getContext().newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.NUMBER && nextSpDef.getParamType() != SearchParamTypeEnum.QUANTITY) {
continue;
}
String nextPath = nextSpDef.getPath();
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof QuantityDt) {
QuantityDt nextValue = (QuantityDt) nextObject;
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue(), nextValue.getSystem().getValueAsString(), nextValue.getUnits().getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
}
theEntity.setParamsNumberPopulated(retVal.size() > 0);
return retVal;
}
protected List<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamString> retVal = new ArrayList<ResourceIndexedSearchParamString>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
FhirTerser t = getContext().newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.STRING) {
continue;
}
if (nextSpDef.getPath().isEmpty()) {
continue; // TODO: implement phoenetic, and any others that have
// no path
}
String nextPath = nextSpDef.getPath();
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof IPrimitiveDatatype<?>) {
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextValue.getValueAsString()), nextValue.getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
if (nextObject instanceof HumanNameDt) {
ArrayList<StringDt> allNames = new ArrayList<>();
HumanNameDt nextHumanName = (HumanNameDt) nextObject;
allNames.addAll(nextHumanName.getFamily());
allNames.addAll(nextHumanName.getGiven());
for (StringDt nextName : allNames) {
if (nextName.isEmpty()) {
continue;
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextName.getValueAsString()), nextName.getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
} else if (nextObject instanceof AddressDt) {
ArrayList<StringDt> allNames = new ArrayList<>();
AddressDt nextAddress = (AddressDt) nextObject;
allNames.addAll(nextAddress.getLine());
allNames.add(nextAddress.getCity());
allNames.add(nextAddress.getState());
allNames.add(nextAddress.getCountry());
allNames.add(nextAddress.getZip());
for (StringDt nextName : allNames) {
if (nextName.isEmpty()) {
continue;
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextName.getValueAsString()), nextName.getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
} else if (nextObject instanceof ContactDt) {
ContactDt nextContact = (ContactDt) nextObject;
if (nextContact.getValue().isEmpty() == false) {
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextContact.getValue().getValueAsString()), nextContact.getValue().getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
}
}
}
}
}
theEntity.setParamsStringPopulated(retVal.size() > 0);
return retVal;
}
protected List<ResourceIndexedSearchParamToken> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamToken> retVal = new ArrayList<ResourceIndexedSearchParamToken>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
FhirTerser t = getContext().newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.TOKEN) {
continue;
}
String nextPath = nextSpDef.getPath();
if (nextPath.isEmpty()) {
continue;
}
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
ResourceIndexedSearchParamToken nextEntity;
if (nextObject instanceof IdentifierDt) {
IdentifierDt nextValue = (IdentifierDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), nextValue.getSystem().getValueAsString(), nextValue.getValue().getValue());
} else if (nextObject instanceof IPrimitiveDatatype<?>) {
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), null, nextValue.getValueAsString());
} else if (nextObject instanceof CodeableConceptDt) {
CodeableConceptDt nextCC = (CodeableConceptDt) nextObject;
for (CodingDt nextCoding : nextCC.getCoding()) {
if (nextCoding.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), nextCoding.getSystem().getValueAsString(), nextCoding.getCode().getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
nextEntity = null;
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
if (nextEntity != null) {
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
}
theEntity.setParamsTokenPopulated(retVal.size() > 0);
return retVal;
}
protected IFhirResourceDao<? extends IResource> getDao(Class<? extends IResource> theType) {
if (myResourceTypeToDao == null) {
myResourceTypeToDao = new HashMap<>();
for (IFhirResourceDao<?> next : myResourceDaos) {
myResourceTypeToDao.put(next.getResourceType(), next);
}
}
Map<Class<? extends IResource>, IFhirResourceDao<?>> resourceTypeToDao = myResourceTypeToDao;
return resourceTypeToDao.get(theType);
}
protected String normalizeString(String theString) {
char[] out = new char[theString.length()];
theString = Normalizer.normalize(theString, Normalizer.Form.NFD);
int j = 0;
for (int i = 0, n = theString.length(); i < n; ++i) {
char c = theString.charAt(i);
if (c <= '\u007F') {
out[j++] = c;
}
}
return new String(out).toUpperCase();
}
protected void populateResourceIntoEntity(IResource theResource, ResourceTable theEntity) {
theEntity.setResource(getContext().newJsonParser().encodeResourceToString(theResource));
theEntity.setEncoding(EncodingEnum.JSON);
TagList tagList = (TagList) theResource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (tagList != null) {
for (Tag next : tagList) {
theEntity.addTag(next.getTerm(), next.getLabel(), next.getScheme());
}
}
}
protected ResourceTable toEntity(IResource theResource) {
ResourceTable retVal = new ResourceTable();
populateResourceIntoEntity(theResource, retVal);
return retVal;
}
protected String toResourceName(IResource theResource) {
return myContext.getResourceDefinition(theResource).getName();
}
}

View File

@ -2,7 +2,6 @@ package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.*;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@ -24,7 +23,6 @@ import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.PlatformTransactionManager;
@ -36,12 +34,10 @@ import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.BaseTag;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
@ -49,23 +45,17 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.AddressDt;
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.ContactDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
@ -74,26 +64,19 @@ import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.FhirTerser;
public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T> {
public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements IFhirResourceDao<T> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDao.class);
private FhirContext myCtx;
@PersistenceContext(name = "FHIR_UT", type = PersistenceContextType.TRANSACTION, unitName = "FHIR_UT")
private EntityManager myEntityManager;
@Autowired
private PlatformTransactionManager myPlatformTransactionManager;
@Autowired
private List<IFhirResourceDao<?>> myResourceDaos;
private String myResourceName;
private Class<T> myResourceType;
private Map<Class<? extends IResource>, IFhirResourceDao<?>> myResourceTypeToDao;
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
@Override
@ -143,22 +126,17 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
return outcome;
}
private String toResourceName(T theResource) {
return myCtx.getResourceDefinition(theResource).getName();
}
public Class<T> getResourceType() {
return myResourceType;
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public List<T> history(IdDt theId) {
ArrayList<T> retVal = new ArrayList<T>();
String resourceType = myCtx.getResourceDefinition(myResourceType).getName();
String resourceType = getContext().getResourceDefinition(myResourceType).getName();
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(ResourceHistoryTable.Q_GETALL, ResourceHistoryTable.class);
q.setParameter("PID", theId.asLong());
q.setParameter("RESTYPE", resourceType);
@ -185,8 +163,7 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
@PostConstruct
public void postConstruct() throws Exception {
myCtx = new FhirContext(myResourceType);
myResourceName = myCtx.getResourceDefinition(myResourceType).getName();
myResourceName = getContext().getResourceDefinition(myResourceType).getName();
}
@Transactional(propagation = Propagation.REQUIRED)
@ -226,7 +203,7 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.where(builder.equal(from.get("myResourceType"), myCtx.getResourceDefinition(myResourceType).getName()));
cq.where(builder.equal(from.get("myResourceType"), getContext().getResourceDefinition(myResourceType).getName()));
if (!theParams.isEmpty()) {
cq.where(from.get("myId").in(pids));
}
@ -268,7 +245,7 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
params = Collections.emptyMap();
}
RuntimeResourceDefinition resourceDef = myCtx.getResourceDefinition(myResourceType);
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(myResourceType);
Set<Long> pids = new HashSet<Long>();
@ -338,7 +315,7 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
final ResourceTable entity = readEntity(theId);
entity.setUpdated(entity.getPublished());
final ResourceHistoryTable historyEntry = entity.toHistory(myCtx);
final ResourceHistoryTable historyEntry = entity.toHistory(getContext());
final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
final List<ResourceIndexedSearchParamToken> tokenParams = extractSearchParamTokens(entity, theResource);
@ -360,7 +337,7 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
for (ResourceIndexedSearchParamString next : stringParams) {
myEntityManager.persist(next);
}
if (entity.isParamsTokenPopulated()) {
for (ResourceIndexedSearchParamToken next : entity.getParamsToken()) {
myEntityManager.remove(next);
@ -378,7 +355,7 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
for (ResourceIndexedSearchParamNumber next : numberParams) {
myEntityManager.persist(next);
}
if (entity.isParamsDatePopulated()) {
for (ResourceIndexedSearchParamDate next : entity.getParamsDate()) {
myEntityManager.remove(next);
@ -387,7 +364,7 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
for (ResourceIndexedSearchParamDate next : dateParams) {
myEntityManager.persist(next);
}
if (entity.isHasLinks()) {
for (ResourceLink next : entity.getResourceLinks()) {
myEntityManager.remove(next);
@ -574,8 +551,8 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
Predicate eq = builder.equal(from.get("myTargetResourcePid"), targetPid);
codePredicates.add(eq);
} else {
String chain = myCtx.getResourceDefinition(myResourceType).getSearchParam(theParamName).getPath();
BaseRuntimeChildDefinition def = myCtx.newTerser().getDefinition(myResourceType, chain);
String chain = getContext().getResourceDefinition(myResourceType).getSearchParam(theParamName).getPath();
BaseRuntimeChildDefinition def = getContext().newTerser().getDefinition(myResourceType, chain);
if (!(def instanceof RuntimeChildResourceDefinition)) {
throw new ConfigurationException("Property " + chain + " of type " + myResourceName + " is not a resource: " + def.getClass());
}
@ -585,17 +562,17 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
resourceTypes = resDef.getResourceTypes();
} else {
resourceTypes = new ArrayList<>();
RuntimeResourceDefinition resDef = myCtx.getResourceDefinition(ref.getResourceType());
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(ref.getResourceType());
resourceTypes.add(resDef.getImplementingClass());
}
for (Class<? extends IResource> nextType : resourceTypes) {
RuntimeResourceDefinition typeDef = myCtx.getResourceDefinition(nextType);
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(nextType);
RuntimeSearchParam param = typeDef.getSearchParam(ref.getChain());
if (param == null) {
ourLog.debug("Type {} doesn't have search param {}", nextType.getSimpleName(), param);
continue;
}
IFhirResourceDao<?> dao = getResourceTypeToDao().get(nextType);
IFhirResourceDao<?> dao = getDao(nextType);
if (dao == null) {
ourLog.debug("Don't have a DAO for type {}", nextType.getSimpleName(), param);
continue;
@ -621,9 +598,9 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
RuntimeSearchParam param = myCtx.getResourceDefinition(getResourceType()).getSearchParam(theParamName);
RuntimeSearchParam param = getContext().getResourceDefinition(getResourceType()).getSearchParam(theParamName);
String path = param.getPath();
Predicate type = builder.equal(from.get("mySourcePath"), path);
if (pidsToRetain.size() > 0) {
Predicate inPids = (from.get("mySourceResourcePid").in(pidsToRetain));
@ -733,360 +710,7 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
return new HashSet<Long>(q.getResultList());
}
private List<ResourceLink> extractResourceLinks(ResourceTable theEntity, T theResource) {
ArrayList<ResourceLink> retVal = new ArrayList<ResourceLink>();
RuntimeResourceDefinition def = myCtx.getResourceDefinition(theResource);
FhirTerser t = myCtx.newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.REFERENCE) {
continue;
}
String nextPath = nextSpDef.getPath();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
if (nextObject == null) {
continue;
}
ResourceLink nextEntity;
if (nextObject instanceof ResourceReferenceDt) {
ResourceReferenceDt nextValue = (ResourceReferenceDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
Class<? extends IResource> type = nextValue.getResourceType();
String id = nextValue.getResourceId();
if (StringUtils.isBlank(id)) {
continue;
}
Map<Class<? extends IResource>, IFhirResourceDao<?>> resourceTypeToDao = getResourceTypeToDao();
IFhirResourceDao<?> dao;
if (type.equals(myResourceType)) {
dao = this;
}else {
dao = resourceTypeToDao.get(type);
}
if (dao == null) {
throw new InvalidRequestException("This server is not able to handle resources of type: " + nextValue.getResourceType());
}
Long valueOf;
try {
valueOf = Long.valueOf(id);
} catch (Exception e) {
String resName = myCtx.getResourceDefinition(type).getName();
throw new InvalidRequestException("Resource ID " + resName + "/" + id + " is invalid (must be numeric), specified in path: " + nextPath);
}
ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf);
if (target == null) {
String resName = myCtx.getResourceDefinition(type).getName();
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPath);
}
nextEntity = new ResourceLink(nextPath, theEntity, target);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
if (nextEntity != null) {
retVal.add(nextEntity);
}
}
}
theEntity.setHasLinks(retVal.size() > 0);
return retVal;
}
private List<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, T theResource) {
ArrayList<ResourceIndexedSearchParamDate> retVal = new ArrayList<ResourceIndexedSearchParamDate>();
RuntimeResourceDefinition def = myCtx.getResourceDefinition(theResource);
FhirTerser t = myCtx.newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.DATE) {
continue;
}
String nextPath = nextSpDef.getPath();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
if (nextObject == null) {
continue;
}
ResourceIndexedSearchParamDate nextEntity;
if (nextObject instanceof BaseDateTimeDt) {
BaseDateTimeDt nextValue = (BaseDateTimeDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue());
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
if (nextEntity != null) {
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
}
theEntity.setParamsDatePopulated(retVal.size() > 0);
return retVal;
}
private ArrayList<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, T theResource) {
ArrayList<ResourceIndexedSearchParamNumber> retVal = new ArrayList<ResourceIndexedSearchParamNumber>();
RuntimeResourceDefinition def = myCtx.getResourceDefinition(theResource);
FhirTerser t = myCtx.newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.NUMBER && nextSpDef.getParamType() != SearchParamTypeEnum.QUANTITY) {
continue;
}
String nextPath = nextSpDef.getPath();
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof QuantityDt) {
QuantityDt nextValue = (QuantityDt) nextObject;
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue(), nextValue.getSystem().getValueAsString(), nextValue.getUnits().getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
}
theEntity.setParamsNumberPopulated(retVal.size() > 0);
return retVal;
}
private List<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, T theResource) {
ArrayList<ResourceIndexedSearchParamString> retVal = new ArrayList<ResourceIndexedSearchParamString>();
RuntimeResourceDefinition def = myCtx.getResourceDefinition(theResource);
FhirTerser t = myCtx.newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.STRING) {
continue;
}
if (nextSpDef.getPath().isEmpty()) {
continue; // TODO: implement phoenetic, and any others that have
// no path
}
String nextPath = nextSpDef.getPath();
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof IPrimitiveDatatype<?>) {
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextValue.getValueAsString()), nextValue.getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
if (nextObject instanceof HumanNameDt) {
ArrayList<StringDt> allNames = new ArrayList<>();
HumanNameDt nextHumanName = (HumanNameDt) nextObject;
allNames.addAll(nextHumanName.getFamily());
allNames.addAll(nextHumanName.getGiven());
for (StringDt nextName : allNames) {
if (nextName.isEmpty()) {
continue;
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextName.getValueAsString()), nextName.getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
} else if (nextObject instanceof AddressDt) {
ArrayList<StringDt> allNames = new ArrayList<>();
AddressDt nextAddress = (AddressDt) nextObject;
allNames.addAll(nextAddress.getLine());
allNames.add(nextAddress.getCity());
allNames.add(nextAddress.getState());
allNames.add(nextAddress.getCountry());
allNames.add(nextAddress.getZip());
for (StringDt nextName : allNames) {
if (nextName.isEmpty()) {
continue;
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextName.getValueAsString()), nextName.getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
} else if (nextObject instanceof ContactDt) {
ContactDt nextContact = (ContactDt) nextObject;
if (nextContact.getValue().isEmpty() == false) {
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextContact.getValue().getValueAsString()), nextContact.getValue().getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
}
}
}
}
}
theEntity.setParamsStringPopulated(retVal.size() > 0);
return retVal;
}
private List<ResourceIndexedSearchParamToken> extractSearchParamTokens(ResourceTable theEntity, T theResource) {
ArrayList<ResourceIndexedSearchParamToken> retVal = new ArrayList<ResourceIndexedSearchParamToken>();
RuntimeResourceDefinition def = myCtx.getResourceDefinition(theResource);
FhirTerser t = myCtx.newTerser();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.TOKEN) {
continue;
}
String nextPath = nextSpDef.getPath();
if (nextPath.isEmpty()) {
continue;
}
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) {
ResourceIndexedSearchParamToken nextEntity;
if (nextObject instanceof IdentifierDt) {
IdentifierDt nextValue = (IdentifierDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), nextValue.getSystem().getValueAsString(), nextValue.getValue().getValue());
} else if (nextObject instanceof IPrimitiveDatatype<?>) {
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), null, nextValue.getValueAsString());
} else if (nextObject instanceof CodeableConceptDt) {
CodeableConceptDt nextCC = (CodeableConceptDt) nextObject;
for (CodingDt nextCoding : nextCC.getCoding()) {
if (nextCoding.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), nextCoding.getSystem().getValueAsString(), nextCoding.getCode().getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
nextEntity = null;
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
if (nextEntity != null) {
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
}
theEntity.setParamsTokenPopulated(retVal.size() > 0);
return retVal;
}
private Map<Class<? extends IResource>, IFhirResourceDao<?>> getResourceTypeToDao() {
if (myResourceTypeToDao == null) {
myResourceTypeToDao = new HashMap<>();
for (IFhirResourceDao<?> next : myResourceDaos) {
myResourceTypeToDao.put(next.getResourceType(), next);
}
}
Map<Class<? extends IResource>, IFhirResourceDao<?>> resourceTypeToDao = myResourceTypeToDao;
return resourceTypeToDao;
}
private String normalizeString(String theString) {
char[] out = new char[theString.length()];
theString = Normalizer.normalize(theString, Normalizer.Form.NFD);
int j = 0;
for (int i = 0, n = theString.length(); i < n; ++i) {
char c = theString.charAt(i);
if (c <= '\u007F') {
out[j++] = c;
}
}
return new String(out).toUpperCase();
}
private void populateResourceIntoEntity(T theResource, ResourceTable theEntity) {
theEntity.setResource(myCtx.newJsonParser().encodeResourceToString(theResource));
theEntity.setEncoding(EncodingEnum.JSON);
TagList tagList = (TagList) theResource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (tagList != null) {
for (Tag next : tagList) {
theEntity.addTag(next.getTerm(), next.getLabel(), next.getScheme());
}
}
}
private ResourceTable readEntity(IdDt theId) {
ResourceTable entity = myEntityManager.find(ResourceTable.class, theId.asLong());
@ -1096,17 +720,10 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
return entity;
}
private ResourceTable toEntity(T theResource) {
ResourceTable retVal = new ResourceTable();
populateResourceIntoEntity(theResource, retVal);
return retVal;
}
private MethodOutcome toMethodOutcome(final ResourceTable entity) {
MethodOutcome outcome = new MethodOutcome();
outcome.setId(new IdDt(entity.getId()));
outcome.setId(new IdDt(entity.getResourceType() + '/' + entity.getId()));
outcome.setVersionId(entity.getVersion());
return outcome;
}
@ -1138,9 +755,18 @@ public class FhirResourceDao<T extends IResource> implements IFhirResourceDao<T>
}
}
@Override
protected IFhirResourceDao<? extends IResource> getDao(Class<? extends IResource> theType) {
if (theType.equals(myResourceType)) {
return this;
}
return super.getDao(theType);
}
private T toResource(BaseHasResource theEntity) {
String resourceText = theEntity.getResource();
IParser parser = theEntity.getEncoding().newParser(myCtx);
IParser parser = theEntity.getEncoding().newParser(getContext());
T retVal = parser.parseResource(myResourceType, resourceText);
retVal.setId(theEntity.getIdDt());
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, theEntity.getVersion());

View File

@ -0,0 +1,82 @@
package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.util.FhirTerser;
public class FhirSystemDao extends BaseFhirDao implements IFhirSystemDao {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDao.class);
@PersistenceContext(name = "FHIR_UT", type = PersistenceContextType.TRANSACTION, unitName = "FHIR_UT")
private EntityManager myEntityManager;
private FhirContext myContext = new FhirContext();
@Transactional(propagation = Propagation.REQUIRED)
@Override
public List<IResource> transaction(List<IResource> theResources) {
ourLog.info("Beginning transaction with {} resources", theResources.size());
FhirTerser terser = myContext.newTerser();
Map<IdDt, IdDt> idConversions = new HashMap<>();
List<ResourceTable> persistedResources = new ArrayList<>();
for (IResource nextResource : theResources) {
IdDt nextId = nextResource.getId();
if (isBlank(nextId.getUnqualifiedId())) {
continue;
}
String resourceName = toResourceName(nextResource);
// IFhirResourceDao<? extends IResource> dao = getDao(nextResource.getClass());
// if (dao == null) {
// throw new InvalidRequestException("This server is not able to handle resources of type: " +
// nextResource.getResourceId().getResourceType());
// }
ResourceTable entity = myEntityManager.find(ResourceTable.class, nextId.asLong());
if (entity == null) {
entity = toEntity(nextResource);
myEntityManager.persist(entity);
myEntityManager.flush();
}
idConversions.put(nextId, new IdDt(resourceName + '/' + entity.getId()));
persistedResources.add(entity);
}
for (IResource nextResource : theResources) {
List<ResourceReferenceDt> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, ResourceReferenceDt.class);
for (ResourceReferenceDt nextRef : allRefs) {
IdDt nextId = nextRef.getResourceId();
if (idConversions.containsKey(nextId)) {
IdDt newId = idConversions.get(nextId);
ourLog.info(" * Replacing resource ref {} with {}", nextId, newId);
nextRef.setResourceId(newId);
}
}
}
return null;
}
}

View File

@ -0,0 +1,11 @@
package ca.uhn.fhir.jpa.dao;
import java.util.List;
import ca.uhn.fhir.model.api.IResource;
public interface IFhirSystemDao {
List<IResource> transaction(List<IResource> theResources);
}

View File

@ -7,8 +7,8 @@ import javax.persistence.Table;
@Entity
@Table(name = "HFJ_SPIDX_TOKEN", indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") })
@org.hibernate.annotations.Table(appliesTo="HFJ_SPIDX_TOKEN",indexes= {
@org.hibernate.annotations.Index(name="IDX_SP_TOKEN", columnNames= {"SP_SYSTEM","SP_VALUE"})})
//@org.hibernate.annotations.Table(appliesTo="HFJ_SPIDX_TOKEN", indexes= {
// @org.hibernate.annotations.Index(name="IDX_SP_TOKEN", columnNames= {"SP_SYSTEM","SP_VALUE"})})
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
private static final long serialVersionUID = 1L;

View File

@ -7,39 +7,39 @@ public class CustomNamingStrategy extends ImprovedNamingStrategy {
private static final long serialVersionUID = 1L;
private static final String PREFIX = "FR_";
@Override
public String classToTableName(final String className) {
return this.addPrefix(super.classToTableName(className));
}
@Override
public String collectionTableName(final String ownerEntity,
final String ownerEntityTable, final String associatedEntity,
final String associatedEntityTable, final String propertyName) {
return this.addPrefix(super.collectionTableName(ownerEntity,
ownerEntityTable, associatedEntity, associatedEntityTable,
propertyName));
}
@Override
public String foreignKeyColumnName(String thePropertyName, String thePropertyEntityName, String thePropertyTableName, String theReferencedColumnName) {
String foreignKeyColumnName = super.foreignKeyColumnName(thePropertyName, thePropertyEntityName, thePropertyTableName, theReferencedColumnName);
return foreignKeyColumnName;
}
@Override
public String logicalCollectionTableName(final String tableName,
final String ownerEntityTable, final String associatedEntityTable,
final String propertyName) {
return this.addPrefix(super.logicalCollectionTableName(tableName,
ownerEntityTable, associatedEntityTable, propertyName));
}
private String addPrefix(final String composedTableName) {
return PREFIX
+ composedTableName.toUpperCase().replace("_", "");
}
// @Override
// public String classToTableName(final String className) {
// return this.addPrefix(super.classToTableName(className));
// }
//
// @Override
// public String collectionTableName(final String ownerEntity,
// final String ownerEntityTable, final String associatedEntity,
// final String associatedEntityTable, final String propertyName) {
// return this.addPrefix(super.collectionTableName(ownerEntity,
// ownerEntityTable, associatedEntity, associatedEntityTable,
// propertyName));
// }
//
// @Override
// public String foreignKeyColumnName(String thePropertyName, String thePropertyEntityName, String thePropertyTableName, String theReferencedColumnName) {
// String foreignKeyColumnName = super.foreignKeyColumnName(thePropertyName, thePropertyEntityName, thePropertyTableName, theReferencedColumnName);
// return foreignKeyColumnName;
// }
//
// @Override
// public String logicalCollectionTableName(final String tableName,
// final String ownerEntityTable, final String associatedEntityTable,
// final String propertyName) {
// return this.addPrefix(super.logicalCollectionTableName(tableName,
// ownerEntityTable, associatedEntityTable, propertyName));
// }
//
// private String addPrefix(final String composedTableName) {
//
// return PREFIX
// + composedTableName.toUpperCase().replace("_", "");
//
// }
}

View File

@ -81,86 +81,82 @@ public class FhirResourceDaoTest {
Patient patient02 = new Patient();
patient02.addIdentifier("urn:system", "testPersistResourceLink02");
IdDt patientId02 = ourPatientDao.create(patient02).getId();
Observation obs01 = new Observation();
obs01.setApplies(new DateTimeDt(new Date()));
obs01.setSubject(new ResourceReferenceDt(Patient.class, patientId01));
obs01.setSubject(new ResourceReferenceDt(patientId01));
IdDt obsId01 = ourObservationDao.create(obs01).getId();
Observation obs02 = new Observation();
obs02.setApplies(new DateTimeDt(new Date()));
obs02.setSubject(new ResourceReferenceDt(Patient.class, patientId02));
obs02.setSubject(new ResourceReferenceDt(patientId02));
IdDt obsId02 = ourObservationDao.create(obs02).getId();
// Create another type, that shouldn't be returned
DiagnosticReport dr01 = new DiagnosticReport();
dr01.setSubject(new ResourceReferenceDt(Patient.class, patientId01));
dr01.setSubject(new ResourceReferenceDt(patientId01));
IdDt drId01 = ourDiagnosticReportDao.create(dr01).getId();
ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]",new Object[] {patientId01,patientId02,obsId01,obsId02,drId01});
List<Observation> result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(patientId01.getValue()));
assertEquals(1,result.size());
assertEquals(obsId01,result.get(0).getId());
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(patientId02.getValue()));
assertEquals(1,result.size());
assertEquals(obsId02,result.get(0).getId());
ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]", new Object[] { patientId01, patientId02, obsId01, obsId02, drId01 });
List<Observation> result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(patientId01.getUnqualifiedId()));
assertEquals(1, result.size());
assertEquals(obsId01.getUnqualifiedId(), result.get(0).getId().getUnqualifiedId());
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(patientId02.getUnqualifiedId()));
assertEquals(1, result.size());
assertEquals(obsId02.getUnqualifiedId(), result.get(0).getId().getUnqualifiedId());
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam("999999999999"));
assertEquals(0,result.size());
assertEquals(0, result.size());
}
@Test
public void testPersistSearchParamDate() {
Patient patient = new Patient();
patient.addIdentifier("urn:system", "001");
patient.setBirthDate(new DateTimeDt("2001-01-01"));
ourPatientDao.create(patient);
List<Patient> found = ourPatientDao.search("birthdate", new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01"));
assertEquals(1,found.size());
assertEquals(1, found.size());
}
@Test
public void testPersistSearchParamObservationString() {
Observation obs = new Observation();
obs.getName().addCoding().setSystem("foo").setCode("testPersistSearchParamQuantity");
obs.setValue(new StringDt("AAAABBBB"));
ourObservationDao.create(obs);
List<Observation> found = ourObservationDao.search("value-string", new StringDt("AAAABBBB"));
assertEquals(1,found.size());
assertEquals(1, found.size());
found = ourObservationDao.search("value-string", new StringDt("AAAABBBBCCC"));
assertEquals(0,found.size());
assertEquals(0, found.size());
}
@Test
public void testPersistSearchParamQuantity() {
Observation obs = new Observation();
obs.getName().addCoding().setSystem("foo").setCode("testPersistSearchParamQuantity");
obs.setValue(new QuantityDt(111));
ourObservationDao.create(obs);
List<Observation> found = ourObservationDao.search("value-quantity", new QuantityDt(111));
assertEquals(1,found.size());
assertEquals(1, found.size());
found = ourObservationDao.search("value-quantity", new QuantityDt(112));
assertEquals(0,found.size());
assertEquals(0, found.size());
}
@Test
public void testPersistSearchParams() {
Patient patient = new Patient();
@ -208,8 +204,7 @@ public class FhirResourceDaoTest {
assertEquals(0, found.size());
}
@Test
public void testSearchAll() {
{
@ -237,7 +232,7 @@ public class FhirResourceDaoTest {
Patient patient = new Patient();
patient.addIdentifier("urn:system", "001");
patient.addName().addFamily("testSearchNameParam01Fam").addGiven("testSearchNameParam01Giv");
id1=ourPatientDao.create(patient).getId();
id1 = ourPatientDao.create(patient).getId();
}
{
Patient patient = new Patient();
@ -250,13 +245,13 @@ public class FhirResourceDaoTest {
params.put(Patient.SP_FAMILY, new StringDt("testSearchNameParam01Fam"));
List<Patient> patients = ourPatientDao.search(params);
assertEquals(1, patients.size());
assertEquals(id1, patients.get(0).getId());
assertEquals(id1.getUnqualifiedId(), patients.get(0).getId().getUnqualifiedId());
params = new HashMap<>();
params.put(Patient.SP_FAMILY, new StringDt("testSearchNameParam01Giv"));
patients = ourPatientDao.search(params);
assertEquals(1, patients.size());
assertEquals(id1, patients.get(0).getId());
assertEquals(id1.getUnqualifiedId(), patients.get(0).getId().getUnqualifiedId());
params = new HashMap<>();
params.put(Patient.SP_FAMILY, new StringDt("testSearchNameParam01Foo"));
@ -276,52 +271,50 @@ public class FhirResourceDaoTest {
patient02.addIdentifier("urn:system", "testSearchResourceLinkWithChainXX");
patient02.addIdentifier("urn:system", "testSearchResourceLinkWithChain02");
IdDt patientId02 = ourPatientDao.create(patient02).getId();
Observation obs01 = new Observation();
obs01.setApplies(new DateTimeDt(new Date()));
obs01.setSubject(new ResourceReferenceDt(Patient.class, patientId01));
obs01.setSubject(new ResourceReferenceDt(patientId01));
IdDt obsId01 = ourObservationDao.create(obs01).getId();
Observation obs02 = new Observation();
obs02.setApplies(new DateTimeDt(new Date()));
obs02.setSubject(new ResourceReferenceDt(Patient.class, patientId02));
obs02.setSubject(new ResourceReferenceDt(patientId02));
IdDt obsId02 = ourObservationDao.create(obs02).getId();
// Create another type, that shouldn't be returned
DiagnosticReport dr01 = new DiagnosticReport();
dr01.setSubject(new ResourceReferenceDt(Patient.class, patientId01));
dr01.setSubject(new ResourceReferenceDt(patientId01));
IdDt drId01 = ourDiagnosticReportDao.create(dr01).getId();
ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]",new Object[] {patientId01,patientId02,obsId01,obsId02,drId01});
ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]", new Object[] { patientId01, patientId02, obsId01, obsId02, drId01 });
List<Observation> result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_IDENTIFIER, "testSearchResourceLinkWithChain01"));
assertEquals(1,result.size());
assertEquals(obsId01,result.get(0).getId());
assertEquals(1, result.size());
assertEquals(obsId01.getUnqualifiedId(), result.get(0).getId().getUnqualifiedId());
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_IDENTIFIER, "999999999999"));
assertEquals(0,result.size());
assertEquals(0, result.size());
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_IDENTIFIER, "testSearchResourceLinkWithChainXX"));
assertEquals(2,result.size());
assertEquals(2, result.size());
}
@Test
public void testCreateWithInvalidReferenceFailsGracefully() {
Patient patient = new Patient();
patient.addName().addFamily("testSearchResourceLinkWithChainWithMultipleTypes01");
patient.setManagingOrganization(new ResourceReferenceDt(Organization.class, "99999999"));
patient.setManagingOrganization(new ResourceReferenceDt("Patient/99999999"));
try {
ourPatientDao.create(patient).getId();
fail();
}catch (InvalidRequestException e) {
ourPatientDao.create(patient);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), StringContains.containsString("99999 not found"));
}
}
@Test
public void testSearchResourceLinkWithChainWithMultipleTypes() {
Patient patient = new Patient();
@ -332,31 +325,31 @@ public class FhirResourceDaoTest {
Location loc01 = new Location();
loc01.getName().setValue("testSearchResourceLinkWithChainWithMultipleTypes01");
IdDt locId01 = ourLocationDao.create(loc01).getId();
Observation obs01 = new Observation();
obs01.setApplies(new DateTimeDt(new Date()));
obs01.setSubject(new ResourceReferenceDt(Patient.class, patientId01));
obs01.setSubject(new ResourceReferenceDt(patientId01));
IdDt obsId01 = ourObservationDao.create(obs01).getId();
Observation obs02 = new Observation();
obs02.setApplies(new DateTimeDt(new Date()));
obs02.setSubject(new ResourceReferenceDt(Location.class, locId01));
obs02.setSubject(new ResourceReferenceDt(locId01));
IdDt obsId02 = ourObservationDao.create(obs02).getId();
ourLog.info("P1[{}] L1[{}] Obs1[{}] Obs2[{}]",new Object[] {patientId01,locId01,obsId01,obsId02});
ourLog.info("P1[{}] L1[{}] Obs1[{}] Obs2[{}]", new Object[] { patientId01, locId01, obsId01, obsId02 });
List<Observation> result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypes01"));
assertEquals(2,result.size());
assertEquals(2, result.size());
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypesXX"));
assertEquals(1,result.size());
assertEquals(1, result.size());
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypesYY"));
assertEquals(0,result.size());
assertEquals(0, result.size());
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam("Patient", Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypes01"));
assertEquals(1,result.size());
assertEquals(obsId01, result.get(0).getId());
assertEquals(1, result.size());
assertEquals(obsId01.getUnqualifiedId(), result.get(0).getId().getUnqualifiedId());
}
@ -385,7 +378,7 @@ public class FhirResourceDaoTest {
assertEquals(0, patients.size());
}
@Test
public void testSearchStringParamWithNonNormalized() {
{
@ -413,7 +406,6 @@ public class FhirResourceDaoTest {
assertEquals(0, patients.size());
}
@Test
public void testTagsWithCreateAndReadAndSearch() {
@ -501,7 +493,6 @@ public class FhirResourceDaoTest {
}
@Test
public void testUpdateMaintainsSearchParams() throws InterruptedException {
Patient p1 = new Patient();
@ -513,29 +504,28 @@ public class FhirResourceDaoTest {
p2.addIdentifier("urn:system", "testUpdateMaintainsSearchParamsBBB");
p2.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsBBB");
IdDt p2id = ourPatientDao.create(p2).getId();
Set<Long> ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
assertEquals(1,ids.size());
assertEquals(1, ids.size());
assertThat(ids, contains(p1id.asLong()));
// Update the name
p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsBBB");
ourPatientDao.update(p1, p1id);
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
assertEquals(0,ids.size());
assertEquals(0, ids.size());
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsBBB"));
assertEquals(2,ids.size());
assertEquals(2, ids.size());
}
@AfterClass
public static void afterClass() {
ourCtx.close();
}
@SuppressWarnings("unchecked")
@BeforeClass
public static void beforeClass() {

View File

@ -32,7 +32,9 @@
<property name="hibernate.cache.use_query_cache" value="false" />
<property name="hibernate.cache.use_second_level_cache" value="false" />
<property name="hibernate.cache.use_structured_entries" value="false" />
<!--
<property name="hibernate.ejb.naming_strategy" value="ca.uhn.fhir.jpa.util.CustomNamingStrategy" />
-->
<!--
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory" />
-->

View File

@ -31,7 +31,6 @@
<property name="hibernate.cache.use_query_cache" value="false" />
<property name="hibernate.cache.use_second_level_cache" value="false" />
<property name="hibernate.cache.use_structured_entries" value="false" />
<property name="hibernate.ejb.naming_strategy" value="ca.uhn.fhir.jpa.util.CustomNamingStrategy" />
<!--
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory" />
-->

View File

@ -395,7 +395,19 @@ public class ${className}
return null;
}
#end
#if ( ${className} == "ResourceReferenceDt" )
/** TODO: document */
@Override
public IdDt getResourceId() {
return new IdDt(myReference.getValue());
}
/** TODO: document */
@Override
public void setResourceId(IdDt theResourceId) {
myReference = new StringDt(theResourceId.getValue());
}
#end
#childExtensionTypes( $childExtensionTypes )
}