1

I have three tables, Member, Address and Mem_Addr. Member and Address entities are mapped / connected by Mem_Addr table. Many to Many.

I have the following entities for Member and Address:

@Entity 
@Table (name="Member")
public class Member{

    @Id  //....
    private Integer mem_id;

    @OneToOne
    @JoinTable     (name = "Mem_Addr", 
             joinColumns = @JoinColumn(name = "addr_id"), 
      inverseJoinColumns = @JoinColumn(name = "mem_id"))
    Set<Address> addresses = new HashSet<Address>;
    ...
}

Mem_Addr table (It has not associated entity).

addr_id // (PK) Sequence
mem_id  // (FK to mem_id in Member table) 
....
....

Address entity.

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

   private String addr_id; // Foreign Key to addr_id in Mem_addr table
   private String address1;
   ...
}

I do not have ID in the Address table/entity. (Please don't ask me about the design. Its not changeable right now.)

So when I load Members, I want to load all address locations for that member.

What is the solution?

Edit: More info...

Basically some Members will have a new entry in Mem_Addr table. For each entry in Mem_addr table there will be an entry in Address table.

To get the address of a member, I need to refer Mem_Addr. Mem_Addr has addr_id as primary and Address has addr_id as foreign key.

Kevin Rave
  • 13,876
  • 35
  • 109
  • 173
  • 1
    Is there an addr_id in Address entity because I see in Mem entity you are calling it? – Rika Sep 24 '14 at 20:03
  • @Rika you are right.. I mistyped it. Corrected. – Kevin Rave Sep 24 '14 at 20:05
  • I think you probably can connect without Ids but it must be have Embeddable annotation, see here http://stackoverflow.com/questions/767277/hibernate-and-no-pk – Rika Sep 24 '14 at 20:13
  • @Rika: I think the @Embeddable approach won't work if the table you're trying to map is a foreign key to something else. A Hibernate entity is supposed to be an independent, stand-alone thing, so Hibernate will try to insert a new row into `Address`, which will fail due to the unsatisfied FK constraint and be rolled back. – Tim Sep 24 '14 at 20:25
  • @Tim It probably can work if he makes the Mem_Addr into an entity, then make the String addr_id; in Address to MemAddr addr_id; then do the Embeddable thing, it should insert it properly with cascading – Rika Sep 24 '14 at 20:58
  • Hey Mr.Rave is it possible for you to make Mem_Addr into an entity? – Rika Sep 24 '14 at 20:59
  • @Rika, yes, I can, If need be. I updated my question. Please check. – Kevin Rave Sep 24 '14 at 21:09
  • @Rika I don't need to do inserting with this. I just need loading of addresses along with members.. No inserts or updates. – Kevin Rave Sep 24 '14 at 21:10
  • @Rika You're right, it would be possible to map both classes and use @Embeddable to embed one within the other, but you shouldn't. The purpose of Hibernate is to let you bridge the differences between your object model and your data model, including the ways where your data model is sub-optimal due to legacy issues, as it seems to be here. Introducing an entity for `Mem_Addr` exposes the mismatch rather than hiding it, and ties the object model to the bad data model design. The better solution is to use a single entity class (as OP intended), and map to the data model using @SecondaryTable. – Tim Sep 24 '14 at 21:22
  • Kevin, why did you change this to @OneToOne? Do you really mean that, rather than @OneToMany? – Tim Sep 24 '14 at 21:26
  • @Tim, yes. I really mean that. – Kevin Rave Sep 24 '14 at 21:34
  • Then you should change the type to be `Address` instead of `Set
    `, since one-to-ones can have only a single associated value. But based on your comments (e.g. "I want to load all address locations for that member."), I don't believe that's actually what you mean, even though you say it is.
    – Tim Sep 24 '14 at 21:40
  • Does it radically change any solution it may have if I use OneToOne vs OneToMany? – Kevin Rave Sep 24 '14 at 21:42
  • There shouldn't be a huge difference if you go with the SecondaryTable approach. If you were going to take @Rika's approach that wouldn't allow updates/inserts, I don't believe you can use a join table with one-to-ones so it would make a difference, but since you're not going down that path it probably doesn't matter. Though I'm not sure that OneToOne is legal on a collection; I've never tried to do that (it's semantically wrong, whether it works or not, so I've never wanted to), so you might hit problems, but if you do they should be easy enough for you to sort out. – Tim Sep 24 '14 at 21:50
  • Okay. may be I am lost. What is your suggested solution.. Assuming, its @OneToMany and leaving the tables updatable and insertable? – Kevin Rave Sep 24 '14 at 21:52
  • Map the `Address` entity to both the `Mem_Addr` and `Address` tables using the SecondaryTable annotation, as I described in my answer below. – Tim Sep 24 '14 at 21:57
  • OKay.. Let me check how that can be done.. – Kevin Rave Sep 24 '14 at 22:02
  • Did you ever get this working? Did the SecondaryTable approach give you what you needed, or did you end up doing something else? – Tim Sep 28 '14 at 17:57
  • @Tim I haven't tried this yet. I did not see a perfect example for my case in google search yet! – Kevin Rave Sep 29 '14 at 15:04

3 Answers3

1

Unfortunately there is no way around, Hibernate requires your table to have primary key.

You can check out this document.

Peter Pei Guo
  • 7,770
  • 18
  • 35
  • 54
1

Okay since, insertions and updates are not necessary, the following (in theory) should perhaps work:

@Entity
@Table(name = "Address")
class Address {

 @Id
 private AddressKey id;

 public void setId(AddressKey id) {
 this.id = id;
 }

 public void getId() {
 return id;
 }
}

and

@Embeddable
class AddressKey implements Serializable {
   @Column(name = "addr_id")
   private String addrId; //also the underscore should be deleted

   @Column(name = "address_1")
   private String address1;



  }

This is a workaround found for tables that don't have Primary Keys. As you can see it pretty much makes a fake Id.

Rika
  • 768
  • 1
  • 5
  • 16
  • If we're not worried about insertions and updates, then why bother with the @Embeddable? Just map `addr_id` as @Id and be done with it. (Besides, `address1` isn't part of the primary key for the table, so I'm not sure why you've included it in the @Embeddable...) – Tim Sep 24 '14 at 21:35
  • But how do you map the Address to Members table. There is no link between those two. It may be a simple thing.. .. you get me.. I am a newbie to Hibernate :-) – Kevin Rave Sep 24 '14 at 21:36
  • 1
    For the record, though, I think it's a bad decision to intentionally map a Hibernate entity that can't be used to write to the database, even if you think you don't need that ability. Odds are you will at some point, and even if you really never need them in production, what if you want to write some test data to a dummy database for use in unit tests? I think that taking this approach would be a mistake when other approaches will also work and don't come with these drawbacks, but we all get to make our own choices so it's your call. – Tim Sep 24 '14 at 21:38
  • @Tim, thats a good point..May be I need to allow Insertions and Updates. I will go with that. – Kevin Rave Sep 24 '14 at 21:39
0

Are you sure the Mem_Addr table is many-to-many, rather than one-to-many? I don't see how a single Address could be associated with more than one Member...

Assuming it's actually one-to-many, map your Address entity to both the Mem_Addr and Address tables via the @SecondaryTable annotation as described in Hibernate Mapping Two Tables to One Class, mapping the id to Mem_Addr.addr_id, the Member via Mem_Addr.mem_id, and everything else via the other fields in Address.

I haven't done this, but I believe it will work:

@Entity
@Table(name = "Address")
@SecondaryTable(name = "Mem_Addr", pkJoinColumns={
    @PrimaryKeyJoinColumn(name="addr_id" referencedColumnName="addr_id")
})
class Address {
    @Id
    @Column
    private String addr_id;

    ....
}
Community
  • 1
  • 1
Tim
  • 2,027
  • 15
  • 24
  • Its possible. Because in Insurance domain, multiple customers can be in same address. Okay, if I assume its going to be @OneToMany, but your solution is not clear. Can you elaborate? – Kevin Rave Sep 24 '14 at 20:30
  • I haven't done this (thankfully I've always had schemas that were more reasonable than what you're stuck with), but you should be able to use the @SecondaryTable annotation for this, as described in http://stackoverflow.com/questions/1667918/hibernate-mapping-two-tables-to-one-class. – Tim Sep 24 '14 at 20:43
  • Also, I agree that in the real world, it's possible to have multiple `Member`s for a single `Address`. But I don't believe it's possible in your database schema (you'd have to create one `Address` object for each `Member` object, even though the values of that `Address` would be the same), which is why hopefully you'll be allowed to change that schema at some point in the future. – Tim Sep 24 '14 at 20:46
  • Okay. I am updating my question with more insight. I guess it may yeild a different solution from you. :-). – Kevin Rave Sep 24 '14 at 20:51
  • Nothing you added changes the solution here. Because the primary key is in the `Mem_Addr` table, the `Address` class has to be mapped to it, but it also needs to be mapped to the `Address` table. To map a single class to two tables, you'll want to use @SecondaryTable, as I suggested. – Tim Sep 24 '14 at 21:16