MrSir
MrSir

Reputation: 596

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
}

Upvotes: 0

Views: 463

Answers (1)

Luiggi Mendoza
Luiggi Mendoza

Reputation: 85781

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

Upvotes: 2

Related Questions