
001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2025 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.util; 021 022import ca.uhn.fhir.jpa.model.dao.JpaPid; 023import ca.uhn.fhir.jpa.model.util.JpaConstants; 024 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.List; 028 029/* 030 This class encapsulate the implementation providing a workaround to a known issue involving Hibernate. If queries are used with "in" clauses with large and varying 031 numbers of parameters, this can overwhelm Hibernate's QueryPlanCache and deplete heap space. See the following link for more info: 032 https://stackoverflow.com/questions/31557076/spring-hibernate-query-plan-cache-memory-usage. 033 034 Normalizing the number of parameters in the "in" clause stabilizes the size of the QueryPlanCache, so long as the number of 035 arguments never exceeds the maximum specified below. 036*/ 037public class InClauseNormalizer { 038 039 public static List<JpaPid> normalizeIdListForInClause(List<JpaPid> theResourceIds) { 040 041 List<JpaPid> retVal = theResourceIds; 042 043 int listSize = theResourceIds.size(); 044 045 if (listSize > 1 && listSize < 10) { 046 retVal = padIdListWithPlaceholders(theResourceIds, 10); 047 } else if (listSize > 10 && listSize < 50) { 048 retVal = padIdListWithPlaceholders(theResourceIds, 50); 049 } else if (listSize > 50 && listSize < 100) { 050 retVal = padIdListWithPlaceholders(theResourceIds, 100); 051 } else if (listSize > 100 && listSize < 200) { 052 retVal = padIdListWithPlaceholders(theResourceIds, 200); 053 } else if (listSize > 200 && listSize < 500) { 054 retVal = padIdListWithPlaceholders(theResourceIds, 500); 055 } else if (listSize > 500 && listSize < 800) { 056 retVal = padIdListWithPlaceholders(theResourceIds, 800); 057 } 058 059 return retVal; 060 } 061 062 private static List<JpaPid> padIdListWithPlaceholders(List<JpaPid> theIdList, int preferredListSize) { 063 List<JpaPid> retVal = theIdList; 064 065 if (isUnmodifiableList(theIdList)) { 066 retVal = new ArrayList<>(preferredListSize); 067 retVal.addAll(theIdList); 068 } 069 070 while (retVal.size() < preferredListSize) { 071 retVal.add(JpaConstants.NO_MORE); 072 } 073 074 return retVal; 075 } 076 077 private static boolean isUnmodifiableList(List<JpaPid> theList) { 078 try { 079 theList.addAll(Collections.emptyList()); 080 } catch (Exception e) { 081 return true; 082 } 083 return false; 084 } 085 086 private InClauseNormalizer() {} 087}