-->
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.  [ 15 posts ] 
Author Message
 Post subject: Assocations Problem
PostPosted: Mon Jun 11, 2007 12:04 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 1:07 am
Posts: 23
Hi,

Hibernate version:
3.2.1 GA annotations/EM


This is my simple association

Customer(transactional) -> ClientPreferences(transactional) -> CommunicationMode(static)

All one-one associationswith cascade enabled between Customer and ClientPreference

I create a new Customer then ClientPreferences...Within ClientPreferences I set communication mode

Unless I actually set the version on the ClientPreferences(any version), when I try to persist the customer, it gives me the foll error:

object references an unsaved transient instance - save the transient instance before flushing: com.iflexsolutions.wm.refdata.model.PrefCommMode

which is the model for the CommunicationMode

When I set the version it ensures that the process works fine...Any idea as to how I tell Hibernate that it should disable the auto checking?

Will @OptimisticLock(exclude=true) work ??

Tks...VJ


Top
 Profile  
 
 Post subject: Re: Assocations Problem
PostPosted: Tue Jun 12, 2007 9:13 am 
Beginner
Beginner

Joined: Tue Nov 29, 2005 4:42 pm
Posts: 49
Location: Atlanta, GA
sharedPK wrote:
Hi,

Hibernate version:
3.2.1 GA annotations/EM


This is my simple association

Customer(transactional) -> ClientPreferences(transactional) -> CommunicationMode(static)

All one-one associationswith cascade enabled between Customer and ClientPreference

I create a new Customer then ClientPreferences...Within ClientPreferences I set communication mode

Unless I actually set the version on the ClientPreferences(any version), when I try to persist the customer, it gives me the foll error:

object references an unsaved transient instance - save the transient instance before flushing: com.iflexsolutions.wm.refdata.model.PrefCommMode

which is the model for the CommunicationMode

When I set the version it ensures that the process works fine...Any idea as to how I tell Hibernate that it should disable the auto checking?

Will @OptimisticLock(exclude=true) work ??

Tks...VJ


Your question is very unclear? I'm not sure I understand much of it, but your exception is typical. I don't understand how you have things setup. I'm not sure what the difference is between transactional and static in your diagram. And I'm not sure what you mean by "setting the version"? I'm going to make some assumptions about how you have things setup, and if their right then great. If not then work your trying to make your question more clear.

Do you mean that Customer and ClientPreferences tables can be inserted into and updated? While CommunicationMode is a fixed table that is prepopulated with possibilities? Like ISDN, DSL, BROADBAND, SAT, T1, T3? Customers just pick which one they want?

What is the cascade setting between your models? I bet you don't have cascades turned on between ClientPreferences and CommunicationMode tables. So hibernate is trying to save the ClientPreferences which has a reference to COmmunicationMode which is probably a new object. That will definitely result in the above exception.

It makes sense if your term static is what I described above that you wouldn't turn on Cascades between the tables. However, if you're simply newing up an object on the fly that's not how it's done. Objects that are new'ed up are what hibernate calls transient in that they don't have any DB Identification. Think of it this way:

Code:
@Entity
public class CommunicationMode {
   @Id
   @GeneratedValue
   private Integer id;
   private String mode;
}

@Entity
public class ClientPreference {
   @Id
   @GeneratedValue
   private Integer id;

   @ManyToOne
   @JoinColumn( name = "CommunicationModeID" )
   private CommunicationMode mode;
}


You probably have something like the above huh? Well if you simply new up a CommunicationMode object the id field will be null right? Well if your ClientPreferences instance is pointing at a CommunicationMode object with a null ID what should it write into the CommunicationModeID field in that table? It can't write NULL because it did have an actual object. But it can't save an ID because that instance didn't have an ID. If cascades were turned on it could persist a new object, but since their turned off it can't do anything. That's usually the conundrum that's happenned when you see the above exception.

You will need to query the DB with hibernate to pull back an object from the DB to get a CommunicationMode that won't be transient. If your table is static table that never gets new objects inserted into it then you can load the Map once (maybe lazily) and cache it. Then when you're constructing your ClientPreferences use that map to lookup the CommunicationMode object that corresponds with their choosing.

Charlie


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 12, 2007 10:28 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 1:07 am
Posts: 23
hanks a ton for the reply...I really really appreciate it....I did get a fair idea but I will explain my problem in much more detail...Do let me know if there is something really fundamental about my association

As you have guessed correctly Customer and ClientPreferences are tables into which I can write data into while CommMode is a static table (not updated quite often). All 3 classes have a version column.

So I will just deal with the association between ClientPreferences and CommMode (Communication Modes)

ClientPreferences.java Domain Model definition

@OneToOne
@JoinColumn (name="PREF_COMM_MODE")
public PrefCommMode getPrefCommMode() {
return prefCommMode;
}

No Cascades and a pure OneToOne association


Now, while creation

ClientPreference clntPref = new ClientPreference();
PrefCommMode prefCommMode = new PrefCommMode();
prefCommMode.setId("EML") --> Primary Key Identifier in PrefCommMode domain class. Reason why we set the primary key is due to the fact that it comes from the UI layer...We do not have surrogate keys here

clntPref.setPrefCommMode(prefCommMode);

This gives the problem as mentioned in the first post. I got a work around this by setting the version on the PrefCommMode object so this worked:

ClientPreference clntPref = new ClientPreference();
PrefCommMode prefCommMode = new PrefCommMode();
prefCommMode.setId("EML");
prefCommMode.setVersion(0L); -> Any version. Does not update the underlying PrefCommMode Table
clntPref.setPrefCommMode(prefCommMode);

Now while updating, I got an even more wierder error

ClientPreference clntPref = cust.getClientPreference();
PrefCommMode prefCommMode = clntPref.getPrefCommMode();
prefCommMode.setId("PHO"); -> This goes and tries to update the Pref Comm Mode static table ??? WHY??
clntPref.setPrefCommMode(prefCommMode);

Any directions would be immensely helpful

Thanks...Vijay


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 12, 2007 2:21 pm 
Beginner
Beginner

Joined: Tue Nov 29, 2005 4:42 pm
Posts: 49
Location: Atlanta, GA
sharedPK wrote:
hanks a ton for the reply...I really really appreciate it....I did get a fair idea but I will explain my problem in much more detail...Do let me know if there is something really fundamental about my association

As you have guessed correctly Customer and ClientPreferences are tables into which I can write data into while CommMode is a static table (not updated quite often). All 3 classes have a version column.

So I will just deal with the association between ClientPreferences and CommMode (Communication Modes)

ClientPreferences.java Domain Model definition

@OneToOne
@JoinColumn (name="PREF_COMM_MODE")
public PrefCommMode getPrefCommMode() {
return prefCommMode;
}

No Cascades and a pure OneToOne association


Now, while creation

ClientPreference clntPref = new ClientPreference();
PrefCommMode prefCommMode = new PrefCommMode();
prefCommMode.setId("EML") --> Primary Key Identifier in PrefCommMode domain class. Reason why we set the primary key is due to the fact that it comes from the UI layer...We do not have surrogate keys here

clntPref.setPrefCommMode(prefCommMode);



The code in read is precisely what I'm talking about that is problem! That new PrefCommMode() statements creates a transient instance even if you set the ID of the object. Hibernate won't recognize it as an object under it's control. Setting the ID just makes it a detached object actually. You have to associate that with the open session in order for hibernate to recognize that object as persistent. So you have to do something like:

Code:
PrefCommMode mode = session.refresh( new PrefCommMode( "EML" ) );


or if you're using entitymanager very similiar:

Code:
PrefCommMode mode = entitymanager.refresh( new PrefCommMode("EML"));


Go back and read the discussion on transistive persistence or read the hibernate in action book chatper 4. That information isn't in the annotations document, but it's vital you understand some basic knowledge in order to user hibernate. Setting the version seems very hacky that it works. The number of the versions shouldn't come into play here. If it does then it's by accident.

So that ends the part that should fix your problem. Now I'd like to give you a little advice to think about. So since the string "EML" is the primary key what's in the ClientPreferences table that links the CommunicationMode and ClientPreferences? "EML"? Are there any other columns in the CommunicationMode object other than that string? If not, have you really normalized anything by using the mode string as a key? It's sometimes more work, but consider seperating out a key like ID from the mode string. Using the string as the primary key means you can't really link to that table. Having an ID will allow you to link more effeciently and without repeating lots of information. This is a good practice you should be aware of.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 13, 2007 12:26 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 1:07 am
Posts: 23
Chubbard,

Thanks a ton ....does make things clearer in perspective......but I still have a nagging question which I think should put an end to this problem...Again your responses have been immensely helpful and I am greatful for that

I got the point of Hibernate making it a transient instance, the minute I create a new object but suppose during modification, this happens during a transaction open-close

Customer cust = mgr.getCustomer(2011l); //Retrieves the Customer
ClientPreference clntPref = cust.getPreference(); //Retrieves the Client Preference
clntPref.getPrefCommMode.setId("EML"); //Changing the preferred communication mode from Phone to Email

save(cust); // Saves the parent customer, but tries to update the preferred communication mode table

Now, I have not tried to create a new PreferredCommunicationMode object,I use the ClientPrefernce parent entity but why does it still try to update the preferred communication mode table ? Its not a transient/detached instance


Thanks...VJ


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 13, 2007 3:07 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
ShaaedPK, add a credit if you believe the person helped you.

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 13, 2007 11:08 pm 
Beginner
Beginner

Joined: Tue Nov 29, 2005 4:42 pm
Posts: 49
Location: Atlanta, GA
sharedPK wrote:
Chubbard,

Customer cust = mgr.getCustomer(2011l); //Retrieves the Customer
ClientPreference clntPref = cust.getPreference(); //Retrieves the Client Preference
clntPref.getPrefCommMode.setId("EML"); //Changing the preferred communication mode from Phone to Email

save(cust); // Saves the parent customer, but tries to update the preferred communication mode table

Now, I have not tried to create a new PreferredCommunicationMode object,I use the ClientPrefernce parent entity but why does it still try to update the preferred communication mode table ? Its not a transient/detached instance



You're violating a cardinal rule doing that. You can't just switch IDs of objects. An ID is something that never, ever, I mean never ever ever ever ever ever changes. You can't call setId(). That's a rule that applies always.

You're trying to change the relationship between ClientPreference and PrefCommMode objects. That relationship is stored in a column in ClientPreference table. And that relationship is represented in the object model by the reference returned by ClientPreference.getPrefCommMode().

Your code above is altering the PrefCommMode object, and hibernate detects that change to the object. Not the relationship between the objects. That's controlled by the reference returned from ClientPreference.getPrefCommMode(). Hibernate uses that reference returned in order to detect changes made to the relationship. You want to use setPreCommMode() method on ClientPreferences in order modify that relationship.

Since you're using the string name as an ID your modifying the object, not the relationship stored in the ClientPreferences. Hibernate doesn't detect that you've modified the relationship instead it sees you modifying the object, and tries to update the PrefCommMode table instead.

If you wanted to change the communication mode of the client's preferences I suggest you do something like the following:

Code:

// you can cache this so you don't have to query for it every time.  Also you'll have to translate between List -> Map, but that code is trivial so I'm not writing it.
Map<String,PrefCommMode> communicationMapCache = toMap( session.createQuery("from CommMode").list() );

Customer cust = mgr.getCustomer(2011);
ClientPreference clntPref = cust.getPreference();
clnPref.setPrefCommMode( communicationMapCache.get( "EML" ) );



In fact I would probably enapsulate that Map instead the PrefCommMode DAO and do something like:

Code:
public class ClientPreferenceDao {
  CommunicationModeDao commModeDao;

  public ClientPreferenceDao( CommunicationModeDao dao ) {
    commModeDao = dao;
  }

  public void updatePreferences() {
    Customer cust = mgr.getCustomer(2011);
    ClientPreference clntPref = cust.getPreference();
    clnPref.setPrefCommMode( commModeDao.get( "EML" ) );
  }
}


This is all discussed again in the Transistive Persistence discussion in the documentation and a better explaination in Hibernate in Action book. My only knock against that book is it only discusses the XML configuration and not annotations. And it's sometimes hard to map back and forth between the two. Someone should rewrite it using annotations because it's very thorough. Much better than the free documentation.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 14, 2007 12:05 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
Java Persistence with Hibernate is such a book.

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 14, 2007 8:36 am 
Beginner
Beginner

Joined: Tue Nov 29, 2005 4:42 pm
Posts: 49
Location: Atlanta, GA
Thanks E. I figured someone had already done it. ;-) I might need to upgrade my bookshelf.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 14, 2007 9:08 pm 
Beginner
Beginner

Joined: Fri Apr 20, 2007 1:07 am
Posts: 23
Chubbard,

Thanks again for your detailed reply .....Sorry but I come from a JDBC world with HIbernate being new to me....things r way clearer now

For me that still remains as a foreign key and not as a primary key...which should be updated when I insert a row into myclient preferences table...I have changed the property to a normal String rather than go for the object...I guess its a tradeoff for a rich domain model but I seriously do not see the benefits of it being an object....I would rather write a query for that

Thank again and have rated you


Rgds...Vijay


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 15, 2007 12:13 am 
Beginner
Beginner

Joined: Tue Nov 29, 2005 4:42 pm
Posts: 49
Location: Atlanta, GA
sharedPK wrote:
Chubbard,

Thanks again for your detailed reply .....Sorry but I come from a JDBC world with HIbernate being new to me....things r way clearer now

For me that still remains as a foreign key and not as a primary key...which should be updated when I insert a row into myclient preferences table...I have changed the property to a normal String rather than go for the object...I guess its a tradeoff for a rich domain model but I seriously do not see the benefits of it being an object....I would rather write a query for that

Thank again and have rated you

Rgds...Vijay


That's ok. Hibernate is very different from JDBC, but also much more powerful. I would highly recommend finding a good book on the subject.

I'm not sure what you mean by "remains a foreign key and not as a primary key". Certianly the foreign key in the ClientPreference table links to a primary key on the PrefCommMode table.

If your PrefCommMode table only contains a single column which is this string then there isn't much of a reason for a seperate object expect to encapsulate sepcial behaviors for each CommMode. I would just use an enum in my object model in that case. But if there are more than one column then an object might make more sense.

Good luck
Charlie


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 17, 2007 1:01 pm 
Beginner
Beginner

Joined: Fri Apr 20, 2007 1:07 am
Posts: 23
Charlie,

By that I meant i have taken a decision to keep my domain model as flat as possible, I am having a nightmarish time right now moving to Hibernate with some hellish errors but it is indeed worth it as I am eliminating a lot of boiler-plate code.....but I am not comfortable with it completely as of now...

Charlie, one more Q....

Is it a good practice to add convenience methods for @OneToOne bi-directional associations too coa I do have to set the associations on both sides for it work correctly right ??

Customer cust = new Customer();
ClientPreference pref = new ClientPreference();

cust.setPreference(pref)
pref.setCustomer(cust)

I wonder why only cust.setPreference() does not work but then I realized bi=directionality requires this kind of setting

Thanks again Charlie.....your answeres have really helped me alot.....appreciate it deeply....will put an easter egg in your name in my code
:)


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 17, 2007 1:58 pm 
Beginner
Beginner

Joined: Tue Nov 29, 2005 4:42 pm
Posts: 49
Location: Atlanta, GA
It is a good practice to encapsulate as much as possible anything you're doing to keep the model consistent. So, making sure the relationships between two objects like A -> B are consistent is a good idea. But you'll typically only want to do it on the owner side of the relationship. So

Code:
A.setB( B b ) {
   this.b = b;
   b.setA( this );
}


Then you don't need to do the same in the B.setA(). Otherwise you can create infinite loops.

Charlie


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 18, 2007 12:38 am 
Beginner
Beginner

Joined: Fri Apr 20, 2007 1:07 am
Posts: 23
Charlie,

by owner side you mean the owner of the foreing key right

Thanks again


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 18, 2007 8:53 am 
Beginner
Beginner

Joined: Tue Nov 29, 2005 4:42 pm
Posts: 49
Location: Atlanta, GA
In some cases yes that's true, but not always. Owner really means the object that controls the lifetime of the relationship. It could also be object that controls the lifetime of the child object. Say for example: CricketPlayer has many Stats. If you delete a CricketPlayer you would also delete all of his Stats. In this case the foreign key would be in the Stats table as CricketPlayerID. But, since CricketPlayer controls the lifetime of the Stats object he'd be the owning side of the relationship.

Charlie


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