简体   繁体   中英

Spring - Issue configuring Hibernate Jpa

I'm moving from a Spring Boot 1.5.9 to a normal Spring project.

In application.properties I've added

spring.autoconfigure.exclude[0]=org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration
spring.autoconfigure.exclude[1]=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
spring.autoconfigure.exclude[2]=org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration
spring.autoconfigure.exclude[3]=org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration

I have a configuration class

@Configuration
public class HibernateJpaConfig {

private Map<String, String> properties = new HashMap<String, String>();

public HibernateJpaConfig() {
    properties.put("hibernate.hbm2ddl.auto", "create-drop");
}

@Bean
@Primary
public DataSource dataSource(){
    DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
    dataSource.setDriverClassName("org.h2.Driver");
    dataSource.setUrl("jdbc:h2:mem:testdb");
    dataSource.setUsername("sa"); dataSource.setPassword("");
    return dataSource;
}

@Autowired
private DataSource dataSource;

@Autowired(required = false)
private PersistenceUnitManager persistenceUnitManager;

@Bean
public PlatformTransactionManager transactionManager() {
    return new JpaTransactionManager();
}

@Bean
public JpaVendorAdapter jpaVendorAdapter() {
    AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
    adapter.setShowSql(false);
    adapter.setDatabase(Database.H2);
    adapter.setDatabasePlatform("H2");
    adapter.setGenerateDdl(true);
    return adapter;
}

@Bean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter) {
    EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(
            jpaVendorAdapter, properties,
            this.persistenceUnitManager);
    builder.setCallback(null);
    return builder;
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder factoryBuilder) {
    Map<String, Object> vendorProperties = new LinkedHashMap<String, Object>();
    vendorProperties.putAll(properties);

    return factoryBuilder.dataSource(this.dataSource).packages("com.fabio.springmvc.domain")
            .properties(vendorProperties).jta(false).build();
}

}

When I run the project I have the following errors (I've added a warning since it seems pertinent to the issue)

    2018-01-07 06:03:34.542  WARN 17268 --- [  restartedMain] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [com/fabio/springmvc/config/HibernateJpaConfig.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
2018-01-07 06:03:34.545  INFO 17268 --- [  restartedMain] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2018-01-07 06:03:34.557  INFO 17268 --- [  restartedMain] utoConfigurationReportLoggingInitializer : 

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2018-01-07 06:03:34.564 ERROR 17268 --- [  restartedMain] o.s.boot.SpringApplication               : Application startup failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [com/fabio/springmvc/config/HibernateJpaConfig.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1080) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:857) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at com.fabio.springmvc.SpringmvcApplication.main(SpringmvcApplication.java:17) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.5.9.RELEASE.jar:1.5.9.RELEASE]
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:954) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:882) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) ~[spring-orm-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:360) ~[spring-orm-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:382) ~[spring-orm-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:371) ~[spring-orm-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:336) ~[spring-orm-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
... 21 common frames omitted
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.fabio.springmvc.domain.Customer column: addressLine1 (should be mapped with insert="false" update="false")
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:830) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:848) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:844) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:870) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:605) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.mapping.RootClass.validate(RootClass.java:265) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:329) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:443) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:879) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
... 28 common frames omitted

The configuration class is based on a course with Spring boot 1.3, here I'm using 1.5.9, I don't get why I'm having this issue.

Edit for customer repeated column

@Entity
public class Customer extends AbstractDomainClass{

private String firstName;
private String lastName;
private String email;
private String phoneNumber;
@Embedded
private Address billingAddress;
@Embedded
private Address shippingAddress;
// getters setters
}

Address class

@Embeddable
public class Address {

private String addressLine1;
private String addressLine2;
private String city;
private String state;
private String zipCode;
// getters setters
}

The issue occurs because you have the following mapping:

@Entity
public class Customer extends AbstractDomainClass {
    //other attributes...
    @Embedded
    private Address billingAddress;
    @Embedded
    private Address shippingAddress;
}

Here, what Hibernate will try to do is to map the attributes of the @Embedded class in columns of your table. This is, it will map Address#addressLine1 from attribute Address billingAddress to the column addressLine1 in the table customer . Similar will happen with the other fields for attribute billingAddress and for class Address . And then, it will do the same with Address shippingAddress , this is, it will map Address#addressLine1 for shippingAddress to column addressLine1 in table customer . This is the cause of the exception.

Since this is more a design problem rather than an issue in your code, I would suggest some ways to solve it.

  1. Have different columns in your table for all the fields, and map each one to the specific attribute in classes. Here's a basic example:

    DDL of your table (using the most common sql I can come up with).

     CREATE TABLE customer ( id INT NOT NULL, billingAddressLine1 VARCHAR(200) NOT NULL, shippingAddressLine1 VARCHAR(200) NOT NULL, -- other columns... PRIMARY KEY(id) ); 

    Hibernate mapping for @Embedded attributes using @AttributeOverrides :

     @Entity public class Customer extends AbstractDomainClass { //other attributes... @Embedded @AttributeOverrides( @AttributeOverride( name = "addressLine1", column = @Column(name = "billingAddressLine1"), ) //do similar for the other attributes... ) private Address billingAddress; @Embedded @AttributeOverrides( @AttributeOverride( name = "addressLine1", column = @Column(name = "shippingAddressLine1") ) //do similar for the other attributes... ) private Address shippingAddress; } 
  2. Instead of having the columns in the customer table, use a separate table called address and a column called addressType (or the name you prefer) to store addresses for a customer.

    DDL of your table (using the most common sql I can come up with).

     CREATE TABLE customer ( id INT NOT NULL, -- other columns... PRIMARY KEY(id) ); CREATE TABLE address ( id INT NOT NULL, addressType INT NOT NULL, customer_id INT NOT NULL, addressLine1 VARCHAR(200) NOT NULL, -- other columns... PRIMARY KEY(id), FOREIGN KEY (customer_id) REFERENCES customer(id) ); 

    Hibernate mapping for the classes (not @Embedded ):

     //@Embedded @Entity public class Address extends AbstractDomainClass { private String addressLine1; private String addressLine2; private String city; private String state; private String zipCode; //new fields... private int addressType; @ManyToOne private Customer customer; // getters and setters ... } @Entity public class Customer extends AbstractDomainClass { //other attributes... @OneToOne(mappedBy="customer", cascade=CascadeType.ALL) private Address billingAddress; @OneToOne(mappedBy="customer", cascade=CascadeType.ALL) private Address shippingAddress; //getters and setters... } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM