ibatis-user-java mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Ingmar Lötzsch <iloetz...@asci-systemhaus.de>
Subject resultClass, properties not set
Date Fri, 08 Aug 2008 13:28:53 GMT
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?

Mime
View raw message