Suppose you have a one-to-one "property-ref" mapping between two tables.
It seems that Hibernate queries from the "pointed-to" end return invalid results.
In this example, the query returns results that don't match the query criteria.
We search for Assets that have tag != null yet some returned Assets have
tag == null.
Is this a bug? Or am I doing something incorrectly? If the latter, any pointers
to the relevant documentation are appreciated.
Hibernate version: 2.1.6
Mapping documents:
This is HBug$Asset.hbm.xml:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="HBug$Asset" dynamic-update="false"
dynamic-insert="false" select-before-update="false"
optimistic-lock="version" table="Asset">
<id name="id" column="id" type="java.lang.Long">
<generator class="native"/>
</id>
<one-to-one name="tag" class="HBug$Tag" cascade="save-update"
outer-join="auto" constrained="false" property-ref="asset"/>
<property name="bool" type="boolean" update="true" insert="true"
access="property" column="bool" not-null="true"/>
</class>
</hibernate-mapping>
This is HBug$Tag.hbm.xml:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="HBug$Tag" dynamic-update="false"
dynamic-insert="false" select-before-update="false"
optimistic-lock="version" table="Tag">
<id name="id" column="id" type="java.lang.Long">
<generator class="native"/>
</id>
<many-to-one name="asset" class="HBug$Asset" cascade="save-update"
outer-join="auto" update="true" insert="true" access="property"
column="asset" unique="true"/>
</class>
</hibernate-mapping>
MySQL tables:
Code:
#
# Table structure for table `Asset`
#
CREATE TABLE `Asset` (
`id` bigint(20) NOT NULL auto_increment,
`type` varchar(16) NOT NULL default '',
`name` varchar(255) default NULL,
`bool` tinyint(1) NOT NULL default '0',
PRIMARY KEY (`id`)
) TYPE=MyISAM;
# --------------------------------------------------------
#
# Table structure for table `Tag`
#
CREATE TABLE `Tag` (
`id` bigint(20) NOT NULL auto_increment,
`asset` bigint(20) default NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM;
Code between sessionFactory.openSession() and session.close():Here is the test program:
Code:
import net.sf.hibernate.*;
import net.sf.hibernate.type.Type;
import net.sf.hibernate.cfg.Configuration;
import java.io.Serializable;
import java.util.*;
public class HBug {
public static abstract class Updater {
private final String info;
public Updater(String info) {
this.info = info;
}
public abstract void update(Session session) throws HibernateException;
public String toString() {
return info;
}
}
public static class Asset {
private Long id;
private Tag tag;
private boolean bool;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Tag getTag() { return tag; }
public void setTag(Tag tag) { this.tag = tag; }
public boolean getBool() { return bool; }
public void setBool(boolean bool) { this.bool = bool; }
public String toString() {
return "Asset[id="+id+",tag="
+(tag != null ? ""+tag.id : "null")+",bool="+bool+"]";
}
}
public static class Tag {
private Long id;
private Asset asset;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Asset getAsset() { return asset; }
public void setAsset(Asset asset) { this.asset = asset; }
public String toString() {
return "Tag[id="+id+",asset="
+(asset != null ? ""+asset.id : "null")+"]";
}
}
private static final Class[] PERSISTENT_CLASSES = {
Asset.class,
Tag.class,
};
private static SessionFactory sessionFactory;
public static void main(String[] args) throws Exception {
// Configure Hibernate
Configuration config = new Configuration();
for (int i = 0; i < PERSISTENT_CLASSES.length; i++)
config.addClass(PERSISTENT_CLASSES[i]);
sessionFactory = config.buildSessionFactory();
// Initialize database
update(new Updater("initializing database") {
public void update(Session session) throws HibernateException {
// Create asset and assigned tag
Asset asset = new Asset();
Tag tag = new Tag();
asset.setTag(tag);
asset.setBool(true);
tag.setAsset(asset);
session.save(tag);
System.out.println("saved " + tag);
session.save(asset);
System.out.println("saved " + asset);
// Create unassigned asset
asset = new Asset();
asset.setBool(true);
session.save(asset);
System.out.println("saved " + asset);
}
});
// Read back tag and asset
update(new Updater("searching for 'true' assets with tags assigned") {
public void update(Session session) throws HibernateException {
List list = session.createQuery(
"from HBug$Asset as asset where asset.bool=true"
+ " and asset.tag is not null").list();
for (Iterator i = list.iterator(); i.hasNext(); ) {
Asset asset = (Asset)i.next();
System.out.println(" GOT: " + asset);
if (asset.getTag() == null)
System.out.println(" --> but tag is null!");
}
}
});
// Done
System.out.println("done");
}
private static void update(Updater updater) throws HibernateException {
System.out.println(">>> creating session for: " + updater);
Session session = sessionFactory.openSession();
Transaction tx = null;
boolean success = false;
try {
tx = session.beginTransaction();
updater.update(session);
tx.commit();
success = true;
} finally {
if (!success && (tx != null))
tx.rollback();
System.out.println("<<< closing session for: " + updater);
session.close();
}
}
}
Name and version of the database you are using: MySQL 3.23.54
The generated SQL (show_sql=true):Here is the program output:
Code:
>>> creating session for: initializing database
Hibernate: insert into Asset (bool) values (?)
Hibernate: insert into Tag (asset) values (?)
saved Tag[id=1,asset=1]
saved Asset[id=1,tag=1,bool=true]
Hibernate: insert into Asset (bool) values (?)
saved Asset[id=2,tag=null,bool=true]
<<< closing session for: initializing database
>>> creating session for: searching for 'true' assets with tags assigned
Hibernate: select hbug_asset0_.id as id, hbug_asset0_.bool as bool from Asset hbug_asset0_ where (hbug_asset0_.bool=1 )and(hbug_asset0_.id is not null )
Hibernate: select hbug_tag0_.id as id1_, hbug_tag0_.asset as asset1_, hbug_asset1_.id as id0_, hbug_asset1_.bool as bool0_ from Tag hbug_tag0_ left outer join Asset hbug_asset1_ on hbug_tag0_.asset=hbug_asset1_.id where hbug_tag0_.asset=?
Hibernate: select hbug_tag0_.id as id1_, hbug_tag0_.asset as asset1_, hbug_asset1_.id as id0_, hbug_asset1_.bool as bool0_ from Tag hbug_tag0_ left outer join Asset hbug_asset1_ on hbug_tag0_.asset=hbug_asset1_.id where hbug_tag0_.asset=?
GOT: Asset[id=1,tag=1,bool=true]
GOT: Asset[id=2,tag=null,bool=true]
--> but tag is null!
<<< closing session for: searching for 'true' assets with tags assigned
done