001/*
002 * #%L
003 * HAPI FHIR JPA Model
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.model.listener;
021
022import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
023import ca.uhn.fhir.jpa.model.entity.StorageSettings;
024import ca.uhn.fhir.jpa.model.search.ISearchParamHashIdentityRegistry;
025import ca.uhn.fhir.rest.server.util.IndexedSearchParam;
026import jakarta.persistence.PostLoad;
027import jakarta.persistence.PostPersist;
028import jakarta.persistence.PostUpdate;
029import jakarta.persistence.PrePersist;
030import jakarta.persistence.PreUpdate;
031import org.springframework.beans.factory.annotation.Autowired;
032import org.springframework.context.ApplicationContext;
033
034import java.util.Optional;
035
036/**
037 * Sets <code>SP_NAME, RES_TYPE, SP_UPDATED</code> column values to null for all HFJ_SPIDX tables
038 * if storage setting {@link ca.uhn.fhir.jpa.model.entity.StorageSettings#isIndexStorageOptimized()} is enabled.
039 * <p>
040 * Using EntityListener to change HFJ_SPIDX column values right before insert/update to database.
041 * </p>
042 * <p>
043 * As <code>SP_NAME, RES_TYPE</code> values could still be used after merge/persist to database, we are restoring
044 * them from <code>HASH_IDENTITY</code> value.
045 *</p>
046 * See {@link ca.uhn.fhir.jpa.model.entity.StorageSettings#setIndexStorageOptimized(boolean)}
047 */
048public class IndexStorageOptimizationListener {
049
050        public IndexStorageOptimizationListener(
051                        @Autowired StorageSettings theStorageSettings, @Autowired ApplicationContext theApplicationContext) {
052                this.myStorageSettings = theStorageSettings;
053                this.myApplicationContext = theApplicationContext;
054        }
055
056        private final StorageSettings myStorageSettings;
057        private final ApplicationContext myApplicationContext;
058
059        @PrePersist
060        @PreUpdate
061        public void optimizeSearchParams(Object theEntity) {
062                if (myStorageSettings.isIndexStorageOptimized() && theEntity instanceof BaseResourceIndexedSearchParam) {
063                        ((BaseResourceIndexedSearchParam) theEntity).optimizeIndexStorage();
064                }
065        }
066
067        @PostLoad
068        @PostPersist
069        @PostUpdate
070        public void restoreSearchParams(Object theEntity) {
071                if (myStorageSettings.isIndexStorageOptimized() && theEntity instanceof BaseResourceIndexedSearchParam) {
072                        restoreSearchParams((BaseResourceIndexedSearchParam) theEntity);
073                }
074        }
075
076        /**
077         * As <code>SP_NAME, RES_TYPE</code> values could still be used after merge/persist to database (mostly by tests),
078         * we are restoring them from <code>HASH_IDENTITY</code> value.
079         * Note that <code>SP_NAME, RES_TYPE</code> values are not recovered if
080         * {@link ca.uhn.fhir.jpa.model.entity.StorageSettings#isIndexOnContainedResources()} or
081         * {@link ca.uhn.fhir.jpa.model.entity.StorageSettings#isIndexOnContainedResourcesRecursively()}
082         * settings are enabled.
083         */
084        private void restoreSearchParams(BaseResourceIndexedSearchParam theResourceIndexedSearchParam) {
085                // getting ISearchParamHashIdentityRegistry from the App Context as it is initialized after EntityListeners
086                ISearchParamHashIdentityRegistry searchParamRegistry =
087                                myApplicationContext.getBean(ISearchParamHashIdentityRegistry.class);
088                Optional<IndexedSearchParam> indexedSearchParamOptional =
089                                searchParamRegistry.getIndexedSearchParamByHashIdentity(
090                                                theResourceIndexedSearchParam.getHashIdentity());
091
092                if (indexedSearchParamOptional.isPresent()) {
093                        theResourceIndexedSearchParam.setResourceType(
094                                        indexedSearchParamOptional.get().getResourceType());
095                        theResourceIndexedSearchParam.restoreParamName(
096                                        indexedSearchParamOptional.get().getParameterName());
097                }
098        }
099}