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?