Skip to content

Commit 211da0a

Browse files
authored
Merge pull request #829 from hapifhir/ja_20250502_improve_dialect_handling
Improve dialect handling
2 parents 20b0977 + 0d8f2aa commit 211da0a

File tree

5 files changed

+105
-15
lines changed

5 files changed

+105
-15
lines changed

src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.Objects;
2020
import java.util.Set;
2121

22+
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
23+
2224
@ConfigurationProperties(prefix = "hapi.fhir")
2325
@Configuration
2426
@EnableConfigurationProperties
@@ -46,6 +48,7 @@ public class AppProperties {
4648
private Boolean mass_ingestion_mode_enabled = false;
4749
private Boolean language_search_parameter_enabled = false;
4850
private Boolean dao_scheduling_enabled = true;
51+
private Boolean delete_enabled = true;
4952
private Boolean delete_expunge_enabled = false;
5053
private Boolean enable_index_missing_fields = false;
5154
private Boolean enable_index_contained_resource = false;
@@ -111,6 +114,8 @@ public class AppProperties {
111114
JpaStorageSettings.StoreMetaSourceInformationEnum.NONE;
112115

113116
private Map<String, RemoteSystem> remote_terminology_service = null;
117+
private Boolean match_url_cache_enabled = false;
118+
private Boolean index_storage_optimized = false;
114119

115120
public List<String> getCustomInterceptorClasses() {
116121
return custom_interceptor_classes;
@@ -376,6 +381,14 @@ public Boolean getDelete_expunge_enabled() {
376381
return delete_expunge_enabled;
377382
}
378383

384+
public boolean getDelete_enabled() {
385+
return defaultIfNull(delete_enabled, true);
386+
}
387+
388+
public void setDelete_enabled(boolean theDelete_enabled) {
389+
delete_enabled = theDelete_enabled;
390+
}
391+
379392
public void setDelete_expunge_enabled(Boolean delete_expunge_enabled) {
380393
this.delete_expunge_enabled = delete_expunge_enabled;
381394
}
@@ -745,6 +758,22 @@ public void setRemote_terminology_service(Map<String, RemoteSystem> remote_termi
745758
this.remote_terminology_service = remote_terminology_service;
746759
}
747760

761+
public boolean getMatch_url_cache_enabled() {
762+
return defaultIfNull(match_url_cache_enabled, false);
763+
}
764+
765+
public void setMatch_url_cache_enabled(boolean theMatchUrlCacheEnabled) {
766+
match_url_cache_enabled = theMatchUrlCacheEnabled;
767+
}
768+
769+
public boolean getIndex_storage_optimized() {
770+
return defaultIfNull(index_storage_optimized, false);
771+
}
772+
773+
public void setIndex_storage_optimized(boolean theIndex_storage_optimized) {
774+
index_storage_optimized = theIndex_storage_optimized;
775+
}
776+
748777
public JpaStorageSettings.StoreMetaSourceInformationEnum getStore_meta_source_information() {
749778
return store_meta_source_information;
750779
}

src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import ca.uhn.fhir.rest.server.mail.MailSvc;
1717
import com.google.common.base.Strings;
1818
import org.hl7.fhir.r4.model.Bundle.BundleType;
19+
import org.slf4j.Logger;
20+
import org.slf4j.LoggerFactory;
1921
import org.springframework.boot.env.YamlPropertySourceLoader;
2022
import org.springframework.context.annotation.Bean;
2123
import org.springframework.context.annotation.Configuration;
@@ -36,7 +38,7 @@
3638
@EnableTransactionManagement
3739
public class FhirServerConfigCommon {
3840

39-
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirServerConfigCommon.class);
41+
private static final Logger ourLog = LoggerFactory.getLogger(FhirServerConfigCommon.class);
4042

4143
public FhirServerConfigCommon(AppProperties appProperties) {
4244
ourLog.info("Server configured to " + (appProperties.getAllow_contains_searches() ? "allow" : "deny")
@@ -176,6 +178,9 @@ public JpaStorageSettings jpaStorageSettings(AppProperties appProperties) {
176178
jpaStorageSettings.setAllowMultipleDelete(appProperties.getAllow_multiple_delete());
177179
jpaStorageSettings.setAllowExternalReferences(appProperties.getAllow_external_references());
178180
jpaStorageSettings.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled());
181+
jpaStorageSettings.setIndexStorageOptimized(appProperties.getIndex_storage_optimized());
182+
jpaStorageSettings.setMatchUrlCacheEnabled(appProperties.getMatch_url_cache_enabled());
183+
jpaStorageSettings.setDeleteEnabled(appProperties.getDelete_enabled());
179184
jpaStorageSettings.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled());
180185
jpaStorageSettings.setExpungeEnabled(appProperties.getExpunge_enabled());
181186
jpaStorageSettings.setLanguageSearchParameterEnabled(appProperties.getLanguage_search_parameter_enabled());
@@ -291,6 +296,19 @@ public PartitionSettings partitionSettings(AppProperties appProperties) {
291296
}
292297
retVal.setConditionalCreateDuplicateIdentifiersEnabled(
293298
appProperties.getPartitioning().getConditional_create_duplicate_identifiers_enabled());
299+
300+
ourLog.info(
301+
"""
302+
Partitioning is enabled on this server. Settings:
303+
* Database Partition Mode Enabled: {}
304+
* Default Partition ID : {}
305+
* Cross-Partition References : {}""",
306+
databasePartitionModeEnabled,
307+
defaultPartitionId,
308+
retVal.getAllowReferencesAcrossPartitions());
309+
310+
} else {
311+
ourLog.info("Partitioning is not enabled on this server");
294312
}
295313

296314
return retVal;

src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,45 @@
22

33
import ca.uhn.fhir.context.ConfigurationException;
44
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
5+
import ca.uhn.fhir.util.ReflectionUtil;
56
import org.hibernate.dialect.Dialect;
67
import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver;
78
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
811
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
912

1013
import java.sql.Connection;
1114
import java.sql.SQLException;
1215
import javax.sql.DataSource;
1316

17+
import static org.apache.commons.lang3.StringUtils.isNotBlank;
18+
1419
public class JpaHibernatePropertiesProvider extends HibernatePropertiesProvider {
20+
private static final Logger ourLog = LoggerFactory.getLogger(JpaHibernatePropertiesProvider.class);
1521

16-
private final Dialect dialect;
22+
private final Dialect myDialect;
1723

18-
public JpaHibernatePropertiesProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) {
19-
DataSource connection = myEntityManagerFactory.getDataSource();
20-
try (Connection dbConnection = connection.getConnection()) {
21-
dialect = new StandardDialectResolver()
22-
.resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(dbConnection.getMetaData()));
23-
} catch (SQLException sqlException) {
24-
throw new ConfigurationException(sqlException.getMessage(), sqlException);
24+
public JpaHibernatePropertiesProvider(LocalContainerEntityManagerFactoryBean theEntityManagerFactory) {
25+
String dialectClass =
26+
(String) theEntityManagerFactory.getJpaPropertyMap().get("hibernate.dialect");
27+
if (isNotBlank(dialectClass)) {
28+
myDialect = ReflectionUtil.newInstanceOrReturnNull(dialectClass, Dialect.class);
29+
} else {
30+
ourLog.warn(
31+
"'hibernate.dialect' not set in application configuration! Please explicitly specify a valid HAPI FHIR hibernate dialect.");
32+
DataSource connection = theEntityManagerFactory.getDataSource();
33+
try (Connection dbConnection = connection.getConnection()) {
34+
myDialect = new StandardDialectResolver()
35+
.resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(dbConnection.getMetaData()));
36+
} catch (SQLException sqlException) {
37+
throw new ConfigurationException(sqlException.getMessage(), sqlException);
38+
}
2539
}
2640
}
2741

2842
@Override
2943
public Dialect getDialect() {
30-
return dialect;
44+
return myDialect;
3145
}
3246
}

src/main/resources/application.yaml

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,12 @@ hapi:
190190
# default_encoding: JSON
191191
# default_pretty_print: true
192192
# default_page_size: 20
193+
# delete_enabled: true
193194
# delete_expunge_enabled: true
195+
# match_url_cache_enabled: false
194196
# enable_repository_validating_interceptor: true
197+
### Reduce the size used by search indexes by not tagging every row with the resource type and parameter name (this setting makes manual inspection of the database more difficult, but does not impact HAPI FHIR functionality in any way)
198+
# index_storage_optimized: false
195199
# enable_index_missing_fields: false
196200
# enable_index_of_type: true
197201
# enable_index_contained_resource: false
@@ -237,11 +241,26 @@ hapi:
237241
- https://unitsofmeasure.org/*
238242
- http://loinc.org/*
239243
- https://loinc.org/*
240-
# partitioning:
241-
# allow_references_across_partitions: false
242-
# partitioning_include_in_search_hashes: false
243-
# conditional_create_duplicate_identifiers_enabled: false
244-
# request_tenant_partitioning_mode: true
244+
245+
### Uncomment the following section, and any sub-properties you need in order to enable
246+
### partitioning support on this server.
247+
partitioning:
248+
allow_references_across_partitions: false
249+
partitioning_include_in_search_hashes: false
250+
default_partition_id: 0
251+
### Enable the following setting to enable Database Partitioning Mode
252+
### See: https://hapifhir.io/hapi-fhir/docs/server_jpa_partitioning/db_partition_mode.html
253+
database_partition_mode_enabled: true
254+
### Partition Style: Partitioning requires a partition interceptor which helps the server
255+
### select which partition(s) should be accessed for a given request. You can supply your
256+
### own interceptor (see https://hapifhir.io/hapi-fhir/docs/server_jpa_partitioning/partitioning.html#partition-interceptors )
257+
### but the following setting can also be used to use a built-in form.
258+
### Patient ID Partitioning Mode uses the patient/subject ID to determine the partition
259+
patient_id_partitioning_mode: true
260+
### Request tenant mode can be used for a multi-tenancy setup where the request path is
261+
### expected to have an additional path element, e.g. GET http://example.com/fhir/TENANT-ID/Patient/A
262+
request_tenant_partitioning_mode: false
263+
245264
cors:
246265
allow_Credentials: true
247266
# These are allowed_origin patterns, see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/cors/CorsConfiguration.html#setAllowedOriginPatterns-java.util.List-

src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package ca.uhn.fhir.jpa.starter;
22

33
import ca.uhn.fhir.context.FhirContext;
4+
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
5+
import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect;
46
import ca.uhn.fhir.jpa.searchparam.config.NicknameServiceConfig;
57
import ca.uhn.fhir.jpa.starter.cr.CrProperties;
68
import ca.uhn.fhir.model.primitive.IdDt;
@@ -80,6 +82,9 @@ class ExampleServerR4IT implements IServerSupport {
8082
@Autowired
8183
private CrProperties crProperties;
8284

85+
@Autowired
86+
private HibernatePropertiesProvider myHibernatePropertiesProvider;
87+
8388
@LocalServerPort
8489
private int port;
8590

@@ -367,6 +372,11 @@ void testValidateRemoteTerminology() {
367372
Parameters localResult = ourClient.operation().onType(CodeSystem.class).named("$validate-code").withParameter(Parameters.class, "url", new UrlType(testCodeSystem)).andParameter("coding", new Coding(testCodeSystem, "yes", null)).execute();
368373
}
369374

375+
@Test
376+
public void testHibernatePropertiesProvider_GetDialect() {
377+
assertEquals(HapiFhirH2Dialect.class, myHibernatePropertiesProvider.getDialect().getClass());
378+
}
379+
370380
@BeforeEach
371381
void beforeEach() {
372382

0 commit comments

Comments
 (0)