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?

Reply via email to