Add history to JPA
This commit is contained in:
parent
2720e7d273
commit
a0b120f70e
|
@ -157,7 +157,9 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get(nextResource);
|
||||
if (versionId == null || versionId.isEmpty()) {
|
||||
throw new InternalErrorException("Server provided resource at index " + index + " with no Version ID set (using IResource#Resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, Object))");
|
||||
if (nextResource.getId().getUnqualifiedVersionId() == null) {
|
||||
throw new InternalErrorException("Server provided resource at index " + index + " with no Version ID set (using IResource#setId(IdDt))");
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
|
|
@ -74,10 +74,10 @@ public class CountParameter implements IParameter {
|
|||
@Override
|
||||
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
||||
if (theOuterCollectionType != null) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Since.class.getName() + " but can not be of collection type");
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + Since.class.getName() + " but can not be of collection type");
|
||||
}
|
||||
if (!ParameterUtil.getBindableIntegerTypes().contains(theParameterType)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Since.class.getName() + " but type '" + theParameterType + "' is an invalid type, must be one of: " + ParameterUtil.getBindableInstantTypes());
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + Since.class.getName() + " but type '" + theParameterType + "' is an invalid type, must be one of: " + ParameterUtil.getBindableIntegerTypes());
|
||||
}
|
||||
myType = theParameterType;
|
||||
}
|
||||
|
|
|
@ -333,17 +333,23 @@ public class RestfulServer extends HttpServlet {
|
|||
private void findResourceMethods(Object theProvider) throws Exception {
|
||||
|
||||
ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
|
||||
int count = 0;
|
||||
|
||||
Class<?> clazz = theProvider.getClass();
|
||||
Class<?> supertype = clazz.getSuperclass();
|
||||
if (!Object.class.equals(supertype)) {
|
||||
findResourceMethods(theProvider, supertype);
|
||||
while (!Object.class.equals(supertype)) {
|
||||
count += findResourceMethods(theProvider, supertype);
|
||||
supertype = supertype.getSuperclass();
|
||||
}
|
||||
|
||||
findResourceMethods(theProvider, clazz);
|
||||
count += findResourceMethods(theProvider, clazz);
|
||||
|
||||
if (count == 0) {
|
||||
throw new ConfigurationException("Did not find any annotated RESTful methods on provider class " + theProvider.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
private void findResourceMethods(Object theProvider, Class<?> clazz) throws ConfigurationException {
|
||||
private int findResourceMethods(Object theProvider, Class<?> clazz) throws ConfigurationException {
|
||||
int count = 0;
|
||||
|
||||
for (Method m : clazz.getDeclaredMethods()) {
|
||||
|
@ -383,9 +389,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
throw new ConfigurationException("Did not find any annotated RESTful methods on provider class " + theProvider.getClass().getCanonicalName());
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void findSystemMethods(Object theSystemProvider) {
|
||||
|
|
|
@ -589,7 +589,7 @@ public interface HistoryClient extends IBasicClient {
|
|||
* also be included in any of the methods above.
|
||||
*/
|
||||
@History
|
||||
Bundle getHistoryServerWithCriteria(@Since Date theDate, @Count int theCount);
|
||||
Bundle getHistoryServerWithCriteria(@Since Date theDate, @Count Integer theCount);
|
||||
|
||||
}
|
||||
//END SNIPPET: historyClient
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public class HistoryTest {
|
||||
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(HistoryTest.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
|
||||
@Test
|
||||
public void testHistory() throws Exception {
|
||||
{
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(2, new FhirContext().newXmlParser().parseBundle(responseContent).getEntries().size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = RandomServerPortProvider.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
DummyProvider patientProvider = new DummyProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer();
|
||||
servlet.setPlainProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public static class DummyProvider {
|
||||
|
||||
@History
|
||||
public List<Patient> findPatient(@Since InstantDt theSince) {
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("Patient/1/_history/1");
|
||||
retVal.add(patient);
|
||||
|
||||
Patient patient2 = new Patient();
|
||||
patient2.setId("Patient/1/_history/2");
|
||||
retVal.add(patient2);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -139,8 +139,16 @@
|
|||
<artifactId>validation-api</artifactId>
|
||||
<version>1.1.0.Final</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Misc -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>17.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<properties>
|
||||
<junit_version>4.11</junit_version>
|
||||
<derby_version>10.10.2.0</derby_version>
|
||||
|
|
|
@ -4,14 +4,24 @@ import static org.apache.commons.lang3.StringUtils.*;
|
|||
|
||||
import java.text.Normalizer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
import javax.persistence.Tuple;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -20,13 +30,17 @@ 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.BaseHasResource;
|
||||
import ca.uhn.fhir.jpa.entity.BaseTag;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTablePk;
|
||||
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.jpa.entity.TagDefinition;
|
||||
import ca.uhn.fhir.model.api.IDatatype;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
|
@ -45,10 +59,15 @@ 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;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public abstract class BaseFhirDao {
|
||||
private FhirContext myContext = new FhirContext();
|
||||
@PersistenceContext(name = "FHIR_UT", type = PersistenceContextType.TRANSACTION, unitName = "FHIR_UT")
|
||||
|
@ -66,83 +85,128 @@ public abstract class BaseFhirDao {
|
|||
myContext = theContext;
|
||||
}
|
||||
|
||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory) {
|
||||
if (entity.getPublished() == null) {
|
||||
entity.setPublished(new Date());
|
||||
private void searchHistoryCurrentVersion(String theResourceName, Long theId, Date theSince, int theLimit, List<HistoryTuple> tuples) {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||
Root<?> from = cq.from(ResourceTable.class);
|
||||
cq.multiselect(from.get("myId").as(Long.class), from.get("myUpdated").as(Date.class));
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
if (theSince != null) {
|
||||
Predicate low = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theSince);
|
||||
predicates.add(low);
|
||||
}
|
||||
|
||||
if (theUpdateHistory) {
|
||||
final ResourceHistoryTable historyEntry = entity.toHistory(getContext());
|
||||
myEntityManager.persist(historyEntry);
|
||||
if (theResourceName != null) {
|
||||
predicates.add(builder.equal(from.get("myResourceType"), theResourceName));
|
||||
}
|
||||
if (theId != null) {
|
||||
predicates.add(builder.equal(from.get("myId"), theId));
|
||||
}
|
||||
|
||||
entity.setVersion(entity.getVersion() + 1);
|
||||
cq.where(builder.and(predicates.toArray(new Predicate[0])));
|
||||
|
||||
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);
|
||||
|
||||
populateResourceIntoEntity(theResource, entity);
|
||||
|
||||
entity.setUpdated(new Date());
|
||||
|
||||
if (entity.getId() == null) {
|
||||
myEntityManager.persist(entity);
|
||||
} else {
|
||||
entity = myEntityManager.merge(entity);
|
||||
cq.orderBy(builder.desc(from.get("myUpdated")));
|
||||
TypedQuery<Tuple> q = myEntityManager.createQuery(cq);
|
||||
if (theLimit > 0) {
|
||||
q.setMaxResults(theLimit);
|
||||
}
|
||||
for (Tuple next : q.getResultList()) {
|
||||
long id = (Long) next.get(0);
|
||||
Date updated = (Date) next.get(1);
|
||||
tuples.add(new HistoryTuple(ResourceTable.class, updated, id));
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.isParamsStringPopulated()) {
|
||||
for (ResourceIndexedSearchParamString next : entity.getParamsString()) {
|
||||
myEntityManager.remove(next);
|
||||
private void searchHistoryCurrentVersion(List<HistoryTuple> theTuples, List<BaseHasResource> theRetVal) {
|
||||
Collection<HistoryTuple> tuples = Collections2.filter(theTuples, new com.google.common.base.Predicate<HistoryTuple>() {
|
||||
@Override
|
||||
public boolean apply(HistoryTuple theInput) {
|
||||
return theInput.getTable().equals(ResourceTable.class);
|
||||
}
|
||||
});
|
||||
Collection<Long> ids = Collections2.transform(tuples, new Function<HistoryTuple, Long>() {
|
||||
@Override
|
||||
public Long apply(HistoryTuple theInput) {
|
||||
return (Long) theInput.getId();
|
||||
}
|
||||
for (ResourceIndexedSearchParamString next : stringParams) {
|
||||
myEntityManager.persist(next);
|
||||
});
|
||||
if (ids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.isParamsTokenPopulated()) {
|
||||
for (ResourceIndexedSearchParamToken next : entity.getParamsToken()) {
|
||||
myEntityManager.remove(next);
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.where(from.get("myId").in(ids));
|
||||
|
||||
cq.orderBy(builder.desc(from.get("myUpdated")));
|
||||
TypedQuery<ResourceTable> q = myEntityManager.createQuery(cq);
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
theRetVal.add(next);
|
||||
}
|
||||
}
|
||||
for (ResourceIndexedSearchParamToken next : tokenParams) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
if (entity.isParamsNumberPopulated()) {
|
||||
for (ResourceIndexedSearchParamNumber next : entity.getParamsNumber()) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
}
|
||||
for (ResourceIndexedSearchParamNumber next : numberParams) {
|
||||
myEntityManager.persist(next);
|
||||
private void searchHistoryHistory(String theResourceName, Long theId, Date theSince, int theLimit, List<HistoryTuple> tuples) {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||
Root<?> from = cq.from(ResourceHistoryTable.class);
|
||||
cq.multiselect(from.get("myPk").as(ResourceHistoryTablePk.class), from.get("myUpdated").as(Date.class));
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
if (theSince != null) {
|
||||
Predicate low = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theSince);
|
||||
predicates.add(low);
|
||||
}
|
||||
|
||||
if (entity.isParamsDatePopulated()) {
|
||||
for (ResourceIndexedSearchParamDate next : entity.getParamsDate()) {
|
||||
myEntityManager.remove(next);
|
||||
if (theResourceName != null) {
|
||||
predicates.add(builder.equal(from.get("myResourceType"), theResourceName));
|
||||
}
|
||||
}
|
||||
for (ResourceIndexedSearchParamDate next : dateParams) {
|
||||
myEntityManager.persist(next);
|
||||
if (theId != null) {
|
||||
predicates.add(builder.equal(from.get("myId"), theId));
|
||||
}
|
||||
|
||||
if (entity.isHasLinks()) {
|
||||
for (ResourceLink next : entity.getResourceLinks()) {
|
||||
myEntityManager.remove(next);
|
||||
cq.where(builder.and(predicates.toArray(new Predicate[0])));
|
||||
|
||||
cq.orderBy(builder.desc(from.get("myUpdated")));
|
||||
TypedQuery<Tuple> q = myEntityManager.createQuery(cq);
|
||||
if (theLimit > 0) {
|
||||
q.setMaxResults(theLimit);
|
||||
}
|
||||
for (Tuple next : q.getResultList()) {
|
||||
ResourceHistoryTablePk id = (ResourceHistoryTablePk) next.get(0);
|
||||
Date updated = (Date) next.get(1);
|
||||
tuples.add(new HistoryTuple(ResourceHistoryTable.class, updated, id));
|
||||
}
|
||||
for (ResourceLink next : links) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
myEntityManager.flush();
|
||||
theResource.setId(new IdDt(entity.getResourceType(), entity.getId().toString(), Long.toString(entity.getVersion())));
|
||||
private void searchHistoryHistory(List<HistoryTuple> theTuples, List<BaseHasResource> theRetVal) {
|
||||
Collection<HistoryTuple> tuples = Collections2.filter(theTuples, new com.google.common.base.Predicate<HistoryTuple>() {
|
||||
@Override
|
||||
public boolean apply(HistoryTuple theInput) {
|
||||
return theInput.getTable().equals(ResourceHistoryTable.class);
|
||||
}
|
||||
});
|
||||
Collection<ResourceHistoryTablePk> ids = Collections2.transform(tuples, new Function<HistoryTuple, ResourceHistoryTablePk>() {
|
||||
@Override
|
||||
public ResourceHistoryTablePk apply(HistoryTuple theInput) {
|
||||
return (ResourceHistoryTablePk) theInput.getId();
|
||||
}
|
||||
});
|
||||
if (ids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return entity;
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceHistoryTable> cq = builder.createQuery(ResourceHistoryTable.class);
|
||||
Root<ResourceHistoryTable> from = cq.from(ResourceHistoryTable.class);
|
||||
cq.where(from.get("myPk").in(ids));
|
||||
|
||||
cq.orderBy(builder.desc(from.get("myUpdated")));
|
||||
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(cq);
|
||||
for (ResourceHistoryTable next : q.getResultList()) {
|
||||
theRetVal.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
protected List<ResourceLink> extractResourceLinks(ResourceTable theEntity, IResource theResource) {
|
||||
|
@ -472,6 +536,41 @@ public abstract class BaseFhirDao {
|
|||
return resourceTypeToDao.get(theType);
|
||||
}
|
||||
|
||||
protected ArrayList<IResource> history(String theResourceName, Long theId, Date theSince, int theLimit) {
|
||||
List<HistoryTuple> tuples = new ArrayList<HistoryTuple>();
|
||||
|
||||
// Get list of IDs
|
||||
searchHistoryCurrentVersion(theResourceName, theId, theSince, theLimit, tuples);
|
||||
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated());
|
||||
searchHistoryHistory(theResourceName, theId, theSince, theLimit, tuples);
|
||||
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated());
|
||||
|
||||
// Sort merged list
|
||||
Collections.sort(tuples, Collections.reverseOrder());
|
||||
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated());
|
||||
|
||||
// Pull actual resources
|
||||
List<BaseHasResource> resEntities = Lists.newArrayList();
|
||||
searchHistoryCurrentVersion(tuples, resEntities);
|
||||
searchHistoryHistory(tuples, resEntities);
|
||||
|
||||
Collections.sort(resEntities, new Comparator<BaseHasResource>() {
|
||||
@Override
|
||||
public int compare(BaseHasResource theO1, BaseHasResource theO2) {
|
||||
return theO2.getUpdated().getValue().compareTo(theO1.getUpdated().getValue());
|
||||
}
|
||||
});
|
||||
if (resEntities.size() > theLimit) {
|
||||
resEntities = resEntities.subList(0, theLimit);
|
||||
}
|
||||
|
||||
ArrayList<IResource> retVal = new ArrayList<IResource>();
|
||||
for (BaseHasResource next : resEntities) {
|
||||
retVal.add(toResource(next));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected String normalizeString(String theString) {
|
||||
char[] out = new char[theString.length()];
|
||||
theString = Normalizer.normalize(theString, Normalizer.Form.NFD);
|
||||
|
@ -485,8 +584,29 @@ public abstract class BaseFhirDao {
|
|||
return new String(out).toUpperCase();
|
||||
}
|
||||
|
||||
|
||||
private TagDefinition getTag(String theScheme, String theTerm, String theLabel) {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<TagDefinition> cq = builder.createQuery(TagDefinition.class);
|
||||
Root<TagDefinition> from = cq.from(TagDefinition.class);
|
||||
cq.where(builder.and(builder.equal(from.get("myScheme"),theScheme), builder.equal(from.get("myTerm"),theTerm)));
|
||||
TypedQuery<TagDefinition> q = myEntityManager.createQuery(cq);
|
||||
try {
|
||||
return q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
TagDefinition retVal = new TagDefinition(theTerm, theLabel, theScheme);
|
||||
myEntityManager.persist(retVal);
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
protected void populateResourceIntoEntity(IResource theResource, ResourceTable theEntity) {
|
||||
|
||||
if (theEntity.getPublished().isEmpty()) {
|
||||
theEntity.setPublished(new Date());
|
||||
}
|
||||
theEntity.setUpdated(new Date());
|
||||
|
||||
theEntity.setResourceType(toResourceName(theResource));
|
||||
theEntity.setResource(getContext().newJsonParser().encodeResourceToString(theResource));
|
||||
theEntity.setEncoding(EncodingEnum.JSON);
|
||||
|
@ -494,7 +614,8 @@ public abstract class BaseFhirDao {
|
|||
TagList tagList = (TagList) theResource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||
if (tagList != null) {
|
||||
for (Tag next : tagList) {
|
||||
theEntity.addTag(next.getTerm(), next.getLabel(), next.getScheme());
|
||||
TagDefinition tag = getTag(next.getScheme(), next.getTerm(),next.getLabel());
|
||||
theEntity.addTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -508,8 +629,110 @@ public abstract class BaseFhirDao {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
protected IResource toResource(BaseHasResource theEntity) {
|
||||
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
||||
return toResource(type.getImplementingClass(), theEntity);
|
||||
}
|
||||
|
||||
protected <T extends IResource> T toResource(Class<T> theResourceType, BaseHasResource theEntity) {
|
||||
String resourceText = theEntity.getResource();
|
||||
IParser parser = theEntity.getEncoding().newParser(getContext());
|
||||
T retVal = parser.parseResource(theResourceType, resourceText);
|
||||
retVal.setId(theEntity.getIdDt());
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, theEntity.getVersion());
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, theEntity.getPublished());
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, theEntity.getUpdated());
|
||||
if (theEntity.getTags().size() > 0) {
|
||||
TagList tagList = new TagList();
|
||||
for (BaseTag next : theEntity.getTags()) {
|
||||
tagList.add(new Tag(next.getTag().getTerm(), next.getTag().getLabel(), next.getTag().getScheme()));
|
||||
}
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected String toResourceName(IResource theResource) {
|
||||
return myContext.getResourceDefinition(theResource).getName();
|
||||
}
|
||||
|
||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory) {
|
||||
if (entity.getPublished() == null) {
|
||||
entity.setPublished(new Date());
|
||||
}
|
||||
|
||||
if (theUpdateHistory) {
|
||||
final ResourceHistoryTable historyEntry = entity.toHistory(getContext());
|
||||
myEntityManager.persist(historyEntry);
|
||||
}
|
||||
|
||||
entity.setVersion(entity.getVersion() + 1);
|
||||
|
||||
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);
|
||||
|
||||
populateResourceIntoEntity(theResource, entity);
|
||||
|
||||
entity.setUpdated(new Date());
|
||||
|
||||
if (entity.getId() == null) {
|
||||
myEntityManager.persist(entity);
|
||||
} else {
|
||||
entity = myEntityManager.merge(entity);
|
||||
}
|
||||
|
||||
if (entity.isParamsStringPopulated()) {
|
||||
for (ResourceIndexedSearchParamString next : entity.getParamsString()) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
}
|
||||
for (ResourceIndexedSearchParamString next : stringParams) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
if (entity.isParamsTokenPopulated()) {
|
||||
for (ResourceIndexedSearchParamToken next : entity.getParamsToken()) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
}
|
||||
for (ResourceIndexedSearchParamToken next : tokenParams) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
if (entity.isParamsNumberPopulated()) {
|
||||
for (ResourceIndexedSearchParamNumber next : entity.getParamsNumber()) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
}
|
||||
for (ResourceIndexedSearchParamNumber next : numberParams) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
if (entity.isParamsDatePopulated()) {
|
||||
for (ResourceIndexedSearchParamDate next : entity.getParamsDate()) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
}
|
||||
for (ResourceIndexedSearchParamDate next : dateParams) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
if (entity.isHasLinks()) {
|
||||
for (ResourceLink next : entity.getResourceLinks()) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
}
|
||||
for (ResourceLink next : links) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
myEntityManager.flush();
|
||||
theResource.setId(new IdDt(entity.getResourceType(), entity.getId().toString(), Long.toString(entity.getVersion())));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import java.util.Set;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
|
@ -26,19 +25,14 @@ import javax.persistence.criteria.Root;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
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.BaseTag;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
|
||||
|
@ -49,16 +43,12 @@ 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.CodingDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
|
||||
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.QualifiedDateParam;
|
||||
|
@ -84,10 +74,11 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
@Override
|
||||
public MethodOutcome create(final T theResource) {
|
||||
|
||||
final ResourceTable entity = toEntity(theResource);
|
||||
|
||||
entity.setPublished(new Date());
|
||||
entity.setUpdated(entity.getPublished());
|
||||
// final ResourceTable entity = toEntity(theResource);
|
||||
//
|
||||
// entity.setPublished(new Date());
|
||||
// entity.setUpdated(entity.getPublished());
|
||||
ResourceTable entity = new ResourceTable();
|
||||
entity.setResourceType(toResourceName(theResource));
|
||||
|
||||
// final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
|
||||
|
@ -147,7 +138,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
// myEntityManager.createQuery(criteriaQuery);
|
||||
List<ResourceHistoryTable> results = q.getResultList();
|
||||
for (ResourceHistoryTable next : results) {
|
||||
retVal.add(toResource(next));
|
||||
retVal.add(toResource(myResourceType,next));
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -173,7 +164,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
public T read(IdDt theId) {
|
||||
ResourceTable entity = readEntity(theId);
|
||||
|
||||
T retVal = toResource(entity);
|
||||
T retVal = toResource(myResourceType,entity);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -187,6 +178,11 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
return search(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> history() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> search(SearchParameterMap theParams) {
|
||||
|
||||
|
@ -213,13 +209,23 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
List<T> retVal = new ArrayList<T>();
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
T resource = toResource(next);
|
||||
T resource = toResource(myResourceType,next);
|
||||
retVal.add(resource);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IResource> history(Date theSince, int theLimit) {
|
||||
return super.history(myResourceName, null, theSince, theLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IResource> history(Long theId, Date theSince, int theLimit) {
|
||||
return super.history(myResourceName, theId, theSince, theLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> search(String theParameterName, IQueryParameterType theValue) {
|
||||
return search(Collections.singletonMap(theParameterName, theValue));
|
||||
|
@ -711,21 +717,5 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
return super.getDao(theType);
|
||||
}
|
||||
|
||||
private T toResource(BaseHasResource theEntity) {
|
||||
String resourceText = theEntity.getResource();
|
||||
IParser parser = theEntity.getEncoding().newParser(getContext());
|
||||
T retVal = parser.parseResource(myResourceType, resourceText);
|
||||
retVal.setId(theEntity.getIdDt());
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, theEntity.getVersion());
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, theEntity.getPublished());
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, theEntity.getUpdated());
|
||||
if (theEntity.getTags().size() > 0) {
|
||||
TagList tagList = new TagList();
|
||||
for (BaseTag next : theEntity.getTags()) {
|
||||
tagList.add(new Tag(next.getTerm(), next.getLabel(), next.getScheme()));
|
||||
}
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
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 javax.persistence.Tuple;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -15,15 +21,14 @@ 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.api.TagList;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
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);
|
||||
|
||||
//name = "FHIR_UT", type = PersistenceContextType.TRANSACTION, unitName = "FHIR_UT"
|
||||
@PersistenceContext()
|
||||
private EntityManager myEntityManager;
|
||||
|
||||
|
@ -94,4 +99,45 @@ public class FhirSystemDao extends BaseFhirDao implements IFhirSystemDao {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IResource> history(Date theSince, int theLimit) {
|
||||
return super.history(null, null, theSince, theLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagList getAllTags() {
|
||||
// CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
// CriteriaQuery<Tuple> cq = builder.createQuery(Tag)
|
||||
// Root<?> from = cq.from(ResourceTable.class);
|
||||
// cq.multiselect(from.get("myId").as(Long.class), from.get("myUpdated").as(Date.class));
|
||||
//
|
||||
// List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
// if (theSince != null) {
|
||||
// Predicate low = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theSince);
|
||||
// predicates.add(low);
|
||||
// }
|
||||
//
|
||||
// if (theResourceName != null) {
|
||||
// predicates.add(builder.equal(from.get("myResourceType"), theResourceName));
|
||||
// }
|
||||
// if (theId != null) {
|
||||
// predicates.add(builder.equal(from.get("myId"), theId));
|
||||
// }
|
||||
//
|
||||
// cq.where(builder.and(predicates.toArray(new Predicate[0])));
|
||||
//
|
||||
// cq.orderBy(builder.desc(from.get("myUpdated")));
|
||||
// TypedQuery<Tuple> q = myEntityManager.createQuery(cq);
|
||||
// if (theLimit > 0) {
|
||||
// q.setMaxResults(theLimit);
|
||||
// }
|
||||
// for (Tuple next : q.getResultList()) {
|
||||
// long id = (Long) next.get(0);
|
||||
// Date updated = (Date) next.get(1);
|
||||
// tuples.add(new HistoryTuple(ResourceTable.class, updated, id));
|
||||
// }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
class HistoryTuple implements Comparable<HistoryTuple> {
|
||||
|
||||
private Object myId;
|
||||
private Class<?> myTable;
|
||||
private Date myUpdated;
|
||||
|
||||
public HistoryTuple(Class<?> theTable, Date theUpdated, Object theId) {
|
||||
super();
|
||||
myTable = theTable;
|
||||
myUpdated = theUpdated;
|
||||
myId = theId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(HistoryTuple theO) {
|
||||
return myUpdated.compareTo(theO.myUpdated);
|
||||
}
|
||||
|
||||
public Object getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public Class<?> getTable() {
|
||||
return myTable;
|
||||
}
|
||||
|
||||
public Date getUpdated() {
|
||||
return myUpdated;
|
||||
}
|
||||
|
||||
public void setId(Object theId) {
|
||||
myId = theId;
|
||||
}
|
||||
|
||||
public void setTable(Class<?> theTable) {
|
||||
myTable = theTable;
|
||||
}
|
||||
|
||||
public void setUpdated(Date theUpdated) {
|
||||
myUpdated = theUpdated;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -8,6 +9,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
|
|||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
|
||||
|
@ -41,4 +43,10 @@ public interface IFhirResourceDao<T extends IResource> {
|
|||
|
||||
Set<Long> searchForIdsWithAndOr(Map<String, List<List<IQueryParameterType>>> theMap);
|
||||
|
||||
List<T> history();
|
||||
|
||||
List<IResource> history(Date theDate, int theLimit);
|
||||
|
||||
List<IResource> history(Long theId, Date theSince, int theLimit);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
|
||||
public interface IFhirSystemDao {
|
||||
|
||||
void transaction(List<IResource> theResources);
|
||||
|
||||
List<IResource> history(Date theDate, int theLimit);
|
||||
|
||||
TagList getAllTags();
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import javax.persistence.MappedSuperclass;
|
|||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
@ -16,25 +17,27 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
|
|||
@MappedSuperclass
|
||||
public abstract class BaseHasResource {
|
||||
|
||||
@Column(name = "ENCODING")
|
||||
@Column(name = "RES_ENCODING", nullable = false)
|
||||
private EncodingEnum myEncoding;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "PUBLISHED")
|
||||
@Column(name = "RES_PUBLISHED", nullable = false)
|
||||
private Date myPublished;
|
||||
|
||||
@Column(name = "RESOURCE_TEXT", length = Integer.MAX_VALUE - 1)
|
||||
@Column(name = "RES_TEXT", length = Integer.MAX_VALUE - 1, nullable = false)
|
||||
@Lob()
|
||||
private String myResource;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "UPDATED")
|
||||
@Column(name = "RES_UPDATED", nullable = false)
|
||||
private Date myUpdated;
|
||||
|
||||
public EncodingEnum getEncoding() {
|
||||
return myEncoding;
|
||||
}
|
||||
|
||||
public abstract String getResourceType();
|
||||
|
||||
public abstract Collection<? extends BaseTag> getTags();
|
||||
|
||||
public abstract IdDt getIdDt();
|
||||
|
|
|
@ -2,7 +2,8 @@ package ca.uhn.fhir.jpa.entity;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
@MappedSuperclass
|
||||
|
@ -10,37 +11,17 @@ public class BaseTag implements Serializable {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name="TAG_LABEL", length=200)
|
||||
private String myLabel;
|
||||
@ManyToOne(cascade= {})
|
||||
@JoinColumn(name="TAG_ID", nullable=false)
|
||||
private TagDefinition myTag;
|
||||
|
||||
@Column(name="TAG_SCHEME", length=200)
|
||||
private String myScheme;
|
||||
|
||||
@Column(name="TAG_TERM", length=200)
|
||||
private String myTerm;
|
||||
|
||||
public String getLabel() {
|
||||
return myLabel;
|
||||
public TagDefinition getTag() {
|
||||
return myTag;
|
||||
}
|
||||
|
||||
public String getScheme() {
|
||||
return myScheme;
|
||||
public void setTag(TagDefinition theTag) {
|
||||
myTag = theTag;
|
||||
}
|
||||
|
||||
public String getTerm() {
|
||||
return myTerm;
|
||||
}
|
||||
|
||||
public void setLabel(String theLabel) {
|
||||
myLabel = theLabel;
|
||||
}
|
||||
|
||||
public void setScheme(String theScheme) {
|
||||
myScheme = theScheme;
|
||||
}
|
||||
|
||||
public void setTerm(String theTerm) {
|
||||
myTerm = theTerm;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,20 +5,21 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import org.hibernate.annotations.Index;
|
||||
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
@Entity
|
||||
@Table(name = "HFJ_RES_VER", uniqueConstraints = {})
|
||||
@org.hibernate.annotations.Table(appliesTo="HFJ_RES_VER", indexes= {@Index(name="IDX_RES_VER_DATE", columnNames= {"RES_UPDATED"})})
|
||||
public class ResourceHistoryTable extends BaseHasResource implements Serializable {
|
||||
|
||||
public static final String Q_GETALL = "SELECT h FROM ResourceHistoryTable h WHERE h.myPk.myId = :PID AND h.myPk.myResourceType = :RESTYPE ORDER BY h.myUpdated ASC";
|
||||
|
@ -28,6 +29,12 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
|||
@EmbeddedId
|
||||
private ResourceHistoryTablePk myPk;
|
||||
|
||||
@Column(name="RES_TYPE",insertable=false, updatable=false)
|
||||
private String myResourceType;
|
||||
|
||||
@Column(name="PID", insertable=false, updatable=false)
|
||||
private Long myId;
|
||||
|
||||
@OneToMany(mappedBy = "myResourceHistory", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
|
||||
private Collection<ResourceHistoryTag> myTags;
|
||||
|
||||
|
@ -41,12 +48,13 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<IResource> getResourceType() {
|
||||
try {
|
||||
return (Class<IResource>) Class.forName(Patient.class.getPackage().getName() + "." + myPk.getResourceType());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
public String getResourceType() {
|
||||
return myPk.getResourceType();
|
||||
// try {
|
||||
// return (Class<IResource>) Class.forName(Patient.class.getPackage().getName() + "." + myPk.getResourceType());
|
||||
// } catch (ClassNotFoundException e) {
|
||||
// throw new InternalErrorException(e);
|
||||
// }
|
||||
}
|
||||
|
||||
public Collection<ResourceHistoryTag> getTags() {
|
||||
|
@ -65,13 +73,26 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
|||
myPk = thePk;
|
||||
}
|
||||
|
||||
public void addTag(String theTerm, String theLabel, String theScheme) {
|
||||
public void addTag(ResourceHistoryTag theTag) {
|
||||
for (ResourceHistoryTag next : getTags()) {
|
||||
if (next.getTerm().equals(theTerm)) {
|
||||
if (next.getTag().getTerm().equals(theTag)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
getTags().add(new ResourceHistoryTag(this, theTerm, theLabel, theScheme));
|
||||
getTags().add(theTag);
|
||||
}
|
||||
|
||||
public boolean hasTag(String theTerm, String theLabel, String theScheme) {
|
||||
for (ResourceHistoryTag next : getTags()) {
|
||||
if (next.getTag().getScheme().equals(theScheme) && next.getTag().getTerm().equals(theTerm)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addTag(ResourceTag theTag) {
|
||||
getTags().add(new ResourceHistoryTag(this, theTag.getTag()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,11 +32,10 @@ public class ResourceHistoryTag extends BaseTag implements Serializable {
|
|||
public ResourceHistoryTag() {
|
||||
}
|
||||
|
||||
public ResourceHistoryTag(ResourceHistoryTable theResourceHistory, String theTerm, String theLabel, String theScheme) {
|
||||
myResourceHistory = theResourceHistory;
|
||||
setTerm(theTerm);
|
||||
setLabel(theLabel);
|
||||
setScheme(theScheme);
|
||||
|
||||
public ResourceHistoryTag(ResourceHistoryTable theResourceHistoryTable, TagDefinition theTag) {
|
||||
myResourceHistory=theResourceHistoryTable;
|
||||
setTag(theTag);
|
||||
}
|
||||
|
||||
public ResourceHistoryTable getResourceHistory() {
|
||||
|
|
|
@ -17,13 +17,17 @@ import javax.persistence.OneToMany;
|
|||
import javax.persistence.Table;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import org.hibernate.annotations.Index;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
@Entity
|
||||
@Table(name = "HFJ_RESOURCE", uniqueConstraints = {})
|
||||
@Inheritance(strategy = InheritanceType.JOINED)
|
||||
@org.hibernate.annotations.Table(appliesTo="HFJ_RESOURCE", indexes= {@Index(name="IDX_RES_DATE", columnNames= {"RES_UPDATED"})})
|
||||
public class ResourceTable extends BaseHasResource implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
@ -75,13 +79,13 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
@Column(name = "RES_VER")
|
||||
private long myVersion;
|
||||
|
||||
public void addTag(String theTerm, String theLabel, String theScheme) {
|
||||
public boolean hasTag(String theTerm, String theLabel, String theScheme) {
|
||||
for (ResourceTag next : getTags()) {
|
||||
if (next.getTerm().equals(theTerm)) {
|
||||
return;
|
||||
if (next.getTag().getTerm().equals(theTerm)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
getTags().add(new ResourceTag(this, theTerm, theLabel, theScheme));
|
||||
return false;
|
||||
}
|
||||
|
||||
public IdDt getIdDt() {
|
||||
|
@ -221,9 +225,13 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
retVal.setResource(getResource());
|
||||
|
||||
for (ResourceTag next : getTags()) {
|
||||
retVal.addTag(next.getTerm(), next.getLabel(), next.getScheme());
|
||||
retVal.addTag(next);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public void addTag(TagDefinition theTag) {
|
||||
getTags().add(new ResourceTag(this, theTag));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,11 +27,9 @@ public class ResourceTag extends BaseTag {
|
|||
public ResourceTag() {
|
||||
}
|
||||
|
||||
public ResourceTag(ResourceTable theResource, String theTerm, String theLabel, String theScheme) {
|
||||
myResource = theResource;
|
||||
setTerm(theTerm);
|
||||
setLabel(theLabel);
|
||||
setScheme(theScheme);
|
||||
public ResourceTag(ResourceTable theResourceTable, TagDefinition theTag) {
|
||||
myResource=theResourceTable;
|
||||
setTag(theTag);
|
||||
}
|
||||
|
||||
public ResourceTable getResource() {
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package ca.uhn.fhir.jpa.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
@Entity
|
||||
@Table(name = "HFJ_TAG_DEF", uniqueConstraints= {@UniqueConstraint(columnNames= {"TAG_SCHEME","TAG_TERM"})})
|
||||
//@org.hibernate.annotations.Table(appliesTo="HFJ_TAG", indexes= {@Index(name)})
|
||||
public class TagDefinition implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(name = "TAG_ID")
|
||||
private Long myId;
|
||||
|
||||
@Column(name = "TAG_LABEL", length = 200)
|
||||
private String myLabel;
|
||||
|
||||
@Column(name = "TAG_SCHEME", length = 200)
|
||||
private String myScheme;
|
||||
|
||||
@Column(name = "TAG_TERM", length = 200)
|
||||
private String myTerm;
|
||||
|
||||
public TagDefinition() {
|
||||
}
|
||||
|
||||
public TagDefinition(String theTerm, String theLabel, String theScheme) {
|
||||
setTerm(theTerm);
|
||||
setScheme(theScheme);
|
||||
setLabel(theLabel);
|
||||
}
|
||||
|
||||
public void setLabel(String theLabel) {
|
||||
myLabel = theLabel;
|
||||
}
|
||||
|
||||
public void setScheme(String theScheme) {
|
||||
myScheme = theScheme;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return myLabel;
|
||||
}
|
||||
|
||||
public String getScheme() {
|
||||
return myScheme;
|
||||
}
|
||||
|
||||
public String getTerm() {
|
||||
return myTerm;
|
||||
}
|
||||
|
||||
public void setTerm(String theTerm) {
|
||||
myTerm = theTerm;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -10,12 +11,14 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
|||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.Count;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
@ -76,4 +79,14 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
return myDao.getResourceType();
|
||||
}
|
||||
|
||||
@History
|
||||
public List<IResource> getHistoryServerWithCriteria(@Since Date theDate, @Count Integer theCount) {
|
||||
return myDao.history(theDate, theCount);
|
||||
}
|
||||
|
||||
@History
|
||||
public List<IResource> getHistoryServerWithCriteria(@IdParam IdDt theId, @Since Date theDate, @Count Integer theCount) {
|
||||
return myDao.history(theId.asLong(), theDate, theCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.rest.annotation.Count;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||
|
||||
|
@ -21,7 +26,6 @@ public class JpaSystemProvider {
|
|||
myDao = theDao;
|
||||
}
|
||||
|
||||
|
||||
@Required
|
||||
public void setDao(IFhirSystemDao theDao) {
|
||||
myDao = theDao;
|
||||
|
@ -33,4 +37,8 @@ public class JpaSystemProvider {
|
|||
return theResources;
|
||||
}
|
||||
|
||||
@History
|
||||
List<IResource> getHistoryServerWithCriteria(@Since Date theDate, @Count Integer theCount) {
|
||||
return myDao.history(theDate, theCount);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import ca.uhn.fhir.model.dstu.resource.Observation;
|
|||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
public class FhirSystemDaoTest {
|
||||
|
@ -37,6 +39,50 @@ public class FhirSystemDaoTest {
|
|||
private static Date ourTestStarted;
|
||||
private static IFhirSystemDao ourSystemDao;
|
||||
|
||||
@Test
|
||||
public void testHistory() throws Exception {
|
||||
Date start = new Date();
|
||||
Thread.sleep(10);
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier("urn:system", "testHistory");
|
||||
patient.addName().addFamily("Tester").addGiven("Joe");
|
||||
IdDt pid = ourPatientDao.create(patient).getId();
|
||||
|
||||
Thread.sleep(10);
|
||||
IdDt newpid = ourPatientDao.update(patient, pid).getId();
|
||||
|
||||
Thread.sleep(10);
|
||||
IdDt newpid2 = ourPatientDao.update(patient, pid).getId();
|
||||
|
||||
Thread.sleep(10);
|
||||
IdDt newpid3 = ourPatientDao.update(patient, pid).getId();
|
||||
|
||||
List<IResource> values = ourSystemDao.history(start, 1000);
|
||||
assertEquals(4, values.size());
|
||||
|
||||
assertEquals(newpid3, values.get(0).getId());
|
||||
assertEquals(newpid2, values.get(1).getId());
|
||||
assertEquals(newpid, values.get(2).getId());
|
||||
assertEquals(pid, values.get(3).getId());
|
||||
|
||||
|
||||
Location loc = new Location();
|
||||
loc.getAddress().addLine("AAA");
|
||||
IdDt lid = ourLocationDao.create(loc).getId();
|
||||
|
||||
Location loc2 = new Location();
|
||||
loc2.getAddress().addLine("AAA");
|
||||
ourLocationDao.create(loc2).getId();
|
||||
|
||||
values = ourLocationDao.history(start, 1000);
|
||||
assertEquals(2, values.size());
|
||||
|
||||
values = ourLocationDao.history(lid.asLong(), start, 1000);
|
||||
assertEquals(1, values.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistWithSimpleLink() {
|
||||
Patient patient = new Patient();
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken</class>
|
||||
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
||||
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
||||
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
||||
|
||||
<exclude-unlisted-classes>false</exclude-unlisted-classes>
|
||||
<properties>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
<persistence-unit name="FHIR_UT" transaction-type="RESOURCE_LOCAL">
|
||||
<provider>org.hibernate.ejb.HibernatePersistence</provider>
|
||||
<class>ca.uhn.test.jpasrv.PatientResourceTable</class>
|
||||
<class>ca.uhn.fhir.jpa.entity.ResourceHistoryTable</class>
|
||||
<class>ca.uhn.fhir.jpa.entity.ResourceHistoryTag</class>
|
||||
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate</class>
|
||||
|
@ -16,6 +15,7 @@
|
|||
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
||||
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
|
||||
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
||||
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
||||
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
<properties>
|
||||
|
|
Loading…
Reference in New Issue