More JPA work

This commit is contained in:
jamesagnew 2014-05-14 18:03:17 -04:00
parent 10dc8b39aa
commit ed0902e40f
14 changed files with 546 additions and 318 deletions

View File

@ -30,6 +30,7 @@ import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.BaseClient; import ca.uhn.fhir.rest.client.BaseClient;
@ -49,6 +50,14 @@ public abstract class BaseResourceReference extends BaseElement {
// nothing // nothing
} }
/**
* Constructor
*/
public BaseResourceReference(Class<? extends IResource> theResourceType, IdDt theResourceId) {
myResourceType = theResourceType;
myResourceId = theResourceId.getValue();
}
/** /**
* Constructor * Constructor
*/ */
@ -57,11 +66,6 @@ public abstract class BaseResourceReference extends BaseElement {
myResourceId = theResourceId; myResourceId = theResourceId;
} }
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && myResource == null && myResourceType == null && StringUtils.isBlank(myResourceId);
}
public BaseResourceReference(IResource theResource) { public BaseResourceReference(IResource theResource) {
myResource=theResource; myResource=theResource;
} }
@ -102,6 +106,11 @@ public abstract class BaseResourceReference extends BaseElement {
return getReference().getValue(); return getReference().getValue();
} }
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && myResource == null && myResourceType == null && StringUtils.isBlank(myResourceId);
}
/** /**
* Returns the referenced resource, fetching it <b>if it has not already been loaded</b>. This method invokes the HTTP client to retrieve the resource unless it has already been loaded, or was a * Returns the referenced resource, fetching it <b>if it has not already been loaded</b>. This method invokes the HTTP client to retrieve the resource unless it has already been loaded, or was a
* contained resource in which case it is simply returned. * contained resource in which case it is simply returned.

View File

@ -45,6 +45,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
/** /**
@ -82,6 +83,16 @@ public class ResourceReferenceDt
super(theResourceType, theResourceId); super(theResourceType, theResourceId);
} }
/**
* Constructor which creates a normal resource reference
*
* @param theResourceType The resource type
* @param theResourceId The resource ID
*/
public ResourceReferenceDt(Class<? extends IResource> theResourceType, IdDt theResourceId) {
super(theResourceType, theResourceId);
}
/** /**
* Constructor which creates a resource reference containing the actual * Constructor which creates a resource reference containing the actual
* resource in question. * resource in question.

View File

@ -1082,6 +1082,9 @@ public class Observation extends BaseResource implements IResource {
* </p> * </p>
*/ */
public java.util.List<ResourceReferenceDt> getPerformer() { public java.util.List<ResourceReferenceDt> getPerformer() {
if (myPerformer == null) {
myPerformer = new java.util.ArrayList<ResourceReferenceDt>();
}
return myPerformer; return myPerformer;
} }

View File

@ -0,0 +1,19 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.api.IQueryParameterType;
public class ReferenceParam implements IQueryParameterType {
private String myValue;
@Override
public void setValueAsQueryToken(String theParameter) {
myValue=theParameter;
}
@Override
public String getValueAsQueryToken() {
return myValue;
}
}

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.rest.server.Constants;
/** /**
* Represents an <b>HTTP 400 Bad Request</b> response. * Represents an <b>HTTP 400 Bad Request</b> response.
* This status indicates that the client's message was invalid (e.g. not a valid FHIR Resource * This status indicates that the client's message was invalid (e.g. not a valid FHIR Resource
* per the specifications), as opposed to the {@link InvalidRequestException} which indicates * per the specifications), as opposed to the {@link UnprocessableEntityException} which indicates
* that data does not pass business rule validation on the server. * that data does not pass business rule validation on the server.
* *
* <p> * <p>

View File

@ -21,6 +21,7 @@ import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@ -42,6 +43,7 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken; 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.model.api.IDatatype;
import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
@ -54,6 +56,7 @@ import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt; import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt; 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.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt; import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -62,8 +65,10 @@ import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam; import ca.uhn.fhir.rest.param.QualifiedDateParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
@ -76,203 +81,106 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
@Autowired @Autowired
private PlatformTransactionManager myPlatformTransactionManager; private PlatformTransactionManager myPlatformTransactionManager;
@Autowired
private List<IFhirResourceDao<?>> myResourceDaos;
private String myResourceName;
private Class<T> myResourceType; private Class<T> myResourceType;
private Class<X> myTableType; private Class<X> myTableType;
private String myResourceName; private Map<Class<? extends IResource>, Class<? extends BaseResourceTable<?>>> myResourceTypeToDao;
@Transactional(propagation = Propagation.SUPPORTS) private Set<Long> addPredicateDate(Set<Long> thePids, List<IQueryParameterType> theOrParams) {
@Override if (theOrParams == null || theOrParams.isEmpty()) {
public MethodOutcome create(T theResource) { return thePids;
final X entity = toEntity(theResource);
entity.setPublished(new Date());
entity.setUpdated(entity.getPublished());
final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
final List<ResourceIndexedSearchParamToken> tokenParams = extractSearchParamTokens(entity, theResource);
final List<ResourceIndexedSearchParamNumber> numberParams = extractSearchParamNumber(entity, theResource);
final List<ResourceIndexedSearchParamDate> dateParams = extractSearchParamDates(entity, theResource);
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
template.execute(new TransactionCallback<X>() {
@Override
public X doInTransaction(TransactionStatus theStatus) {
myEntityManager.persist(entity);
for (ResourceIndexedSearchParamString next : stringParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamToken next : tokenParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamNumber next : numberParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamDate next : dateParams) {
myEntityManager.persist(next);
}
return entity;
}
});
MethodOutcome outcome = toMethodOutcome(entity);
return outcome;
} }
@Transactional(propagation = Propagation.REQUIRED)
@Override
public List<T> history(IdDt theId) {
ArrayList<T> retVal = new ArrayList<T>();
String resourceType = myCtx.getResourceDefinition(myResourceType).getName();
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(ResourceHistoryTable.Q_GETALL, ResourceHistoryTable.class);
q.setParameter("PID", theId.asLong());
q.setParameter("RESTYPE", resourceType);
// TypedQuery<ResourceHistoryTable> query =
// myEntityManager.createQuery(criteriaQuery);
List<ResourceHistoryTable> results = q.getResultList();
for (ResourceHistoryTable next : results) {
retVal.add(toResource(next));
}
try {
retVal.add(read(theId));
} catch (ResourceNotFoundException e) {
// ignore
}
if (retVal.isEmpty()) {
throw new ResourceNotFoundException(theId);
}
return retVal;
}
@PostConstruct
public void postConstruct() throws Exception {
myResourceType = myTableType.newInstance().getResourceType();
myCtx = new FhirContext(myResourceType);
myResourceName = myCtx.getResourceDefinition(myResourceType).getName();
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public T read(IdDt theId) {
X entity = readEntity(theId);
T retVal = toResource(entity);
return retVal;
}
@Override
public List<T> search(Map<String, IQueryParameterType> theParams) {
Map<String, List<List<IQueryParameterType>>> map = new HashMap<String, List<List<IQueryParameterType>>>();
for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) {
map.put(nextEntry.getKey(), new ArrayList<List<IQueryParameterType>>());
map.get(nextEntry.getKey()).add(Collections.singletonList(nextEntry.getValue()));
}
return searchWithAndOr(map);
}
@Override
public List<T> search(String theSpName, IQueryParameterType theValue) {
return search(Collections.singletonMap(theSpName, theValue));
}
@Override
public List<T> searchWithAndOr(Map<String, List<List<IQueryParameterType>>> theParams) {
Map<String, List<List<IQueryParameterType>>> params = theParams;
if (params == null) {
params = Collections.emptyMap();
}
RuntimeResourceDefinition resourceDef = myCtx.getResourceDefinition(myResourceType);
Set<Long> pids = new HashSet<Long>();
for (Entry<String, List<List<IQueryParameterType>>> nextParamEntry : params.entrySet()) {
String nextParamName = nextParamEntry.getKey();
RuntimeSearchParam nextParamDef = resourceDef.getSearchParam(nextParamName);
if (nextParamDef != null) {
if (nextParamDef.getParamType() == SearchParamTypeEnum.TOKEN) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateToken(pids, nextAnd);
if (pids.isEmpty()) {
return new ArrayList<T>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.STRING) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateString(pids, nextAnd);
if (pids.isEmpty()) {
return new ArrayList<T>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.QUANTITY) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateQuantity(pids, nextAnd);
if (pids.isEmpty()) {
return new ArrayList<T>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.DATE) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateDate(pids, nextAnd);
if (pids.isEmpty()) {
return new ArrayList<T>();
}
}
} else {
throw new IllegalArgumentException("Don't know how to handle parameter of type: " + nextParamDef.getParamType());
}
}
}
// Execute the query and make sure we return distinct results
{
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<X> cq = builder.createQuery(myTableType); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<X> from = cq.from(myTableType); Root<ResourceIndexedSearchParamDate> from = cq.from(ResourceIndexedSearchParamDate.class);
if (!params.isEmpty()) { cq.select(from.get("myResourcePid").as(Long.class));
cq.where(from.get("myId").in(pids));
}
TypedQuery<X> q = myEntityManager.createQuery(cq);
List<T> retVal = new ArrayList<>(); List<Predicate> codePredicates = new ArrayList<Predicate>();
for (X next : q.getResultList()) { for (IQueryParameterType nextOr : theOrParams) {
T resource = toResource(next); IQueryParameterType params = nextOr;
retVal.add(resource);
} if (params instanceof QualifiedDateParam) {
return retVal; QualifiedDateParam id = (QualifiedDateParam) params;
} DateRangeParam range = new DateRangeParam(id);
addPredicateDateFromRange(builder, from, codePredicates, range);
} else if (params instanceof DateRangeParam) {
DateRangeParam range = (DateRangeParam) params;
addPredicateDateFromRange(builder, from, codePredicates, range);
} else {
throw new IllegalArgumentException("Invalid token type: " + params.getClass());
} }
@Required
public void setTableType(Class<X> theTableType) {
myTableType = theTableType;
} }
@Transactional(propagation = Propagation.SUPPORTS) Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
@Override
public MethodOutcome update(final T theResource, final IdDt theId) {
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
X savedEntity = template.execute(new TransactionCallback<X>() {
@Override
public X doInTransaction(TransactionStatus theStatus) {
final X entity = readEntity(theId);
final ResourceHistoryTable existing = entity.toHistory(myCtx);
populateResourceIntoEntity(theResource, entity); Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
myEntityManager.persist(existing); if (thePids.size() > 0) {
Predicate inPids = (from.get("myResourcePid").in(thePids));
entity.setUpdated(new Date()); cq.where(builder.and(type, inPids, masterCodePredicate));
myEntityManager.persist(entity); } else {
return entity; cq.where(builder.and(type, masterCodePredicate));
} }
});
return toMethodOutcome(savedEntity); TypedQuery<Long> q = myEntityManager.createQuery(cq);
return new HashSet<Long>(q.getResultList());
}
private Set<Long> addPredicateReference(Set<Long> thePids, List<IQueryParameterType> theOrParams) {
if (theOrParams == null || theOrParams.isEmpty()) {
return thePids;
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamDate> from = cq.from(ResourceIndexedSearchParamDate.class);
cq.select(from.get("myResourcePid").as(Long.class));
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theOrParams) {
IQueryParameterType params = nextOr;
if (params instanceof ReferenceParam) {
ReferenceParam id = (ReferenceParam) params;
} else {
throw new IllegalArgumentException("Invalid token type: " + params.getClass());
}
}
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
if (thePids.size() > 0) {
Predicate inPids = (from.get("myResourcePid").in(thePids));
cq.where(builder.and(type, inPids, masterCodePredicate));
} else {
cq.where(builder.and(type, masterCodePredicate));
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
return new HashSet<Long>(q.getResultList());
}
private void addPredicateDateFromRange(CriteriaBuilder builder, Root<ResourceIndexedSearchParamDate> from, List<Predicate> codePredicates, DateRangeParam range) {
Predicate singleCode;
Date lowerBound = range.getLowerBoundAsInstant();
Date upperBound = range.getUpperBoundAsInstant();
if (lowerBound != null && upperBound != null) {
Predicate low = builder.greaterThanOrEqualTo(from.<Date> get("myValueLow"), lowerBound);
Predicate high = builder.lessThanOrEqualTo(from.<Date> get("myValueHigh"), upperBound);
singleCode = builder.and(low, high);
} else if (lowerBound != null) {
singleCode = builder.greaterThanOrEqualTo(from.<Date> get("myValueLow"), lowerBound);
} else {
singleCode = builder.lessThanOrEqualTo(from.<Date> get("myValueHigh"), upperBound);
}
codePredicates.add(singleCode);
} }
private Set<Long> addPredicateQuantity(Set<Long> thePids, List<IQueryParameterType> theOrParams) { private Set<Long> addPredicateQuantity(Set<Long> thePids, List<IQueryParameterType> theOrParams) {
@ -359,65 +267,6 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
private Set<Long> addPredicateDate(Set<Long> thePids, List<IQueryParameterType> theOrParams) {
if (theOrParams == null || theOrParams.isEmpty()) {
return thePids;
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamDate> from = cq.from(ResourceIndexedSearchParamDate.class);
cq.select(from.get("myResourcePid").as(Long.class));
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theOrParams) {
IQueryParameterType params = nextOr;
if (params instanceof QualifiedDateParam) {
QualifiedDateParam id = (QualifiedDateParam) params;
DateRangeParam range = new DateRangeParam(id);
addPredicateDateFromRange(builder, from, codePredicates, range);
} else if (params instanceof DateRangeParam) {
DateRangeParam range = (DateRangeParam) params;
addPredicateDateFromRange(builder, from, codePredicates, range);
} else {
throw new IllegalArgumentException("Invalid token type: " + params.getClass());
}
}
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
if (thePids.size() > 0) {
Predicate inPids = (from.get("myResourcePid").in(thePids));
cq.where(builder.and(type, inPids, masterCodePredicate));
} else {
cq.where(builder.and(type, masterCodePredicate));
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
return new HashSet<Long>(q.getResultList());
}
private void addPredicateDateFromRange(CriteriaBuilder builder, Root<ResourceIndexedSearchParamDate> from, List<Predicate> codePredicates, DateRangeParam range) {
Predicate singleCode;
Date lowerBound = range.getLowerBoundAsInstant();
Date upperBound = range.getUpperBoundAsInstant();
if (lowerBound != null && upperBound != null) {
Predicate low = builder.greaterThanOrEqualTo(from.<Date> get("myValueLow"), lowerBound);
Predicate high = builder.lessThanOrEqualTo(from.<Date> get("myValueHigh"), upperBound);
singleCode = builder.and(low, high);
} else if (lowerBound != null) {
singleCode = builder.greaterThanOrEqualTo(from.<Date> get("myValueLow"), lowerBound);
} else {
singleCode = builder.lessThanOrEqualTo(from.<Date> get("myValueHigh"), upperBound);
}
codePredicates.add(singleCode);
}
private Set<Long> addPredicateString(Set<Long> thePids, List<IQueryParameterType> theOrParams) { private Set<Long> addPredicateString(Set<Long> thePids, List<IQueryParameterType> theOrParams) {
if (theOrParams == null || theOrParams.isEmpty()) { if (theOrParams == null || theOrParams.isEmpty()) {
return thePids; return thePids;
@ -511,6 +360,203 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
@Transactional(propagation = Propagation.REQUIRED, readOnly=true)
@Override
public MethodOutcome create(T theResource) {
final X entity = toEntity(theResource);
entity.setPublished(new Date());
entity.setUpdated(entity.getPublished());
final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
final List<ResourceIndexedSearchParamToken> tokenParams = extractSearchParamTokens(entity, theResource);
final List<ResourceIndexedSearchParamNumber> numberParams = extractSearchParamNumber(entity, theResource);
final List<ResourceIndexedSearchParamDate> dateParams = extractSearchParamDates(entity, theResource);
final List<ResourceLink> links = extractResourceLinks(entity, theResource);
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
template.setReadOnly(false);
template.execute(new TransactionCallback<X>() {
@Override
public X doInTransaction(TransactionStatus theStatus) {
myEntityManager.persist(entity);
for (ResourceIndexedSearchParamString next : stringParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamToken next : tokenParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamNumber next : numberParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamDate next : dateParams) {
myEntityManager.persist(next);
}
for (ResourceLink next : links) {
myEntityManager.persist(next);
}
return entity;
}
});
MethodOutcome outcome = toMethodOutcome(entity);
return outcome;
}
private List<ResourceLink> extractResourceLinks(X 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) {
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;
}
if (myResourceTypeToDao == null) {
myResourceTypeToDao=new HashMap<>();
for (IFhirResourceDao<?> next : myResourceDaos) {
myResourceTypeToDao.put(next.getResourceType(), next.getTableType());
}
}
Class<? extends BaseResourceTable<?>> tableType = myResourceTypeToDao.get(type);
BaseResourceTable<?> target = myEntityManager.find(tableType, Long.valueOf(id));
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);
}
}
}
return retVal;
}
@Override
public Class<X> getTableType() {
return myTableType;
}
private List<ResourceIndexedSearchParamDate> extractSearchParamDates(X 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, def.getName());
retVal.add(nextEntity);
}
}
}
return retVal;
}
private ArrayList<ResourceIndexedSearchParamNumber> extractSearchParamNumber(X 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, def.getName());
retVal.add(nextEntity);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
}
return retVal;
}
private List<ResourceIndexedSearchParamString> extractSearchParamStrings(X theEntity, T theResource) { private List<ResourceIndexedSearchParamString> extractSearchParamStrings(X theEntity, T theResource) {
ArrayList<ResourceIndexedSearchParamString> retVal = new ArrayList<ResourceIndexedSearchParamString>(); ArrayList<ResourceIndexedSearchParamString> retVal = new ArrayList<ResourceIndexedSearchParamString>();
@ -528,7 +574,7 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
String nextPath = nextSpDef.getPath(); String nextPath = nextSpDef.getPath();
List<Object> values = t.getValues(theResource, nextPath); List<Object> values = t.getValues(theResource, nextPath);
for (Object nextObject : values) { for (Object nextObject : values) {
if (((IDatatype) nextObject).isEmpty()) { if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
continue; continue;
} }
@ -563,47 +609,6 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return retVal; return retVal;
} }
private ArrayList<ResourceIndexedSearchParamNumber> extractSearchParamNumber(X 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 (((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, def.getName());
retVal.add(nextEntity);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
}
return retVal;
}
private List<ResourceIndexedSearchParamToken> extractSearchParamTokens(X theEntity, T theResource) { private List<ResourceIndexedSearchParamToken> extractSearchParamTokens(X theEntity, T theResource) {
ArrayList<ResourceIndexedSearchParamToken> retVal = new ArrayList<ResourceIndexedSearchParamToken>(); ArrayList<ResourceIndexedSearchParamToken> retVal = new ArrayList<ResourceIndexedSearchParamToken>();
@ -664,44 +669,31 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return retVal; return retVal;
} }
private List<ResourceIndexedSearchParamDate> extractSearchParamDates(X theEntity, T theResource) { @Transactional(propagation = Propagation.REQUIRED)
ArrayList<ResourceIndexedSearchParamDate> retVal = new ArrayList<ResourceIndexedSearchParamDate>(); @Override
public List<T> history(IdDt theId) {
ArrayList<T> retVal = new ArrayList<T>();
RuntimeResourceDefinition def = myCtx.getResourceDefinition(theResource); String resourceType = myCtx.getResourceDefinition(myResourceType).getName();
FhirTerser t = myCtx.newTerser(); TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(ResourceHistoryTable.Q_GETALL, ResourceHistoryTable.class);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { q.setParameter("PID", theId.asLong());
if (nextSpDef.getParamType() != SearchParamTypeEnum.DATE) { q.setParameter("RESTYPE", resourceType);
continue;
// TypedQuery<ResourceHistoryTable> query =
// myEntityManager.createQuery(criteriaQuery);
List<ResourceHistoryTable> results = q.getResultList();
for (ResourceHistoryTable next : results) {
retVal.add(toResource(next));
} }
String nextPath = nextSpDef.getPath(); try {
retVal.add(read(theId));
boolean multiType = false; } catch (ResourceNotFoundException e) {
if (nextPath.endsWith("[x]")) { // ignore
multiType = true;
} }
List<Object> values = t.getValues(theResource, nextPath); if (retVal.isEmpty()) {
for (Object nextObject : values) { throw new ResourceNotFoundException(theId);
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, def.getName());
retVal.add(nextEntity);
}
}
} }
return retVal; return retVal;
@ -720,6 +712,26 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
} }
@PostConstruct
public void postConstruct() throws Exception {
myResourceType = myTableType.newInstance().getResourceType();
myCtx = new FhirContext(myResourceType);
myResourceName = myCtx.getResourceDefinition(myResourceType).getName();
}
public Class<T> getResourceType() {
return myResourceType;
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public T read(IdDt theId) {
X entity = readEntity(theId);
T retVal = toResource(entity);
return retVal;
}
private X readEntity(IdDt theId) { private X readEntity(IdDt theId) {
X entity = (X) myEntityManager.find(myTableType, theId.asLong()); X entity = (X) myEntityManager.find(myTableType, theId.asLong());
if (entity == null) { if (entity == null) {
@ -728,6 +740,101 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return entity; return entity;
} }
@Override
public List<T> search(Map<String, IQueryParameterType> theParams) {
Map<String, List<List<IQueryParameterType>>> map = new HashMap<String, List<List<IQueryParameterType>>>();
for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) {
map.put(nextEntry.getKey(), new ArrayList<List<IQueryParameterType>>());
map.get(nextEntry.getKey()).add(Collections.singletonList(nextEntry.getValue()));
}
return searchWithAndOr(map);
}
@Override
public List<T> search(String theSpName, IQueryParameterType theValue) {
return search(Collections.singletonMap(theSpName, theValue));
}
@Override
public List<T> searchWithAndOr(Map<String, List<List<IQueryParameterType>>> theParams) {
Map<String, List<List<IQueryParameterType>>> params = theParams;
if (params == null) {
params = Collections.emptyMap();
}
RuntimeResourceDefinition resourceDef = myCtx.getResourceDefinition(myResourceType);
Set<Long> pids = new HashSet<Long>();
for (Entry<String, List<List<IQueryParameterType>>> nextParamEntry : params.entrySet()) {
String nextParamName = nextParamEntry.getKey();
RuntimeSearchParam nextParamDef = resourceDef.getSearchParam(nextParamName);
if (nextParamDef != null) {
if (nextParamDef.getParamType() == SearchParamTypeEnum.TOKEN) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateToken(pids, nextAnd);
if (pids.isEmpty()) {
return new ArrayList<T>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.STRING) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateString(pids, nextAnd);
if (pids.isEmpty()) {
return new ArrayList<T>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.QUANTITY) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateQuantity(pids, nextAnd);
if (pids.isEmpty()) {
return new ArrayList<T>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.DATE) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateDate(pids, nextAnd);
if (pids.isEmpty()) {
return new ArrayList<T>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.REFERENCE) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateReference(pids, nextAnd);
if (pids.isEmpty()) {
return new ArrayList<T>();
}
}
} else {
throw new IllegalArgumentException("Don't know how to handle parameter of type: " + nextParamDef.getParamType());
}
}
}
// Execute the query and make sure we return distinct results
{
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<X> cq = builder.createQuery(myTableType);
Root<X> from = cq.from(myTableType);
if (!params.isEmpty()) {
cq.where(from.get("myId").in(pids));
}
TypedQuery<X> q = myEntityManager.createQuery(cq);
List<T> retVal = new ArrayList<>();
for (X next : q.getResultList()) {
T resource = toResource(next);
retVal.add(resource);
}
return retVal;
}
}
@Required
public void setTableType(Class<X> theTableType) {
myTableType = theTableType;
}
private X toEntity(T theResource) { private X toEntity(T theResource) {
X retVal; X retVal;
try { try {
@ -766,4 +873,26 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return retVal; return retVal;
} }
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public MethodOutcome update(final T theResource, final IdDt theId) {
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
X savedEntity = template.execute(new TransactionCallback<X>() {
@Override
public X doInTransaction(TransactionStatus theStatus) {
final X entity = readEntity(theId);
final ResourceHistoryTable existing = entity.toHistory(myCtx);
populateResourceIntoEntity(theResource, entity);
myEntityManager.persist(existing);
entity.setUpdated(new Date());
myEntityManager.persist(entity);
return entity;
}
});
return toMethodOutcome(savedEntity);
}
} }

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.jpa.entity.BaseResourceTable;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -31,4 +32,8 @@ public interface IFhirResourceDao<T extends IResource> {
List<T> searchWithAndOr(Map<String, List<List<IQueryParameterType>>> theMap); List<T> searchWithAndOr(Map<String, List<List<IQueryParameterType>>> theMap);
Class<T> getResourceType();
Class<? extends BaseResourceTable<T>> getTableType();
} }

View File

@ -39,6 +39,17 @@ public class ResourceLink implements Serializable {
@Column(name = "TARGET_RESOURCE_PID", insertable = false, updatable = false) @Column(name = "TARGET_RESOURCE_PID", insertable = false, updatable = false)
private Long myTargetResourcePid; private Long myTargetResourcePid;
public ResourceLink() {
//nothing
}
public ResourceLink(String theSourcePath, BaseResourceTable<?> theSourceResource, BaseResourceTable<?> theTargetResource) {
super();
mySourcePath = theSourcePath;
mySourceResource = theSourceResource;
myTargetResource = theTargetResource;
}
public String getSourcePath() { public String getSourcePath() {
return mySourcePath; return mySourcePath;
} }

View File

@ -22,11 +22,13 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt; import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.AdministrativeGenderCodesEnum; import ca.uhn.fhir.model.dstu.valueset.AdministrativeGenderCodesEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
@ -72,6 +74,29 @@ public class FhirResourceDaoTest {
assertTrue(updated.before(now)); assertTrue(updated.before(now));
} }
@Test
public void testPersistResourceLink() {
Patient patient = new Patient();
patient.addIdentifier("urn:system", "testPersistResourceLink01");
IdDt patientId01 = ourPatientDao.create(patient).getId();
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));
IdDt obsId01 = ourObservationDao.create(obs01).getId();
Observation obs02 = new Observation();
obs01.setApplies(new DateTimeDt(new Date()));
obs01.setSubject(new ResourceReferenceDt(Patient.class, patientId01));
IdDt obsId02 = ourObservationDao.create(obs01).getId();
}
@Test @Test
public void testPersistSearchParamObservationString() { public void testPersistSearchParamObservationString() {
Observation obs = new Observation(); Observation obs = new Observation();

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry excluding="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/> <classpathentry excluding="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry kind="src" path="target/generated/valuesets"/>
<classpathentry including="**/*.java" kind="src" path="src/main/java"/> <classpathentry including="**/*.java" kind="src" path="src/main/java"/>
<classpathentry excluding="**/*.java" kind="src" path="src/main/resources"/> <classpathentry excluding="**/*.java" kind="src" path="src/main/resources"/>
<classpathentry kind="var" path="M2_REPO/javax/json/javax.json-api/1.0/javax.json-api-1.0.jar" sourcepath="M2_REPO/javax/json/javax.json-api/1.0/javax.json-api-1.0-sources.jar"/> <classpathentry kind="var" path="M2_REPO/javax/json/javax.json-api/1.0/javax.json-api-1.0.jar" sourcepath="M2_REPO/javax/json/javax.json-api/1.0/javax.json-api-1.0-sources.jar"/>

View File

@ -192,7 +192,7 @@ public class TinderStructuresMojo extends AbstractMojo {
// dtp.writeAll(dtOutputDir); // dtp.writeAll(dtOutputDir);
// //
ResourceGeneratorUsingSpreadsheet rp = new ResourceGeneratorUsingSpreadsheet(); ResourceGeneratorUsingSpreadsheet rp = new ResourceGeneratorUsingSpreadsheet();
rp.setBaseResourceNames(Arrays.asList("patient", "diagnosticorder")); rp.setBaseResourceNames(Arrays.asList("observation"));
rp.parse(); rp.parse();
// rp.bindValueSets(vsp); // rp.bindValueSets(vsp);

View File

@ -34,6 +34,11 @@ public abstract class Child extends BaseElement {
} }
@Override
public String toString() {
return getClass().getSimpleName()+"[" + getName() + "]";
}
/** /**
* Strips off "[x]" * Strips off "[x]"
*/ */

View File

@ -165,6 +165,16 @@ public class ${className}
super(theResourceType, theResourceId); super(theResourceType, theResourceId);
} }
/**
* Constructor which creates a normal resource reference
*
* @param theResourceType The resource type
* @param theResourceId The resource ID
*/
public ResourceReferenceDt(Class<? extends IResource> theResourceType, IdDt theResourceId) {
super(theResourceType, theResourceId);
}
/** /**
* Constructor which creates a resource reference containing the actual * Constructor which creates a resource reference containing the actual
* resource in question. * resource in question.

View File

@ -62,7 +62,7 @@
* </p> * </p>
*/ */
public ${child.referenceType} get${child.methodName}() { public ${child.referenceType} get${child.methodName}() {
#if ( ${child.hasMultipleTypes} == false && ${child.singleChildInstantiable} == true ) #if ( (${child.hasMultipleTypes} == false && ${child.singleChildInstantiable} == true) || (${child.resourceRef}) )
if (${child.variableName} == null) { if (${child.variableName} == null) {
#if ( ${child.boundCode} && ${child.repeatable} == false ) #if ( ${child.boundCode} && ${child.repeatable} == false )
${child.variableName} = new ${child.referenceTypeForConstructor}(${child.bindingClass}.VALUESET_BINDER); ${child.variableName} = new ${child.referenceTypeForConstructor}(${child.bindingClass}.VALUESET_BINDER);