-->
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.  [ 8 posts ] 
Author Message
 Post subject: How to map two objects to two different properties
PostPosted: Fri Sep 24, 2010 11:26 am 
Newbie

Joined: Fri May 09, 2008 8:30 am
Posts: 10
Hello everyone,

I struggle with the following problem for a long time now, not knowing how to map (using Hibernate/JPA annotations) this class/dependancy construct correctly, and I really hope you can help me with this.

To simplify things, I created two example classes, representing my mapping problem (please do not question if the classes make sense, these are just only examples to get the point of the problem :). These classes look like this:

Code:
@Entity
public class Plane {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;
 
  @OneToOne(mappedBy = "plane", cascade = CascadeType.ALL)
  private Wing    leftWing;

  @OneToOne(mappedBy = "plane", cascade = CascadeType.ALL)
  private Wing    rightWing;

  // [...] (Getters/Setters) 
}


Code:
@Entity
public class Wing {

  public enum WingPosition {
    LEFT, RIGHT
  }

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer      id;
 
  @OneToOne
  private Plane        plane;

  @Enumerated(EnumType.STRING)
  private WingPosition wingPosition;
 
  private Boolean      defect;
 
  // [...] (Getters/Setters)
}


To wire together theses objects, one can do the following:

Code:
Plane plane = new Plane();

Wing leftWing = new Wing();
leftWing.setPlane( plane );
leftWing.setWingPosition( WingPosition.LEFT );
leftWing.setDefect( false );

Wing rightWing = new Wing();
rightWing.setPlane( plane );
rightWing.setWingPosition( WingPosition.RIGHT );
rightWing.setDefect( true );

plane.setLeftWing( leftWing );
plane.setRightWing( rightWing );



When I now persist this with Hibernate, everything is okay, there is one Plane in the Plane-Table and two Wings in the Wing-Table.

But, loading this construct is not possible.

It is not possible, because Hibernate, when loading the Wing for a Plane, exprects exactly one occurrence of a Wing with the ID of the Plane, but there are two Wings referring to the same Plane (ID).


To make the situation clear and to show what I want to achieve, I created this image of the two tables:

Image

So the simple question is, how can I tell Hibernate to map the Property Plane.leftWing to that Object, which has the Property Wing.WingPosition set to "LEFT", and Plane.rightWing to the Object with Wing.WingPosition set to "RIGHT"?

I was thinking of using a compound key in any way, but I can't figure out how to do that.

(Currently, my workaround is to use a List of Wings, and to do the necessary logic to get the right and left Wing in Java)

I can't imagine why there shoudn't be some easy way to map this correctly.


My Hibernate version: 3.5.6


I really appreciate your help!


Thanks a lot!


Top
 Profile  
 
 Post subject: Re: How to map two objects to two different properties
PostPosted: Sat Sep 25, 2010 1:00 am 
Newbie

Joined: Wed Jun 18, 2008 9:17 am
Posts: 12
Hi

What you want to do is not possible using OneToOne relationship as the correct relationship is OneToMany between plane and the wings.

You need to have OneToMany and use discriminator to create different instance/types of the Wings.

Hope this helps


Top
 Profile  
 
 Post subject: Re: How to map two objects to two different properties
PostPosted: Sat Sep 25, 2010 3:38 am 
Newbie

Joined: Fri May 09, 2008 8:30 am
Posts: 10
Hello ozguy,

thanks a lot for your answer.

Until now, I thought that the annotation "@DiscriminatorColumn" can only be used in combination with inheritance? Of course it would make sense to use "@DiscriminatorColumn" to define that for one property, the "LEFT" wing should be loaded, and the "RIGHT" wing for the other property of Plane.

So, if I understand you right, I must use the @OneToMany annotation (two times, for the leftWing and rightWing property) in combination with "@DiscriminatorColumn", which distinguishes a LEFT and a RIGHT wing via the enum column "wingPosition"?

I will try that out this evening, thanks a lot so far!


Top
 Profile  
 
 Post subject: Re: How to map two objects to two different properties
PostPosted: Sat Sep 25, 2010 5:59 am 
Newbie

Joined: Wed Jun 18, 2008 9:17 am
Posts: 12
Hi

OneToMany relationship will need a collections so you will need to have something like this to work with your datamodel.

<code>
@Entity
@DiscriminatorColumn(name="wingType" , discriminatorType=DiscriminatorType.STRING )
@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
AbstractWing{
id
}

@Entity
@DiscriminatorValue(value="left")

LeftWing extends AbstractWing{

}
@Entity
@DiscriminatorValue(value="right")

RightWing entends AbstractWing{

}


Plane{
Integer id;
... annotations here for the mapping....
Collection<? extends AbstractWing> wings;
}
</code>

I think your datamodel can be updated to correctly model the Plane Wing relatoinship. Currently you have wing ids in the plane table and then repeating the plane id in the wings table.

You can have something like this also which will simplify things and is normalized

<code>
create table Plane(
planeId PK
...other plane attributes ( excluding wing ids)
);

create table Wing(
wingid PK,
planeid FK,
wing related attributes
);


</code>

If you need to store wing ids in the plane table and want OneToOne relationship then remove the plane id from the wings column. This will make it One to one relationship between Plane and Wing.


Top
 Profile  
 
 Post subject: Re: How to map two objects to two different properties
PostPosted: Sat Sep 25, 2010 6:54 am 
Newbie

Joined: Fri May 09, 2008 8:30 am
Posts: 10
Hello ozguy

thanks again for your reply.

With your first suggestion (the collection way), you introduce two things I would like to avoid: Using a collection of Wings, and using inheritence. I don't quite understand why you combine those two ways, because with inheritance, you don't need any collection, because you can use the "LeftWing" and "RightWing" classes as properties of the Plane model directly.

But, as I said, I don't want a collection (because there is never a third wing - using a collection I must ristrict this case via Java, but strictly seen a collection is a wrong data modeling in this case; and there are other reasons, like the more complex data handling logic, more difficult event processing in Java etc.), and I don't want inheritance (but the latter is OK for me, better than using a collection). In my opinioin, the Java classes should not be modified just to satisfy the database schema or ORM, like introducing an "artificial property" or something (the "id" property is an exception here).

But: your hint with just removing the plane_id from the Wing table is not bad! To be honest, I'm not sure why I made such a property (Wing.plane), I think some book about ORM made this once that way and I just copied that :)

I will try all this out this evening when I'm back home.


Thanks a lot so far!


Top
 Profile  
 
 Post subject: Re: How to map two objects to two different properties
PostPosted: Mon Oct 04, 2010 10:04 am 
Newbie

Joined: Fri May 09, 2008 8:30 am
Posts: 10
Hello ozguy,

so now I tried different ways but I just can't map it correctly. What I do NOT want is:

  • artificial inheritance (I want one Wing class, not derived LeftWing and RightWing classes)
  • I do not want to map a collection in Plane, so I want to have two different properties Plane.leftWing and Plane.rightWing

In plain Java, this is modelled correctly. So I thought Hibernate/JPA provides a way to map this situation - but either I'm too dump or Hibernate/JPA just isn't able to map this simple example (seen in my first post).

I also tried the "@DiscriminatorColumn" annotation at the Plane.leftWing property, but this can't work because "@DiscriminatorColumn" can only be used as a class-annotation.

I really would like to know how to proceed... I mean, my demand is not very complicated, I just want to tell Hibernate it should load all Wing objects where the wingPosition value is "LEFT" for the property Plane.leftWing (and Plane.rightPlane accordingly for "RIGHT" wings) - which in SQL is a really simple "...WHERE wingPosition=`LEFT`" condition.


I'm at my wits' end :(


Top
 Profile  
 
 Post subject: Re: How to map two objects to two different properties
PostPosted: Tue Oct 05, 2010 5:50 am 
Newbie

Joined: Wed Jun 18, 2008 9:17 am
Posts: 12
May be first look at your data model/db design.
From what you posted previously, you db model says "A plane has many wings", this because you have a planeID in the Wings table. You will always get More than 1 Wing row thus OneToMany.

1. So fix the data model first and then this will solve the mapping issues. You are saying that you want A Plane with Left Wing and Right Wing with OneToOne relationship, but your datamodel is saying something else.

2. if you do need to have planeId in the Wings table as you have now, then you might have to use composite keys instead of just wing id to get a unique row for a plane. So your composite key can be made of (planeid,Wing Position). This will give you Left Wing and Right Wing with OneToOne mapping.


Top
 Profile  
 
 Post subject: Re: How to map two objects to two different properties
PostPosted: Thu Oct 07, 2010 9:43 am 
Newbie

Joined: Fri May 09, 2008 8:30 am
Posts: 10
Hello ozguy,

many thanks for your reply, again :)

ozguy wrote:
May be first look at your data model/db design.
From what you posted previously, you db model says "A plane has many wings", this because you have a planeID in the Wings table. You will always get More than 1 Wing row thus OneToMany.

1. So fix the data model first and then this will solve the mapping issues. You are saying that you want A Plane with Left Wing and Right Wing with OneToOne relationship, but your datamodel is saying something else.


Well, yes, to a certain degree this is right. Now this is a problem of my example - although it demonstrates the mapping problem, it does not reflect the complexity of my real domain model. Some words on the real model:

It must represent a three page document, so the domain model is build of several (20+) single classes. I have one "root" document domain class and several references in Java to that other classes, representing other single parts of the real document. Most of those document parts (classes) are One-To-One (in other words, I just split up the document into several classes instead of having one really big class), but if I would insert all foreign keys into the main document table, I end up with a large table full of foreign keys. To get some kind of homogeneity into the whole DB schema, I decided to put the foreign keys (a "document_id", which is basically the same as "plane_id") to the document in the other classes/tables (so every table just has a foreign key to the document table, regardless if it is Many-To-One or One-ToOne).

Since this is one of my first big domain models with object relational mapping, I'm not sure if that decision was right (for those One-To-Ones), at the moment.

Would you suggest that those One-To-One relationships should NOT have a foreign key to the main table (document, or in my example: plane), so my main table (plane/document) has a foreign key column for every One-To-One relation?

(If you do, I really consider changing my model, maybe this keeps it more simple and prevents other problems?)


ozguy wrote:
2. if you do need to have planeId in the Wings table as you have now, then you might have to use composite keys instead of just wing id to get a unique row for a plane. So your composite key can be made of (planeid,Wing Position). This will give you Left Wing and Right Wing with OneToOne mapping.


Okay, composite keys may be another solution, but after writing the above text, I do not think, that this is a good solution, sounds more like another workaround for my "bad" domain model?

I eagerly await your suggestion ;) (and I tend to change my domain model for One-To-One relations)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 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.