Add support for JPA mapping resources

Closes gh-10684
This commit is contained in:
Stephane Nicoll 2017-10-19 17:14:41 +02:00
parent c4026806a6
commit c2f649df54
8 changed files with 127 additions and 1 deletions

View File

@ -130,7 +130,8 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware {
Map<String, Object> 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<String> mappingResources = this.properties.getMappingResources();
return mappingResources.toArray(new String[mappingResources.size()]);
}
/**
* Return the JTA transaction manager.
* @return the transaction manager or {@code null}

View File

@ -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<String, String> properties = new HashMap<>();
/**
* Mapping resources (equivalent to "mapping-file" entries in persistence.xml).
*/
private final List<String> 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<String> getMappingResources() {
return this.mappingResources;
}
public String getDatabasePlatform() {
return this.databasePlatform;
}

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"
version="2.1">
<entity class="org.springframework.boot.autoconfigure.orm.jpa.mapping.NonAnnotatedEntity">
<table name="NON_ANNOTATED"/>
<attributes>
<id name="id">
<column name="id"/>
<generated-value strategy="IDENTITY"/>
</id>
<basic name="value">
<column name="value"/>
</basic>
</attributes>
</entity>
</entity-mappings>

View File

@ -0,0 +1 @@
INSERT INTO NON_ANNOTATED (ID, VALUE) values (2000, 'Test');

View File

@ -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.

View File

@ -113,6 +113,8 @@ public class EntityManagerFactoryBuilder {
private Map<String, Object> 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 <mapping-file>} entries in
* {@code persistence.xml}) for the persistence unit.
* <p>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