Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 25 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: mapping composite primary key with foreign key references
PostPosted: Thu Apr 27, 2006 4:10 pm 
Newbie

Joined: Tue Jul 20, 2004 1:25 pm
Posts: 12
Hibernate version: 3.2 cr1
Hibernate Annotations version: 3.1 beta10

I'm trying to use annotations to map an existing schema where a table (InventoryItem) table as a composite primary key comprised of 2 foreign key references. No matter what I try I keep getting this exception...

org.hibernate.MappingException: property not found: location on entity InventoryItem

The schema is defined as such...
Code:
create table TblLocation
(
  id number(38,0) not null,
  name varchar2(255) not null,
  primary key (id)
);

create table TblItem
(
  id number(38,0) not null,
  name varchar2(255) not null,
  primary key (id)
);

create table TblInventoryItem
(
  location_id number(38,0) not null,
  item_id number(38,0) not null,
  quantity number(38,0) not null,
  price number(38,2) not null,
  primary key (location_id,item_id),
  foreign key (location_id) references TblLocation(id),
  foreign key (item_id) references TblItem(id)
);



And here are the mapped classes

Code:
@Entity
@Table(name="TblLocation")
@SequenceGenerator(name="LocationSeq",sequenceName="LOCATION_SEQ")
public class Location
{
   private long id;
   private String name;
   private Set<InventoryItem> _items;
   
   @Id
   @GeneratedValue(strategy=GenerationType.SEQUENCE,generator="LocationSeq")
   public long getId()
   {
      return id;
   }
   
   public void setId(long id)
   {
      this.id = id;
   }
   
   public String getName()
   {
      return name;
   }
   
   public void setName(String name)
   {
      this.name = name;
   }

   @OneToMany(mappedBy="location")
   public Set<InventoryItem> getItems()
   {
      return _items;
   }

   public void setItems(Set<InventoryItem> items)
   {
      _items = items;
   }
}


@Entity
@Table(name="TblItem")
@SequenceGenerator(name="ItemSeq",sequenceName="ITEM_SEQ")
public class Item
{
   private long id;
   private String name;
   
   @Id
   @GeneratedValue(strategy=GenerationType.SEQUENCE,generator="ItemSeq")
   public long getId()
   {
      return id;
   }
   
   public void setId(long id)
   {
      this.id = id;
   }
   
   public String getName()
   {
      return name;
   }
   
   public void setName(String name)
   {
      this.name = name;
   }
}


@Entity
@Table(name="TblInventoryItem")
@IdClass(InventoryItemId.class)
public class InventoryItem
{
   private Location _location;
   private Item _item;
   private long _quantity;
   private float _price;
   
   @Id
   @ManyToOne
   @JoinColumn(name="location_id")
   public Location getLocation()
   {
      return _location;
   }
   
   public void setLocation(Location location)
   {
      _location = location;
   }
   
   @Id
   @ManyToOne
   @JoinColumn(name="item_id")
   public Item getItem()
   {
      return _item;
   }
   
   public void setItem(Item item)
   {
      _item = item;
   }
   
   public float getPrice()
   {
      return _price;
   }
   
   public void setPrice(float price)
   {
      _price = price;
   }
   
   public long getQuantity()
   {
      return _quantity;
   }
   
   public void setQuantity(long quantity)
   {
      _quantity = quantity;
   }
}

@Embeddable
public class InventoryItemId
{
   private Location   _location;
   private Item      _item;
   
   public Item getItem()
   {
      return _item;
   }
   public void setItem(Item item)
   {
      _item = item;
   }
   public Location getLocation()
   {
      return _location;
   }
   public void setLocation(Location location)
   {
      _location = location;
   }
}



Is it possible to map a composite primary key using foreign key references? I've tried several variations using @ManyToOne, @JoinColumn, ... but haven't been able to get this to work.

any help would be appreciated.

thanks,
Brett


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 28, 2006 3:12 am 
Beginner
Beginner

Joined: Fri Apr 28, 2006 3:04 am
Posts: 22
Location: Amsterdam
I have a working example for you:
Note you have to use ejb annotations 3.1 beta 10 or it won't work.

Image

Code:
 
  @Entity
  @Table(name = "Product")
  public class Product extends AbstractRecordImpl
  {
    @NotNull private String name;
    private String description;
   
    //Some getters & setters
    ...
  }
 
  @Entity
  @Table(name = "Item")
  public class Item extends AbstractRecordImpl
  {
    @NotNull private String name;

    //Some getters & setters
    ...
  }


Code:
  @Entity
  @Table(name = "ProductItem")
  public class ProductItem
  {
    @Id
    private ProductItemPK primaryKey = new ProductItemPK();
    private String description;

    public String getDescription() {
      return description;
    }

    public void setDescription(String description) {
      this.description = description;
    }

    //needed for hibernate
    public void setPrimaryKey(ProductItemPK primaryKey) {
      this.primaryKey = primaryKey;
    }

    public void setProduct(Product product) {
      primaryKey.setProduct(product);
    }

    public Product getProduct(){
      return primaryKey.getProduct();
    }

    public void setItem(Item item) {
      primaryKey.setItem(item);
    }

    public Item getItem() {
      return primaryKey.getItem();
    }


    @Embeddable
    private class ProductItemPK implements Serializable
    {
      @ManyToOne
      private Item item;

      @ManyToOne
      private Product product;

      public ProductItemPK() {}

      //Some getters and setters
      ...
    }
  }



The only thing is i can't get it to work bidirectional. I want to map a List of ProductItems in both Product and Item, but it seems i can't get the mapping right.
I tried:
Code:
  @OneToMany(mappedBy="primaryKey")
  private List<ProductItem> productItems = new ArrayList<ProductItem>();

But hibernate can't find the property primaryKey.. :(


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 28, 2006 9:14 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7179
Location: Atlanta, USA
try primaryKey.item

_________________
Emmanuel
Check Hibernate Search in Action out


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 28, 2006 9:21 am 
Beginner
Beginner

Joined: Fri Apr 28, 2006 3:04 am
Posts: 22
Location: Amsterdam
emmanuel wrote:
try primaryKey.item


Nope, gives a error:
MappingException: property not found: primaryKey.item on entity ProductItem


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 28, 2006 4:12 pm 
Newbie

Joined: Tue Jul 20, 2004 1:25 pm
Posts: 12
Using mapping files I'm able to load the Location object and its associated InventoryItem objects.

Code:
Location location = (Location)session.get(Location.class, new Long(1));
Set<InventoryItem> items = location.getItems();
for(InventoryItem item : items)
{
    .... access InventoryItems here ...
}


So, is this a problem with the way I've configured my annotations?

thanks,
Brett



Below are the mapping files I used for these classes...

Code:
<class
    name="Location"
    table="TblLocation">

    <id name="id" type="long" column="ID" >
        <generator class="sequence">
            <param name="sequence">LOCATION_SEQ</param>
        </generator>
    </id>

    <property
        name="name"
        type="java.lang.String"
        column="NAME"
        not-null="true"
        length="255">
    </property>

    <!-- associations -->
    <set name="items" inverse="true" fetch="join" cascade="save-update">
        <key column="location_id"/>
        <one-to-many class="InventoryItem"/>
    </set>
</class>


<class
    name="Item"
    table="TblItem"
    lazy="false">

    <id name="id" type="long" column="ID" >
        <generator class="sequence">
            <param name="sequence">ITEM_SEQ</param>
        </generator>
    </id>

    <property
        name="name"
        type="java.lang.String"
        column="NAME"
        not-null="true"
        length="255">
    </property>
</class>

<class
    name="InventoryItem"
    table="TblInventoryItem"
    lazy="false">

    <composite-id name="id" class="InventoryItemId">
        <key-many-to-one
            name="location"
            class="Location"
            column="location_id"/>
        <key-many-to-one
            name="item"
            class="Item"
            column="item_id"/>
    </composite-id>

    <property name="quantity" type="long" column="quantity" not-null="true"/>
    <property name="price" type="float" column="price" not-null="true"/>
</class>


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 28, 2006 4:21 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7179
Location: Atlanta, USA
marcelpanse wrote:
emmanuel wrote:
try primaryKey.item


Nope, gives a error:
MappingException: property not found: primaryKey.item on entity ProductItem


Open a Jira issue with a runnable test case please, I'll try and fix that.

_________________
Emmanuel
Check Hibernate Search in Action out


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 28, 2006 4:22 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7179
Location: Atlanta, USA
@ManyToOne and @Id are not supported through annotations so far.

_________________
Emmanuel
Check Hibernate Search in Action out


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 28, 2006 6:04 pm 
Newbie

Joined: Tue Jul 20, 2004 1:25 pm
Posts: 12
So I changed my class to use @EmbeddedId instead if @Id and @ManyToOne and am still getting the same error...

Code:
property not found: location on entity InventoryItem


The example is now similar to that attached to ANN-16, but I still can't seem to get it to work. I'm not sure at this point if this is unsupported or my annotations are incorrect. Could someone clarify this for me.


New Annotated classes...
Code:
@Entity
@Table(name="TblInventoryItem")
public class InventoryItem
{
   private InventoryItemId _id;
   private long _quantity;
   private float _price;

   @EmbeddedId
   public InventoryItemId getId()
   {
      return _id;
   }
   ... other getters and setters ...
}

@Embeddable
public class InventoryItemId implements Serializable
{
   private Location   _location;
   private Item      _item;
   
   @ManyToOne
   @JoinColumn(name="item_id",nullable=false)
   public Item getItem()
   {
      return _item;
   }
   
   @ManyToOne
   @JoinColumn(name="location_id",nullable=false)
   public Location getLocation()
   {
      return _location;
   }

   ... setters removed ...
}




thanks,
Brett


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 5:59 am 
Beginner
Beginner

Joined: Fri Apr 28, 2006 3:04 am
Posts: 22
Location: Amsterdam
emmanuel wrote:
marcelpanse wrote:
emmanuel wrote:
try primaryKey.item


Nope, gives a error:
MappingException: property not found: primaryKey.item on entity ProductItem


Open a Jira issue with a runnable test case please, I'll try and fix that.


I have a working (workaround) solution for this particular problem:

Instead of using primaryKey.item, ill use just 'item'. The problem is hibernate can't find a property called 'item'. So i kinda fool hibernate by making a property item which never is used and is not updatable etc.:

Code:
@Column(name="item_id", nullable=false, updatable=false, insertable=false)
private Item item;



The getters and setters of item exists, but they call primaryKey.getItem(). and primaryKey.setItem()...

Another strange thing is this ONLY works when i put my PK class in a seperate file as a normal class. When i put it as a innerclass, then ill get the exception that hibernate can't find the default construct of my innerclass. (but it is there).
[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 09, 2006 5:51 pm 
Beginner
Beginner

Joined: Wed Apr 26, 2006 2:41 pm
Posts: 30
Have you been able to figure out if this mapping is correct or not? I cant find a Jira issue opened for this so I'm assuming the way it's mapped is wrong. We have a similar table layout here and I cannot get the mapping right either. When I try marcelpanse's work around on our classes, the FROM clause of the select uses a default tablename even though all of our classes specify tables.


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 09, 2006 6:17 pm 
Newbie

Joined: Tue Jul 20, 2004 1:25 pm
Posts: 12
No, I haven't been able to get this to work. Until I'm able to figure this out I've revered back to using mapping files for my objects that require a composite key (which is also part of a FK).

I still haven't figured out if my annotations are incorrect or if this type of mapping isn't yet supported.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 10, 2006 2:39 am 
Beginner
Beginner

Joined: Fri Apr 28, 2006 3:04 am
Posts: 22
Location: Amsterdam
poswald wrote:
Have you been able to figure out if this mapping is correct or not? I cant find a Jira issue opened for this so I'm assuming the way it's mapped is wrong. We have a similar table layout here and I cannot get the mapping right either. When I try marcelpanse's work around on our classes, the FROM clause of the select uses a default tablename even though all of our classes specify tables.


First try to get it the composite foreign keys working without the bidirectional relationships.. I described in my post above an Embedded class as inner class. I found this is not working properly, you should use an 'normal' class in a seperate file to use as your embedded primaryKey.
I made a blog with a better description of how to do this: http://java-aap.blogspot.com/2006/04/hibernate-annoations-composite-primary.html

After you have it working without the Sets at the other tables (product & item) .. then you should try to make it bidirectional by added the sets with the @ManyToOne at the product & item classes and use the workaround i specified.

It is kind of tricky.. i think it only works if you specify the annotations at field level (not at the getters as i specified in my post).
And it only works with the latest hibernate-annotations beta 10!!

Well at least i have it working like this..


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 10, 2006 12:49 pm 
Beginner
Beginner

Joined: Wed Apr 26, 2006 2:41 pm
Posts: 30
marcelpanse wrote:
It is kind of tricky.. i think it only works if you specify the annotations at field level (not at the getters as i specified in my post).
And it only works with the latest hibernate-annotations beta 10!!

Well at least i have it working like this..



YES! That was the problem! I moved the annotations from the getters/setters to annotations on the properties of the join table and the embedded primary key and it worked. I also had to specify the @Joincolumn(name="column_name") on the primary keys because our database does not follow the default naming convention.

Perhaps we can make your blog post into a test case and submit it into the Jira so this bug can get fixed.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 11, 2006 3:15 pm 
Newbie

Joined: Tue Jul 20, 2004 1:25 pm
Posts: 12
marcelpanse wrote:
After you have it working without the Sets at the other tables (product & item) .. then you should try to make it bidirectional by added the sets with the @ManyToOne at the product & item classes and use the workaround i specified.


Everything works just fine if I exclude the assocation. Once I add the bidirectional association (via Set) an exception is thrown. I tried the workaround (switched to field annotations, added "magical" getter/setter for primary keys) mentioned in the blog post, but I still get the "property not found" exception. Could you post (or update the blog entry) a complete example showing the bidirectional association?

TIA,
Brett


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 12, 2006 4:51 am 
Beginner
Beginner

Joined: Fri Apr 28, 2006 3:04 am
Posts: 22
Location: Amsterdam
brettbandy22 wrote:
Everything works just fine if I exclude the assocation. Once I add the bidirectional association (via Set) an exception is thrown. I tried the workaround (switched to field annotations, added "magical" getter/setter for primary keys) mentioned in the blog post, but I still get the "property not found" exception. Could you post (or update the blog entry) a complete example showing the bidirectional association?

TIA,
Brett



I updated my blog with a full example of the bidirectional version so check out the next link:

http://java-aap.blogspot.com/2006/04/hibernate-annotations-composite.html#links


Last edited by marcelpanse on Fri May 19, 2006 5:03 am, edited 1 time in total.

Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 25 posts ]  Go to page 1, 2  Next

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.