Merge remote-tracking branch 'remotes/origin/master' into im_20200316_lastn_operation_elasticsearch

This commit is contained in:
ianmarshall 2020-05-08 09:19:44 -04:00
commit e56cb049f7
91 changed files with 1147 additions and 278 deletions

View File

@ -1056,6 +1056,9 @@ public enum Pointcut {
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.storage.TransactionDetails - The outer transaction details object (since 5.0.0)
* </li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
@ -1064,7 +1067,8 @@ public enum Pointcut {
STORAGE_PRESTORAGE_RESOURCE_CREATED(void.class,
"org.hl7.fhir.instance.model.api.IBaseResource",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"ca.uhn.fhir.rest.api.server.storage.TransactionDetails"
),
/**
@ -1094,6 +1098,9 @@ public enum Pointcut {
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.storage.TransactionDetails - The outer transaction details object (since 5.0.0)
* </li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
@ -1103,7 +1110,8 @@ public enum Pointcut {
"org.hl7.fhir.instance.model.api.IBaseResource",
"org.hl7.fhir.instance.model.api.IBaseResource",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"ca.uhn.fhir.rest.api.server.storage.TransactionDetails"
),
/**
@ -1131,6 +1139,9 @@ public enum Pointcut {
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.storage.TransactionDetails - The outer transaction details object (since 5.0.0)
* </li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
@ -1139,7 +1150,8 @@ public enum Pointcut {
STORAGE_PRESTORAGE_RESOURCE_DELETED(void.class,
"org.hl7.fhir.instance.model.api.IBaseResource",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"ca.uhn.fhir.rest.api.server.storage.TransactionDetails"
),
@ -1170,6 +1182,9 @@ public enum Pointcut {
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.storage.TransactionDetails - The outer transaction details object (since 5.0.0)
* </li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
@ -1178,7 +1193,8 @@ public enum Pointcut {
STORAGE_PRECOMMIT_RESOURCE_CREATED(void.class,
"org.hl7.fhir.instance.model.api.IBaseResource",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"ca.uhn.fhir.rest.api.server.storage.TransactionDetails"
),
/**
@ -1209,6 +1225,9 @@ public enum Pointcut {
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.storage.TransactionDetails - The outer transaction details object (since 5.0.0)
* </li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
@ -1218,7 +1237,8 @@ public enum Pointcut {
"org.hl7.fhir.instance.model.api.IBaseResource",
"org.hl7.fhir.instance.model.api.IBaseResource",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"ca.uhn.fhir.rest.api.server.storage.TransactionDetails"
),
@ -1245,6 +1265,9 @@ public enum Pointcut {
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.storage.TransactionDetails - The outer transaction details object (since 5.0.0)
* </li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
@ -1253,7 +1276,8 @@ public enum Pointcut {
STORAGE_PRECOMMIT_RESOURCE_DELETED(void.class,
"org.hl7.fhir.instance.model.api.IBaseResource",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"ca.uhn.fhir.rest.api.server.storage.TransactionDetails"
),
/**
@ -1278,6 +1302,9 @@ public enum Pointcut {
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.storage.TransactionDetails - The outer transaction details object (since 5.0.0)
* </li>
* </ul>
* <p>
* Hooks should return <code>ca.uhn.fhir.jpa.delete.DeleteConflictOutcome</code>.
@ -1291,7 +1318,8 @@ public enum Pointcut {
// Params
"ca.uhn.fhir.jpa.api.model.DeleteConflictList",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"ca.uhn.fhir.rest.api.server.storage.TransactionDetails"
),
/**

View File

@ -316,7 +316,7 @@ public class UrlUtil {
}
}
if (url.matches("/[a-zA-Z]+\\?.*")) {
if (url.length() > 1 && url.charAt(0) == '/' && Character.isLetter(url.charAt(1)) && url.contains("?")) {
url = url.substring(1);
}
int nextStart = 0;

View File

@ -365,11 +365,12 @@ public class InterceptorServiceTest {
params.add(String.class, "C");
params.add(String.class, "D");
params.add(String.class, "E");
params.add(String.class, "F");
try {
svc.haveAppropriateParams(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, params);
fail();
} catch (IllegalArgumentException e) {
assertEquals("Wrong number of params for pointcut " + Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED + " - Wanted ca.uhn.fhir.rest.api.server.RequestDetails,ca.uhn.fhir.rest.server.servlet.ServletRequestDetails,org.hl7.fhir.instance.model.api.IBaseResource,org.hl7.fhir.instance.model.api.IBaseResource but found [String, String, String, String, String]", e.getMessage());
assertEquals("Wrong number of params for pointcut STORAGE_PRECOMMIT_RESOURCE_UPDATED - Wanted ca.uhn.fhir.rest.api.server.RequestDetails,ca.uhn.fhir.rest.api.server.storage.TransactionDetails,ca.uhn.fhir.rest.server.servlet.ServletRequestDetails,org.hl7.fhir.instance.model.api.IBaseResource,org.hl7.fhir.instance.model.api.IBaseResource but found [String, String, String, String, String, String]", e.getMessage());
}
}
@ -383,6 +384,7 @@ public class InterceptorServiceTest {
params.add((Class) String.class, 2);
params.add((Class) String.class, 3);
params.add((Class) String.class, 4);
params.add((Class) String.class, 5);
try {
svc.haveAppropriateParams(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, params);
fail();

View File

@ -23,6 +23,7 @@ package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
@ -140,6 +141,10 @@ public class AuthorizationInterceptors {
// let's pretend there is some storage code here.
theResource.setId(theId.withVersion("2"));
// One TransactionDetails object should be created for each FHIR operation. Interceptors
// may use it for getting/setting details about the running transaction.
TransactionDetails transactionDetails = new TransactionDetails();
// Notify the interceptor framework when we're about to perform an update. This is
// useful as the authorization interceptor will pick this event up and use it
// to factor into a decision about whether the operation should be allowed to proceed.
@ -149,7 +154,8 @@ public class AuthorizationInterceptors {
.add(IBaseResource.class, previousContents)
.add(IBaseResource.class, newContents)
.add(RequestDetails.class, theRequestDetails)
.add(ServletRequestDetails.class, theRequestDetails);
.add(ServletRequestDetails.class, theRequestDetails)
.add(TransactionDetails.class, transactionDetails);
theInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, params);
MethodOutcome retVal = new MethodOutcome();

View File

@ -0,0 +1,6 @@
---
type: add
issue: 1836
title: "In HAPI FHIR 4.2.0, when performing a Bulk Export on a server with Binary Storage enabled, the bulk export files
were not able to take advantage of the externalized binary stroage and would be stored in the relational DB. This has
now been enhanced to allow bulk export files to store externally."

View File

@ -26,10 +26,11 @@ import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.EncodingEnum;
@ -76,10 +77,9 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
/**
* @param thePerformIndexing Use with caution! If you set this to false, you need to manually perform indexing or your resources
* won't be indexed and searches won't work.
* @param theUpdateTimestamp
* @param theRequestDetails TODO
*/
DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTimestamp, RequestDetails theRequestDetails);
DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, TransactionDetails theTransactionDetails, RequestDetails theRequestDetails);
DaoMethodOutcome create(T theResource, String theIfNoneExist, RequestDetails theRequestDetails);
@ -95,7 +95,7 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
*
* @param theRequestDetails TODO
*/
DaoMethodOutcome delete(IIdType theResource, DeleteConflictList theDeleteConflictsListToPopulate, RequestDetails theRequestDetails);
DaoMethodOutcome delete(IIdType theResource, DeleteConflictList theDeleteConflictsListToPopulate, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails);
/**
* This method throws an exception if there are delete conflicts
@ -241,7 +241,7 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
* @param theForceUpdateVersion Create a new version with the same contents as the current version even if the content hasn't changed (this is mostly useful for
* resources mapping to external content such as external code systems)
*/
DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequestDetails);
DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails);
/**
* Not supported in DSTU1!

View File

@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.api.dao;
* #L%
*/
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -32,8 +32,8 @@ public interface IJpaDao<T extends IBaseResource> {
@SuppressWarnings("unchecked")
IBasePersistedResource updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource
theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry);
IBasePersistedResource updateInternal(RequestDetails theRequestDetails, T theResource, boolean thePerformIndexing, boolean theForceUpdateVersion,
IBasePersistedResource theEntity, IIdType theResourceId, IBaseResource theOldResource);
IBasePersistedResource theEntity, IIdType theResourceId, IBaseResource theOldResource, TransactionDetails theTransactionDetails);
}

View File

@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.api.svc;
*/
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.server.IBundleProvider;

View File

@ -28,12 +28,16 @@ import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.IModelVisitor2;
import org.apache.commons.io.FileUtils;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.IdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -44,7 +48,11 @@ import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@ -84,7 +92,9 @@ public class BinaryStorageInterceptor {
@SuppressWarnings("unchecked")
@PostConstruct
public void start() {
myBinaryType = (Class<? extends IPrimitiveType<byte[]>>) myCtx.getElementDefinition("base64Binary").getImplementingClass();
BaseRuntimeElementDefinition<?> base64Binary = myCtx.getElementDefinition("base64Binary");
assert base64Binary != null;
myBinaryType = (Class<? extends IPrimitiveType<byte[]>>) base64Binary.getImplementingClass();
myDeferredListKey = getClass().getName() + "_" + hashCode() + "_DEFERRED_LIST";
}
@ -97,7 +107,7 @@ public class BinaryStorageInterceptor {
.stream()
.flatMap(t -> ((IBaseHasExtensions) t).getExtension().stream())
.filter(t -> JpaConstants.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
.map(t -> ((IPrimitiveType) t.getValue()).getValueAsString())
.map(t -> ((IPrimitiveType<?>) t.getValue()).getValueAsString())
.collect(Collectors.toList());
for (String next : attachmentIds) {
@ -110,14 +120,14 @@ public class BinaryStorageInterceptor {
}
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED)
public void extractLargeBinariesBeforeCreate(ServletRequestDetails theRequestDetails, IBaseResource theResource, Pointcut thePointcut) throws IOException {
extractLargeBinaries(theRequestDetails, theResource, thePointcut);
public void extractLargeBinariesBeforeCreate(TransactionDetails theTransactionDetails, IBaseResource theResource, Pointcut thePointcut) throws IOException {
extractLargeBinaries(theTransactionDetails, theResource, thePointcut);
}
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED)
public void extractLargeBinariesBeforeUpdate(ServletRequestDetails theRequestDetails, IBaseResource thePreviousResource, IBaseResource theResource, Pointcut thePointcut) throws IOException {
public void extractLargeBinariesBeforeUpdate(TransactionDetails theTransactionDetails, IBaseResource thePreviousResource, IBaseResource theResource, Pointcut thePointcut) throws IOException {
blockIllegalExternalBinaryIds(thePreviousResource, theResource);
extractLargeBinaries(theRequestDetails, theResource, thePointcut);
extractLargeBinaries(theTransactionDetails, theResource, thePointcut);
}
/**
@ -136,7 +146,7 @@ public class BinaryStorageInterceptor {
.stream()
.filter(t -> t.getUserData(JpaConstants.EXTENSION_EXT_SYSTEMDEFINED) == null)
.filter(t -> EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
.map(t -> (IPrimitiveType) t.getValue())
.map(t -> (IPrimitiveType<?>) t.getValue())
.map(t -> t.getValueAsString())
.filter(t -> isNotBlank(t))
.forEach(t -> existingBinaryIds.add(t));
@ -152,12 +162,11 @@ public class BinaryStorageInterceptor {
.stream()
.filter(t -> t.getUserData(JpaConstants.EXTENSION_EXT_SYSTEMDEFINED) == null)
.filter(t -> t.getUrl().equals(EXT_EXTERNALIZED_BINARY_ID))
.map(t->(IPrimitiveType) t.getValue())
.map(t->t.getValueAsString())
.filter(t->isNotBlank(t))
.filter(t->{
return !existingBinaryIds.contains(t);
}).findFirst();
.map(t -> (IPrimitiveType<?>) t.getValue())
.map(t -> t.getValueAsString())
.filter(t -> isNotBlank(t))
.filter(t -> !existingBinaryIds.contains(t))
.findFirst();
if (hasExternalizedBinaryReference.isPresent()) {
String msg = myCtx.getLocalizer().getMessage(BaseHapiFhirDao.class, "externalizedBinaryStorageExtensionFoundInRequestBody", EXT_EXTERNALIZED_BINARY_ID, hasExternalizedBinaryReference.get());
@ -168,11 +177,8 @@ public class BinaryStorageInterceptor {
}
private void extractLargeBinaries(ServletRequestDetails theRequestDetails, IBaseResource theResource, Pointcut thePointcut) throws IOException {
if (theRequestDetails == null) {
// RequestDetails will only be null for internal HAPI events. If externalization is required for them it will need to be done in a different way.
return;
}
private void extractLargeBinaries(TransactionDetails theTransactionDetails, IBaseResource theResource, Pointcut thePointcut) throws IOException {
IIdType resourceId = theResource.getIdElement();
if (!resourceId.hasResourceType() && resourceId.hasIdPart()) {
String resourceType = myCtx.getResourceDefinition(theResource).getName();
@ -197,7 +203,7 @@ public class BinaryStorageInterceptor {
} else {
assert thePointcut == Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED : thePointcut.name();
newBlobId = myBinaryStorageSvc.newBlobId();
List<DeferredBinaryTarget> deferredBinaryTargets = getOrCreateDeferredBinaryStorageMap(theRequestDetails);
List<DeferredBinaryTarget> deferredBinaryTargets = getOrCreateDeferredBinaryStorageMap(theTransactionDetails);
DeferredBinaryTarget newDeferredBinaryTarget = new DeferredBinaryTarget(newBlobId, nextTarget, data);
deferredBinaryTargets.add(newDeferredBinaryTarget);
}
@ -207,26 +213,20 @@ public class BinaryStorageInterceptor {
}
}
}
@Nonnull
@SuppressWarnings("unchecked")
private List<DeferredBinaryTarget> getOrCreateDeferredBinaryStorageMap(ServletRequestDetails theRequestDetails) {
List<DeferredBinaryTarget> deferredBinaryTargets = (List<DeferredBinaryTarget>) theRequestDetails.getUserData().get(getDeferredListKey());
if (deferredBinaryTargets == null) {
deferredBinaryTargets = new ArrayList<>();
theRequestDetails.getUserData().put(getDeferredListKey(), deferredBinaryTargets);
}
return deferredBinaryTargets;
private List<DeferredBinaryTarget> getOrCreateDeferredBinaryStorageMap(TransactionDetails theTransactionDetails) {
return theTransactionDetails.getOrCreateUserData(getDeferredListKey(), () -> new ArrayList<>());
}
@SuppressWarnings("unchecked")
@Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED)
public void storeLargeBinariesBeforeCreatePersistence(ServletRequestDetails theRequestDetails, IBaseResource theResource, Pointcut thePoincut) throws IOException {
if (theRequestDetails == null) {
public void storeLargeBinariesBeforeCreatePersistence(TransactionDetails theTransactionDetails, IBaseResource theResource, Pointcut thePoincut) throws IOException {
if (theTransactionDetails == null) {
return;
}
List<DeferredBinaryTarget> deferredBinaryTargets = (List<DeferredBinaryTarget>) theRequestDetails.getUserData().get(getDeferredListKey());
List<DeferredBinaryTarget> deferredBinaryTargets = theTransactionDetails.getUserData(getDeferredListKey());
if (deferredBinaryTargets != null) {
IIdType resourceId = theResource.getIdElement();
for (DeferredBinaryTarget next : deferredBinaryTargets) {
@ -284,9 +284,7 @@ public class BinaryStorageInterceptor {
if (theElement.getClass().equals(myBinaryType)) {
IBase parent = theContainingElementPath.get(theContainingElementPath.size() - 2);
Optional<IBinaryTarget> binaryTarget = myBinaryAccessProvider.toBinaryTarget(parent);
if (binaryTarget.isPresent()) {
binaryTargets.add(binaryTarget.get());
}
binaryTarget.ifPresent(binaryTargets::add);
}
return true;
}

View File

@ -34,7 +34,7 @@ import ca.uhn.fhir.jpa.dao.data.IBulkExportJobDao;
import ca.uhn.fhir.jpa.entity.BulkExportCollectionEntity;
import ca.uhn.fhir.jpa.entity.BulkExportCollectionFileEntity;
import ca.uhn.fhir.jpa.entity.BulkExportJobEntity;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;

View File

@ -47,6 +47,7 @@ import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProviderFactory;
@ -970,16 +971,16 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
return myContext.getResourceDefinition(theResource).getName();
}
protected ResourceTable updateEntityForDelete(RequestDetails theRequest, ResourceTable entity) {
protected ResourceTable updateEntityForDelete(RequestDetails theRequest, TransactionDetails theTransactionDetails, ResourceTable entity) {
Date updateTime = new Date();
return updateEntity(theRequest, null, entity, updateTime, true, true, updateTime, false, true);
return updateEntity(theRequest, null, entity, updateTime, true, true, theTransactionDetails, false, true);
}
@SuppressWarnings("unchecked")
@Override
public ResourceTable updateEntity(RequestDetails theRequest, final IBaseResource theResource, IBasePersistedResource
theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
Validate.notNull(theEntity);
Validate.isTrue(theDeletedTimestampOrNull != null || theResource != null, "Must have either a resource[%s] or a deleted timestamp[%s] for resource PID[%s]", theDeletedTimestampOrNull != null, theResource != null, theEntity.getPersistentId());
@ -1004,9 +1005,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
}
if (entity.getPublished() == null) {
ourLog.debug("Entity has published time: {}", new InstantDt(theUpdateTime));
entity.setPublished(theUpdateTime);
ourLog.debug("Entity has published time: {}", theTransactionDetails.getTransactionDate());
entity.setPublished(theTransactionDetails.getTransactionDate());
}
ResourceIndexedSearchParams existingParams = null;
@ -1033,11 +1033,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
if (thePerformIndexing) {
newParams = new ResourceIndexedSearchParams();
mySearchParamWithInlineReferencesExtractor.populateFromResource(newParams, theUpdateTime, entity, theResource, existingParams, theRequest);
mySearchParamWithInlineReferencesExtractor.populateFromResource(newParams, theTransactionDetails, entity, theResource, existingParams, theRequest);
changed = populateResourceIntoEntity(theRequest, theResource, entity, true);
if (changed.isChanged()) {
entity.setUpdated(theUpdateTime);
entity.setUpdated(theTransactionDetails.getTransactionDate());
if (theResource instanceof IResource) {
entity.setLanguage(((IResource) theResource).getLanguage().getValue());
} else {
@ -1052,7 +1052,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
changed = populateResourceIntoEntity(theRequest, theResource, entity, false);
entity.setUpdated(theUpdateTime);
entity.setUpdated(theTransactionDetails.getTransactionDate());
entity.setIndexStatus(null);
}
@ -1120,7 +1120,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
.stream()
.filter(t -> Constants.EXT_META_SOURCE.equals(t.getUrl()))
.filter(t -> t.getValue() instanceof IPrimitiveType)
.map(t -> ((IPrimitiveType) t.getValue()).getValueAsString())
.map(t -> ((IPrimitiveType<?>) t.getValue()).getValueAsString())
.findFirst()
.orElse(null);
}
@ -1221,7 +1221,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
@Override
public ResourceTable updateInternal(RequestDetails theRequestDetails, T theResource, boolean thePerformIndexing, boolean theForceUpdateVersion,
IBasePersistedResource theEntity2, IIdType theResourceId, IBaseResource theOldResource) {
IBasePersistedResource theEntity2, IIdType theResourceId, IBaseResource theOldResource, TransactionDetails theTransactionDetails) {
ResourceTable entity = (ResourceTable) theEntity2;
@ -1241,11 +1241,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
.add(IBaseResource.class, theOldResource)
.add(IBaseResource.class, theResource)
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(TransactionDetails.class, theTransactionDetails);
doCallHooks(theRequestDetails, Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, hookParams);
// Perform update
ResourceTable savedEntity = updateEntity(theRequestDetails, theResource, entity, null, thePerformIndexing, thePerformIndexing, new Date(), theForceUpdateVersion, thePerformIndexing);
ResourceTable savedEntity = updateEntity(theRequestDetails, theResource, entity, null, thePerformIndexing, thePerformIndexing, theTransactionDetails, theForceUpdateVersion, thePerformIndexing);
/*
* If we aren't indexing (meaning we're probably executing a sub-operation within a transaction),
@ -1274,7 +1275,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
.add(IBaseResource.class, theOldResource)
.add(IBaseResource.class, theResource)
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(TransactionDetails.class, theTransactionDetails);
doCallHooks(theRequestDetails, Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, hookParams);
}
});

View File

@ -35,7 +35,7 @@ import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
import ca.uhn.fhir.jpa.model.entity.BaseTag;
import ca.uhn.fhir.jpa.model.entity.ForcedId;
@ -45,6 +45,7 @@ import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
@ -181,12 +182,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public DaoMethodOutcome create(final T theResource) {
return create(theResource, null, true, new Date(), null);
return create(theResource, null, true, new TransactionDetails(), null);
}
@Override
public DaoMethodOutcome create(final T theResource, RequestDetails theRequestDetails) {
return create(theResource, null, true, new Date(), theRequestDetails);
return create(theResource, null, true, new TransactionDetails(), theRequestDetails);
}
@Override
@ -195,7 +196,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
@Override
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTimestamp, RequestDetails theRequestDetails) {
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, TransactionDetails theTransactionDetails, RequestDetails theRequestDetails) {
if (theResource == null) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "missingBody");
throw new InvalidRequestException(msg);
@ -217,12 +218,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineCreatePartitionForRequest(theRequestDetails, theResource, getResourceName());
return doCreate(theResource, theIfNoneExist, thePerformIndexing, theUpdateTimestamp, theRequestDetails, requestPartitionId);
return doCreate(theResource, theIfNoneExist, thePerformIndexing, theTransactionDetails, theRequestDetails, requestPartitionId);
}
@Override
public DaoMethodOutcome create(final T theResource, String theIfNoneExist, RequestDetails theRequestDetails) {
return create(theResource, theIfNoneExist, true, new Date(), theRequestDetails);
return create(theResource, theIfNoneExist, true, new TransactionDetails(), theRequestDetails);
}
private IInstanceValidatorModule getInstanceValidator() {
@ -235,7 +236,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
@Override
public DaoMethodOutcome delete(IIdType theId, DeleteConflictList theDeleteConflicts, RequestDetails theRequest) {
public DaoMethodOutcome delete(IIdType theId, DeleteConflictList theDeleteConflicts, RequestDetails theRequest, TransactionDetails theTransactionDetails) {
validateIdPresentForDelete(theId);
validateDeleteEnabled();
@ -272,10 +273,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
HookParams hook = new HookParams()
.add(IBaseResource.class, resourceToDelete)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(TransactionDetails.class, theTransactionDetails);
doCallHooks(theRequest, Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, hook);
myDeleteConflictService.validateOkToDelete(theDeleteConflicts, entity, false, theRequest);
myDeleteConflictService.validateOkToDelete(theDeleteConflicts, entity, false, theRequest, theTransactionDetails);
preDelete(resourceToDelete, entity);
@ -285,7 +287,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
}
ResourceTable savedEntity = updateEntityForDelete(theRequest, entity);
ResourceTable savedEntity = updateEntityForDelete(theRequest, theTransactionDetails, entity);
resourceToDelete.setId(entity.getIdDt());
// Notify JPA interceptors
@ -295,7 +297,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
HookParams hookParams = new HookParams()
.add(IBaseResource.class, resourceToDelete)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(TransactionDetails.class, theTransactionDetails);
doCallHooks(theRequest, Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
}
});
@ -324,7 +327,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
StopWatch w = new StopWatch();
DaoMethodOutcome retVal = delete(theId, deleteConflicts, theRequestDetails);
DaoMethodOutcome retVal = delete(theId, deleteConflicts, theRequestDetails, new TransactionDetails());
DeleteConflictService.validateDeleteConflictsEmptyOrThrowException(getContext(), deleteConflicts);
@ -349,6 +352,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
}
TransactionDetails transactionDetails = new TransactionDetails();
List<ResourceTable> deletedResources = new ArrayList<>();
for (ResourcePersistentId pid : resourceIds) {
ResourceTable entity = myEntityManager.find(ResourceTable.class, pid.getId());
@ -360,10 +364,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
HookParams hooks = new HookParams()
.add(IBaseResource.class, resourceToDelete)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(TransactionDetails.class, transactionDetails);
doCallHooks(theRequest, Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, hooks);
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, false, theRequest);
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, false, theRequest, transactionDetails);
// Notify interceptors
IdDt idToDelete = entity.getIdDt();
@ -374,7 +379,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
// Perform delete
updateEntityForDelete(theRequest, entity);
updateEntityForDelete(theRequest, transactionDetails, entity);
resourceToDelete.setId(entity.getIdDt());
// Notify JPA interceptors
@ -384,7 +389,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
HookParams hookParams = new HookParams()
.add(IBaseResource.class, resourceToDelete)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(TransactionDetails.class, transactionDetails);
doCallHooks(theRequest, Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
}
});
@ -446,7 +452,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
}
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTime, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing, TransactionDetails theTransactionDetails, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
StopWatch w = new StopWatch();
preProcessResourceForStorage(theResource);
@ -506,11 +512,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
HookParams hookParams = new HookParams()
.add(IBaseResource.class, theResource)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(TransactionDetails.class, theTransactionDetails);
doCallHooks(theRequest, Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, hookParams);
// Perform actual DB update
ResourceTable updatedEntity = updateEntity(theRequest, theResource, entity, null, thePerformIndexing, thePerformIndexing, theUpdateTime, false, thePerformIndexing);
ResourceTable updatedEntity = updateEntity(theRequest, theResource, entity, null, thePerformIndexing, thePerformIndexing, theTransactionDetails, false, thePerformIndexing);
theResource.setId(entity.getIdDt());
if (serverAssignedId) {
@ -551,7 +558,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
HookParams hookParams = new HookParams()
.add(IBaseResource.class, theResource)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(TransactionDetails.class, theTransactionDetails);
doCallHooks(theRequest, Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, hookParams);
}
});
@ -1083,7 +1091,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (theResource != null) {
CURRENTLY_REINDEXING.put(theResource, Boolean.TRUE);
}
updateEntity(null, theResource, theEntity, theEntity.getDeleted(), true, false, theEntity.getUpdatedDate(), true, false);
TransactionDetails transactionDetails = new TransactionDetails(theEntity.getUpdatedDate());
updateEntity(null, theResource, theEntity, theEntity.getDeleted(), true, false, transactionDetails, true, false);
if (theResource != null) {
CURRENTLY_REINDEXING.put(theResource, null);
}
@ -1272,11 +1282,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, RequestDetails theRequestDetails) {
return update(theResource, theMatchUrl, thePerformIndexing, false, theRequestDetails);
return update(theResource, theMatchUrl, thePerformIndexing, false, theRequestDetails, new TransactionDetails());
}
@Override
public DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequest) {
public DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequest, TransactionDetails theTransactionDetails) {
if (theResource == null) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "missingBody");
throw new InvalidRequestException(msg);
@ -1299,7 +1309,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
entity = myEntityManager.find(ResourceTable.class, pid.getId());
resourceId = entity.getIdDt();
} else {
return create(theResource, null, thePerformIndexing, new Date(), theRequest);
return create(theResource, null, thePerformIndexing, theTransactionDetails, theRequest);
}
} else {
/*
@ -1313,7 +1323,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
try {
entity = readEntityLatestVersion(resourceId, requestPartitionId);
} catch (ResourceNotFoundException e) {
return doCreate(theResource, null, thePerformIndexing, new Date(), theRequest, requestPartitionId);
return doCreate(theResource, null, thePerformIndexing, theTransactionDetails, theRequest, requestPartitionId);
}
}
@ -1358,7 +1368,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
/*
* Otherwise, we're not in a transaction
*/
ResourceTable savedEntity = updateInternal(theRequest, theResource, thePerformIndexing, theForceUpdateVersion, entity, resourceId, oldResource);
ResourceTable savedEntity = updateInternal(theRequest, theResource, thePerformIndexing, theForceUpdateVersion, entity, resourceId, oldResource, theTransactionDetails);
DaoMethodOutcome outcome = toMethodOutcome(theRequest, savedEntity, theResource).setCreated(wasDeleted);
if (!thePerformIndexing) {
@ -1389,7 +1399,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
// would prevent deletion
DeleteConflictList deleteConflicts = new DeleteConflictList();
if (myDaoConfig.isEnforceReferentialIntegrityOnDelete()) {
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, true, theRequest);
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, true, theRequest, new TransactionDetails());
}
DeleteConflictService.validateDeleteConflictsEmptyOrThrowException(getContext(), deleteConflicts);

View File

@ -171,10 +171,6 @@ public abstract class BaseStorageDao {
JpaInterceptorBroadcaster.doCallHooks(getInterceptorBroadcaster(), theRequestDetails, thePointcut, theParams);
}
protected Object doCallHooksAndReturnObject(RequestDetails theRequestDetails, Pointcut thePointcut, HookParams theParams) {
return JpaInterceptorBroadcaster.doCallHooksAndReturnObject(getInterceptorBroadcaster(), theRequestDetails, thePointcut, theParams);
}
protected abstract IInterceptorBroadcaster getInterceptorBroadcaster();
public IBaseOperationOutcome createErrorOperationOutcome(String theMessage, String theCode) {

View File

@ -32,9 +32,10 @@ import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
@ -84,7 +85,18 @@ import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
@ -327,7 +339,7 @@ public abstract class BaseTransactionProcessor {
ourLog.debug("Beginning {} with {} resources", theActionName, myVersionAdapter.getEntries(theRequest).size());
final Date updateTime = new Date();
final TransactionDetails transactionDetails = new TransactionDetails();
final StopWatch transactionStopWatch = new StopWatch();
final Set<IIdType> allIds = new LinkedHashSet<>();
@ -391,7 +403,7 @@ public abstract class BaseTransactionProcessor {
*/
TransactionTemplate txManager = new TransactionTemplate(myTxManager);
Map<IBase, IBasePersistedResource> entriesToProcess = txManager.execute(status -> {
Map<IBase, IBasePersistedResource> retVal = doTransactionWriteOperations(theRequestDetails, theActionName, updateTime, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, entries, transactionStopWatch);
Map<IBase, IBasePersistedResource> retVal = doTransactionWriteOperations(theRequestDetails, theActionName, transactionDetails, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, entries, transactionStopWatch);
transactionStopWatch.startTask("Commit writes to database");
return retVal;
@ -508,7 +520,7 @@ public abstract class BaseTransactionProcessor {
}
private Map<IBase, IBasePersistedResource> doTransactionWriteOperations(final ServletRequestDetails theRequest, String theActionName, Date theUpdateTime, Set<IIdType> theAllIds,
private Map<IBase, IBasePersistedResource> doTransactionWriteOperations(final ServletRequestDetails theRequest, String theActionName, TransactionDetails theTransactionDetails, Set<IIdType> theAllIds,
Map<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome, IBaseBundle theResponse, IdentityHashMap<IBase, Integer> theOriginalRequestOrder, List<IBase> theEntries, StopWatch theTransactionStopWatch) {
if (theRequest != null) {
@ -594,7 +606,7 @@ public abstract class BaseTransactionProcessor {
for (int i = 0; i < theEntries.size(); i++) {
if (i % 250 == 0) {
ourLog.info("Processed {} non-GET entries out of {} in transaction", i, theEntries.size());
ourLog.debug("Processed {} non-GET entries out of {} in transaction", i, theEntries.size());
}
IBase nextReqEntry = theEntries.get(i);
@ -652,7 +664,7 @@ public abstract class BaseTransactionProcessor {
DaoMethodOutcome outcome;
String matchUrl = myVersionAdapter.getEntryRequestIfNoneExist(nextReqEntry);
matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl);
outcome = resourceDao.create(res, matchUrl, false, theUpdateTime, theRequest);
outcome = resourceDao.create(res, matchUrl, false, theTransactionDetails, theRequest);
if (nextResourceId != null) {
handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequest);
}
@ -676,7 +688,7 @@ public abstract class BaseTransactionProcessor {
if (parts.getResourceId() != null) {
IIdType deleteId = newIdType(parts.getResourceType(), parts.getResourceId());
if (!deletedResources.contains(deleteId.getValueAsString())) {
DaoMethodOutcome outcome = dao.delete(deleteId, deleteConflicts, theRequest);
DaoMethodOutcome outcome = dao.delete(deleteId, deleteConflicts, theRequest, theTransactionDetails);
if (outcome.getEntity() != null) {
deletedResources.add(deleteId.getValueAsString());
entriesToProcess.put(nextRespEntry, outcome.getEntity());
@ -717,7 +729,7 @@ public abstract class BaseTransactionProcessor {
version = ParameterUtil.parseETagValue(myVersionAdapter.getEntryRequestIfMatch(nextReqEntry));
}
res.setId(newIdType(parts.getResourceType(), parts.getResourceId(), version));
outcome = resourceDao.update(res, null, false, false, theRequest);
outcome = resourceDao.update(res, null, false, false, theRequest, theTransactionDetails);
} else {
res.setId((String) null);
String matchUrl;
@ -727,7 +739,7 @@ public abstract class BaseTransactionProcessor {
matchUrl = parts.getResourceType();
}
matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl);
outcome = resourceDao.update(res, matchUrl, false, false, theRequest);
outcome = resourceDao.update(res, matchUrl, false, false, theRequest, theTransactionDetails);
if (Boolean.TRUE.equals(outcome.getCreated())) {
conditionalRequestUrls.put(matchUrl, res.getClass());
}
@ -839,6 +851,8 @@ public abstract class BaseTransactionProcessor {
}
DeleteConflictService.validateDeleteConflictsEmptyOrThrowException(myContext, deleteConflicts);
theIdToPersistedOutcome.entrySet().forEach(t -> theTransactionDetails.addResolvedResourceId(t.getKey(), t.getValue().getEntity().getPersistentId()));
/*
* Perform ID substitutions and then index each resource we have saved
*/
@ -849,7 +863,7 @@ public abstract class BaseTransactionProcessor {
for (DaoMethodOutcome nextOutcome : theIdToPersistedOutcome.values()) {
if (i++ % 250 == 0) {
ourLog.info("Have indexed {} entities out of {} in transaction", i, theIdToPersistedOutcome.values().size());
ourLog.debug("Have indexed {} entities out of {} in transaction", i, theIdToPersistedOutcome.values().size());
}
IBaseResource nextResource = nextOutcome.getResource();
@ -899,9 +913,9 @@ public abstract class BaseTransactionProcessor {
IJpaDao jpaDao = (IJpaDao) dao;
if (updatedEntities.contains(nextOutcome.getEntity())) {
jpaDao.updateInternal(theRequest, nextResource, true, false, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
jpaDao.updateInternal(theRequest, nextResource, true, false, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource(), theTransactionDetails);
} else if (!nonUpdatedEntities.contains(nextOutcome.getEntity())) {
jpaDao.updateEntity(theRequest, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true);
jpaDao.updateEntity(theRequest, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theTransactionDetails, false, true);
}
}

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.dao.data.ISubscriptionTableDao;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -70,8 +71,8 @@ public class FhirResourceDaoSubscriptionDstu2 extends BaseHapiFhirResourceDao<Su
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (theDeletedTimestampOrNull != null) {
mySubscriptionTableDao.deleteAllForSubscription((ResourceTable) theEntity);

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;

View File

@ -28,9 +28,10 @@ import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
@ -194,7 +195,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size());
long start = System.currentTimeMillis();
Date updateTime = new Date();
TransactionDetails transactionDetails = new TransactionDetails();
Set<IdDt> allIds = new LinkedHashSet<IdDt>();
Map<IdDt, IdDt> idSubstitutions = new HashMap<IdDt, IdDt>();
@ -233,7 +234,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
*/
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.execute(t->{
handleTransactionWriteOperations(theRequestDetails, theRequest, theActionName, updateTime, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, deletedResources, deleteConflicts, entriesToProcess, nonUpdatedEntities, updatedEntities);
handleTransactionWriteOperations(theRequestDetails, theRequest, theActionName, transactionDetails, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, deletedResources, deleteConflicts, entriesToProcess, nonUpdatedEntities, updatedEntities);
return null;
});
@ -318,7 +319,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
return response;
}
private void handleTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date theUpdateTime, Set<IdDt> theAllIds, Map<IdDt, IdDt> theIdSubstitutions, Map<IdDt, DaoMethodOutcome> theIdToPersistedOutcome, Bundle theResponse, IdentityHashMap<Entry, Integer> theOriginalRequestOrder, List<IIdType> theDeletedResources, DeleteConflictList theDeleteConflicts, Map<Entry, IBasePersistedResource> theEntriesToProcess, Set<IBasePersistedResource> theNonUpdatedEntities, Set<IBasePersistedResource> theUpdatedEntities) {
private void handleTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, TransactionDetails theTransactionDetails, Set<IdDt> theAllIds, Map<IdDt, IdDt> theIdSubstitutions, Map<IdDt, DaoMethodOutcome> theIdToPersistedOutcome, Bundle theResponse, IdentityHashMap<Entry, Integer> theOriginalRequestOrder, List<IIdType> theDeletedResources, DeleteConflictList theDeleteConflicts, Map<Entry, IBasePersistedResource> theEntriesToProcess, Set<IBasePersistedResource> theNonUpdatedEntities, Set<IBasePersistedResource> theUpdatedEntities) {
/*
* Loop through the request and process any entries of type
* PUT, POST or DELETE
@ -382,7 +383,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(res.getClass());
res.setId((String) null);
DaoMethodOutcome outcome;
outcome = resourceDao.create(res, nextReqEntry.getRequest().getIfNoneExist(), false, theUpdateTime, theRequestDetails);
outcome = resourceDao.create(res, nextReqEntry.getRequest().getIfNoneExist(), false, theTransactionDetails, theRequestDetails);
handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
theEntriesToProcess.put(nextRespEntry, outcome.getEntity());
if (outcome.getCreated() == false) {
@ -397,7 +398,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
IFhirResourceDao<? extends IBaseResource> dao = toDao(parts, verb.getCode(), url);
int status = Constants.STATUS_HTTP_204_NO_CONTENT;
if (parts.getResourceId() != null) {
DaoMethodOutcome outcome = dao.delete(new IdDt(parts.getResourceType(), parts.getResourceId()), theDeleteConflicts, theRequestDetails);
DaoMethodOutcome outcome = dao.delete(new IdDt(parts.getResourceType(), parts.getResourceId()), theDeleteConflicts, theRequestDetails, theTransactionDetails);
if (outcome.getEntity() != null) {
theDeletedResources.add(outcome.getId().toUnqualifiedVersionless());
theEntriesToProcess.put(nextRespEntry, outcome.getEntity());
@ -504,9 +505,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
if (theUpdatedEntities.contains(nextOutcome.getEntity())) {
updateInternal(theRequestDetails, nextResource, true, false, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
updateInternal(theRequestDetails, nextResource, true, false, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource(), theTransactionDetails);
} else if (!theNonUpdatedEntities.contains(nextOutcome.getEntity())) {
updateEntity(theRequestDetails, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true);
updateEntity(theRequestDetails, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theTransactionDetails, false, true);
}
}

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.jpa.dao;
import java.util.List;
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.RequestDetails;

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import java.io.Closeable;
import java.util.Iterator;

View File

@ -22,7 +22,7 @@ package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.Include;

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;

View File

@ -41,7 +41,7 @@ import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;

View File

@ -27,8 +27,9 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
@ -139,8 +140,8 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem csDstu3 = (CodeSystem) theResource;

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -164,8 +165,8 @@ public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao<Conc
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.dao.data.ISubscriptionTableDao;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.dstu3.model.Subscription;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -67,8 +68,8 @@ public class FhirResourceDaoSubscriptionDstu3 extends BaseHapiFhirResourceDao<Su
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (theDeletedTimestampOrNull != null) {
mySubscriptionTableDao.deleteAllForSubscription((ResourceTable) theEntity);

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -373,8 +374,8 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao<ValueS
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
import ca.uhn.fhir.jpa.model.cross.ResourceLookup;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ForcedId;
import ca.uhn.fhir.jpa.util.QueryChunker;
import ca.uhn.fhir.model.primitive.IdDt;
@ -364,20 +364,42 @@ public class IdHelperService {
}
private void resolvePids(@Nonnull RequestPartitionId theRequestPartitionId, List<Long> thePidsToResolve, List<IResourceLookup> theTarget) {
Collection<Object[]> lookup;
if (theRequestPartitionId.isAllPartitions()) {
lookup = myResourceTableDao.findLookupFieldsByResourcePid(thePidsToResolve);
} else {
if (theRequestPartitionId.getPartitionId() != null) {
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartition(thePidsToResolve, theRequestPartitionId.getPartitionId());
} else {
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionNull(thePidsToResolve);
if (!myDaoConfig.isDeleteEnabled()) {
for (Iterator<Long> forcedIdIterator = thePidsToResolve.iterator(); forcedIdIterator.hasNext(); ) {
Long nextPid = forcedIdIterator.next();
String nextKey = Long.toString(nextPid);
IResourceLookup cachedLookup = myResourceLookupCache.getIfPresent(nextKey);
if (cachedLookup != null) {
forcedIdIterator.remove();
theTarget.add(cachedLookup);
}
}
}
lookup
.stream()
.map(t -> new ResourceLookup((String) t[0], (Long) t[1], (Date) t[2]))
.forEach(theTarget::add);
if (thePidsToResolve.size() > 0) {
Collection<Object[]> lookup;
if (theRequestPartitionId.isAllPartitions()) {
lookup = myResourceTableDao.findLookupFieldsByResourcePid(thePidsToResolve);
} else {
if (theRequestPartitionId.getPartitionId() != null) {
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartition(thePidsToResolve, theRequestPartitionId.getPartitionId());
} else {
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionNull(thePidsToResolve);
}
}
lookup
.stream()
.map(t -> new ResourceLookup((String) t[0], (Long) t[1], (Date) t[2]))
.forEach(t->{
theTarget.add(t);
if (!myDaoConfig.isDeleteEnabled()) {
String nextKey = Long.toString(t.getResourceId());
myResourceLookupCache.put(nextKey, t);
}
});
}
}
public void clearCache() {

View File

@ -25,11 +25,12 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.MatchResourceUrlService;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
@ -57,7 +58,6 @@ import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@ -94,7 +94,7 @@ public class SearchParamWithInlineReferencesExtractor {
@Autowired
private PartitionSettings myPartitionSettings;
public void populateFromResource(ResourceIndexedSearchParams theParams, Date theUpdateTime, ResourceTable theEntity, IBaseResource theResource, ResourceIndexedSearchParams theExistingParams, RequestDetails theRequest) {
public void populateFromResource(ResourceIndexedSearchParams theParams, TransactionDetails theTransactionDetails, ResourceTable theEntity, IBaseResource theResource, ResourceIndexedSearchParams theExistingParams, RequestDetails theRequest) {
extractInlineReferences(theResource, theRequest);
RequestPartitionId partitionId;
@ -104,7 +104,7 @@ public class SearchParamWithInlineReferencesExtractor {
partitionId = RequestPartitionId.allPartitions();
}
mySearchParamExtractorService.extractFromResource(partitionId, theRequest, theParams, theEntity, theResource, theUpdateTime, true);
mySearchParamExtractorService.extractFromResource(partitionId, theRequest, theParams, theEntity, theResource, theTransactionDetails, true);
Set<Map.Entry<String, RuntimeSearchParam>> activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType()).entrySet();
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.ENABLED) {

View File

@ -38,7 +38,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;

View File

@ -27,8 +27,9 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
@ -137,8 +138,8 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem cs = (CodeSystem) theResource;

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -164,8 +165,8 @@ public class FhirResourceDaoConceptMapR4 extends BaseHapiFhirResourceDao<Concept
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.dao.data.ISubscriptionTableDao;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -67,8 +68,8 @@ public class FhirResourceDaoSubscriptionR4 extends BaseHapiFhirResourceDao<Subsc
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (theDeletedTimestampOrNull != null) {
Long subscriptionId = getSubscriptionTablePidForSubscriptionResource(theEntity.getIdDt(), theRequest);

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -352,8 +353,8 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao<ValueSet>
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {

View File

@ -28,8 +28,9 @@ import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
@ -139,8 +140,8 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem cs = (CodeSystem) theResource;

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.convertors.VersionConvertor_40_50;
@ -165,8 +166,8 @@ public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao<Concept
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.dao.data.ISubscriptionTableDao;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -67,8 +68,8 @@ public class FhirResourceDaoSubscriptionR5 extends BaseHapiFhirResourceDao<Subsc
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (theDeletedTimestampOrNull != null) {
Long subscriptionId = getSubscriptionTablePidForSubscriptionResource(theEntity.getIdDt(), theRequest);

View File

@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -354,8 +355,8 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao<ValueSet>
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -64,7 +65,7 @@ public class DeleteConflictService {
@Autowired
protected IInterceptorBroadcaster myInterceptorBroadcaster;
public int validateOkToDelete(DeleteConflictList theDeleteConflicts, ResourceTable theEntity, boolean theForValidate, RequestDetails theRequest) {
public int validateOkToDelete(DeleteConflictList theDeleteConflicts, ResourceTable theEntity, boolean theForValidate, RequestDetails theRequest, TransactionDetails theTransactionDetails) {
// We want the list of resources that are marked to be the same list even as we
// drill into conflict resolution stacks.. this allows us to not get caught by
@ -74,30 +75,30 @@ public class DeleteConflictService {
// In most cases, there will be no hooks, and so we only need to check if there is at least FIRST_QUERY_RESULT_COUNT conflict and populate that.
// Only in the case where there is a hook do we need to go back and collect larger batches of conflicts for processing.
DeleteConflictOutcome outcome = findAndHandleConflicts(theRequest, newConflicts, theEntity, theForValidate, FIRST_QUERY_RESULT_COUNT);
DeleteConflictOutcome outcome = findAndHandleConflicts(theRequest, newConflicts, theEntity, theForValidate, FIRST_QUERY_RESULT_COUNT, theTransactionDetails);
int retryCount = 0;
while (outcome != null) {
int shouldRetryCount = Math.min(outcome.getShouldRetryCount(), MAX_RETRY_ATTEMPTS);
if (!(retryCount < shouldRetryCount)) break;
newConflicts = new DeleteConflictList();
outcome = findAndHandleConflicts(theRequest, newConflicts, theEntity, theForValidate, RETRY_QUERY_RESULT_COUNT);
outcome = findAndHandleConflicts(theRequest, newConflicts, theEntity, theForValidate, RETRY_QUERY_RESULT_COUNT, theTransactionDetails);
++retryCount;
}
theDeleteConflicts.addAll(newConflicts);
return retryCount;
}
private DeleteConflictOutcome findAndHandleConflicts(RequestDetails theRequest, DeleteConflictList theDeleteConflicts, ResourceTable theEntity, boolean theForValidate, int theMinQueryResultCount) {
private DeleteConflictOutcome findAndHandleConflicts(RequestDetails theRequest, DeleteConflictList theDeleteConflicts, ResourceTable theEntity, boolean theForValidate, int theMinQueryResultCount, TransactionDetails theTransactionDetails) {
List<ResourceLink> resultList = myDeleteConflictFinderService.findConflicts(theEntity, theMinQueryResultCount);
if (resultList.isEmpty()) {
return null;
}
return handleConflicts(theRequest, theDeleteConflicts, theEntity, theForValidate, resultList);
return handleConflicts(theRequest, theDeleteConflicts, theEntity, theForValidate, resultList, theTransactionDetails);
}
private DeleteConflictOutcome handleConflicts(RequestDetails theRequest, DeleteConflictList theDeleteConflicts, ResourceTable theEntity, boolean theForValidate, List<ResourceLink> theResultList) {
private DeleteConflictOutcome handleConflicts(RequestDetails theRequest, DeleteConflictList theDeleteConflicts, ResourceTable theEntity, boolean theForValidate, List<ResourceLink> theResultList, TransactionDetails theTransactionDetails) {
if (!myDaoConfig.isEnforceReferentialIntegrityOnDelete() && !theForValidate) {
ourLog.debug("Deleting {} resource dependencies which can no longer be satisfied", theResultList.size());
myResourceLinkDao.deleteAll(theResultList);
@ -114,7 +115,8 @@ public class DeleteConflictService {
HookParams hooks = new HookParams()
.add(DeleteConflictList.class, theDeleteConflicts)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(TransactionDetails.class, theTransactionDetails);
return (DeleteConflictOutcome)JpaInterceptorBroadcaster.doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, hooks);
}

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.delete.DeleteConflictOutcome;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.DeleteCascadeModeEnum;
@ -94,7 +95,7 @@ public class CascadingDeleteInterceptor {
}
@Hook(Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS)
public DeleteConflictOutcome handleDeleteConflicts(DeleteConflictList theConflictList, RequestDetails theRequest) {
public DeleteConflictOutcome handleDeleteConflicts(DeleteConflictList theConflictList, RequestDetails theRequest, TransactionDetails theTransactionDetails) {
ourLog.debug("Have delete conflicts: {}", theConflictList);
if (shouldCascade(theRequest) == DeleteCascadeModeEnum.NONE) {
@ -130,7 +131,7 @@ public class CascadingDeleteInterceptor {
// Actually perform the delete
ourLog.info("Have delete conflict {} - Cascading delete", next);
dao.delete(nextSource, theConflictList, theRequest);
dao.delete(nextSource, theConflictList, theRequest, theTransactionDetails);
cascadedDeletes.add(nextSourceId);

View File

@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.interceptor;
*/
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails;
import ca.uhn.fhir.util.ICallable;
import org.hl7.fhir.instance.model.api.IBaseResource;

View File

@ -34,7 +34,7 @@ import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc;

View File

@ -22,22 +22,17 @@ package ca.uhn.fhir.jpa.search;
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl.SearchTask;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import com.google.common.annotations.VisibleForTesting;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;

View File

@ -38,7 +38,7 @@ import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchInclude;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.search.cache;
* #L%
*/
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchResult;

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.search.cache;
* #L%
*/
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.entity.Search;
import javax.annotation.Nullable;

View File

@ -35,7 +35,7 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;

View File

@ -40,7 +40,7 @@ import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.entity.TermConceptProperty;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.term.api;
* #L%
*/
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;

View File

@ -81,7 +81,8 @@ public abstract class BaseCaptureQueriesListener implements ProxyDataSourceBuild
long elapsedTime = theExecutionInfo.getElapsedTime();
long startTime = System.currentTimeMillis() - elapsedTime;
queryList.add(new SqlQuery(sql, params, startTime, elapsedTime, stackTraceElements, size));
SqlQuery sqlQuery = new SqlQuery(sql, params, startTime, elapsedTime, stackTraceElements, size);
queryList.add(sqlQuery);
}
}

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import org.junit.Test;

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.jpa.dao.dstu2;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao;

View File

@ -4,7 +4,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3Test;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.jpa.dao.dstu2;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;

View File

@ -6,7 +6,7 @@ import static org.junit.Assert.assertThat;
import java.util.List;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.junit.AfterClass;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

View File

@ -5,7 +5,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;

View File

@ -3,7 +3,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.api.MethodOutcome;

View File

@ -6,7 +6,7 @@ import static org.junit.Assert.assertThat;
import java.util.List;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.hl7.fhir.dstu3.model.Organization;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.AfterClass;

View File

@ -519,7 +519,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
});
myDaoConfig.setSchedulingDisabled(true);
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
}
@After

View File

@ -59,6 +59,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
@After
public void after() {
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
}
@Override
@ -181,6 +182,8 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
@Test
public void testSearchAndBlockSomeOnRevIncludes() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
create50Observations();
AtomicInteger hitCount = new AtomicInteger(0);
@ -205,6 +208,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
@Test
public void testSearchAndBlockSomeOnRevIncludes_LoadSynchronous() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
create50Observations();
AtomicInteger hitCount = new AtomicInteger(0);
@ -217,11 +221,14 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true);
map.setSort(new SortSpec(Observation.SP_IDENTIFIER, SortOrderEnum.ASC));
map.addRevInclude(IBaseResource.INCLUDE_ALL);
myCaptureQueriesListener.clear();
IBundleProvider outcome = myPatientDao.search(map, mySrd);
ourLog.info("Search UUID: {}", outcome.getUuid());
// Fetch the first 10 (don't cross a fetch boundary)
List<IBaseResource> resources = outcome.getResources(0, 100);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
List<String> returnedIdValues = toUnqualifiedVersionlessIdValues(resources);
assertEquals(sort(myPatientIdsEvenOnly, myObservationIdsEvenOnly), sort(returnedIdValues));
assertEquals(2, hitCount.get());
@ -427,7 +434,8 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
IPreResourceAccessDetails accessDetails = theArgs.get(IPreResourceAccessDetails.class);
assertThat(accessDetails.size(), greaterThan(0));
// FIXME: restore
// assertThat(accessDetails.size(), greaterThan(0));
List<String> currentPassIds = new ArrayList<>();
for (int i = 0; i < accessDetails.size(); i++) {

View File

@ -8,8 +8,12 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.ReferenceParam;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.ServiceRequest;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@ -53,7 +57,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
myPatientDao.update(p);
});
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(5, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size());
assertThat(myCaptureQueriesListener.getInsertQueriesForCurrentThread(), empty());
@ -77,7 +81,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
myPatientDao.update(p).getResource();
});
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(6, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size());
myCaptureQueriesListener.logInsertQueriesForCurrentThread();
@ -453,7 +457,572 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
}
@Test
public void testTransactionWithMultipleReferences() {
Bundle input = new Bundle();
Patient patient = new Patient();
patient.setId(IdType.newRandomUuid());
patient.setActive(true);
input.addEntry()
.setFullUrl(patient.getId())
.setResource(patient)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Patient");
Practitioner practitioner = new Practitioner();
practitioner.setId(IdType.newRandomUuid());
practitioner.setActive(true);
input.addEntry()
.setFullUrl(practitioner.getId())
.setResource(practitioner)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Practitioner");
ServiceRequest sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
Bundle output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(0, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
myCaptureQueriesListener.logInsertQueriesForCurrentThread();
assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
}
@Test
public void testTransactionWithMultiplePreExistingReferences_ForcedId() {
myDaoConfig.setDeleteEnabled(true);
Patient patient = new Patient();
patient.setId("Patient/A");
patient.setActive(true);
myPatientDao.update(patient);
Practitioner practitioner = new Practitioner();
practitioner.setId("Practitioner/B");
practitioner.setActive(true);
myPractitionerDao.update(practitioner);
// Create transaction
Bundle input = new Bundle();
ServiceRequest sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
Bundle output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Lookup the two existing IDs to make sure they are legit
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
// Do the same a second time - Deletes are enabled so we expect to have to resolve the
// targets again to make sure they weren't deleted
input = new Bundle();
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Lookup the two existing IDs to make sure they are legit
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
}
@Test
public void testTransactionWithMultiplePreExistingReferences_Numeric() {
myDaoConfig.setDeleteEnabled(true);
Patient patient = new Patient();
patient.setActive(true);
IIdType patientId = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
Practitioner practitioner = new Practitioner();
practitioner.setActive(true);
IIdType practitionerId = myPractitionerDao.create(practitioner).getId().toUnqualifiedVersionless();
// Create transaction
Bundle input = new Bundle();
ServiceRequest sr = new ServiceRequest();
sr.getSubject().setReferenceElement(patientId);
sr.addPerformer().setReferenceElement(practitionerId);
sr.addPerformer().setReferenceElement(practitionerId);
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReferenceElement(patientId);
sr.addPerformer().setReferenceElement(practitionerId);
sr.addPerformer().setReferenceElement(practitionerId);
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
Bundle output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Lookup the two existing IDs to make sure they are legit
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
// Do the same a second time - Deletes are enabled so we expect to have to resolve the
// targets again to make sure they weren't deleted
input = new Bundle();
sr = new ServiceRequest();
sr.getSubject().setReferenceElement(patientId);
sr.addPerformer().setReferenceElement(practitionerId);
sr.addPerformer().setReferenceElement(practitionerId);
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReferenceElement(patientId);
sr.addPerformer().setReferenceElement(practitionerId);
sr.addPerformer().setReferenceElement(practitionerId);
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Lookup the two existing IDs to make sure they are legit
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
}
@Test
public void testTransactionWithMultiplePreExistingReferences_ForcedId_DeletesDisabled() {
myDaoConfig.setDeleteEnabled(false);
Patient patient = new Patient();
patient.setId("Patient/A");
patient.setActive(true);
myPatientDao.update(patient);
Practitioner practitioner = new Practitioner();
practitioner.setId("Practitioner/B");
practitioner.setActive(true);
myPractitionerDao.update(practitioner);
// Create transaction
Bundle input = new Bundle();
ServiceRequest sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
Bundle output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Lookup the two existing IDs to make sure they are legit
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
// See notes in testTransactionWithMultiplePreExistingReferences_Numeric_DeletesDisabled below
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
// Do the same a second time - Deletes are enabled so we expect to have to resolve the
// targets again to make sure they weren't deleted
input = new Bundle();
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// We do not need to resolve the target IDs a second time
assertEquals(0, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
}
@Test
public void testTransactionWithMultiplePreExistingReferences_Numeric_DeletesDisabled() {
myDaoConfig.setDeleteEnabled(false);
Patient patient = new Patient();
patient.setActive(true);
IIdType patientId = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
Practitioner practitioner = new Practitioner();
practitioner.setActive(true);
IIdType practitionerId = myPractitionerDao.create(practitioner).getId().toUnqualifiedVersionless();
// Create transaction
Bundle input = new Bundle();
ServiceRequest sr = new ServiceRequest();
sr.getSubject().setReferenceElement(patientId);
sr.addPerformer().setReferenceElement(practitionerId);
sr.addPerformer().setReferenceElement(practitionerId);
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReferenceElement(patientId);
sr.addPerformer().setReferenceElement(practitionerId);
sr.addPerformer().setReferenceElement(practitionerId);
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
Bundle output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Lookup the two existing IDs to make sure they are legit
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
// TODO: We have 2 updates here that are caused by Hibernate deciding to flush its action queue half way through
// the transaction because a read is about to take place. I think these are unnecessary but I don't see a simple
// way of getting rid of them. Hopefully these can be optimized out later
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
// Do the same a second time - Deletes are enabled so we expect to have to resolve the
// targets again to make sure they weren't deleted
input = new Bundle();
sr = new ServiceRequest();
sr.getSubject().setReferenceElement(patientId);
sr.addPerformer().setReferenceElement(practitionerId);
sr.addPerformer().setReferenceElement(practitionerId);
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReferenceElement(patientId);
sr.addPerformer().setReferenceElement(practitionerId);
sr.addPerformer().setReferenceElement(practitionerId);
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// We do not need to resolve the target IDs a second time
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(0, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
// Similar to the note above - No idea why this update is here, it's basically a NO-OP
assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
}
@Test
public void testTransactionWithMultiplePreExistingReferences_IfNoneExist() {
myDaoConfig.setDeleteEnabled(true);
Patient patient = new Patient();
patient.setId("Patient/A");
patient.setActive(true);
myPatientDao.update(patient);
Practitioner practitioner = new Practitioner();
practitioner.setId("Practitioner/B");
practitioner.setActive(true);
myPractitionerDao.update(practitioner);
// Create transaction
Bundle input = new Bundle();
patient = new Patient();
patient.setId(IdType.newRandomUuid());
patient.setActive(true);
input.addEntry()
.setFullUrl(patient.getId())
.setResource(patient)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Patient")
.setIfNoneExist("Patient?active=true");
practitioner = new Practitioner();
practitioner.setId(IdType.newRandomUuid());
practitioner.setActive(true);
input.addEntry()
.setFullUrl(practitioner.getId())
.setResource(practitioner)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Practitioner")
.setIfNoneExist("Practitioner?active=true");
ServiceRequest sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
Bundle output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Lookup the two existing IDs to make sure they are legit
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
// Do the same a second time
input = new Bundle();
patient = new Patient();
patient.setId(IdType.newRandomUuid());
patient.setActive(true);
input.addEntry()
.setFullUrl(patient.getId())
.setResource(patient)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Patient")
.setIfNoneExist("Patient?active=true");
practitioner = new Practitioner();
practitioner.setId(IdType.newRandomUuid());
practitioner.setActive(true);
input.addEntry()
.setFullUrl(practitioner.getId())
.setResource(practitioner)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Practitioner")
.setIfNoneExist("Practitioner?active=true");
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
sr = new ServiceRequest();
sr.getSubject().setReference(patient.getId());
sr.addPerformer().setReference(practitioner.getId());
sr.addPerformer().setReference(practitioner.getId());
input.addEntry()
.setFullUrl(sr.getId())
.setResource(sr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("ServiceRequest");
myCaptureQueriesListener.clear();
output = mySystemDao.transaction(mySrd, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Lookup the two existing IDs to make sure they are legit
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
}
@AfterClass
public static void afterClassClearContext() {

View File

@ -68,6 +68,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
mySearchCoordinatorSvcImpl.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
myCaptureQueriesListener.setCaptureQueryStackTrace(false);
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
}
private void create200Patients() {
@ -102,6 +103,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
@Test
public void testFetchCountWithMultipleIndexesOnOneResource() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
create200Patients();
// Already have 200, let's add number 201 with a bunch of similar names
@ -753,6 +755,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
@Test
public void testWritesPerformMinimalSqlStatements() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
Patient p = new Patient();
p.addIdentifier().setSystem("sys1").setValue("val1");
p.addIdentifier().setSystem("sys2").setValue("val2");

View File

@ -12,7 +12,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortOrderEnum;
@ -30,6 +31,7 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test {
@After
public final void after() {
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
}
@Test
@ -210,7 +212,7 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test {
@SuppressWarnings("unused")
@Test
public void testSortOnSparselyPopulatedFields() {
// myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
IIdType pid1, pid2, pid3, pid4, pid5, pid6;
{
@ -257,7 +259,9 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test {
}
@Test
public void testSortOnSparselyPopulatedSearchParameter() throws Exception {
public void testSortOnSparselyPopulatedSearchParameter() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
Patient pCA = new Patient();
pCA.setId("CA");
pCA.setActive(false);

View File

@ -5,7 +5,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;

View File

@ -6,7 +6,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.JpaResourceDao;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
@ -153,6 +153,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
myDaoConfig.setTreatReferencesAsLogical(new DaoConfig().getTreatReferencesAsLogical());
myDaoConfig.setEnforceReferentialIntegrityOnDelete(new DaoConfig().isEnforceReferentialIntegrityOnDelete());
myDaoConfig.setEnforceReferenceTargetTypes(new DaoConfig().isEnforceReferenceTargetTypes());
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
}
@Before
@ -1689,6 +1690,8 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
@Test
public void testDeleteWithMatchUrlQualifierMissing() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
String methodName = "testDeleteWithMatchUrlChainedProfile";
/*
@ -3106,6 +3109,8 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
@Test
public void testSortByDate() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testtestSortByDate");
p.addName().setFamily("testSortF1").addGiven("testSortG1");
@ -3435,6 +3440,8 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
@Test
public void testSortByString01() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
Patient p = new Patient();
String string = "testSortByString01";
p.addIdentifier().setSystem("urn:system").setValue(string);

View File

@ -1,9 +1,8 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.TestUtil;

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.param.StringAndListParam;

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
@ -114,6 +115,8 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest implements ITestData
myEntityManager.createNativeQuery("alter table HFJ_FORCED_ID add constraint IDX_FORCEDID_TYPE_FID unique (RESOURCE_TYPE, FORCED_ID)");
});
}
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
}
@Override
@ -139,6 +142,8 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest implements ITestData
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(1).setName("PART-1"));
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(2).setName("PART-2"));
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(3).setName("PART-3"));
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
}
@Test

View File

@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@ -65,7 +66,7 @@ public class DeleteConflictServiceTest {
link.setSourceResource(entity);
list.add(link);
when(myDeleteConflictFinderService.findConflicts(any(), anyInt())).thenReturn(list);
int retryCount = myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, false, null);
int retryCount = myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, false, null, new TransactionDetails());
assertEquals(0, retryCount);
}
}

View File

@ -7,7 +7,7 @@ import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;

View File

@ -80,7 +80,7 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
@Test
public void testCreateAndRetrieveBinary_ServerAssignedId_ExternalizedBinary_NullServletRequest() {
public void testCreateAndRetrieveBinary_ServerAssignedId_ExternalizedBinary_NoServletRequest() {
// Create a resource with a big enough binary
Binary binary = new Binary();
@ -92,14 +92,17 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
IIdType id = outcome.getId().toUnqualifiedVersionless();
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getResource());
ourLog.info("Encoded: {}", encoded);
assertThat(encoded, not(containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID)));
assertThat(encoded, containsString("\"data\""));
assertThat(encoded, containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, not(containsString("\"data\"")));
Binary output = myBinaryDao.read(id);
// Now read it back and make sure it was de-externalized
Binary output = myBinaryDao.read(id, mySrd);
assertEquals("application/octet-stream", output.getContentType());
assertArrayEquals(SOME_BYTES, output.getData());
}
@Test
public void testCreateAndRetrieveBinary_ServerAssignedId_NonExternalizedBinary() {

View File

@ -99,6 +99,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo());
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null);
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
@ -4337,6 +4338,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test
public void testSearchWithEmptyParameter() throws Exception {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
Observation obs = new Observation();
obs.setStatus(ObservationStatus.FINAL);
obs.getCode().addCoding().setSystem("foo").setCode("bar");
@ -4442,6 +4445,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test
public void testSearchWithMissing() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
ourLog.info("Starting testSearchWithMissing");
String methodName = "testSearchWithMissing";
@ -4512,6 +4516,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test
public void testSearchWithMissing2() throws Exception {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
checkParamMissing(Observation.SP_CODE);
checkParamMissing(Observation.SP_CATEGORY);
checkParamMissing(Observation.SP_VALUE_STRING);
@ -4521,6 +4526,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test
public void testSearchWithMissingDate2() throws Exception {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
MedicationRequest mr1 = new MedicationRequest();
mr1.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral");
mr1.addDosageInstruction().getTiming().addEventElement().setValueAsString("2017-01-01");
@ -4654,6 +4661,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test
public void testSmallResultIncludes() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
Patient p = new Patient();
p.setId("p");
p.setActive(true);

View File

@ -10,7 +10,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;

View File

@ -10,7 +10,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;

View File

@ -12,7 +12,7 @@ import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;

View File

@ -5,7 +5,7 @@ import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CodeSystem;

View File

@ -9,8 +9,9 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
@ -222,7 +223,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
cs = new TermCodeSystemVersion();
TermConcept parentA = new TermConcept(cs, "ParentA");
cs.getConcepts().add(parentA);
id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified();
id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd, new TransactionDetails()).getId().toUnqualified();
table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
cs.setResource(table);
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getPersistentId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs, table);

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.cross;
* #L%
*/
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.Date;

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
* #L%
*/
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import org.hibernate.annotations.OptimisticLock;

View File

@ -22,7 +22,7 @@ package ca.uhn.fhir.jpa.model.entity;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.search.IndexNonDeletedInterceptor;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
@ -41,6 +42,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.parser.DataFormatException;
@ -59,8 +61,6 @@ import javax.annotation.Nonnull;
import javax.validation.constraints.NotNull;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -87,16 +87,16 @@ public class SearchParamExtractorService {
* This method is responsible for scanning a resource for all of the search parameter instances. I.e. for all search parameters defined for
* a given resource type, it extracts the associated indexes and populates {@literal theParams}.
*/
public void extractFromResource(RequestPartitionId theRequestPartitionId, RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, Date theUpdateTime, boolean theFailOnInvalidReference) {
public void extractFromResource(RequestPartitionId theRequestPartitionId, RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference) {
IBaseResource resource = normalizeResource(theResource);
// All search parameter types except Reference
extractSearchIndexParameters(theRequestDetails, theParams, resource, theEntity);
// Reference search parameters
extractResourceLinks(theRequestPartitionId, theParams, theEntity, resource, theUpdateTime, theFailOnInvalidReference, theRequestDetails);
extractResourceLinks(theRequestPartitionId, theParams, theEntity, resource, theTransactionDetails, theFailOnInvalidReference, theRequestDetails);
theParams.setUpdatedTime(theUpdateTime);
theParams.setUpdatedTime(theTransactionDetails.getTransactionDate());
}
private void extractSearchIndexParameters(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity) {
@ -171,25 +171,25 @@ public class SearchParamExtractorService {
return theResource;
}
private void extractResourceLinks(RequestPartitionId theRequestPartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, Date theUpdateTime, boolean theFailOnInvalidReference, RequestDetails theRequest) {
private void extractResourceLinks(RequestPartitionId theRequestPartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference, RequestDetails theRequest) {
String resourceName = myContext.getResourceDefinition(theResource).getName();
ISearchParamExtractor.SearchParamSet<PathAndRef> refs = mySearchParamExtractor.extractResourceLinks(theResource);
SearchParamExtractorService.handleWarnings(theRequest, myInterceptorBroadcaster, refs);
Map<String, IResourceLookup> resourceIdToResolvedTarget = new HashMap<>();
for (PathAndRef nextPathAndRef : refs) {
RuntimeSearchParam searchParam = mySearchParamRegistry.getActiveSearchParam(resourceName, nextPathAndRef.getSearchParamName());
extractResourceLinks(theRequestPartitionId, theParams, theEntity, theUpdateTime, searchParam, nextPathAndRef, theFailOnInvalidReference, theRequest, resourceIdToResolvedTarget);
extractResourceLinks(theRequestPartitionId, theParams, theEntity, theTransactionDetails, searchParam, nextPathAndRef, theFailOnInvalidReference, theRequest);
}
theEntity.setHasLinks(theParams.myLinks.size() > 0);
}
private void extractResourceLinks(@NotNull RequestPartitionId theRequestPartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, Date theUpdateTime, RuntimeSearchParam theRuntimeSearchParam, PathAndRef thePathAndRef, boolean theFailOnInvalidReference, RequestDetails theRequest, Map<String, IResourceLookup> theResourceIdToResolvedTarget) {
private void extractResourceLinks(@NotNull RequestPartitionId theRequestPartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, TransactionDetails theTransactionDetails, RuntimeSearchParam theRuntimeSearchParam, PathAndRef thePathAndRef, boolean theFailOnInvalidReference, RequestDetails theRequest) {
IBaseReference nextReference = thePathAndRef.getRef();
IIdType nextId = nextReference.getReferenceElement();
String path = thePathAndRef.getPath();
Date transactionDate = theTransactionDetails.getTransactionDate();
/*
* This can only really happen if the DAO is being called
@ -205,7 +205,7 @@ public class SearchParamExtractorService {
boolean canonical = thePathAndRef.isCanonical();
if (LogicalReferenceHelper.isLogicalReference(myModelConfig, nextId) || canonical) {
String value = nextId.getValue();
ResourceLink resourceLink = ResourceLink.forLogicalReference(thePathAndRef.getPath(), theEntity, value, theUpdateTime);
ResourceLink resourceLink = ResourceLink.forLogicalReference(thePathAndRef.getPath(), theEntity, value, transactionDate);
if (theParams.myLinks.add(resourceLink)) {
ourLog.debug("Indexing remote resource reference URL: {}", nextId);
}
@ -247,7 +247,7 @@ public class SearchParamExtractorService {
String msg = myContext.getLocalizer().getMessage(BaseSearchParamExtractor.class, "externalReferenceNotAllowed", nextId.getValue());
throw new InvalidRequestException(msg);
} else {
ResourceLink resourceLink = ResourceLink.forAbsoluteReference(thePathAndRef.getPath(), theEntity, nextId, theUpdateTime);
ResourceLink resourceLink = ResourceLink.forAbsoluteReference(thePathAndRef.getPath(), theEntity, nextId, transactionDate);
if (theParams.myLinks.add(resourceLink)) {
ourLog.debug("Indexing remote resource reference URL: {}", nextId);
}
@ -267,28 +267,52 @@ public class SearchParamExtractorService {
}
}
ResourcePersistentId resolvedTargetId = theTransactionDetails.getResolvedResourceIds().get(thePathAndRef.getRef().getReferenceElement());
ResourceLink resourceLink;
if (theFailOnInvalidReference) {
if (resolvedTargetId != null) {
/*
* If we have already resolved the given reference within this transaction, we don't
* need to resolve it again
*/
myResourceLinkResolver.validateTypeOrThrowException(type);
resourceLink = resolveTargetAndCreateResourceLinkOrReturnNull(theRequestPartitionId, theEntity, theUpdateTime, theRuntimeSearchParam, path, thePathAndRef, nextId, typeString, type, nextReference, theRequest, theResourceIdToResolvedTarget);
resourceLink = ResourceLink.forLocalReference(thePathAndRef.getPath(), theEntity, typeString, resolvedTargetId.getIdAsLong(), nextId.getIdPart(), transactionDate);
} else if (theFailOnInvalidReference) {
/*
* The reference points to another resource, so let's look it up. We need to do this
* since the target may be a forced ID, but also so that we can throw an exception
* if the reference is invalid
*/
myResourceLinkResolver.validateTypeOrThrowException(type);
resourceLink = resolveTargetAndCreateResourceLinkOrReturnNull(theRequestPartitionId, theEntity, transactionDate, theRuntimeSearchParam, path, thePathAndRef, nextId, typeString, type, nextReference, theRequest);
if (resourceLink == null) {
return;
} else {
// Cache the outcome in the current transaction in case there are more references
ResourcePersistentId persistentId = new ResourcePersistentId(resourceLink.getTargetResourcePid());
theTransactionDetails.addResolvedResourceId(thePathAndRef.getRef().getReferenceElement(), persistentId);
}
} else {
/*
* Just assume the reference is valid. This is used for in-memory matching since there
* is no expectation of a database in this situation
*/
ResourceTable target;
target = new ResourceTable();
target.setResourceType(typeString);
resourceLink = ResourceLink.forLocalReference(thePathAndRef.getPath(), theEntity, typeString, null, nextId.getIdPart(), theUpdateTime);
resourceLink = ResourceLink.forLocalReference(thePathAndRef.getPath(), theEntity, typeString, null, nextId.getIdPart(), transactionDate);
}
theParams.myLinks.add(resourceLink);
}
private ResourceLink resolveTargetAndCreateResourceLinkOrReturnNull(@Nonnull RequestPartitionId theRequestPartitionId, ResourceTable theEntity, Date theUpdateTime, RuntimeSearchParam nextSpDef, String theNextPathsUnsplit, PathAndRef nextPathAndRef, IIdType theNextId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest, Map<String, IResourceLookup> theResourceIdToResolvedTarget) {
private ResourceLink resolveTargetAndCreateResourceLinkOrReturnNull(@Nonnull RequestPartitionId theRequestPartitionId, ResourceTable theEntity, Date theUpdateTime, RuntimeSearchParam nextSpDef, String theNextPathsUnsplit, PathAndRef nextPathAndRef, IIdType theNextId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest) {
/*
* We keep a cache of resolved target resources. This is good since for some resource types, there
* are multiple search parameters that map to the same element path within a resource (e.g.
@ -301,18 +325,12 @@ public class SearchParamExtractorService {
targetRequestPartitionId = RequestPartitionId.allPartitions();
}
String key = RequestPartitionId.stringifyForKey(targetRequestPartitionId) + "/" + theNextId.getValue();
IResourceLookup targetResource = theResourceIdToResolvedTarget.get(key);
if (targetResource == null) {
targetResource = myResourceLinkResolver.findTargetResource(targetRequestPartitionId, nextSpDef, theNextPathsUnsplit, theNextId, theTypeString, theType, theReference, theRequest);
}
IResourceLookup targetResource = myResourceLinkResolver.findTargetResource(targetRequestPartitionId, nextSpDef, theNextPathsUnsplit, theNextId, theTypeString, theType, theReference, theRequest);
if (targetResource == null) {
return null;
}
theResourceIdToResolvedTarget.put(key, targetResource);
String targetResourceType = targetResource.getResourceType();
Long targetResourcePid = targetResource.getResourceId();
String targetResourceIdPart = theNextId.getIdPart();

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.searchparam.matcher;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -36,10 +37,11 @@ public class IndexedSearchParamExtractor {
public ResourceIndexedSearchParams extractIndexedSearchParams(IBaseResource theResource, RequestDetails theRequest) {
ResourceTable entity = new ResourceTable();
TransactionDetails transactionDetails = new TransactionDetails();
String resourceType = myContext.getResourceDefinition(theResource).getName();
entity.setResourceType(resourceType);
ResourceIndexedSearchParams resourceIndexedSearchParams = new ResourceIndexedSearchParams();
mySearchParamExtractorService.extractFromResource(null, theRequest, resourceIndexedSearchParams, entity, theResource, theResource.getMeta().getLastUpdated(), false);
mySearchParamExtractorService.extractFromResource(null, theRequest, resourceIndexedSearchParams, entity, theResource, transactionDetails, false);
return resourceIndexedSearchParams;
}
}

View File

@ -26,7 +26,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.model.cross;
package ca.uhn.fhir.rest.api.server.storage;
/*-
* #%L
* HAPI FHIR Model
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%

View File

@ -0,0 +1,130 @@
package ca.uhn.fhir.rest.api.server.storage;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
/**
* This object contains runtime information that is gathered and relevant to a single <i>database transaction</i>.
* This doesn't mean a FHIR transaction necessarily, but rather any operation that happens within a single DB transaction
* (i.e. a FHIR create, read, transaction, etc.).
* <p>
* The intent with this class is to hold things we want to pass from operation to operation within a transaction in
* order to avoid looking things up multime times, etc.
* </p>
*
* @since 5.0.0
*/
public class TransactionDetails {
private final Date myTransactionDate;
private Map<IIdType, ResourcePersistentId> myResolvedResourceIds = Collections.emptyMap();
private Map<String, Object> myUserData;
/**
* Constructor
*/
public TransactionDetails() {
myTransactionDate = new Date();
}
/**
* Constructor
*/
public TransactionDetails(Date theTransactionDate) {
myTransactionDate = theTransactionDate;
}
/**
* A <b>Resolved Resource ID</b> is a mapping between a resource ID (e.g. "<code>Patient/ABC</code>" or
* "<code>Observation/123</code>") and a storage ID for that resource. Resources should only be placed within
* the TransactionDetails if they are known to exist and be valid targets for other resources to link to.
*/
public Map<IIdType, ResourcePersistentId> getResolvedResourceIds() {
return myResolvedResourceIds;
}
/**
* A <b>Resolved Resource ID</b> is a mapping between a resource ID (e.g. "<code>Patient/ABC</code>" or
* "<code>Observation/123</code>") and a storage ID for that resource. Resources should only be placed within
* the TransactionDetails if they are known to exist and be valid targets for other resources to link to.
*/
public void addResolvedResourceId(IIdType theResourceId, ResourcePersistentId thePersistentId) {
assert theResourceId != null;
assert thePersistentId != null;
if (myResolvedResourceIds.isEmpty()) {
myResolvedResourceIds = new HashMap<>();
}
myResolvedResourceIds.put(theResourceId, thePersistentId);
}
/**
* This is the wall-clock time that a given transaction started.
*/
public Date getTransactionDate() {
return myTransactionDate;
}
/**
* Sets an arbitraty object that will last the lifetime of the current transaction
*
* @see #getUserData(String)
*/
public void putUserData(String theKey, Object theValue) {
if (myUserData == null) {
myUserData = new HashMap<>();
}
myUserData.put(theKey, theValue);
}
/**
* Gets an arbitraty object that will last the lifetime of the current transaction
*
* @see #putUserData(String, Object)
*/
public <T> T getUserData(String theKey) {
if (myUserData != null) {
return (T) myUserData.get(theKey);
}
return null;
}
/**
* Fetches the existing value in the user data map, or uses {@literal theSupplier} to create a new object and
* puts that in the map, and returns it
*/
public <T> T getOrCreateUserData(String theKey, Supplier<T> theSupplier) {
T retVal = (T) getUserData(theKey);
if (retVal == null) {
retVal = theSupplier.get();
putUserData(theKey, retVal);
}
return retVal;
}
}

View File

@ -45,6 +45,7 @@ import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.SimplePreResourceAccessDetails;
import ca.uhn.fhir.rest.api.server.SimplePreResourceShowDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
@ -97,9 +98,9 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
protected Map<String, TreeMap<Long, T>> myIdToVersionToResourceMap = Collections.synchronizedMap(new LinkedHashMap<>());
protected Map<String, LinkedList<T>> myIdToHistory = Collections.synchronizedMap(new LinkedHashMap<>());
protected LinkedList<T> myTypeHistory = new LinkedList<>();
protected AtomicLong mySearchCount = new AtomicLong(0);
private long myNextId;
private AtomicLong myDeleteCount = new AtomicLong(0);
protected AtomicLong mySearchCount = new AtomicLong(0);
private AtomicLong myUpdateCount = new AtomicLong(0);
private AtomicLong myCreateCount = new AtomicLong(0);
private AtomicLong myReadCount = new AtomicLong(0);
@ -140,7 +141,9 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
@Create
public MethodOutcome create(@ResourceParam T theResource, RequestDetails theRequestDetails) {
createInternal(theResource, theRequestDetails);
TransactionDetails transactionDetails = new TransactionDetails();
createInternal(theResource, theRequestDetails, transactionDetails);
myCreateCount.incrementAndGet();
@ -150,17 +153,19 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
.setId(theResource.getIdElement());
}
private void createInternal(@ResourceParam T theResource, RequestDetails theRequestDetails) {
private void createInternal(@ResourceParam T theResource, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
long idPart = myNextId++;
String idPartAsString = Long.toString(idPart);
Long versionIdPart = 1L;
IIdType id = store(theResource, idPartAsString, versionIdPart, theRequestDetails);
IIdType id = store(theResource, idPartAsString, versionIdPart, theRequestDetails, theTransactionDetails);
theResource.setId(id);
}
@Delete
public MethodOutcome delete(@IdParam IIdType theId, RequestDetails theRequestDetails) {
TransactionDetails transactionDetails = new TransactionDetails();
TreeMap<Long, T> versions = myIdToVersionToResourceMap.get(theId.getIdPart());
if (versions == null || versions.isEmpty()) {
throw new ResourceNotFoundException(theId);
@ -168,7 +173,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
long nextVersion = versions.lastEntry().getKey() + 1L;
IIdType id = store(null, theId.getIdPart(), nextVersion, theRequestDetails);
IIdType id = store(null, theId.getIdPart(), nextVersion, theRequestDetails, transactionDetails);
myDeleteCount.incrementAndGet();
@ -335,7 +340,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
return fireInterceptorsAndFilterAsNeeded(retVal, theRequestDetails);
}
private IIdType store(@ResourceParam T theResource, String theIdPart, Long theVersionIdPart, RequestDetails theRequestDetails) {
private IIdType store(@ResourceParam T theResource, String theIdPart, Long theVersionIdPart, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
IIdType id = myFhirContext.getVersion().newIdType();
String versionIdPart = Long.toString(theVersionIdPart);
id.setParts(null, myResourceName, theIdPart, versionIdPart);
@ -383,7 +388,8 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
HookParams params = new HookParams()
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(IBaseResource.class, theResource);
.add(IBaseResource.class, theResource)
.add(TransactionDetails.class, theTransactionDetails);
interceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, params);
interceptorBroadcaster.callHooks(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, params);
@ -394,7 +400,8 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(IBaseResource.class, myIdToHistory.get(theIdPart).getFirst())
.add(IBaseResource.class, theResource);
.add(IBaseResource.class, theResource)
.add(TransactionDetails.class, theTransactionDetails);
interceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, params);
interceptorBroadcaster.callHooks(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, params);
@ -421,10 +428,11 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
@ResourceParam T theResource,
@ConditionalUrlParam String theConditional,
RequestDetails theRequestDetails) {
TransactionDetails transactionDetails = new TransactionDetails();
ValidateUtil.isTrueOrThrowInvalidRequest(isBlank(theConditional), "This server doesn't support conditional update");
boolean created = updateInternal(theResource, theRequestDetails);
boolean created = updateInternal(theResource, theRequestDetails, transactionDetails);
myUpdateCount.incrementAndGet();
return new MethodOutcome()
@ -433,7 +441,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
.setId(theResource.getIdElement());
}
private boolean updateInternal(@ResourceParam T theResource, RequestDetails theRequestDetails) {
private boolean updateInternal(@ResourceParam T theResource, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
String idPartAsString = theResource.getIdElement().getIdPart();
TreeMap<Long, T> versionToResource = getVersionToResource(idPartAsString);
@ -447,7 +455,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
created = false;
}
IIdType id = store(theResource, idPartAsString, versionIdPart, theRequestDetails);
IIdType id = store(theResource, idPartAsString, versionIdPart, theRequestDetails, theTransactionDetails);
theResource.setId(id);
return created;
}
@ -466,9 +474,9 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
*/
public IIdType store(T theResource) {
if (theResource.getIdElement().hasIdPart()) {
updateInternal(theResource, null);
updateInternal(theResource, null, new TransactionDetails());
} else {
createInternal(theResource, null);
createInternal(theResource, null, new TransactionDetails());
}
return theResource.getIdElement();
}

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.api.PatchTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
@ -3760,7 +3761,8 @@ public class AuthorizationInterceptorR4Test {
HookParams params = new HookParams()
.add(IBaseResource.class, next)
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(TransactionDetails.class, new TransactionDetails());
theRequestOperationCallback.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, params);
}
return new MethodOutcome();
@ -3918,7 +3920,8 @@ public class AuthorizationInterceptorR4Test {
HookParams params = new HookParams()
.add(IBaseResource.class, next)
.add(RequestDetails.class, theRequestDetails)
.add(ServletRequestDetails.class, theRequestDetails);
.add(ServletRequestDetails.class, theRequestDetails)
.add(TransactionDetails.class, new TransactionDetails());
theInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, params);
}
}