Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ERROR Table with that name [user_address] already associated with entity when using the same join table to refer to subclasses of an associated entity

Using Spring Boot starter, I am trying to create a simple example project that involves a user that has multiple address fields. I am experimenting on using @DiscriminatorColumn and @DiscriminatorValue to differentiate the different types of addresses a user may have.

Here is an abbreviated sample of the tables in my project:

CREATE TABLE user ( id INT AUTO_INCREMENT);
CREATE TABLE user_address ( user_id INT, address_id INT);
CREATE TABLE address ( id INT AUTO_INCREMENT, TYPE VARCHAR(31));

And here are the classes I am trying to join:

@Entity
@DiscriminatorColumn(name = "type")
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String type;
}

@Entity
@DiscriminatorValue("HOME")
public class HomeAddress extends Address {}

@Entity
@DiscriminatorValue("CURRENT")
public class CurrentAddress extends Address{}

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String type;

    @OneToOne
    @JoinTable(
        name = "user_address",
        joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
        inverseJoinColumns = {@JoinColumn(name = "address_id", referencedColumnName = "id")}
    )
    private HomeAddress homeAddress;

    @OneToOne
    @JoinTable(
        name = "user_address",
        joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
        inverseJoinColumns = {@JoinColumn(name = "address_id", referencedColumnName = "id")}
    )
    private CurrentAddress currentAddress;

}

I've tried replacing @OneToOne with @OneToMany but still it doesn't work.

The reason I want to be able to do this is that I am thinking of associating an Address with other entities. For example, ShippingAddress for an order or LocationAddress for a Building, etc.

Here is a dump of the error:

Caused by: org.hibernate.boot.spi.InFlightMetadataCollector$DuplicateSecondaryTableException: Table with that name [user_address] already associated with entity
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl$EntityTableXrefImpl.addSecondaryTable(InFlightMetadataCollectorImpl.java:1420) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.cfg.annotations.EntityBinder.addJoin(EntityBinder.java:972) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.cfg.annotations.EntityBinder.addJoin(EntityBinder.java:868) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.cfg.ClassPropertyHolder.addJoin(ClassPropertyHolder.java:207) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1792) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:904) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:731) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:245) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:222) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:265) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:847) ~[hibernate-entitymanager-5.0.9.Final.jar:5.0.9.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:874) ~[hibernate-entitymanager-5.0.9.Final.jar:5.0.9.Final]
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) ~[spring-orm-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:338) ~[spring-orm-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:373) ~[spring-orm-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:362) ~[spring-orm-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
    ... 16 common frames omitted
like image 404
Tony Alejandro Avatar asked Aug 20 '16 08:08

Tony Alejandro


3 Answers

Unfortunately you can't make separate relations for the two address type, as pure JPA is not able to 'discriminate' results of joins in queries, i.e. is not making joins using the discriminator value for getting only objects of a certain type for an association and of another type for the other association. Hibernate, instead, has methods to do it, but out of the JPA specification.

So the only way it works is allowing you to have a @OneToMany relationship with Address objects, giving you in return a polymorphic collection containing in turn HomeAddress and CurrentAddress objects

I hade a similar issue. The only solution for me was to work a little bit differently (and basically in a way I didn't like so much, but that's it).

Basically you have 4 options (and several alternatives):

1- having only one (@OneToMany) relationship, scan your collection for objects of type HomeAddress and/or CurrentAddress after retrieve and assign them to the correct fields.

2- Change the way your inheritance is designed. In this case you can use Address as @MappedSuperclass and your subclasses as entities extending the Address Class. The two classes should be mapped in two separated tables. You don't need a join table. This is the way I proceeded, even if I didn't like it too much.

3- Throw away inheritance and use 2 separate tables

4- use hibernate (non-jpa) annotations.

Sorry for not being pasting code, but I'm unable to do it here & now

like image 99
Massimo Petrus Avatar answered Sep 22 '22 11:09

Massimo Petrus


Your error is :

Caused by: org.hibernate.boot.spi.InFlightMetadataCollector$DuplicateSecondaryTableException: Table with that name [user_address] already associated with entity

You have forgoten the @Inheritance annotation. I suppose that you use a single table for all adress classes. So you should precise that you want to use a SINGLE_TABLE strategy for handling OOP->SQL mapping. It is which used by default but you can precise it since more readable.

Add it on the class declaration of your super class entity : @Inheritance(strategy = InheritanceType.SINGLE_TABLE)

in this way :

@Entity
@DiscriminatorColumn(name = "type")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String type;
}

Edit :

user_address is used as jointable in multiple relations in the User entity. Hibernate gives the impression that it doesn't accept it.

If you use a single relation, you lose benefits to categorize the adresses.
If you may change you schema, you could remove the join table and add a fk in address to specify the userId. Sorry, I have not other clues.

like image 27
davidxxx Avatar answered Sep 19 '22 11:09

davidxxx


Have you tried the Attribute

@ManyToMany

Because with this definition you will archive what you have

A and B in which A may contain a parent instance for which there are many children in B and vice versa.

like image 27
Gatusko Avatar answered Sep 22 '22 11:09

Gatusko