Transaction write performance fix (#1832)

* Performance fix for transactions

* Test fix

* Fixes

* Add docs

* Test fixes
This commit is contained in:
James Agnew 2020-05-07 10:56:41 -04:00 committed by GitHub
parent a77aa6a28e
commit b67509d7ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 923 additions and 142 deletions

View File

@ -1278,6 +1278,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.jpa.model.util.TransactionDetails - The outer transaction details object
* </li>
* </ul>
* <p>
* Hooks should return <code>ca.uhn.fhir.jpa.delete.DeleteConflictOutcome</code>.
@ -1291,7 +1294,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.jpa.model.util.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

@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.model.cross.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.jpa.model.util.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.jpa.model.util.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

@ -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.jpa.model.util.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;
@ -1245,7 +1245,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
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),

View File

@ -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.jpa.model.util.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();
@ -275,7 +276,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
.addIfMatchesType(ServletRequestDetails.class, theRequest);
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 +286,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
@ -324,7 +325,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 +350,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());
@ -363,7 +365,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
.addIfMatchesType(ServletRequestDetails.class, theRequest);
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 +376,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
@ -446,7 +448,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);
@ -510,7 +512,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
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) {
@ -1083,7 +1085,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 +1276,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 +1303,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 +1317,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 +1362,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 +1393,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

@ -32,6 +32,7 @@ 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.jpa.model.util.TransactionDetails;
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
@ -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.jpa.model.util.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

@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.jpa.model.util.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

@ -29,6 +29,7 @@ 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.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.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.jpa.model.util.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.jpa.model.util.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.jpa.model.util.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

@ -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,6 +25,7 @@ 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.jpa.model.util.TransactionDetails;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.MatchResourceUrlService;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
@ -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

@ -29,6 +29,7 @@ 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.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.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.jpa.model.util.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.jpa.model.util.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.jpa.model.util.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

@ -30,6 +30,7 @@ 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.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.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.jpa.model.util.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.jpa.model.util.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.jpa.model.util.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.jpa.model.util.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.jpa.model.util.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

@ -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

@ -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

@ -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

@ -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

@ -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.jpa.model.util.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

@ -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

@ -11,6 +11,7 @@ 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.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.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

@ -0,0 +1,90 @@
package ca.uhn.fhir.jpa.model.util;
/*-
* #%L
* HAPI FHIR Model
* %%
* 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 ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 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>
*/
public class TransactionDetails {
private final Date myTransactionDate;
private Map<IIdType, ResourcePersistentId> myResolvedResourceIds = Collections.emptyMap();
/**
* 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;
}
}

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.jpa.model.cross.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.jpa.model.util.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,9 +22,11 @@ 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.jpa.model.util.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;
import org.hibernate.Transaction;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
@ -36,10 +38,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;
}
}