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.dialect;
021
022import ca.uhn.fhir.jpa.model.entity.StorageSettings;
023import ca.uhn.fhir.jpa.util.ISequenceValueMassager;
024import org.apache.commons.lang3.Validate;
025import org.hibernate.HibernateException;
026import org.hibernate.MappingException;
027import org.hibernate.boot.model.relational.Database;
028import org.hibernate.boot.model.relational.ExportableProducer;
029import org.hibernate.boot.model.relational.SqlStringGenerationContext;
030import org.hibernate.engine.spi.SharedSessionContractImplementor;
031import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
032import org.hibernate.id.IdentifierGenerator;
033import org.hibernate.id.OptimizableGenerator;
034import org.hibernate.id.PersistentIdentifierGenerator;
035import org.hibernate.id.enhanced.Optimizer;
036import org.hibernate.id.enhanced.SequenceStyleGenerator;
037import org.hibernate.id.enhanced.StandardOptimizerDescriptor;
038import org.hibernate.service.ServiceRegistry;
039import org.hibernate.type.Type;
040import org.springframework.beans.factory.annotation.Autowired;
041
042import java.io.Serializable;
043import java.util.Properties;
044
045/**
046 * This is a sequence generator that wraps the Hibernate default sequence generator {@link SequenceStyleGenerator}
047 * and by default will therefore work exactly as the default would, but allows for customization.
048 */
049@SuppressWarnings("unused")
050public class HapiSequenceStyleGenerator
051                implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, ExportableProducer {
052        public static final String ID_MASSAGER_TYPE_KEY = "hapi_fhir.sequence_generator_massager";
053        private final SequenceStyleGenerator myGen = new SequenceStyleGenerator();
054
055        @Autowired
056        private StorageSettings myStorageSettings;
057
058        private ISequenceValueMassager myIdMassager;
059        private boolean myConfigured;
060        private String myGeneratorName;
061
062        @Override
063        public boolean supportsBulkInsertionIdentifierGeneration() {
064                return myGen.supportsBulkInsertionIdentifierGeneration();
065        }
066
067        @Override
068        public String determineBulkInsertionIdentifierGenerationSelectFragment(SqlStringGenerationContext theContext) {
069                return myGen.determineBulkInsertionIdentifierGenerationSelectFragment(theContext);
070        }
071
072        @Override
073        public Serializable generate(SharedSessionContractImplementor theSession, Object theObject)
074                        throws HibernateException {
075                Long retVal = myIdMassager != null ? myIdMassager.generate(myGeneratorName) : null;
076                if (retVal == null) {
077                        Long next = (Long) myGen.generate(theSession, theObject);
078                        retVal = myIdMassager.massage(myGeneratorName, next);
079                }
080                return retVal;
081        }
082
083        @Override
084        public void configure(Type theType, Properties theParams, ServiceRegistry theServiceRegistry)
085                        throws MappingException {
086
087                myIdMassager = theServiceRegistry.getService(ISequenceValueMassager.class);
088                if (myIdMassager == null) {
089                        myIdMassager = new ISequenceValueMassager.NoopSequenceValueMassager();
090                }
091
092                // Create a HAPI FHIR sequence style generator
093                myGeneratorName = theParams.getProperty(IdentifierGenerator.GENERATOR_NAME);
094                Validate.notBlank(myGeneratorName, "No generator name found");
095
096                Properties props = new Properties(theParams);
097                props.put(OptimizableGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName());
098                props.put(OptimizableGenerator.INITIAL_PARAM, "1");
099                props.put(OptimizableGenerator.INCREMENT_PARAM, "50");
100                props.put(GENERATOR_NAME, myGeneratorName);
101
102                myGen.configure(theType, props, theServiceRegistry);
103
104                myConfigured = true;
105        }
106
107        @Override
108        public void registerExportables(Database database) {
109                myGen.registerExportables(database);
110        }
111
112        @Override
113        public void initialize(SqlStringGenerationContext context) {
114                myGen.initialize(context);
115        }
116
117        @Override
118        public boolean supportsJdbcBatchInserts() {
119                return myGen.supportsJdbcBatchInserts();
120        }
121
122        @Override
123        public Optimizer getOptimizer() {
124                return myGen.getOptimizer();
125        }
126}