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