For bit of work on JPA perf enhancements

This commit is contained in:
James Agnew 2017-03-30 06:28:34 +08:00
parent 2c9a6e65e7
commit ce73e89715
9 changed files with 125 additions and 203 deletions

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api;
import java.io.Serializable;
/*
* #%L
* HAPI FHIR - Core Library
@ -26,7 +28,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public interface IQueryParameterAnd<T extends IQueryParameterOr<?>> {
public interface IQueryParameterAnd<T extends IQueryParameterOr<?>> extends Serializable {
/**
*

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api;
import java.io.Serializable;
/*
* #%L
* HAPI FHIR - Core Library
@ -25,7 +27,7 @@ import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.method.QualifiedParamList;
public interface IQueryParameterOr<T extends IQueryParameterType> {
public interface IQueryParameterOr<T extends IQueryParameterType> extends Serializable {
public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, QualifiedParamList theParameters);

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api;
import java.io.Serializable;
import ca.uhn.fhir.context.FhirContext;
/*
@ -22,7 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
* #L%
*/
public interface IQueryParameterType {
public interface IQueryParameterType extends Serializable {
/**
* This method is generally only called by HAPI itself, and should not need to be called from user code.

View File

@ -952,8 +952,13 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public IBundleProvider search(final SearchParameterMap theParams) {
return search(theParams, null);
}
@Override
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theParams.getRequestDetails(), getContext(), getResourceName(), null);
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), getResourceName(), null);
notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao,

View File

@ -176,6 +176,8 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
IBundleProvider search(SearchParameterMap theMap);
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails);
IBundleProvider search(String theParameterName, IQueryParameterType theValue);
Set<Long> searchForIds(Map<String, IQueryParameterType> theParams);

View File

@ -36,6 +36,7 @@ import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -99,7 +100,8 @@ public class SearchBuilder {
private IHapiTerminologySvc myTerminologySvc;
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, IFulltextSearchSvc theSearchDao, ISearchResultDao theSearchResultDao, BaseHapiFhirDao<?> theDao,
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, IFulltextSearchSvc theSearchDao,
ISearchResultDao theSearchResultDao, BaseHapiFhirDao<?> theDao,
IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao, IForcedIdDao theForcedIdDao, IHapiTerminologySvc theTerminologySvc, ISearchParamRegistry theSearchParamRegistry) {
myContext = theFhirContext;
myEntityManager = theEntityManager;
@ -325,7 +327,8 @@ public class SearchBuilder {
return;
}
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates,
IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
@ -339,7 +342,8 @@ public class SearchBuilder {
return missingFalse;
}
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates,
IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
@ -560,7 +564,7 @@ public class SearchBuilder {
BaseRuntimeChildDefinition def = myContext.newTerser().getDefinition(myResourceType, paramPath);
if (def instanceof RuntimeChildChoiceDefinition) {
RuntimeChildChoiceDefinition choiceDef = (RuntimeChildChoiceDefinition)def;
RuntimeChildChoiceDefinition choiceDef = (RuntimeChildChoiceDefinition) def;
resourceTypes = choiceDef.getResourceTypes();
} else if (def instanceof RuntimeChildResourceDefinition) {
RuntimeChildResourceDefinition resDef = (RuntimeChildResourceDefinition) def;
@ -1132,7 +1136,8 @@ public class SearchBuilder {
predicates.addAll(createLastUpdatedPredicates(myParams.getLastUpdatedAndRemove(), builder, from));
}
private Predicate createPredicateNumeric(CriteriaBuilder builder, IQueryParameterType params, ParamPrefixEnum cmpValue, BigDecimal valueValue, final Expression<BigDecimal> path, String invalidMessageName, String theValueString) {
private Predicate createPredicateNumeric(CriteriaBuilder builder, IQueryParameterType params, ParamPrefixEnum cmpValue, BigDecimal valueValue, final Expression<BigDecimal> path,
String invalidMessageName, String theValueString) {
Predicate num;
switch (cmpValue) {
case GREATERTHAN:
@ -1230,7 +1235,6 @@ public class SearchBuilder {
}
private void createPredicateResourceId(CriteriaBuilder builder, CriteriaQuery<?> cq, List<Predicate> thePredicates, Expression<Long> theExpression) {
if (myParams.isPersistResults()) {
if (mySearchEntity.getTotalCount() > -1) {
Subquery<Long> subQ = cq.subquery(Long.class);
Root<SearchResult> subQfrom = subQ.from(SearchResult.class);
@ -1240,15 +1244,10 @@ public class SearchBuilder {
thePredicates.add(theExpression.in(subQ));
}
} else {
if (myPids != null) {
thePredicates.add(theExpression.in(myPids));
}
}
}
private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theFrom) {
private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theFrom) {
String rawSearchTerm;
if (theParameter instanceof TokenParam) {
TokenParam id = (TokenParam) theParameter;
@ -1267,7 +1266,8 @@ public class SearchBuilder {
}
if (rawSearchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed ("
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
}
String likeExpression = BaseHapiFhirDao.normalizeString(rawSearchTerm);
@ -1297,7 +1297,8 @@ public class SearchBuilder {
return orPredicates;
}
private Predicate createPredicateToken(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theFrom) {
private Predicate createPredicateToken(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theFrom) {
String code;
String system;
TokenParamModifier modifier = null;
@ -1319,11 +1320,13 @@ public class SearchBuilder {
}
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
throw new InvalidRequestException("Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
throw new InvalidRequestException(
"Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
}
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
throw new InvalidRequestException("Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
throw new InvalidRequestException(
"Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
}
/*
@ -1533,25 +1536,16 @@ public class SearchBuilder {
}
public Set<Long> doGetPids() {
if (myParams.isPersistResults()) {
HashSet<Long> retVal = new HashSet<Long>();
for (SearchResult next : mySearchResultDao.findWithSearchUuid(mySearchEntity)) {
retVal.add(next.getResourcePid());
}
return retVal;
} else {
return new HashSet<Long>(myPids);
}
}
private boolean doHaveNoResults() {
if (myParams.isPersistResults()) {
return mySearchEntity.getTotalCount() == 0;
} else {
return myPids != null && myPids.isEmpty();
}
}
private void doInitializeSearch() {
@ -1561,19 +1555,10 @@ public class SearchBuilder {
}
private IBundleProvider doReturnProvider() {
if (myParams.isPersistResults()) {
return new PersistedJpaBundleProvider(mySearchEntity.getUuid(), myCallingDao);
} else {
if (myPids == null) {
return new SimpleBundleProvider();
} else {
return new BundleProviderInMemory(myPids);
}
}
}
private void doSetPids(Collection<Long> thePids) {
if (myParams.isPersistResults()) {
if (mySearchEntity.getTotalCount() != null) {
reinitializeSearch();
}
@ -1593,10 +1578,6 @@ public class SearchBuilder {
mySearchEntity = myEntityManager.merge(mySearchEntity);
myEntityManager.flush();
} else {
myPids = thePids;
}
}
private void filterResourceIdsByLastUpdated(final DateRangeParam theLastUpdated) {
@ -1675,6 +1656,7 @@ public class SearchBuilder {
mySearchEntity.setUuid(UUID.randomUUID().toString());
mySearchEntity.setCreated(new Date());
mySearchEntity.setTotalCount(-1);
mySearchEntity.setSearchParamMap(SerializationUtils.serialize(myParams));
mySearchEntity.setPreferredPageSize(myParams.getCount());
mySearchEntity.setSearchType(myParams.getEverythingMode() != null ? SearchTypeEnum.EVERYTHING : SearchTypeEnum.SEARCH);
mySearchEntity.setLastUpdated(myParams.getLastUpdated());
@ -1686,13 +1668,11 @@ public class SearchBuilder {
mySearchEntity.getIncludes().add(new SearchInclude(mySearchEntity, next.getValue(), true, next.isRecurse()));
}
if (myParams.isPersistResults()) {
myEntityManager.persist(mySearchEntity);
for (SearchInclude next : mySearchEntity.getIncludes()) {
myEntityManager.persist(next);
}
}
}
public IBundleProvider search(final SearchParameterMap theParams) {
myParams = theParams;
@ -1814,7 +1794,7 @@ public class SearchBuilder {
doInitializeSearch();
// RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceType);
// RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceType);
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamEntry : params.entrySet()) {
String nextParamName = nextParamEntry.getKey();
@ -2038,7 +2018,8 @@ public class SearchBuilder {
return likeExpression.replace("%", "[%]") + "%";
}
private static Predicate createResourceLinkPathPredicate(IDao theCallingDao, FhirContext theContext, String theParamName, Root<? extends ResourceLink> from, Class<? extends IBaseResource> resourceType) {
private static Predicate createResourceLinkPathPredicate(IDao theCallingDao, FhirContext theContext, String theParamName, Root<? extends ResourceLink> from,
Class<? extends IBaseResource> resourceType) {
RuntimeResourceDefinition resourceDef = theContext.getResourceDefinition(resourceType);
RuntimeSearchParam param = theCallingDao.getSearchParamByName(resourceDef, theParamName);
List<String> path = param.getPathsSplit();
@ -2062,7 +2043,8 @@ public class SearchBuilder {
return resultList;
}
public static void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation, EntityManager entityManager, FhirContext context, IDao theDao) {
public static void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation,
EntityManager entityManager, FhirContext context, IDao theDao) {
if (theIncludePids.isEmpty()) {
return;
}
@ -2111,7 +2093,8 @@ public class SearchBuilder {
*
* @param theLastUpdated
*/
public static HashSet<Long> loadReverseIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode, DateRangeParam theLastUpdated) {
public static HashSet<Long> loadReverseIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes,
boolean theReverseMode, DateRangeParam theLastUpdated) {
if (theMatches.size() == 0) {
return new HashSet<Long>();
}
@ -2253,65 +2236,4 @@ public class SearchBuilder {
return thePredicates.toArray(new Predicate[thePredicates.size()]);
}
private final class BundleProviderInMemory implements IBundleProvider {
private final ArrayList<Long> myPids;
private BundleProviderInMemory(Collection<Long> thePids) {
final ArrayList<Long> pids;
if (!(thePids instanceof List)) {
pids = new ArrayList<Long>(thePids);
} else {
pids = (ArrayList<Long>) thePids;
}
myPids = pids;
}
@Override
public InstantDt getPublished() {
return new InstantDt(mySearchEntity.getCreated());
}
@Override
public List<IBaseResource> getResources(final int theFromIndex, final int theToIndex) {
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
return template.execute(new TransactionCallback<List<IBaseResource>>() {
@Override
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
List<Long> pidsSubList = myPids.subList(theFromIndex, theToIndex);
// Load includes
pidsSubList = new ArrayList<Long>(pidsSubList);
Set<Long> revIncludedPids = new HashSet<Long>();
if (myParams.getEverythingMode() == null) {
revIncludedPids.addAll(loadReverseIncludes(myCallingDao, myContext, myEntityManager, pidsSubList, myParams.getRevIncludes(), true, myParams.getLastUpdated()));
}
revIncludedPids.addAll(loadReverseIncludes(myCallingDao, myContext, myEntityManager, pidsSubList, myParams.getIncludes(), false, myParams.getLastUpdated()));
// Execute the query and make sure we return distinct results
List<IBaseResource> resources = new ArrayList<IBaseResource>();
loadResourcesByPid(pidsSubList, resources, revIncludedPids, false);
return resources;
}
});
}
@Override
public Integer preferredPageSize() {
return myParams.getCount();
}
@Override
public int size() {
return myPids.size();
}
@Override
public String getUuid() {
return null;
}
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao;
import java.io.Serializable;
/*
* #%L
* HAPI FHIR JPA Server
@ -38,7 +40,7 @@ import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.Constants;
public class SearchParameterMap extends LinkedHashMap<String, List<List<? extends IQueryParameterType>>> {
public class SearchParameterMap extends LinkedHashMap<String, List<List<? extends IQueryParameterType>>> implements Serializable {
private static final long serialVersionUID = 1L;
@ -46,8 +48,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
private EverythingModeEnum myEverythingMode = null;
private Set<Include> myIncludes;
private DateRangeParam myLastUpdated;
private boolean myPersistResults = true;
private RequestDetails myRequestDetails;
private Set<Include> myRevIncludes;
private SortSpec mySort;
@ -137,10 +137,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return retVal;
}
public RequestDetails getRequestDetails() {
return myRequestDetails;
}
public Set<Include> getRevIncludes() {
if (myRevIncludes == null) {
myRevIncludes = new HashSet<Include>();
@ -152,10 +148,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return mySort;
}
public boolean isPersistResults() {
return myPersistResults;
}
public void setCount(Integer theCount) {
myCount = theCount;
}
@ -173,14 +165,11 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
}
/**
* Should results be persisted into a table for paging
* @deprecated As of HAPI FHIR 2.4 this method no longer does anything
*/
@Deprecated
public void setPersistResults(boolean thePersistResults) {
myPersistResults = thePersistResults;
}
public void setRequestDetails(RequestDetails theRequestDetails) {
myRequestDetails = theRequestDetails;
// does nothing as of HAPI FHIR 2.4
}
public void setRevIncludes(Set<Include> theRevIncludes) {

View File

@ -28,20 +28,7 @@ import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import javax.persistence.*;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.param.DateRangeParam;
@ -91,6 +78,10 @@ public class Search implements Serializable {
@OneToMany(mappedBy="mySearch")
private Collection<SearchResult> myResults;
@Lob
@Column(name="SEARCH_PARAM_MAP", nullable=true)
private byte[] mySearchParamMap;
@Enumerated(EnumType.ORDINAL)
@Column(name="SEARCH_TYPE", nullable=false)
private SearchTypeEnum mySearchType;
@ -116,14 +107,6 @@ public class Search implements Serializable {
return myIncludes;
}
public Date getLastUpdatedHigh() {
return myLastUpdatedHigh;
}
public Date getLastUpdatedLow() {
return myLastUpdatedLow;
}
public DateRangeParam getLastUpdated() {
if (myLastUpdatedLow == null && myLastUpdatedHigh == null) {
return null;
@ -132,6 +115,14 @@ public class Search implements Serializable {
}
}
public Date getLastUpdatedHigh() {
return myLastUpdatedHigh;
}
public Date getLastUpdatedLow() {
return myLastUpdatedLow;
}
public Integer getPreferredPageSize() {
return myPreferredPageSize;
}
@ -144,15 +135,19 @@ public class Search implements Serializable {
return myResourceType;
}
public byte[] getSearchParamMap() {
return mySearchParamMap;
}
public SearchTypeEnum getSearchType() {
return mySearchType;
}
public Integer getTotalCount() {
return myTotalCount;
}
public String getUuid() {
return myUuid;
}
@ -160,11 +155,11 @@ public class Search implements Serializable {
public void setCreated(Date theCreated) {
myCreated = theCreated;
}
public void setLastUpdated(Date theLowerBound, Date theUpperBound) {
myLastUpdatedLow = theLowerBound;
myLastUpdatedHigh = theUpperBound;
}
public void setLastUpdated(DateRangeParam theLastUpdated) {
if (theLastUpdated == null) {
myLastUpdatedLow = null;
@ -183,11 +178,15 @@ public class Search implements Serializable {
myResourceId = theResourceId;
}
public void setResourceType(String theResourceType) {
myResourceType = theResourceType;
}
public void setSearchParamMap(byte[] theSearchParamMap) {
mySearchParamMap = theSearchParamMap;
}
public void setSearchType(SearchTypeEnum theSearchType) {
mySearchType = theSearchType;
}

View File

@ -152,11 +152,10 @@ public class ${className}ResourceProvider extends
paramMap.setIncludes(theIncludes);
paramMap.setSort(theSort);
paramMap.setCount(theCount);
paramMap.setRequestDetails(theRequestDetails);
getDao().translateRawParameters(theAdditionalRawParams, paramMap);
ca.uhn.fhir.rest.server.IBundleProvider retVal = getDao().search(paramMap);
ca.uhn.fhir.rest.server.IBundleProvider retVal = getDao().search(paramMap, theRequestDetails);
return retVal;
} finally {
endRequest(theServletRequest);