-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Hibernate limitation when composite PK has FK relation?
PostPosted: Tue Feb 13, 2007 5:38 am 
Newbie

Joined: Tue Feb 13, 2007 5:25 am
Posts: 1
I faced an unexpected problem which I am unable to solve. The problem is VERY specific:

I have a DB table which's primary key is composite of three fields. So I cannot use @Id, and I use @Embeddable to define PK class. After that in the entity class I use @Embedded (as class annotation) and/or (tried both variants) @EmbeddedId (as PK field annotation).

The actual problem is that one of the fields in th PK is used as foreign key to another table and on this field I define @ManyToOne relationship.

In other words - in the PK class I have two normal fields (int and String) and one field which represents the relation to the MASTER table as get/set Entity object.

I found that when the PK is composite then @ManyToOne confuses Hibernate. I tried to surround the problem in many different ways, but nothing helped.

I think that this specific problem is a limitation in Hibernate, and I would like to confirm that in some way if this is the case really! Can you give me suggestions how to find if this is Hibernate limitation or what to do if the problem is solvable how to solve it in some way?

_________________
<<< Ivan Davidov >>>


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 13, 2007 1:21 pm 
Newbie

Joined: Thu Mar 03, 2005 3:25 pm
Posts: 5
Hibernate has some support for what you are describing, but I am not entirely sure how far it goes. What I'll give you is the approach that should be supported by EJB3.0.

If the table/class with the composite id is Slave, and the table it references is Master, then the following code should do what you want:
Code:
@Entity
class Master {
    @Id
    @Column(name="MASTER_PK")
    long id;

    @Column(name = "DATA_COL_A")
    private Object fieldA;
}

@Entity
class Slave {
    @EmbeddedId
    private SlavePK id;

    @ManyToOne
    @JoinColumn(name = "MASTER_FK", referencedColumnName = "MASTER_PK", insertable = false, updatable = false)
    private Master master;

    @Column(name="DATA_COL_B")
    private Object fieldB;
}

@Embeddable
class SlavePK {
    @Column(name="MASTER_FK")
    private long masterId;

    @Column(name="PART2_PK")
    private int pkPart2;

    @Column(name="PART3_PK")
    private String pkPart3;
}


By setting the insertable/updatable properties on the @JoinColumn annotation, you make the reference non-authoritative. This is what you want -- the PK is the authoritative reference (and s.b. immutable).

You'd need to set up the constructors, getter/setters, and delegate methods to ID fields, etc. but this gives the basic idea.

Hibernate does go beyond this and allows a @ManyToOne mapping in the @Embeddable class, but I'm not exactly clear how far you could/should push this.

Good luck


Top
 Profile  
 
 Post subject: How to do this bidirectional
PostPosted: Wed Feb 14, 2007 11:17 am 
Newbie

Joined: Wed Feb 14, 2007 10:54 am
Posts: 2
Hi!

After googling half the day this post solves the first part of my problem ;-)

Now my question: How can I do this bidirectional ?
I would like to have an Collection in the Master mapped to the slaves, eg:
Code:
class Master {
....
@OneToMany()
@JoinColumn(name="MASTER_PK",referencedColumnName = "MASTER_FK",insertable = false, updatable = false)
private Set<Slave> slaveCollection;


While writing this post I tried the code and it works. Eclipse only shows up an error on the annotation:

The join table Master_Slave cannot be found on the database jpaTest/src/entities Master.java line 21 1171465981478 17350
The referenced column null cannot be found on the table Slave jpaTest/src/entities Master.java line 22 1171465981478 17351


Is the code valid? Or do I have a problem in future?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 15, 2007 7:44 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
Which version are you using? We do support @ManyToOne in @EmnbeddedId

For the bidir relationship, the correct solution is to use @OneToMany(mappedBy=...)

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 16, 2007 9:07 am 
Newbie

Joined: Wed Feb 14, 2007 10:54 am
Posts: 2
I first tried it with the mappedBy and allways got errors, because I used the foreign key field "master_fk". But now I noticed that I have to use the Master Property in the slave:

Code:
@OneToMany(mappedBy="master")


Now it works without problems ;-)

By the way it was the latest Relase 3.2.1.
But for interest how will the example look if we use @ManyToOn in the @EmbeddedId ?

With the solution above I have to fill the foreign key field "by hand". Thats not a big deal I just put some code in the setMaster method. But I can imagine that Hibernate could do this for me.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 14, 2007 3:05 pm 
Newbie

Joined: Tue Oct 24, 2006 10:50 am
Posts: 3
davidmarkcarlson wrote:
Hibernate has some support for what you are describing, but I am not entirely sure how far it goes. What I'll give you is the approach that should be supported by EJB3.0.

If the table/class with the composite id is Slave, and the table it references is Master, then the following code should do what you want:
Code:
@Entity
class Master {
    @Id
    @Column(name="MASTER_PK")
    long id;

    @Column(name = "DATA_COL_A")
    private Object fieldA;
}

@Entity
class Slave {
    @EmbeddedId
    private SlavePK id;

    @ManyToOne
    @JoinColumn(name = "MASTER_FK", referencedColumnName = "MASTER_PK", insertable = false, updatable = false)
    private Master master;

    @Column(name="DATA_COL_B")
    private Object fieldB;
}

@Embeddable
class SlavePK {
    @Column(name="MASTER_FK")
    private long masterId;

    @Column(name="PART2_PK")
    private int pkPart2;

    @Column(name="PART3_PK")
    private String pkPart3;
}


By setting the insertable/updatable properties on the @JoinColumn annotation, you make the reference non-authoritative. This is what you want -- the PK is the authoritative reference (and s.b. immutable).

You'd need to set up the constructors, getter/setters, and delegate methods to ID fields, etc. but this gives the basic idea.

Hibernate does go beyond this and allows a @ManyToOne mapping in the @Embeddable class, but I'm not exactly clear how far you could/should push this.

Good luck

Do you have a sample code to persist an instance of the Slave class? This is how I did in my code, but it doesn't work (it compiles, runs well, produces no error/exception but doesn't insert anything - and I didn't see the INSERT statement in the log file although I turned everything on):

- I created an instance m of Master, persisted it.
- Created an instance s of Slave, created the id for the instance s which contains the id of the instance m.
- If I use entityManager.persist(s), the log file shows no SQL statement.
If I use entityManager.merge(s), the log file has a SELECT sql statement like this:
select ... from Slave slave_0 where slave_0.masterId = ? and slave_0.pkPart2 = ? and slave_0.pkPart3 = ?
and there's no INSERT sql statement as I can see when I persisted the instance m of Master.

Do you have any advice for this case?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.