001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2024 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 java.util.ArrayList; 023import java.util.Collections; 024import java.util.List; 025 026/* 027 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 028 numbers of parameters, this can overwhelm Hibernate's QueryPlanCache and deplete heap space. See the following link for more info: 029 https://stackoverflow.com/questions/31557076/spring-hibernate-query-plan-cache-memory-usage. 030 031 Normalizing the number of parameters in the "in" clause stabilizes the size of the QueryPlanCache, so long as the number of 032 arguments never exceeds the maximum specified below. 033*/ 034public class InClauseNormalizer { 035 036 public static List<Long> normalizeIdListForInClause(List<Long> theResourceIds) { 037 038 List<Long> retVal = theResourceIds; 039 040 int listSize = theResourceIds.size(); 041 042 if (listSize > 1 && listSize < 10) { 043 retVal = padIdListWithPlaceholders(theResourceIds, 10); 044 } else if (listSize > 10 && listSize < 50) { 045 retVal = padIdListWithPlaceholders(theResourceIds, 50); 046 } else if (listSize > 50 && listSize < 100) { 047 retVal = padIdListWithPlaceholders(theResourceIds, 100); 048 } else if (listSize > 100 && listSize < 200) { 049 retVal = padIdListWithPlaceholders(theResourceIds, 200); 050 } else if (listSize > 200 && listSize < 500) { 051 retVal = padIdListWithPlaceholders(theResourceIds, 500); 052 } else if (listSize > 500 && listSize < 800) { 053 retVal = padIdListWithPlaceholders(theResourceIds, 800); 054 } 055 056 return retVal; 057 } 058 059 private static List<Long> padIdListWithPlaceholders(List<Long> theIdList, int preferredListSize) { 060 List<Long> retVal = theIdList; 061 062 if (isUnmodifiableList(theIdList)) { 063 retVal = new ArrayList<>(preferredListSize); 064 retVal.addAll(theIdList); 065 } 066 067 while (retVal.size() < preferredListSize) { 068 retVal.add(-1L); 069 } 070 071 return retVal; 072 } 073 074 private static boolean isUnmodifiableList(List<Long> theList) { 075 try { 076 theList.addAll(Collections.emptyList()); 077 } catch (Exception e) { 078 return true; 079 } 080 return false; 081 } 082 083 private InClauseNormalizer() {} 084}