From ccc57aedf6bb53e13f11c5bca2a170ebdbf7e848 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 21 Feb 2020 16:11:26 +0000 Subject: [PATCH 1/3] No need to ignore non-string values from JPA configuration --- .../boot/LightPersistenceXmlDescriptor.java | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java index 9893a01cd8ff3..d1e26398b6e14 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java @@ -3,9 +3,7 @@ import java.net.URL; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Properties; -import java.util.Set; import javax.persistence.SharedCacheMode; import javax.persistence.ValidationMode; @@ -36,7 +34,7 @@ public LightPersistenceXmlDescriptor(final PersistenceUnitDescriptor toClone) { this.validationMode = toClone.getValidationMode(); this.sharedCachemode = toClone.getSharedCacheMode(); this.managedClassNames = Collections.unmodifiableList(toClone.getManagedClassNames()); - this.properties = filterNonStrings(toClone.getProperties()); + this.properties = toClone.getProperties(); verifyIgnoredFields(toClone); } @@ -63,25 +61,6 @@ private static void verifyIgnoredFields(final PersistenceUnitDescriptor toClone) } } - private static final Properties filterNonStrings(final Properties properties) { - Properties clean = new Properties(); - final Set> entries = properties.entrySet(); - for (Map.Entry e : entries) { - final Object key = e.getKey(); - if (!(key instanceof String)) { - log.infof("Ignoring persistence unit property key '%s' as it's not a String", key); - continue; - } - final Object value = e.getValue(); - if (!(value instanceof String)) { - log.infof("Ignoring persistence unit property for key '%s' as the value is not a String", key); - continue; - } - clean.setProperty((String) key, (String) value); - } - return clean; - } - @Override public URL getPersistenceUnitRootUrl() { return null; From b51d4e681c4bb2ec0f36e52424b9ac5dfc4623e0 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 15 Oct 2019 11:25:24 +0100 Subject: [PATCH 2/3] Allow to disable Hibernate ORM 2nd level cache --- .../orm/deployment/HibernateOrmConfig.java | 11 ++++++++ .../orm/deployment/HibernateOrmProcessor.java | 28 ++++++++++++++++--- .../runtime/boot/FastBootMetadataBuilder.java | 18 ------------ 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java index 1979c497e689e..d232028661602 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java @@ -148,6 +148,17 @@ public class HibernateOrmConfig { @ConfigItem(name = "metrics.enabled", defaultValue = "false") public boolean metricsEnabled; + /** + * The default in Quarkus is for 2nd level caching to be enabled, + * and a good implementation is already integrated for you. + *

+ * Just cherry-pick which entities should be using the cache. + *

+ * Set this to false to disable all 2nd level caches. + */ + @ConfigItem(defaultValue = "true") + public boolean secondLevelCachingEnabled; + public boolean isAnyPropertySet() { return dialect.isPresent() || dialectStorageEngine.isPresent() || diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index 5dd0a319845f8..30664a2f20a50 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -2,6 +2,10 @@ import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; +import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_MODE; +import static org.hibernate.cfg.AvailableSettings.USE_DIRECT_REFERENCE_CACHE_ENTRIES; +import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE; +import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE; import java.io.IOException; import java.net.URL; @@ -17,12 +21,14 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; import javax.enterprise.inject.Produces; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceUnit; +import javax.persistence.SharedCacheMode; import javax.persistence.spi.PersistenceUnitTransactionType; import org.eclipse.microprofile.metrics.Metadata; @@ -672,10 +678,24 @@ private void handleHibernateORMWithNoPersistenceXml( } // Caching - Map cacheConfigEntries = HibernateConfigUtil - .getCacheConfigEntries(hibernateConfig); - for (Entry entry : cacheConfigEntries.entrySet()) { - desc.getProperties().setProperty(entry.getKey(), entry.getValue()); + if (hibernateConfig.secondLevelCachingEnabled) { + Properties p = desc.getProperties(); + //Only set these if the user isn't making an explicit choice: + p.putIfAbsent(USE_DIRECT_REFERENCE_CACHE_ENTRIES, Boolean.TRUE); + p.putIfAbsent(USE_SECOND_LEVEL_CACHE, Boolean.TRUE); + p.putIfAbsent(USE_QUERY_CACHE, Boolean.TRUE); + p.putIfAbsent(JPA_SHARED_CACHE_MODE, SharedCacheMode.ENABLE_SELECTIVE); + Map cacheConfigEntries = HibernateConfigUtil.getCacheConfigEntries(hibernateConfig); + for (Entry entry : cacheConfigEntries.entrySet()) { + desc.getProperties().setProperty(entry.getKey(), entry.getValue()); + } + } else { + //Unless the global switch is explicitly set to off, in which case we disable all caching: + Properties p = desc.getProperties(); + p.put(USE_DIRECT_REFERENCE_CACHE_ENTRIES, Boolean.FALSE); + p.put(USE_SECOND_LEVEL_CACHE, Boolean.FALSE); + p.put(USE_QUERY_CACHE, Boolean.FALSE); + p.put(JPA_SHARED_CACHE_MODE, SharedCacheMode.NONE); } descriptors.add(desc); diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java index ac6b49ef93855..db456e077dde3 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootMetadataBuilder.java @@ -7,15 +7,11 @@ import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_PASSWORD; import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_URL; import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_USER; -import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_MODE; import static org.hibernate.cfg.AvailableSettings.JPA_TRANSACTION_TYPE; import static org.hibernate.cfg.AvailableSettings.PASS; import static org.hibernate.cfg.AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY; import static org.hibernate.cfg.AvailableSettings.URL; import static org.hibernate.cfg.AvailableSettings.USER; -import static org.hibernate.cfg.AvailableSettings.USE_DIRECT_REFERENCE_CACHE_ENTRIES; -import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE; -import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE; import static org.hibernate.cfg.AvailableSettings.WRAP_RESULT_SETS; import static org.hibernate.cfg.AvailableSettings.XML_MAPPING_ENABLED; import static org.hibernate.internal.HEMLogging.messageLogger; @@ -33,7 +29,6 @@ import java.util.concurrent.ConcurrentHashMap; import javax.persistence.PersistenceException; -import javax.persistence.SharedCacheMode; import javax.persistence.spi.PersistenceUnitTransactionType; import org.hibernate.boot.CacheRegionDefinition; @@ -284,8 +279,6 @@ private MergedSettings mergeSettings(PersistenceUnitDescriptor persistenceUnit) } cfg.remove(JACC_ENABLED); - enableCachingByDefault(cfg); - // here we are going to iterate the merged config settings looking for: // 1) additional JACC permissions // 2) additional cache region declarations @@ -327,17 +320,6 @@ private MergedSettings mergeSettings(PersistenceUnitDescriptor persistenceUnit) return mergedSettings; } - /** - * Enable 2LC for entities and queries by default. Also allow "reference caching" by default. - */ - private void enableCachingByDefault(final Map configurationValues) { - //Only set these if the user isn't making an explicit choice: - configurationValues.putIfAbsent(USE_DIRECT_REFERENCE_CACHE_ENTRIES, Boolean.TRUE); - configurationValues.putIfAbsent(USE_SECOND_LEVEL_CACHE, Boolean.TRUE); - configurationValues.putIfAbsent(USE_QUERY_CACHE, Boolean.TRUE); - configurationValues.putIfAbsent(JPA_SHARED_CACHE_MODE, SharedCacheMode.ENABLE_SELECTIVE); - } - public RecordedState build() { MetadataImpl fullMeta = (MetadataImpl) MetadataBuildingProcess.complete( managedResources, From a5d996e95a9fd74a52d50ef55093564d99c1535b Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 15 Oct 2019 11:53:51 +0100 Subject: [PATCH 3/3] Integration tests for disabling 2nd level cache --- integration-tests/jpa/pom.xml | 5 +++ .../jpa/configurationless/CRUDResource.java | 13 +++++++ .../configurationless/JPACacheDisabled.java | 34 +++++++++++++++++++ .../JPAConfigurationlessTest.java | 4 +++ 4 files changed, 56 insertions(+) create mode 100644 integration-tests/jpa/src/test/java/io/quarkus/it/jpa/configurationless/JPACacheDisabled.java diff --git a/integration-tests/jpa/pom.xml b/integration-tests/jpa/pom.xml index 8f58ab9bbf0d3..363095f402eeb 100644 --- a/integration-tests/jpa/pom.xml +++ b/integration-tests/jpa/pom.xml @@ -43,6 +43,11 @@ rest-assured test + + io.quarkus + quarkus-junit5-internal + test + diff --git a/integration-tests/jpa/src/main/java/io/quarkus/it/jpa/configurationless/CRUDResource.java b/integration-tests/jpa/src/main/java/io/quarkus/it/jpa/configurationless/CRUDResource.java index 0e97f7eb128b8..faec3b6ddcc51 100644 --- a/integration-tests/jpa/src/main/java/io/quarkus/it/jpa/configurationless/CRUDResource.java +++ b/integration-tests/jpa/src/main/java/io/quarkus/it/jpa/configurationless/CRUDResource.java @@ -13,6 +13,9 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import org.hibernate.cache.spi.TimestampsCache; +import org.hibernate.internal.SessionImpl; + import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.ProfileManager; @@ -96,4 +99,14 @@ public String getCake() { Cake c = (Cake) em.createQuery("from Cake").getSingleResult(); return c.getType(); } + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Path("/timestamps") + public String checkCachingState() { + SessionImpl sessionImpl = em.unwrap(SessionImpl.class); + TimestampsCache timestampsCache = sessionImpl.getSessionFactory().getCache().getTimestampsCache(); + return timestampsCache.getClass().getName(); + } + } diff --git a/integration-tests/jpa/src/test/java/io/quarkus/it/jpa/configurationless/JPACacheDisabled.java b/integration-tests/jpa/src/test/java/io/quarkus/it/jpa/configurationless/JPACacheDisabled.java new file mode 100644 index 0000000000000..272b986b91c2d --- /dev/null +++ b/integration-tests/jpa/src/test/java/io/quarkus/it/jpa/configurationless/JPACacheDisabled.java @@ -0,0 +1,34 @@ +package io.quarkus.it.jpa.configurationless; + +import static org.hamcrest.core.StringContains.containsString; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +/** + * Similar to JPAConfigurationlessTest, yet explicitly disabling 2nd level caching, + * then asserting the TimestampsCacheDisabledImpl have been disabled as a consequence. + */ +public class JPACacheDisabled { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-orm.second-level-caching-enabled", "false"); + + @Test + public void testInjection() { + RestAssured.when().get("/jpa-test").then() + .body(containsString("jpa=OK")); + + RestAssured.when().get("/jpa-test/user-tx").then() + .body(containsString("jpa=OK")); + + RestAssured.when().get("/jpa-test/timestamps").then() + .body(containsString("org.hibernate.cache.internal.TimestampsCacheDisabledImpl")); + } + +} diff --git a/integration-tests/jpa/src/test/java/io/quarkus/it/jpa/configurationless/JPAConfigurationlessTest.java b/integration-tests/jpa/src/test/java/io/quarkus/it/jpa/configurationless/JPAConfigurationlessTest.java index 8db415be9da60..ef0bb5248904b 100644 --- a/integration-tests/jpa/src/test/java/io/quarkus/it/jpa/configurationless/JPAConfigurationlessTest.java +++ b/integration-tests/jpa/src/test/java/io/quarkus/it/jpa/configurationless/JPAConfigurationlessTest.java @@ -20,5 +20,9 @@ public void testInjection() { RestAssured.when().get("/jpa-test/user-tx").then() .body(containsString("jpa=OK")); + + RestAssured.when().get("/jpa-test/timestamps").then() + .body(containsString("org.hibernate.cache.internal.TimestampsCacheEnabledImpl")); } + }