Here’s another problem that has a simple solution that took me longer than I expected to find.
I am using Hibernate 3.2.5 as my ORM. In one case, I want to map a child collection of items as an ordered List, taking advantage of the database to do my ordering.
I’m using Annotations intead of the hbm.xml format for my mappings in this project. Whenever possible, I’m using javax.persistence mappings in preference too Hibernate’s annoations.
Referring to Java Persistence with Hibernate, the mapping looks quite complicated, using Hibernate’s annotations. With some experimentation, I found a simple solution, using just the javax.persistence mappings. For simplicity, I’ll show the example annotating the Item / Bid class idea we’re used to from the Hibernate Caveat Emptor example.
package com.stevideter.domain; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Table; import org.apache.commons.collections.CollectionUtils; @Entity @Table(name = "ITEM") public class Item implements Serializable { private static final long serialVersionUID = -7990110946186412551L; private Integer id; private List bids = new ArrayList(); @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @OneToMany(mappedBy="item",cascade=CascadeType.ALL) @OrderBy("userId,date,amount") public List getBids() { return bids; } private void setBids(List bids) { this.bids = bids; } public void addBid(Bid bid) { if (CollectionUtils.isEmpty(bids)) { bids = new ArrayList();} bids.add(bid); bid.setItem(this); } public void removeBid(Bid bid) { if (CollectionUtils.isNotEmpty(bids)) { bids.remove(bid); } } } |
And for reference, here’s the mapping in the Bid with the Item as a parent:
package com.stevideter.domain; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "BID") public class Bid implements Serializable { private static final long serialVersionUID = 8165733510722884274L; private Integer id; private Item item; private Date date; private BigDecimal amount; private Integer userId; @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @ManyToOne( targetEntity = com.stevideter.domain.Item.class ) @JoinColumn(name = "itemid", nullable = false) public Item getItem() { return item; } public void setItem(Item item) { this.item = item; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public BigDecimal getAmount() { return amount; } public void setAmount(BigDecimal amount) { this.amount = amount; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } } |
With this mapping, the bids collection is managed by the Item, so saving the Item automatically saves the bid collection.
This example does follow the JPA standard of configuration by exception, so column names, etc., that are not explicitly called out follow the default standards.
What has been your trickiest Hibernate or javax.persistence mapping problem? How did you solve it?
–
Edited to make mapping on Bids properly bidirectional.
Thanks for this. Saved me a lot of time.
But the mapping does not work if I add a database constraint saying that the itemid column in item table is not null. Then when I try to save an item class with associated bids I get an error saying “not-null property references a null or transient value”.
Any pointers?
Without seeing your code, my first question is have you added an ID generating strategy?
That is one element I omitted from the example.
For example, if you’re using MySQL or SQL Server, you can use the built in identity generation:
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
public Integer getId() {
return id;
}
When using Oracle, I usually use the sequence strategy:
@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator=”ITEMID_SEQ”)
public Integer getId() {
return id;
}
What I meant was the itemid column in bids table. I apologize for the typo.
I resolved it. Thanks.
thanks for giving the difference between merge and saveOrUpdate() in hibernate. This is very good explination.
Hi i was just wondering about the OneToMany mapping you have created in your class Item.
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name=”itemid”)
@OrderBy(“userId,date,amount”)
public List getBids() {
return bids;
}
Is this the right way of doing this as my question is how would the ITEM table would have multiple foreign keys for a particular Item.
I think the join column should come on the ManyToOne side of the table and not the oneToMany side and if you dont want that to be a bidirectional relationship then probably you got to use a join table to make it unidirectional.
Correct me if i am wrong.
Does it not look like the itemid is going to be in the ITEM table?
Thanks
Prem
Hi ,
Instead of the join column in the OneToMany mapping it should be
@OneToMany(
mappedBy = “item”, cascade=CascadeType.ALL)
@OrderBy(“userId,date,amount”)
public List getBids() {
return bids;
}
Correct me if i am wrong.
Prem
Prem,
The JoinColumn referred to in the mapping on Bids in Item refers to the Foreign Key in the Bid table; we are explicitly telling Hibernate “the Foreign Key to Item in Bids is a column named itemid”
Another way to phrase that is – we are telling Item “find all rows in Bid where bid.itemid = item.id”
If we annotate the Bids in Item with mappedBy, we’re also telling Item how it relates to Bids, but with an important caveat — we’re telling Item it is not responsible for managing that relationship.
The decision then comes to what behavior you want. Usually, when I’m mapping child collections, I want the parent to be responsible for the collection. The reason is I want to be able to save the parent item, and have Hibernate automatically recognize I want it to also save any changes to the children.
If this is not the behavior you want (which is quite possible), then mappedBy would be the more appropriate choice.
In this example, though, you’d have mappedBy on both ends of the relationship, which sort of defeats the purpose.
Hi Stevi,
I am not saying that mappedBy shoudl be comming on both the ends but
@OneToMany(
mappedBy = “item”, cascade=CascadeType.ALL)
@OrderBy(“userId,date,amount”)
public List getBids() {
return bids;
}
and at the Bids class
@ManyToOne( targetEntity = com.stevideter.domain.Item.class )
@JoinColumn(name = “itemid”, nullable = false)
public Item getItem() {
return item;
}
your comment:
If we annotate the Bids in Item with mappedBy, we’re also telling Item how it relates to Bids, but with an important caveat — we’re telling Item it is not responsible for managing that relationship.
I really dont think this is what we are telling the hibernate when we use the mappedBy attribute as
the mappedBy=”item” is pointing to the item and does it not mean that it is the item at the Bids class responsible for the relationship.
By using mappedBy attribute we are making the Item and the Bids class to be bidirectional and if we don’t have the mappedBy attribute would it not be treated as unidirectional.
In your ManyToOne mapping in the Bids class, You have defined that itemid is the foreign key and what is the need to say the same thing in your OneToMany mapping aswell.
Please do clarify and if i am missing some important point you were trying to highlight to me, do accept my ignorance.
Prem,
I see what you’re saying now. I think I also suffered from a case of looking at the code, and similar code, so often that I wasn’t really reading what was there.
My mapping is unidirectional, not bidirectional, and would probably cause problems because neither end is marked as insertable/updatable false.
Makes me think I should write an entry about unidirectional/bidirectional and all the gotchas.
Thanks for pointing this out!