Hi.
Hibernate 3.1.3 appears to have a problem when returning scalars in combination with a one-to-one relationship fetch.
Hibernate 3.1.3
Postgres 8.1.2
Java 1.5.0_06
Given that "a" is a table whose id is reflected in table "b" as a PRIMARY FOREIGN KEY.
Mapping a->b and b->a with the appropriate "one-to-one" mappings (I hope), then
Code:
select a, b from A a join a.b b
returns
a and
b instances correctly..
However:
this is where I see the problemCode:
select a, b, 'Some scalar' from A a join a.b b
returns
a and the
scalar correctly, but
NULLS out
b!
Please note that I have no idea if this problem occurs using other relation types or in any other circumstancesHere's my schema:Code:
-- Postgres 8.1.2
drop database problem;
create database problem;
\c problem
create schema problem;
-- primary..
create table problem.a (id int, amessage varchar, primary key (id));
-- PFK #1..
create table problem.b (id int, bmessage varchar, primary key (id));
-- PFK #2..
create table problem.c (id int, cmessage varchar, primary key (id));
-- PFK constraints..
alter table problem.b add foreign key (id) references problem.a (id) on update restrict on delete restrict;
alter table problem.c add foreign key (id) references problem.a (id) on update restrict on delete restrict;
-- data..
insert into problem.a values (1, 'a-1');
insert into problem.a values (2, 'a-2');
insert into problem.a values (3, 'a-3');
insert into problem.b values (1, 'b-1');
insert into problem.b values (2, 'b-2');
insert into problem.b values (3, 'b-3');
insert into problem.c values (1, 'c-1');
insert into problem.c values (2, 'c-2');
insert into problem.c values (3, 'c-3');
And my mappings..Code:
<hibernate-mapping package="problem.bean">
<class name="A" schema="problem" table="a" dynamic-update="false" dynamic-insert="false" select-before-update="false">
<id name="id" column="id" type="java.lang.Integer" unsaved-value="null"/>
<property name="amessage" type="java.lang.String"/>
<one-to-one name="B" class="B" constrained="false"/>
</class>
<class name="B" schema="problem" table="b" dynamic-update="false" dynamic-insert="false" select-before-update="false">
<id name="id" column="id" type="java.lang.Integer" unsaved-value="null"/>
<property name="bmessage" type="java.lang.String"/>
<one-to-one name="A" class="A" constrained="true"/>
</class>
</hibernate-mapping>
And the test class and beans..Code:
package problem;
import java.io.File;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/*
Hibernate 3.1.3
one-to-one vs. scalar problem
*/
class Test
{
private Configuration cfg;
private SessionFactory sessionFactory;
private Session session;
public Test()
{
}
public void go()
{
say("\n\n\n\n\n\n");
say("Testing one-to-one vs. scalar problem");
say(".................................................................");
say(" FIRST: Select a, b with generic joins, returning all 3..");
say(".................................................................");
say("");
String hql = "select a, b from A a join a.B b";
say("HQL is \"%s\"", hql);
Query q = session.createQuery(hql);
List l = q.list();
dump(l);
say("\n\n\n");
say(".................................................................");
say(" NEXT: Select a, b AND SCALAR.. (B IS SET TO NULL!!)");
say(".................................................................");
say("");
hql = "select a, b, 'Scalar value' as x from A a join a.B b";
say("HQL is \"%s\"", hql);
q = session.createQuery(hql);
l = q.list();
dump(l);
say(".................................................................");
say(" FINALLY: Select SCALAR, a and b.. (postion of scalar makes no difference)");
say(".................................................................");
say("");
hql = "select 'Scalar value', a, b from A a join a.B b";
say("HQL is \"%s\"", hql);
q = session.createQuery(hql);
l = q.list();
dump(l);
say("");
say("======================================================================");
say(" As you can see, the one-to-one relation from A to B combined with a scalar return sets B to NULL");
say("======================================================================");
say("");
}
private void dump(List l)
{
int idx = 0;
for (Object row : l)
{
System.out.printf("%2d: |", idx++);
for (Object item : (Object[])row)
System.out.printf(" %s |", item);
System.out.printf("\n");
}
}
public void initialiseHibernate()
{
try
{
cfg = new Configuration();
cfg.configure(new File("hibernate.cfg.xml"));
cfg.addFile(new File("problem.hbm.xml"));
sessionFactory = cfg.buildSessionFactory();
session = sessionFactory.openSession();
}
catch (Exception ex)
{
error("Error during Hibernate initialisation", ex);
}
}
private void say(String message, Object... args)
{
System.out.printf(message + "\n", args);
}
private void error(String message, Throwable t, Object... args)
{
System.err.printf(message + "\n", args);
if (t != null)
t.printStackTrace();
}
public static void main(String[] args)
{
Test test = new Test();
test.initialiseHibernate();
test.go();
}
}
Code:
package problem.bean;
class A
{
private int id;
public int getId() { return id; }
public void setId(int id ) { this.id = id; }
private String amessage;
public String getAmessage() { return amessage; }
public void setAmessage(String amessage) { this.amessage = amessage; }
private B b;
public B getB() { return b; }
public void setB(B b) { this.b = b; }
public String toString()
{
return String.format("[A: id=%s message=%s]", id, amessage);
}
}
Code:
package problem.bean;
class B
{
private int id;
public int getId() { return id; }
public void setId(int id ) { this.id = id; }
private String bmessage;
public String getBmessage() { return bmessage; }
public void setBmessage(String bmessage) { this.bmessage = bmessage; }
private A a;
public A getA() { return a; }
public void setA(A a) { this.a = a; }
public String toString()
{
return String.format("[B: id=%s message=%s]", id, bmessage);
}
}
And the outputCode:
Testing one-to-one vs. scalar problem
.................................................................
FIRST: Select a, b with generic joins, returning all 3..
.................................................................
HQL is "select a, b from A a join a.B b"
0: | [A: id=1 message=a-1] | [B: id=1 message=b-1] |
1: | [A: id=2 message=a-2] | [B: id=2 message=b-2] |
2: | [A: id=3 message=a-3] | [B: id=3 message=b-3] |
.................................................................
NEXT: Select a, b AND SCALAR.. (B IS SET TO NULL!!)
.................................................................
HQL is "select a, b, 'Scalar value' as x from A a join a.B b"
0: | [A: id=1 message=a-1] | null | Scalar value |
1: | [A: id=2 message=a-2] | null | Scalar value |
2: | [A: id=3 message=a-3] | null | Scalar value |
.................................................................
FINALLY: Select SCALAR, a and b.. (postion of scalar makes no difference)
.................................................................
HQL is "select 'Scalar value', a, b from A a join a.B b"
0: | Scalar value | [A: id=1 message=a-1] | null |
1: | Scalar value | [A: id=2 message=a-2] | null |
2: | Scalar value | [A: id=3 message=a-3] | null |
======================================================================
As you can see, the one-to-one relation from A to B combined with a scalar return sets B to NULL
======================================================================