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.config.util;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
024import ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect;
025import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean;
026import ca.uhn.fhir.jpa.util.ISequenceValueMassager;
027import ca.uhn.fhir.util.ReflectionUtil;
028import jakarta.persistence.spi.PersistenceUnitInfo;
029import org.hibernate.boot.registry.BootstrapServiceRegistry;
030import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
031import org.hibernate.jpa.HibernatePersistenceProvider;
032import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl;
033import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor;
034import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder;
035import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
036import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
037
038import java.util.Map;
039
040public final class HapiEntityManagerFactoryUtil {
041        private HapiEntityManagerFactoryUtil() {}
042
043        /**
044         * This method provides a partially completed entity manager
045         * factory with HAPI FHIR customizations
046         */
047        public static LocalContainerEntityManagerFactoryBean newEntityManagerFactory(
048                        ConfigurableListableBeanFactory myConfigurableListableBeanFactory,
049                        FhirContext theFhirContext,
050                        JpaStorageSettings theStorageSettings) {
051
052                LocalContainerEntityManagerFactoryBean retVal =
053                                new HapiFhirLocalContainerEntityManagerFactoryBean(myConfigurableListableBeanFactory);
054
055                configureEntityManagerFactory(retVal, theFhirContext, theStorageSettings);
056                return retVal;
057        }
058
059        public static void configureEntityManagerFactory(
060                        LocalContainerEntityManagerFactoryBean theFactory,
061                        FhirContext theFhirContext,
062                        JpaStorageSettings theStorageSettings) {
063                theFactory.setJpaDialect(new HapiFhirHibernateJpaDialect(theFhirContext.getLocalizer()));
064                theFactory.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity");
065                theFactory.setPersistenceProvider(new MyHibernatePersistenceProvider(theStorageSettings));
066        }
067
068        private static class MyHibernatePersistenceProvider extends HibernatePersistenceProvider {
069
070                private final JpaStorageSettings myStorageSettings;
071
072                public MyHibernatePersistenceProvider(JpaStorageSettings theStorageSettings) {
073                        myStorageSettings = theStorageSettings;
074                }
075
076                /**
077                 * @see MyEntityManagerFactoryBuilderImpl for an explanation of why we do this
078                 */
079                @Override
080                protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder(
081                                PersistenceUnitInfo info, Map<?, ?> integration) {
082                        return new MyEntityManagerFactoryBuilderImpl(info, integration);
083                }
084
085                /**
086                 * This class extends the default hibernate EntityManagerFactoryBuilder in order to
087                 * register a custom service (the {@link ISequenceValueMassager}, which is used in
088                 * {@link ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator}.
089                 * <p>
090                 * In Hibernate 5 we didn't need to do this, since we could just register
091                 * the service with Spring and Hibernate would ask Spring for it. This no longer
092                 * seems to work in Hibernate 6, so we now have to manually register it.
093                 */
094                private class MyEntityManagerFactoryBuilderImpl extends EntityManagerFactoryBuilderImpl {
095                        public MyEntityManagerFactoryBuilderImpl(PersistenceUnitInfo theInfo, Map<?, ?> theIntegration) {
096                                super(new PersistenceUnitInfoDescriptor(theInfo), (Map) theIntegration);
097                        }
098
099                        @Override
100                        protected StandardServiceRegistryBuilder getStandardServiceRegistryBuilder(BootstrapServiceRegistry bsr) {
101                                StandardServiceRegistryBuilder retVal = super.getStandardServiceRegistryBuilder(bsr);
102                                ISequenceValueMassager sequenceValueMassager =
103                                                ReflectionUtil.newInstance(myStorageSettings.getSequenceValueMassagerClass());
104                                retVal.addService(ISequenceValueMassager.class, sequenceValueMassager);
105                                return retVal;
106                        }
107                }
108        }
109}