Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One-to-One mappings of basic type, into a single class in Hibernate

So the situation is as follows: there is an entity which needs to be connected with a dictionary. Imagine a following structure

create table Address (
    addressId bigint not null,
    addressLine1 varchar(255),
    city varchar(255),
    country varchar(255),
    state varchar(255),
    zipCode varchar(255),
    primary key (addressId)
)

create table STATES_DICT (
    state_code varchar(255),
    state_fullname varchar(255), 
    primary key (state_code)
)

I want to map both ADDRESS and STATE_DICTIONARY into a single entity.

@Entity
@Table(name = "ADDRESS")
public class Address implements Serializable {

    @Id
    @Column(name = "ADDRESSID")
    private int addressId;

    @Column(name = "ADDRESSLINE1")
    private String addressLine1;

    @Column(name = "STATE")
    private String state;

    //??? annotations
    private String fullStateName;

    @Column(name = "ZIPCODE")
    private String zipCode;

    @Column(name = "CITY")
    private String city;

    @Column(name = "COUNTRY")
    private String country;
    //... getters and setters
}

For a pure SQL I'll run

select a.ADDRESSID, a.ADDRESSLINE1, a.CITY, a.ZIPCODE, a.STATE, 
    d.STATE_FULLNAME, a.COUNTRY 
from ADDRESS a, STATES_DICT d where a.STATE = d.STATE_CODE

but I'm having severe problems with mapping it with JPA.

  1. I cannot use @SecondaryTable because the tables are not mapped by primary keys
  2. The best I could get was:
    @ElementCollection
    @JoinTable(name="STATES_DICT", 
            joinColumns=@JoinColumn(name="STATE_CODE", referencedColumnName="STATE"))
    @Column(name = "STATE_FULLNAME")
    private Collection<String> fullStateName;

Downside is - the mapping is always one-to-one and the Collection brings confusion and the relation is more of one-to-one (many-to-one) not one-to-many.

Any ideas? Is there an equivalent of @ElementCollection for one-to-one mappings? Dropping the @ElementCollection does not help. fullStateName field is expected to be in ADDRESS column - which is not the case.

Some notes: * I need those two to keep together in a single entity. * I'm extending existing solution, need to add just this dictionary column * The entity is processed later on by some other service which runs through primitive types only. I'd rather not change the service, that's why adding a @OneToOne relation is not preferable

Many thanks


I'm extending the question with @SecondaryTable example - which didn't work for me.

@Entity
@Table(name = "ADDRESS")
@SecondaryTable(name="STATES_DICT", 
        pkJoinColumns=@PrimaryKeyJoinColumn(columnDefinition="STATE_CODE", referencedColumnName="STATE"))
public class Address implements Serializable {

    @Id
    @Column(name = "ADDRESSID")
    private int addressId;

    @Column(name = "ADDRESSLINE1")
    private String addressLine1;

    @Column(name = "STATE")
    private String state;

    @Column(table="STATES_DICT", name = "STATE_FULLNAME")
    private String fullStateName;

    @Column(name = "ZIPCODE")
    private String zipCode;

    @Column(name = "CITY")
    private String city;

    @Column(name = "COUNTRY")
    private String country;

    //... getters and setters
}

That caused a nasty exception of: Caused by: org.hibernate.AnnotationException: SecondaryTable JoinColumn cannot reference a non primary key


For the record - I couldn't find a way to do this (and assumed it's simply not the way it should be done). I've gone with relation annotations (@ManyToOne, @OneToOne) and @JoinColumn - so the proper way. I've adjusted the further processing logic to treat @JoinColumn annotations in the same way it's working with @Column. It worked.

The further processing is a security feature which suppresses values based on user roles and original database column names. That's why it was so important for me to stick with the @Column annotation

like image 692
Jakub Marchwicki Avatar asked Nov 14 '22 16:11

Jakub Marchwicki


1 Answers

Answer from user2601805 is correct using

@PrimaryKeyJoinColumn(name="STATE_CODE", referencedColumnName="STATE")

In your case, this should suffice as all you want is a property from STATES_DICT.

I also asked a question relating to JPA that shows an example for using @SecondaryTable and @Embeddable to achieve something similar to @ElementCollection but for @OneToOne

Also see this blog for example http://www.bagdemir.com/2013/03/03/mapping-embeddable-objects-whichve-no-identities-using-multiple-tables-with-jpahibernate/

like image 57
Tommy Avatar answered Dec 25 '22 07:12

Tommy