diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index 04ca4e3440..74d8c6e521 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -130,7 +130,8 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { Map vendorProperties = getVendorProperties(); customizeVendorProperties(vendorProperties); return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan()) - .properties(vendorProperties).jta(isJta()).build(); + .properties(vendorProperties).mappingResources(getMappingResources()) + .jta(isJta()).build(); } protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter(); @@ -158,6 +159,11 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { return packages.toArray(new String[packages.size()]); } + private String[] getMappingResources() { + List mappingResources = this.properties.getMappingResources(); + return mappingResources.toArray(new String[mappingResources.size()]); + } + /** * Return the JTA transaction manager. * @return the transaction manager or {@code null} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java index bc0282671c..896f88edaf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java @@ -16,7 +16,9 @@ package org.springframework.boot.autoconfigure.orm.jpa; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.sql.DataSource; @@ -43,6 +45,11 @@ public class JpaProperties { */ private Map properties = new HashMap<>(); + /** + * Mapping resources (equivalent to "mapping-file" entries in persistence.xml). + */ + private final List mappingResources = new ArrayList<>(); + /** * Name of the target database to operate on, auto-detected by default. Can be * alternatively set using the "Database" enum. @@ -75,6 +82,10 @@ public class JpaProperties { this.properties = properties; } + public List getMappingResources() { + return this.mappingResources; + } + public String getDatabasePlatform() { return this.databasePlatform; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java index 31a6cf7bf5..dd3d304d54 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java @@ -45,6 +45,7 @@ import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.boot.autoconfigure.orm.jpa.mapping.NonAnnotatedEntity; import org.springframework.boot.autoconfigure.orm.jpa.test.City; import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration; import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform; @@ -255,6 +256,22 @@ public class HibernateJpaAutoConfigurationTests }); } + @Test + public void customResourceMapping() { + contextRunner() + .withClassLoader(new HideDataScriptClassLoader()) + .withPropertyValues( + "spring.datasource.data:classpath:/db/non-annotated-data.sql", + "spring.jpa.mapping-resources=META-INF/mappings/non-annotated.xml") + .run((context) -> { + EntityManager em = context.getBean(EntityManagerFactory.class) + .createEntityManager(); + NonAnnotatedEntity found = em.find(NonAnnotatedEntity.class, 2000L); + assertThat(found).isNotNull(); + assertThat(found.getValue()).isEqualTo("Test"); + }); + } + @Configuration @TestAutoConfigurationPackage(City.class) static class TestInitializedJpaConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/mapping/NonAnnotatedEntity.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/mapping/NonAnnotatedEntity.java new file mode 100644 index 0000000000..ec87347449 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/mapping/NonAnnotatedEntity.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2017 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 + * + * http://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. + */ + +package org.springframework.boot.autoconfigure.orm.jpa.mapping; + +/** + * A non annotated entity that is handled by a custom "mapping-file". + * + * @author Stephane Nicoll + */ +public class NonAnnotatedEntity { + + private Long id; + + private String value; + + protected NonAnnotatedEntity() { + } + + public NonAnnotatedEntity(String value) { + this.value = value; + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/mappings/non-annotated.xml b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/mappings/non-annotated.xml new file mode 100644 index 0000000000..35df949a1d --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/mappings/non-annotated.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/non-annotated-data.sql b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/non-annotated-data.sql new file mode 100644 index 0000000000..481cd974ac --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/non-annotated-data.sql @@ -0,0 +1 @@ +INSERT INTO NON_ANNOTATED (ID, VALUE) values (2000, 'Test'); \ No newline at end of file diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 84cbb690d5..a6f2187dc3 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -714,6 +714,7 @@ content into your application; rather pick only the properties that you need. spring.jpa.hibernate.naming.implicit-strategy= # Hibernate 5 implicit naming strategy fully qualified name. spring.jpa.hibernate.naming.physical-strategy= # Hibernate 5 physical naming strategy fully qualified name. spring.jpa.hibernate.use-new-id-generator-mappings= # Use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE. + spring.jpa.mapping-resources= # Mapping resources (equivalent to "mapping-file" entries in persistence.xml). spring.jpa.open-in-view=true # Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request. spring.jpa.properties.*= # Additional native properties to set on the JPA provider. spring.jpa.show-sql=false # Enable logging of SQL statements. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java index af046592db..d0b02ae82b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java @@ -113,6 +113,8 @@ public class EntityManagerFactoryBuilder { private Map properties = new HashMap<>(); + private String[] mappingResources; + private boolean jta; private Builder(DataSource dataSource) { @@ -166,6 +168,20 @@ public class EntityManagerFactoryBuilder { return this; } + /** + * The mapping resources (equivalent to {@code } entries in + * {@code persistence.xml}) for the persistence unit. + *

Note that mapping resources must be relative to the classpath root, + * e.g. "META-INF/mappings.xml" or "com/mycompany/repository/mappings.xml", + * so that they can be loaded through {@code ClassLoader.getResource}. + * @param mappingResources the mapping resources to use + * @return the builder for fluent usage + */ + public Builder mappingResources(String... mappingResources) { + this.mappingResources = mappingResources; + return this; + } + /** * Configure if using a JTA {@link DataSource}, i.e. if * {@link LocalContainerEntityManagerFactoryBean#setDataSource(DataSource) @@ -203,6 +219,9 @@ public class EntityManagerFactoryBuilder { entityManagerFactoryBean.getJpaPropertyMap() .putAll(EntityManagerFactoryBuilder.this.jpaProperties); entityManagerFactoryBean.getJpaPropertyMap().putAll(this.properties); + if (this.mappingResources != null) { + entityManagerFactoryBean.setMappingResources(this.mappingResources); + } URL rootLocation = EntityManagerFactoryBuilder.this.persistenceUnitRootLocation; if (rootLocation != null) { entityManagerFactoryBean