From 8efc8baf45535e372a08f125299cf3f4c1d0f239 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 29 Apr 2025 12:10:55 +0100 Subject: [PATCH] Move persistence exception translation auto-config into tx module --- .../boot/autoconfigure/dao/package-info.java | 20 --- ...itional-spring-configuration-metadata.json | 6 - ...ot.autoconfigure.AutoConfiguration.imports | 1 - .../spring-boot-tx/build.gradle | 4 + ...ExceptionTranslationAutoConfiguration.java | 2 +- ...itional-spring-configuration-metadata.json | 10 ++ ...ot.autoconfigure.AutoConfiguration.imports | 3 +- ...tionTranslationAutoConfigurationTests.java | 129 +++++++++++++++++- 8 files changed, 139 insertions(+), 36 deletions(-) delete mode 100644 spring-boot-project/spring-boot-autoconfigure-all/src/main/java/org/springframework/boot/autoconfigure/dao/package-info.java rename spring-boot-project/{spring-boot-autoconfigure-all/src/main/java/org/springframework/boot/autoconfigure/dao => spring-boot-tx/src/main/java/org/springframework/boot/transaction/autoconfigure}/PersistenceExceptionTranslationAutoConfiguration.java (97%) create mode 100644 spring-boot-project/spring-boot-tx/src/main/resources/META-INF/additional-spring-configuration-metadata.json rename spring-boot-project/{spring-boot-autoconfigure-all/src/test/java/org/springframework/boot/autoconfigure/dao => spring-boot-tx/src/test/java/org/springframework/boot/transaction/autoconfigure}/PersistenceExceptionTranslationAutoConfigurationTests.java (53%) diff --git a/spring-boot-project/spring-boot-autoconfigure-all/src/main/java/org/springframework/boot/autoconfigure/dao/package-info.java b/spring-boot-project/spring-boot-autoconfigure-all/src/main/java/org/springframework/boot/autoconfigure/dao/package-info.java deleted file mode 100644 index f9cdd022fa..0000000000 --- a/spring-boot-project/spring-boot-autoconfigure-all/src/main/java/org/springframework/boot/autoconfigure/dao/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Auto-configuration for Spring DAO. - */ -package org.springframework.boot.autoconfigure.dao; diff --git a/spring-boot-project/spring-boot-autoconfigure-all/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure-all/src/main/resources/META-INF/additional-spring-configuration-metadata.json index fcaba9fdfb..57f93544ea 100644 --- a/spring-boot-project/spring-boot-autoconfigure-all/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure-all/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,12 +1,6 @@ { "groups": [], "properties": [ - { - "name": "spring.dao.exceptiontranslation.enabled", - "type": "java.lang.Boolean", - "description": "Whether to enable the PersistenceExceptionTranslationPostProcessor.", - "defaultValue": true - }, { "name": "spring.http.encoding.charset", "type": "java.nio.charset.Charset", diff --git a/spring-boot-project/spring-boot-autoconfigure-all/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-autoconfigure-all/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index b7a733f8cc..38587bce9d 100644 --- a/spring-boot-project/spring-boot-autoconfigure-all/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-autoconfigure-all/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,4 +1,3 @@ -org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration org.springframework.boot.autoconfigure.http.client.service.HttpServiceClientAutoConfiguration org.springframework.boot.autoconfigure.http.client.reactive.ClientHttpConnectorAutoConfiguration diff --git a/spring-boot-project/spring-boot-tx/build.gradle b/spring-boot-project/spring-boot-tx/build.gradle index c813049969..d2ea4e1c20 100644 --- a/spring-boot-project/spring-boot-tx/build.gradle +++ b/spring-boot-project/spring-boot-tx/build.gradle @@ -20,9 +20,13 @@ dependencies { testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) testImplementation("com.github.h-thurow:simple-jndi") testImplementation("com.zaxxer:HikariCP") + testImplementation("jakarta.persistence:jakarta.persistence-api") + testImplementation("org.hibernate.orm:hibernate-core") testImplementation("org.hsqldb:hsqldb") testImplementation("org.springframework:spring-jdbc") + testImplementation("org.springframework:spring-orm") + testRuntimeOnly(project(":spring-boot-project:spring-boot-reactor")) testRuntimeOnly("ch.qos.logback:logback-classic") testRuntimeOnly(project(":spring-boot-project:spring-boot-reactor")) } diff --git a/spring-boot-project/spring-boot-autoconfigure-all/src/main/java/org/springframework/boot/autoconfigure/dao/PersistenceExceptionTranslationAutoConfiguration.java b/spring-boot-project/spring-boot-tx/src/main/java/org/springframework/boot/transaction/autoconfigure/PersistenceExceptionTranslationAutoConfiguration.java similarity index 97% rename from spring-boot-project/spring-boot-autoconfigure-all/src/main/java/org/springframework/boot/autoconfigure/dao/PersistenceExceptionTranslationAutoConfiguration.java rename to spring-boot-project/spring-boot-tx/src/main/java/org/springframework/boot/transaction/autoconfigure/PersistenceExceptionTranslationAutoConfiguration.java index a5d79bcb6c..f3407c7f01 100644 --- a/spring-boot-project/spring-boot-autoconfigure-all/src/main/java/org/springframework/boot/autoconfigure/dao/PersistenceExceptionTranslationAutoConfiguration.java +++ b/spring-boot-project/spring-boot-tx/src/main/java/org/springframework/boot/transaction/autoconfigure/PersistenceExceptionTranslationAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.dao; +package org.springframework.boot.transaction.autoconfigure; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; diff --git a/spring-boot-project/spring-boot-tx/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-tx/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000000..03d1720dde --- /dev/null +++ b/spring-boot-project/spring-boot-tx/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,10 @@ +{ + "properties": [ + { + "name": "spring.dao.exceptiontranslation.enabled", + "type": "java.lang.Boolean", + "description": "Whether to enable the PersistenceExceptionTranslationPostProcessor.", + "defaultValue": true + } + ] +} diff --git a/spring-boot-project/spring-boot-tx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-tx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index fa6e22aff6..c5b3ed79ee 100644 --- a/spring-boot-project/spring-boot-tx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-tx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,4 @@ +org.springframework.boot.transaction.autoconfigure.PersistenceExceptionTranslationAutoConfiguration org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizationAutoConfiguration -org.springframework.boot.transaction.jta.autoconfigure.JtaAutoConfiguration \ No newline at end of file +org.springframework.boot.transaction.jta.autoconfigure.JtaAutoConfiguration diff --git a/spring-boot-project/spring-boot-autoconfigure-all/src/test/java/org/springframework/boot/autoconfigure/dao/PersistenceExceptionTranslationAutoConfigurationTests.java b/spring-boot-project/spring-boot-tx/src/test/java/org/springframework/boot/transaction/autoconfigure/PersistenceExceptionTranslationAutoConfigurationTests.java similarity index 53% rename from spring-boot-project/spring-boot-autoconfigure-all/src/test/java/org/springframework/boot/autoconfigure/dao/PersistenceExceptionTranslationAutoConfigurationTests.java rename to spring-boot-project/spring-boot-tx/src/test/java/org/springframework/boot/transaction/autoconfigure/PersistenceExceptionTranslationAutoConfigurationTests.java index 3ecd152469..1a37ff717b 100644 --- a/spring-boot-project/spring-boot-autoconfigure-all/src/test/java/org/springframework/boot/autoconfigure/dao/PersistenceExceptionTranslationAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-tx/src/test/java/org/springframework/boot/transaction/autoconfigure/PersistenceExceptionTranslationAutoConfigurationTests.java @@ -14,23 +14,38 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.dao; +package org.springframework.boot.transaction.autoconfigure; +import java.io.Serializable; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; import java.util.Map; +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariDataSource; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.jdbc.autoconfigure.EmbeddedDataSourceConfiguration; -import org.springframework.boot.jpa.autoconfigure.hibernate.HibernateJpaAutoConfiguration; import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.boot.testsupport.classpath.resources.WithResource; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.stereotype.Repository; import static org.assertj.core.api.Assertions.assertThat; @@ -87,16 +102,16 @@ class PersistenceExceptionTranslationAutoConfigurationTests { } @Test + @WithMetaInfPersistenceXmlResource void persistOfNullThrowsIllegalArgumentExceptionWithoutExceptionTranslation() { - this.context = new AnnotationConfigApplicationContext(EmbeddedDataSourceConfiguration.class, - HibernateJpaAutoConfiguration.class, TestConfiguration.class); + this.context = new AnnotationConfigApplicationContext(JpaConfiguration.class, TestConfiguration.class); assertThatIllegalArgumentException().isThrownBy(() -> this.context.getBean(TestRepository.class).doSomething()); } @Test + @WithMetaInfPersistenceXmlResource void persistOfNullThrowsInvalidDataAccessApiUsageExceptionWithExceptionTranslation() { - this.context = new AnnotationConfigApplicationContext(EmbeddedDataSourceConfiguration.class, - HibernateJpaAutoConfiguration.class, TestConfiguration.class, + this.context = new AnnotationConfigApplicationContext(JpaConfiguration.class, TestConfiguration.class, PersistenceExceptionTranslationAutoConfiguration.class); assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) .isThrownBy(() -> this.context.getBean(TestRepository.class).doSomething()); @@ -127,4 +142,104 @@ class PersistenceExceptionTranslationAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class JpaConfiguration { + + @Bean + DataSource dataSource() { + HikariDataSource dataSource = new HikariDataSource(); + dataSource.setDriverClassName("org.hsqldb.jdbc.JDBCDriver"); + dataSource.setJdbcUrl("jdbc:hsqldb:mem:tx"); + dataSource.setUsername("sa"); + return dataSource; + } + + @Bean + LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource) { + LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); + localContainerEntityManagerFactoryBean.setDataSource(dataSource); + localContainerEntityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + localContainerEntityManagerFactoryBean.setJpaPropertyMap(configureJpaProperties()); + return localContainerEntityManagerFactoryBean; + } + + private static Map configureJpaProperties() { + Map properties = new HashMap<>(); + properties.put("configured", "manually"); + properties.put("hibernate.transaction.jta.platform", NoJtaPlatform.INSTANCE); + return properties; + } + + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @WithResource(name = "META-INF/persistence.xml", + content = """ + + + + org.springframework.boot.transaction.autoconfigure.PersistenceExceptionTranslationAutoConfigurationTests$City + true + + + """) + @interface WithMetaInfPersistenceXmlResource { + + } + + @Entity + public static class City implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue + private Long id; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private String state; + + @Column(nullable = false) + private String country; + + @Column(nullable = false) + private String map; + + protected City() { + } + + City(String name, String state, String country, String map) { + this.name = name; + this.state = state; + this.country = country; + this.map = map; + } + + public String getName() { + return this.name; + } + + public String getState() { + return this.state; + } + + public String getCountry() { + return this.country; + } + + public String getMap() { + return this.map; + } + + @Override + public String toString() { + return getName() + "," + getState() + "," + getCountry(); + } + + } + }