I am trying to confirm my relationship configuration is correct.
It is like one-to-one but one side is optional and there is a bi-directional association which is NULL when the optional side does not exist. This is all contained within two tables.
I have been using many-to-one mapping from both sides (With no inverse=true set on either side) and I'm having a problem lazy loading from spouse to person. I have not used one-to-one as this seems to imply rows always exist in both tables and they share the same primary key.
So what would the correct way be to describe a one-to-zeroOrOne relationship ?
The long story:
The problem I am chasing (thinking I have a mapping error) simplified:
Spouse spouse = (Spouse) Session.get(Spouse.class, new Long(1001));
long dataInteger = spouse.getPerson().getDataInteger();
The result is ZERO, but the value from a different SQL session to the same table around the Hibernate select is not zero. The next saveOrUpdate() causes a zero value to be written to the table trashing the previous value.
The only odity with the arrangement is that I'm using MySQL TINYINT UNSIGNED (this has the range 0 to 255) and using a Java type 'short' to store the ranged data, and mapping type="short" in the HBM.
When I was using a MySQL 4.0 server there was no problem, I have upgraded to 4.1 and this single piece of code has this problem now.
The setting of the person.dataInteger column is done from JDBC code, however as I open the Session I do a clear(), the Java code snippet at the end of this post is more like what is really going on, I've commented where the problem occurs.
If there is any more info I can provide please say, at the moment I'm trying to produce a cut-down test project so I can see the error and start modifiying my configuration until it works, but I'd much rather understand the real cause thans fiddling until it works.
Thanks in advance.
Code:
class Person {
private long id;
private long spouseId;
private short dataInteger;
public void setPersionId(long id) { ... }
public void setSpouseId(long spouseId) { ... }
public void setDataInteger(short dataInteger) {
if(dataInteger < 0 || dataInteger > 255)
return; // SQL is MySQL TINYINT UNSIGNED which Java type short can hold its range
this.dataInteger = dataInteger;
}
// getters exist too ...
}
CREATE TABLE person (
personId BIGINT NOT NULL AUTO_INCREMENT,
spouseId BIGINT NULL, # FK spouse.spouseId
dataInteger TINYINT UNSIGNED NOT NULL,
PRUMARY KEY(personId)
);
CREATE TABLE spouse (
spouseId BIGINT NOT NULL AUTO_INCREMENT,
personId BIGINT NOT NULL, # FK person.personId,
keyOne BIGINT NOT NULL,
PRIMARY KEY(spouseId),
KEY(keyOne)
);
Manual operation of bidi-assoc:
> INSERT INTO person SET personId=NULL, spouseId=NULL, dataInteger=1;
> SELECT LAST_INSERT_ID();
1001
Now person exists, then at some point later I want to give them a spouse:
> START TRANSACTION;
> INSERT INTO spouse SET spouseId=NULL, personId=1001;
> SELECT LAST_INSERT_ID();
9992
> UPDATE person SET spouseId=9992 WHERE personId=1001;
> COMMIT;
Then later I may want to remove the spouse:
> START TRANSACTION;
> UPDATE person SET spouseId=NULL WHERE personId=1001;
> DELETE FROM spouse WHERE spouseId=9992;
> COMMIT;
// Example of how my real Java code looks around the error
void func(long keyOne) {
Session hsession = null;
Transaction tx = null;
try {
hsession = HibernateSession.currentSession();
hsession.clear(); // I use this to fix external JDBC interaction with this data
tx = hsession.beginTransaction();
Query q = hsession.createQuery("from Spouse s where s.keyOne=:keyOne");
q.setLong("keyOne", keyOne);
Iterator it = q.iterate();
if(it.hasNext()) {
Spouse spouse = (Spouse) it.next();
Parent parent = spouse.getParent();
long wrongValue = parent.getDataInteger();
// THE ERROR CAN BE SEEN HERE wrongValue==0
parent.setSomethingElse(42);
hsession.saveOrUpdate(parent);
// When this hits SQL server my parent.dataInteger column is trashed
}
hsession.flush();
tx.commit();
tx = null;
} catch(Exception e) {
// blah blah
} finally {
if(tx != null)
tx.rollback();
if(hsession != null)
hsession.close();
}
}