Hibernate annotations: The many-to-many association with composite key
Posted on July 19th, 2008 in EN | 53 Comments »
Also many-to-many associations are usually referenced as a bad design solution, they are widely used in almost all modern database-centric applications (especially in those built around the existing legacy database). The very common scenario is a case when you not only need to handle a many-to-many association but also hold some additional property on the association.
In a J2EE world, Hibernate still remains the most popular ORM tool (or the JPA provider, if you wish). But implementing such a scenario with Hibernate Annotations is not so simple as you might imagine.
First of all, in the good old spirit of the Hibernate community, there is almost no documentation about many-to-many associations and composite keys (two paragraphs stating that there is @ManyToMany and @EmbeddedId annotations are not considered a documentation).
And the second hope of every open-source tool consumer, the world wide web community, provides almost no help: everybody are referencing the same post by Marcel Panse, written in April 2006. Though Marsel’s post is very clean and descriptive, it become outdated and the solution provided there is simply not working with the latest versions of Hibernate Annotations.
So I had to reinvent the wheel by my own, based on Marsel’s sample. Below is the solution working with Hibernate Annotations 3.3.1.GA.
The database part is the same: we have three tables (item, product and product_item), two POJO classes, and two classes for a many-to-many association and its primary key. The main difference from Marsel’s solution is that I’m not using any kind of “fake” properties on ProductItem in order to reference Item and Product, but just a plain transient properties delegating to ProductItemPk.
@Entity @Table(name = "item") public class Item { private Integer id; private String name; private List<ProductItem> productItems = new LinkedList<ProductItem>(); public Item() { } @Id @GenericGenerator(name = "generator", strategy = "increment") @GeneratedValue(generator = "generator") @Column(name = "item_id", nullable = false) public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } @Column(name = "name") public String getName() { return this.name; } public void setName(String name) { this.name = name; } @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.item") public List<ProductItem> getProductItems() { return this.productItems; } public void setProductItems(List<ProductItem> productItems) { this.productItems = productItems; } }
@Entity @Table(name = "product") public class Product { private Integer id; private String name; private List<ProductItem> productItems = new LinkedList<ProductItem>(); public Product() { } @Id @GenericGenerator(name = "generator", strategy = "increment") @GeneratedValue(generator = "generator") @Column(name = "product_id", nullable = false) public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } @Column(name = "name") public String getName() { return this.name; } public void setName(String name) { this.name = name; } @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.product") public List<ProductItem> getProductItems() { return this.productItems; } public void setProductItems(List<ProductItem> productItems) { this.productItems = productItems; } }
Note the two important points: ProductItem has two transient properties for Product and Item (since an unidirectional relationships will be meaningless here) and a use of @AssociationOverrides annotation to specify the database columns
@Entity @Table(name = "product_item") @AssociationOverrides({ @AssociationOverride(name = "pk.item", joinColumns = @JoinColumn(name = "item_id")), @AssociationOverride(name = "pk.product", joinColumns = @JoinColumn(name = "product_id")) }) public class ProductItem { private ProductItemPk pk = new ProductItemPk(); @EmbeddedId private ProductItemPk getPk() { return pk; } private void setPk(ProductItemPk pk) { this.pk = pk; } @Transient public Item getItem() { return getPk().getItem(); } public void setItem(Item item) { getPk().setItem(item); } @Transient public Product getProduct() { return getPk().getProduct(); } public void setProduct(Product product) { getPk().setProduct(product); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ProductItem that = (ProductItem) o; if (getPk() != null ? !getPk().equals(that.getPk()) : that.getPk() != null) return false; return true; } public int hashCode() { return (getPk() != null ? getPk().hashCode() : 0); } }
@Embeddable public class ProductItemPk implements Serializable { private Item item; private Product product; @ManyToOne public Item getItem() { return item; } public void setItem(Item item) { this.item = item; } @ManyToOne public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ProductItemPk that = (ProductItemPk) o; if (item != null ? !item.equals(that.item) : that.item != null) return false; if (product != null ? !product.equals(that.product) : that.product != null) return false; return true; } public int hashCode() { int result; result = (item != null ? item.hashCode() : 0); result = 31 * result + (product != null ? product.hashCode() : 0); return result; } }
Hope this helps.
53 Responses
Hey! Thanks for the great tutorial. I’m trying to use your solution with Spring 2 and Struts 2 and it seems like Hibernate is trying to map the LinkedLists to table columns. Have you ever used this mapping with Spring? It seems like it should work, but I really don’t know very much about either framework.
Thanks for your thought!
–Sally
Actually, I figured out that the problem is JPA’s automatic mapping. So, never mind. I think I can figure it out.
–Sally
Okay, problem solved. Just had to add
@OneToMany(fetch = FetchType.LAZY, mappedBy = “pk.item”)
above the instance variable declaration
private List productItems = new LinkedList();
so that JPA knew not to automatically map it.
–Sally
Hi Sally
I made this sample with Hibernate Annotations 3.3.1.GA
Are you using another JPA provider?
Boris
Hi! Thanks for the example!
But I’ve a question: if ProductItem has a own attribute (an attribute of the N:N relation), which is the way to access it from Product or Item?
Perhaps the way is directly use HQL in a query, but i really don’t know. Can you explain a way to do this?
Thanks.
Diego
I’m using Hibernate Annotations 3.4.0. Maybe in the update they decided that the annotations should go on the instance variables and not the methods. I’m pretty new to hibernate and J2EE and JPA and all of that stuff, so this is the only version I’ve ever used.
–Sally
Great post! Thank you. It works!
Regards && Smiles,
Adam Woźniak (POLAND)
btw: There is a typo: “many-to-may”.
Thanks Adam, fixed the typo.
Hello Diego
You may access all associated ProductItem items (and their properties) by iterating over productItems collection of either Product or Item.
Hi Boris,
Great article, good to see an up to date version of Marcel’s article.
What would be great if you could add this to this article, to make it more complete:
- What the resulting tables should be. What is the resulting extra property (column) in the assocation when implementing this? I guess the same as Marcel’s diagram: “name”, but that’s not a given, definitely not for beginners like me. Maybe put Marcel’s ER diagram in this post? If you ask Marcel nicely I bet he’d allow it
- Maybe clearer would be adding a field in the association that is not in any of the Product or Item tables. For example, a created-timestamp. That makes more clear what’s going on.
- Would be great if you’d add an example on how to correctly invoke ProductItem to have something correctly saved. I myself used the testcase described here as a starting point.
Some things I found out while implementing this:
- FetchType.EAGER worked for me just fine in the @OneToMany
- If you get a StackOverflow while trying the code, you might have switched the pk.item and pk.proaduct.
- I set the @OneToMany at productItems level (so not at method level).
btw:
I don’t understand why there is a need to use AssociationOverride annotation
I think (am I wrong?) that
mappedBy = “pk.item”
in:
@OneToMany(fetch = FetchType.LAZY, mappedBy = “pk.item”)
…is unambiguous. So why – the hell – we must to use AssociationOverride annotation?
Somebody know the reason?
btw II:
Regarding StackOverflow while fetching large collection:
a) see my post on Hibernate forum:
http://forum.hibernate.org/viewtopic.php?t=988409
b)… and corresponding Hibernate JIRA feature request:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-3421
Smiles,
Adam Woźniak (Poland)
AFAIK last standards requires to add all the annotations at field level. Indeed I needed to do that with the “@EmbeddedId” annotation to get it work (without it was failing during the export of the ddl)
To keep all the source coherent (my IDE was complaining about that) I added two fields in ProductItem.java:
Product product;
Item item;
Then above both I added the annotation @Transient. Then I deleted the same annotation from the two methods.
Does it make sense?
Btw I tested the classes without the @Transient annotation and they seem to work flawlessly. What is the meaning of this annotation wrt the many-to-many problem?
Hi Stefano
Since in order to solve the many-to-many problem we define an additional properties on ProductItem entity, we don’t want them to be persistent.
JPA specifies that for field access any field that is not mapped as @Transient is persistent, so for this case according to specification you must use this annotation.
The case of persistent properties is not defined so clear, and the decision whether the property without any annotation on a getter method is persistent or not should be done by a specific JPA implementation. In this case you should refer to you JPA provider implementation docs.
However, when we talk about Hibernate Annotations, it makes an assumption that any entity property is persistent if not marked as @Transient (see here).
Hi Adam
You’re right – I would expect from Hibernate Annotations (and the samples in Embedded objects is like to work this way) that the mapping will succeed in our case, since we’re not really redefined the mapping.
Hi Kirzner,
I have a example:
table A (unid (PK))
table item(item_id(PK),a_unid(FK),item_code) (a_unid,item_code) is Unique keys
table product(product_id(PK),a_undi(FK),product_code) (a_unid,product_code) is Unique keys
table product_item(a_unid,item_code,product_code) composite keys
How to do about this example?
Hi Jansky,
I don’t know if this is exactly what you’re asking, but when I implemented Boris’ example, I needed to be able to have two “ProductItem” associations with the same product & item, but behave as different entities. I did that in my database by adding a unique id for each mapping. To carry that over, I added:
@Id
@GeneratedValue
private Integer mappingId;
@Column(name=”mappingId”)
public Integer getMappingId() {
return mappingId;
}
public void setMappingId(Integer mappingId) {
this.mappingId = mappingId;
}
to my ProductItemPk class. Also make sure to change the equals method to include the mappingId with the following line above “return true”:
if(this.mappingId!=that.getMappingId()) return false;
Your composite keys are more complicated, but hopefully that will point you in the right direction!
Hi Boris/all,
I’ve got a question:
Should this way of updating the association work too as in the code below?
This (or closely similar code) would work in te case where you don’t need an extra field/property. Then you’d let Hibernate do the mapping with @Many-to-many.
// Get an item and a product
Product product = getStoredProduct();
Item item = getStoredItem();
// Now create a new productItem with the product and item
ProductItem productItem = new ProductItem();
productItem.setItem(item);
productItem.setProduct(product);
// Add it via the collection(!)
item.getProductItems().add(productItem);
// Or maybe in this way:
// List itemProductItems = item.getProductItems();
// itemProductItems.add(productItem);
// item.setProductItems(itemProductItems);
// And save it
dao.save(item);
Should Hibernate create the related ProductItem? It does not seem to do that, but I could imagine Hibernate could know how to do it…
Is it indeed not possible and if so why? Else, what would make it work via the Lists?
Thanksssssss
Hello,
I have a problem with the (great) example and declaring a query when selecting all ProductItem’s for a given Item. The following query works:
select p from ProductItem p, Item i where i = :item and p.pk.item = i
, but has those additional “pk” in it. I expected the following query to work:
select p from ProductItem p, Item i where i = :item and p.item = i
(without pk), but gives me an error message
“org.hibernate.QueryException: could not resolve property: item of: ProductItem”
Can anybody give me a hint, how to get the second query working ?
Greetings, Rainer
Hi Rainer
You can’t get a second query work with this example since form the Hibernate perspective the only (non-transient) property
ProductItemhas is apkproperty.Hello Boris
thanks for clarification.
Rainer
Fantastic tutorial! A number of my clients have found it very helpful.
May i know what is the propose of overridden the methods for equals and hashCode?
Hi ,
there is a more simple solution following the EJB 3.0 standard annotations.
see example below
regards
Yves
@Entity
@Table(name = “item”)
public class Item {
private Integer id;
private String name;
/**
* @return the id
*/
@Id
@Column(name=”item_id”)
public Integer getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Integer id) {
this.id = id;
}
/**
* @return the name
*/
@Column(name=”name”)
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
@Entity
@Table(name = “product”)
public class Product {
private Integer id;
private String name;
public Product(){
}
/**
* @return the id
*/
@Id
@Column(name=”product_id”)
public Integer getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Integer id) {
this.id = id;
}
/**
* @return the name
*/
@Column(name=”name”)
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
@Entity
@Table(name = “product_item”)
public class ProductItem {
@EmbeddedId
ProductItemPk productItemPk;
@ManyToOne
@JoinColumn(name = “itemId”)
private Item item;
@ManyToOne
@JoinColumn(name = “productId”)
private Product product;
/**
* @return the product
*/
public Product getProduct() {
return product;
}
/**
* @return the item
*/
public Item getItem() {
return item;
}
/**
* @param item the item to set
*/
public void setItem(Item item) {
this.item = item;
}
/**
* @param product the product to set
*/
public void setProduct(Product product) {
this.product = product;
}
}
@Embeddable
public class ProductItemPk implements Serializable {
private int productId;
private int itemId;
public ProductItemPk(){
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + itemId;
result = prime * result + productId;
return result;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ProductItemPk other = (ProductItemPk) obj;
if (itemId != other.itemId) {
return false;
}
if (productId != other.productId) {
return false;
}
return true;
}
}
schema :
create table item (
item_id integer not null,
name varchar(255),
primary key (item_id)
);
create table product (
product_id integer not null,
name varchar(255),
primary key (product_id)
);
create table product_item (
itemId integer not null,
productId integer not null,
primary key (itemId, productId)
);
alter table product_item
add constraint FK3C742463DC71CF6F
foreign key (itemId)
references item;
alter table product_item
add constraint FK3C7424631010938B
foreign key (productId)
references product;
Any answers to the above question from TTLNews about how to create an entry in the related ProductItem mapping table along with the product and item when saving the Item object.
I am doing something similar and was wondering how Hibernate would handle this scenario.
Thanks
Hello TTL and James
If you defined a proper cascading on
Item.getProductItems()association than adding theProductItemto the collection should trigger Hibernate to persist this object.However, if you do not need to store an additional data on the association between
ProductandItem, theProductItemis completely redundant – the@ManyToManyonItem.getProducts()andProduct.getItems()will do the job (note that@ManyToManyassociation should not be defined symmetrically on those properties).Can someone lend a hand, I seem to be having alot of trouble here. I have two tables that are quite similar to Product and ID, except the join table uses a unique index in both tables that is not a foreign key. Here is the basic table layout:
product
===========
id PK
legacy_product_id Unique
product_name
item
===========
id PK
legacy_item_id Unique
item_name
product_to_item_relationship
===========
id PK
legacy_product_id
legacy_item_id
This is a many to many relationship. I have had to do some mappings for OneToMany’s using this legacy_XXX_id column, and it works as long as I add in a “referencedColumnName” attribute on my annotation. For example:
@JoinColumn(name=”legacy_foo_id”, referencedColumnName=”legacy_foo_id”)
I implemented what you have above and it works, assuming the PK of my item and product table are what I use to create the relationship. In fact, when I write the code identical to what you have there, it seems to work as it should be using the PK of the table.
So in order to force the legacy_id, I altered the ProductItem class association annotation to look like this:
@AssociationOverrides({
@AssociationOverride(name=”pk.item”, joinColumns=@JoinColumn(name=”legacy_item_id”, referencedColumnName=”legacy_item_id”)),
@AssociationOverride(name=”pk.product”, joinColumns=@JoinColumn(name=”legacy_product_id”, referencedColumnName=”legacy_product_id”))
})
When I include the “referencedColumnName” i get the following error:
java.lang.ExceptionInInitializerError
Caused by: org.hibernate.MappingException: property [_my_package_ProductItem_pk.item] not found on entity [my.package.Item]
When I remove the referencedColumnName it works, but uses the wrong id for the join:
item.id -> item_to_product_relationship.legacy_item_id
and
product.id -> item_to_product_relationship.legacy_product_id
but what I really need is
item.legacy_item_id -> item_to_product_relationship.legacy_item_id
and
product.legacy_item_id -> item_to_product_relationship.legacy_product_id
forgive me if I ran on a bit here. I don’t have control over this database schema, and was hoping there was a way I could accomplish this with ORM.
Regards,
Robin
I’m confused.
The first paragraph of this posting states: “The very common scenario is a case when you not only need to handle a many-to-many association but also hold some additional property on the association”.
Where is the “additional property on the association”. In this example, shouldn’t the ProductItem class contain accessors for the additional property?
[...] public links >> hashcode Gary Benson: Debug option fun First saved by garcon | 4 days ago Hibernate annotations: The many-to-many association with composite key First saved by cgracio | 14 days ago Getting A Java Object’s Reference ID First saved by [...]
Hi Joe
You’re right. The property does not appear in the example.
However, you may easily add a persistent property to
ProductItementity, for example:private Integer count; @Column(name = "count") public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; }I implemented this mapping in my project, but when I querying database with such HQL:
“from ProductItem”
… I got 3 SQL statements.
To decrease this number of SELECT SQL statements I changed:
@ManyToOne
to:
@ManyToOne(fetch=FetchType.LAZY, optional=false)
If you agree with this my tip probably it would be good to update your source code in this post (for other colleagues).
Smiles,
Adam
Hi all,
I am working with spring framework.I am having two entity Leave and StaffLeave.These values are associatied in another entity LeaveAssociation.
In Leave.java
protected List leaveAssociation=new LinkedList();
In StaffLeave.java
protected List leaveAssociation=new LinkedList();
I Have list all leave and staffleave the value in list box of LeaveAssociation.
I my LeaveAssociation .java
private Leaves leaves;
private StaffLeaveCategory staffLeaveCategory;
@ManyToOne(targetEntity=Leaves.class )
public Leaves getLeaves() {
return leaves;
}
public void setLeaves(Leaves leaves) {
this.leaves = leaves;
}
@ManyToOne(targetEntity=StaffLeaveCategory.class)
public StaffLeaveCategory getStaffLeaveCategory() {
return staffLeaveCategory;
}
public void setStaffLeaveCategory(StaffLeaveCategory staffLeaveCategory) {
this.staffLeaveCategory = staffLeaveCategory;
}
Once i select leave and staffleave value and click save it showing
Failed to convert property value of type [java.lang.String] to required type [com.pennent.LeaveMaster.model.Leaves] for property leaves; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [java.lang.String] to required type [com.pennent.LeaveMaster.model.Leaves] for property leaves: no matching editors or conversion strategy found
Failed to convert property value of type [java.lang.String] to required type [com.pennent.StaffLeaveCategory.Model.StaffLeaveCategory] for property staffLeaveCategory; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [java.lang.String] to required type [com.pennent.StaffLeaveCategory.Model.StaffLeaveCategory] for property staffLeaveCategory: no matching editors or conversion strategy found
So please help me to solve this
Thanks
Hi
It’s hard to understand what is the problem you’re facing from the code snippet you provided, but I would suggest that the problem is JSF-related and not Hibernate related, so you should probably check your page code (for example, make sure you’re using the appropriate converter).
Suppose i join three tables in hibernate and retrieve 10 columns..how to classify that these 10 objects belong to which classes.
Regarding the example provided by Yves,
Toplink Essentials assumes and creates fields in table product_item twice.
This results in an error in the execution of the schema DDL generated from class ProductItem as follows:
JDO76609: Got SQLException executing statement
“CREATE TABLE product_item (productId INTEGER, ITEMID INTEGER NOT NULL, PRODUCTID INTEGER NOT NULL, itemId INTEGER, PRIMARY KEY (ITEMID, PRODUCTID))”:
java.sql.SQLSyntaxErrorException: Column name ‘PRODUCTID’ appears more than once in the CREATE TABLE statement.
I would be interested to know what generic EJB 3.0 solution exists for many-to-many with additional fields in the join table.
Regarding the comment at the top “many-to-many associations are usually referenced as a bad design solution”, I would be eager to learn what the better alternative is.
I can’t thank you enough for putting this example together. I wasted a good chunk of my day struggling with this problem until I found this page.
Once again my heartiest Thanks for putting this page.
To TTL and James,
Regarding automatically inserting an entry in the ProductItem whenever you create a Product with relation to an Item… yes the sample code DOES NOT provide this functionality.
You would have to add the following code to Product and Item class:
public class Product {
…
@OneToMany(fetch = FetchType.LAZY, mappedBy = \pk.product\, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
public List getProductItems() {
return this.productItems;
}
…
Do the same for getProductItems() method in Item class.
What you really want here is the CascadeType.SAVE_UPDATE of hibernate annotation. DELETE_ORPHAN is also a good feature so that when a Product is deleted, the entry in table ProductItem is also deleted related to that Product. Likewise, if you delete an Item, ProductItem is updated to remove any product-item related to that deleted item.
ref: http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-cascade
This is very convenient because you do not have to manipulate ProductItem at all.
example use:
—————————————————————————————————-
Product product = new Product();
//add product values
Item item = new Item();
//add item values
List productItemList = new ArrayList();
ProductItem productItem = new ProductItem();
productItem.setItem(productItem);
//set other productItem values (if there are any more attributes other than Product and Item)
productItemList.add(productItem);
product.setProductItem(productItemList);
productManager.save(productItemList);
—————————————————————————————————-
As you can see, you only need to create ProductManager and ItemManager (in the service layer) but you do not need to create ProductItemManager because you do not need to explicitly insert to ProductItem table. Entries will be automatically created when you create/update a new Product with Item relation or Item with Product relation (bidirectional many to many association).
Hope this helps! Ü
I just reviewed my previous post and noticed that I messed up on the example code. Below is the corrected one (plus added comments for convenience)
Sample code for creating new Product that links to one or many Items:
—————————————————————————————————-
Product product = new Product();
//set product values
Item item = new Item();
//set item values
List productItemList = new ArrayList();
ProductItem productItem = new ProductItem();
productItem.setItem(item);
//set other productItem values (if there are any more attributes other than Product and Item)
//no need to set product since we are creating a new product
productItemList.add(productItem);
product.setProductItems(productItemList);
productManager.save(product);
—————————————————————————————————-
This will also work for creating a new Item that links to one or many Product since its bidirectional relationship. Table ProductItem should be updated automatically. Works for Create and Update (if CascadeType.SAVE_UPDATE is added) and Delete (if CascadeType.DELETE_ORPHAN is added).
Thanks and sorry for the confusion in my previous post. Ü
Tks a million for this post, very helpful.
Hi all.
Nice solution.
But i tried to use the Cascade solution from user “AnD”, but i got an exception when the save would be performed.
I think the problem is that we don’t set product (“no need to set product since we are creating a new product”) in productItem’s from productItemList … so hibernate can’t get the productTO beeing save to set automatically in ProductItem that should be saved because of cascade.
The exception is described in this link:
http://www.guj.com.br/posts/list/133845.java#721178
Please see the exception there. The other text in portuguese language is what i’d explained here
Hi Romulo
It is not easy to guess from the only stack trace what is the original code causing the problem, but assuming you are using the same example provided by AnD: if you take a look at the code in
org.hibernate.type.EntityType#getHashCode(), you’ll see that hash code evaluation of an entity uses entity identifier as a part of evaluation.Thus, entity having no identifier (i.e. not saved yet) will cause the same problem you’re faced with.
In general, I would not agree with AnD’s decision not updating the relation between
ProductandProductItem(the list) from the both sides, since in this case you’re assuming a particular order of entity updates. This is error-prone, especially in the case of new entities, when you cannot even assume that proxied objects will do a part of a job and update the association under the hood.Great tutorial, It’s very helpfull
I faced a strange behavior while connecting to a MySQL database.
If the generators were declared as in the example
@GenericGenerator(name = “generator”, strategy = “increment”)
@GeneratedValue(generator = “generator”)
hibernate was not able to automatically flush (even with an always policy) the session (I had to call session.flush() explicitly).
Whereas, if I declare the generator in the following way:
@GeneratedValue(strategy = GenerationType.AUTO)
Eveything works like a charm.
Hopes this helps,
Stefano
Hi,
I am getting the following exception
org.hibernate.AnnotationException: mappedBy reference an unknown target entity property
Can you help me on this
thanks
Hello *,
This is a nice post and very useful however using it I got into a problem.
while eagerly fetching data I get a StackOverFlow error .. not sure how to overcome this. I probably have to use a IndexColumn annotation but not sure where to put it and to what field in the EmbeddeId to associtate to.
Do you have any clues on that?
Thanks,
Marius
Hi,
when implementing this, my database complains that it cannot create a primary key constraint for the ProductItemPK because the product and item property can be null. Any ideas how to make it issue create table with not null for product and item?
Thanks,
Tim
Hello! great tutorial!! can we see the xml mapping file vonly ersion of this?? classes and xml file..
Hello!
I hope you check this from time to time – would be great cause I have a problem with this hole thing.
I have a similar mapping and persisting to db works fine, but when searching I need to get everything instant, so I turned FetchMode to EAGER.
And now, here is the problem, I get StackOverflow, even there is only 1 entry in the DB ?!
Do you know how to fix it ?
Greets
Chris
Hi Chris
Fetching all with EAGER mode seems a bad practice.
I’d suggest you take a look at Hibernate Search.
This tutorial is exactly what I was looking for.
It fits perfectly in my Spring-Hibernate-Tiles webapplication
Nice work dude,
Greets
Dani
ditto…this tutorial is exactly what I was looking for.
dumb question – once you build the tables and classes, how do you USE them, particularly in an app using DAOs. Must I create DAOs and DAOHibernates for both the association class and the pk class?
Great tutorial, thx!
Jenn
Hi, Your post gave me good understanding of many – many association with composite primary key in the join table(Product_Item).
I appreciate if you give guidance in my case.
I’ve a problem in mapping the referenceColumn attribute in @JoinColumn element in ProductItem.java page.
Assume both Product and Item table having the exact columns in your example.
But in the associated table, instead of having Product.ID and Item.ID as members of composite pk, I’m having Product.Name and Item.Name as members of Composite key.
Entity Table: ProcessMaster
———————
id (pk)
processId (unique)
Entity Table: Errors
——————
id (pk)
Code (unique)
Join Table: tblaeErrors
————————
ScreenID(FK) — ProcessID field of ProcessMaster table
Code(FK) — ErrorCode field of Errors table
Response – not null
primary_key (ScreenID, Code);
I’ve modified your code, to point to the reference column, but no success.
@Entity
@Table(name=”[tblaeErrors]“)
@AssociationOverrides({
@AssociationOverride(name = “pk.error”, joinColumns = @JoinColumn(name = “[errorCode]“, referencedColumnName=”[code]")),
@AssociationOverride(name = "pk.process", joinColumns = @JoinColumn(name = "[ScreenID]", referencedColumnName="[processID]"))
})
When I compile the code, I'm getting the following error.
Caused by: org.hibernate.MappingException: Unable to find logical column name from physical name: [TBL:DT:MST:Process].id
at org.hibernate.cfg.Mappings.getLogicalColumnName(Mappings.java:513)
Please guide me how to point to unique column of parent table.
--Velu
primary_key(Product.name, Item.name);
Hi hibernewbie
Once you put the associated classes into your Hibernate model, they become available for querying by either using HQL or a Query API. There is no a “must” need for defining a DAO class for each type of objects.
However, if querying those classes is a major part of your application logic, DAO for one (or each one of them) might serve you as a useful utility.