You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user-java@ibatis.apache.org by Ingmar Lötzsch <il...@asci-systemhaus.de> on 2008/08/08 15:28:53 UTC
resultClass, properties not set
Hallo,
I'am using Ibatis 2.3.0.677. Under certain circumstances properties of
the result class are not set, although contained in the result map. This
happens, when the only property of a result map is mapped to a result
map. The essence is, that
<resultMap id="resultIdOnly" class="Type2" >
<result property="type1" resultMap="type1.result"/>
</resultMap>
does not work, because the property type1 of the instance of Type2 was
not set and remains null. I will illustrate this with the following
example. I'am using 3 classes instead of 2 for motivation only.
Example:
Database
CREATE TABLE type1
(
id int NOT NULL,
attribute1 text NOT NULL,
CONSTRAINT pk_type1 PRIMARY KEY (id)
);
CREATE TABLE type2
(
type1id int NOT NULL,
attribute2 text NOT NULL,
CONSTRAINT pk_type2 PRIMARY KEY (type1id),
CONSTRAINT fk_type2_type1 FOREIGN KEY (type1id)
REFERENCES type1(id)
);
(1 to 1 relationship between type1 and type2)
CREATE TABLE type3
(
id int NOT NULL,
type2id int NULL,
CONSTRAINT pk_type3 PRIMARY KEY (id),
CONSTRAINT fk_type3_type2 FOREIGN KEY (type2id)
REFERENCES type2(type1id)
);
Java
class Type1
{
private int id;
private String attribute1;
public void setId(int id)
{
this.id = id;
}
public void getId()
{
return this.id;
}
public void setAttribute1(String attribute1)
{
this.attribute1 = attribute1;
}
public String getAttribute1()
{
return this.attribute1;
}
}
class Type2
{
private Type1 type1;
private String attribute2;
public void setType1(Type1 type1)
{
this.type1 = type1;
}
public void getType1()
{
return this.type1;
}
public void setAttribute2(String attribute2)
{
this.attribute2 = attribute2;
}
public String getAttribute2()
{
return this.attribute2;
}
}
class Type3
{
private int id;
private Type2 type2;
public void setId(int id)
{
this.id = id;
}
public void getId()
{
return this.id;
}
public void setType2(Type2 type2)
{
this.type2 = type2;
}
public void getType2()
{
return this.type2;
}
}
Imagine that an instance of Type2 is a complex object, that I don't want
to load twice. That's why I intend to let Ibatis create an instance of
Type3, containing an instance of Type2 containing only an instance of
Type1 which contains the property id only. This way I can identify the
already loaded instance of Type2.
XML
type3.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap namespace="type3" >
<resultMap id="result" class="Type3" >
<result column="t3_id" jdbcType="INTEGER" property="id" />
<result property="type2" resultMap="type2.resultIdOnly" />
</resultMap>
<select id="select" resultMap="result" parameterClass="int">
SELECT
t3.id AS t3_id,
t3.type2id AS t1_id
WHERE t3.id = #value#
</select>
</sqlMap>
type2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap namespace="type2" >
<!-- Not used in this example -->
<resultMap id="result" class="Type2" >
<result property="type1" resultMap="type1.result"/>
<result column="t2_attribute2" jdbcType="VARCHAR" property="attribute2"/>
</resultMap>
<!-- Does not work -->
<resultMap id="resultIdOnly" class="Type2" >
<result property="type1" resultMap="type1.result"/>
</resultMap>
</sqlMap>
type1.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap namespace="type1" >
<!-- Not used in this example -->
<resultMap id="result" class="Type1" >
<result column="t1_id" jdbcType="INTEGER" property="id"/>
<result column="t1_attribute1" jdbcType="VARCHAR" property="attribute2"/>
</resultMap>
<resultMap id="resultIdOnly" class="Type1" >
<result column="t1_id" jdbcType="INTEGER" property="id" />
</resultMap>
</sqlMap>
DAO
class Type3DAOImpl
{
public Type3 select(int id)
{
return (Type3)
getSqlMapClientTemplate().queryForObject("type3.select", id);
}
}
The returned instance of Type3 does not contain the expected instance of
Type2. Feature or bug?
Workaround 1
If I don't want to change the data model or the application logic, I
have to let Ibatis set a second property on the instance of Type2, let's
say property attribute2:
type2.xml
<resultMap id="resultIdOnly" class="Type2" >
<result property="type1" resultMap="type1.result"/>
<result column="t2_attribute2" jdbcType="VARCHAR"
</resultMap>
type3.xml
<select id="select" resultMap="result" parameterClass="int">
SELECT
t3.id AS t3_id,
t3.type2id AS t1_id,
'' AS t2_attribute2
FROM type3 AS t3
WHERE t3.id = #value#
</select>
The last leads to false result, if the t3.type2id is NULL, because an
instance of Type2 is created in every case due to the empty string value
of attribut2. I can use proprietary SQL to avoid this.
Workaround 2
<select id="select" resultMap="result" parameterClass="int">
SELECT
t3.id AS t3_id,
t3.type2id AS t1_id,
CASE WHEN t3.type2id IS NULL THEN NULL ELSE '' END AS t2_attribute2
FROM type3 AS t3
WHERE t3.id = #value#
</select>
Another idea is to use a JOIN.
Workaround 3
<select id="select" resultMap="result" parameterClass="int">
SELECT
t3.id AS t3_id,
t3.type2id AS t1_id,
t2.attribute2 AS t2_attribute2
FROM type3 AS t3
LEFT JOIN type2 AS t2 ON t2.type1id = t3.type2id
WHERE t3.id = #value#
</select>
But what happens, when column type2.attribute2 contains NULL values?
Ibatis cannot distinct the case, where a matching record in type2 with
attribut2 is NULL exists respectively no matching record in type2 exists.
Is this behaviour known?
Does anybody know a solution?