-->
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.  [ 2 posts ] 
Author Message
 Post subject: OneToOne optimization
PostPosted: Fri Apr 18, 2008 2:04 pm 
Newbie

Joined: Sun Dec 24, 2006 10:23 pm
Posts: 11
Hi,
I'm trying to understand how to optimize OneToOne relationship between entities. In several of my code I can see that hibernate treat a property as EAGER even if I specified manually the LAZY into my mapping.
For example with the following code, we can see that the same entity SimEntity is fetched twice before the sql update, so I would like it to be only fetched once. any idea how to do this ?
Off course I could use session.update instead of session.merge but I think it doesn't deal very good with @Version..

Hibernate version: 3.2
Name and version of the database you are using: Postgresql 8.2
Mapping documents:
Code:
@Entity
public class SimEntity {
   private Integer simId;
   private String phoneNumber;
   private PhoneEntity phone;
   private Integer version;

   public SimEntity() {
   }

   @Id
   @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sim_seq")
   @SequenceGenerator(name = "sim_seq", sequenceName = "sim_seq")
   public Integer getSimId() {
      return simId;
   }

   public void setSimId(Integer simId) {
      this.simId = simId;
   }

   public String getPhoneNumber() {
      return phoneNumber;
   }

   public void setPhoneNumber(String phoneNumber) {
      this.phoneNumber = phoneNumber;
   }

   @OneToOne(optional = true)
   @JoinColumn(name = "`phoneId`", nullable = true)
   public PhoneEntity getPhone() {
      return phone;
   }

   public void setPhone(PhoneEntity phone) {
      this.phone = phone;
   }
   
   @Version
   @Column(name = "`OPTLOCK`")
   public Integer getVersion() {
      return version;
   }

   public void setVersion(Integer version) {
      this.version = version;
   }
}

Code:
@Entity
public class PhoneEntity {
   private Integer phoneId;
   private String color;
   private SimEntity sim;
   private Integer version;

   public PhoneEntity() {
   }

   @Id
   @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "phone_seq")
   @SequenceGenerator(name = "phone_seq", sequenceName = "phone_seq")
   public Integer getPhoneId() {
      return phoneId;
   }

   public void setPhoneId(Integer phoneId) {
      this.phoneId = phoneId;
   }

   public String getColor() {
      return color;
   }

   public void setColor(String color) {
      this.color = color;
   }

   @OneToOne(mappedBy = "phone", fetch = FetchType.LAZY, optional = true)
   public SimEntity getSim() {
      return sim;
   }

   public void setSim(SimEntity sim) {
      this.sim = sim;
   }
   
   @Version
   @Column(name = "`OPTLOCK`")
   public Integer getVersion() {
      return version;
   }

   public void setVersion(Integer version) {
      this.version = version;
   }
}


Code:
SimEntity sim1 = new SimEntity();
      sim1.setPhoneNumber("3333");
      simDAO.save(sim1);
      PhoneEntity p1 = new PhoneEntity();
      p1.setColor("blue");
      p1.setSim(sim1);
      phoneDAO.save(p1);
      System.out.println("saved phone: "+p1.getPhoneId());
      
      sim1.setPhone(p1);
      sim1 = simDAO.merge(sim1);



The generated SQL (show_sql=true):
Quote:
Hibernate:
select
nextval ('sim_seq')
Hibernate:
/* insert hibernate.SimEntity
*/ insert
into
SimEntity
("phoneId", phoneNumber, "OPTLOCK", simId)
values
(?, ?, ?, ?)
Hibernate:
select
nextval ('phone_seq')
Hibernate:
/* insert hibernate.PhoneEntity
*/ insert
into
PhoneEntity
(color, "OPTLOCK", phoneId)
values
(?, ?, ?)
saved phone: 50
Hibernate:
/* load hibernate.SimEntity */ select
simentity0_.simId as simId3_0_,
simentity0_."phoneId" as phoneId4_3_0_,
simentity0_.phoneNumber as phoneNum2_3_0_,
simentity0_."OPTLOCK" as OPTLOCK3_3_0_
from
SimEntity simentity0_
where
simentity0_.simId=?
Hibernate:
/* load hibernate.PhoneEntity */ select
phoneentit0_.phoneId as phoneId2_0_,
phoneentit0_.color as color2_0_,
phoneentit0_."OPTLOCK" as OPTLOCK3_2_0_
from
PhoneEntity phoneentit0_
where
phoneentit0_.phoneId=?
Hibernate:
/* load hibernate.SimEntity */ select
simentity0_.simId as simId3_1_,
simentity0_."phoneId" as phoneId4_3_1_,
simentity0_.phoneNumber as phoneNum2_3_1_,
simentity0_."OPTLOCK" as OPTLOCK3_3_1_,
phoneentit1_.phoneId as phoneId2_0_,
phoneentit1_.color as color2_0_,
phoneentit1_."OPTLOCK" as OPTLOCK3_2_0_
from
SimEntity simentity0_
left outer join
PhoneEntity phoneentit1_
on simentity0_."phoneId"=phoneentit1_.phoneId
where
simentity0_."phoneId"=?
Hibernate:
/* update
hibernate.SimEntity */ update
SimEntity
set
"phoneId"=?,
phoneNumber=?,
"OPTLOCK"=?
where
simId=?
and "OPTLOCK"=?
Code:


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 21, 2008 12:23 pm 
Newbie

Joined: Sun Dec 24, 2006 10:23 pm
Posts: 11
After a few search, I found some possible solution.
I would now need your help to know what is the best to implement.
So after reading http://www.hibernate.org/315.html I can see that it is possible to have a OneToOne lazy by using optional=false.
So I tried it, but in my case it doesn't work...
Let's take a simple example, I have a VehicleEntity which has dependency to many other entities, VehicleStatusEntity, VehicleModelEntity, VehicleCharacteristicsEntity, etc... All these relations are @OneToOne.

So in my VehicleEntity I did:
Code:
@OneToOne(mappedBy = "vehicle", fetch = FetchType.LAZY, optional = false)

That line is the same on all the getCharacteristics(), getVehicleStatus(), ...

On the other side (on vehicleStatus, vehicleCharacteristics, etc) I have:
Code:
@OneToOne(fetch = FetchType.LAZY, optional = false)
   @JoinColumn(name = "`vehicle_id`", nullable = false)
   public VehicleEntity getVehicle() {


Then I create a query which retrieve a single VehicleEntity in my vehicleDAO, as I defined my OneToOne with optional=false it shouldn't load all the status,characteristics,model entities.
Code:
          Criteria criteria = session
                     .createCriteria(VehicleEntity.class);
               criteria.add(Restrictions.idEq(1));
               VehicleEntity vehicle = (VehicleEntity) criteria
                     .uniqueResult();

Here is the generated query:
Quote:
/* criteria query */ select
this_."vehicle_id" as vehicle1_18_0_,
...
from
config."Vehicle" this_
where
this_."vehicle_id"=?
/* load model.VehicleCharacteristicsEntity */ select
vehiclecha0_."vehicleCharacteristics_id" as vehicleC1_21_1_,
...
vehicleent1_."vehicle_id" as vehicle1_18_0_,
...
from
config."VehicleCharacteristics" vehiclecha0_
inner join
config."Vehicle" vehicleent1_
on vehiclecha0_."vehicle_id"=vehicleent1_."vehicle_id"
where
vehiclecha0_."vehicle_id"=?
/* load model.VehicleModelEntity */ select
vehiclemod0_."vehicleModel_id" as vehicleM1_20_1_,
...
vehicleent1_."vehicle_id" as vehicle1_18_0_,
...
from
config."VehicleModel" vehiclemod0_
left outer join
config."Vehicle" vehicleent1_
on vehiclemod0_."vehicle_id"=vehicleent1_."vehicle_id"
where
vehiclemod0_."vehicle_id"=?
...

As you can see this can result as a performance nightmare...
So I would really need any feedback on how to really use this optional=false and why it doesn't work on my case;)

The possible way to solve these problems are:
1) Simulate a @ManyToOne with unique="true":
as described in the comment of http://www.hibernate.org/162.html.
but I don't like this solution because @ManyToOne is simply not supposed to be a OneToOne...
2) Using a JoinColumn on both side of the relation and make it nullable="true":
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "`vehicleStatus_id`", nullable = true)
but I don't like this solution because I don't want to add a foreign key to all my tables.. When there are many relation and then a foreign key for each of them it will end with a lot of column on my table which i'd prefer to avoid...
3) Using a common JoinTable for all OneToOne association:
for example I could have the fields: vehicle_id, vehicleStatus_id, vehicleCharacteristics_id, etc.. in this table.
I think this can be a good solution but only when there are many relation on the table so sometimes I just don't want a join table and then I'm back to the original problem.


So here I would like to know what you think about these solution, and what would be best practise ?


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