diff --git a/spring-orm/spring-orm.gradle b/spring-orm/spring-orm.gradle index c2c5a4afc98..93920fe56da 100644 --- a/spring-orm/spring-orm.gradle +++ b/spring-orm/spring-orm.gradle @@ -11,6 +11,7 @@ dependencies { optional("org.eclipse.persistence:org.eclipse.persistence.jpa") optional("org.hibernate:hibernate-core-jakarta") optional("jakarta.servlet:jakarta.servlet-api") + optional("jakarta.validation:jakarta.validation-api") testImplementation(project(":spring-core-test")) testImplementation(testFixtures(project(":spring-beans"))) testImplementation(testFixtures(project(":spring-context"))) diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java index 8e33ba53b93..0819f428efd 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java @@ -31,6 +31,8 @@ import jakarta.persistence.PersistenceException; import jakarta.persistence.SharedCacheMode; import jakarta.persistence.ValidationMode; import jakarta.persistence.spi.PersistenceUnitInfo; +import jakarta.validation.NoProviderFoundException; +import jakarta.validation.Validation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -48,6 +50,7 @@ import org.springframework.jdbc.datasource.lookup.DataSourceLookup; import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup; import org.springframework.jdbc.datasource.lookup.MapDataSourceLookup; import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ResourceUtils; @@ -100,6 +103,9 @@ public class DefaultPersistenceUnitManager public static final String ORIGINAL_DEFAULT_PERSISTENCE_UNIT_NAME = "default"; + private static final boolean beanValidationPresent = ClassUtils.isPresent( + "jakarta.validation.Validation", DefaultPersistenceUnitManager.class.getClassLoader()); + protected final Log logger = LogFactory.getLog(getClass()); private String[] persistenceXmlLocations = new String[] {DEFAULT_PERSISTENCE_XML_LOCATION}; @@ -449,7 +455,7 @@ public class DefaultPersistenceUnitManager pui.setPersistenceUnitRootUrl(determineDefaultPersistenceUnitRootUrl()); } - // Override DataSource and cache/validation mode + // Override DataSource and shared cache mode if (pui.getJtaDataSource() == null && this.defaultJtaDataSource != null) { pui.setJtaDataSource(this.defaultJtaDataSource); } @@ -459,9 +465,16 @@ public class DefaultPersistenceUnitManager if (this.sharedCacheMode != null) { pui.setSharedCacheMode(this.sharedCacheMode); } + + // Override validation mode or pre-resolve provider detection if (this.validationMode != null) { pui.setValidationMode(this.validationMode); } + else if (pui.getValidationMode() == ValidationMode.AUTO) { + pui.setValidationMode( + beanValidationPresent && BeanValidationDelegate.isValidationProviderPresent() ? + ValidationMode.CALLBACK : ValidationMode.NONE); + } // Initialize persistence unit ClassLoader if (this.loadTimeWeaver != null) { @@ -697,4 +710,21 @@ public class DefaultPersistenceUnitManager return pui; } + + /** + * Inner class to avoid a hard dependency on the Bean Validation API at runtime. + */ + private static class BeanValidationDelegate { + + public static boolean isValidationProviderPresent() { + try { + Validation.byDefaultProvider().configure(); + return true; + } + catch (NoProviderFoundException ex) { + return false; + } + } + } + }