ibatis 之 复杂部类属性(即自定义类型的属性)

ibatis 之 复杂类型属性(即自定义类型的属性)

复杂类型用以表示在数据库中相互关系为一对一,一对多的数据。

 

映射文件:

<!--complex type property that defined by user-->
		<resultMap id="get-product-complex" class="product">
			<result property="id" column="prd_id"/>
			<result property="description" column="prd_description"/>
			<result property="price" column="prd_price"/>
			<result property="category" column="prd_cat_id" select="getCategory-complex"/>
		</resultMap>
		<resultMap id="get-category-complex" class="category">
			<result property="id" column="cat_id"/>
			<result property="description" column="cat_description"/>
		</resultMap>	
		<select id="getCategory-complex" resultMap="get-category-complex">
			<![CDATA[
				select * from t_category where cat_id = #value# 
			]]>
		</select>
		<select id="getProduct-complex" resultMap="get-product-complex" parameterClass="java.lang.Integer">
			<![CDATA[
				select * from t_product where prd_id = #value#
			]]>
		</select> 
		<!--END-->

 DAO层:

  

public Product getProductUseComplexType(int id) throws SQLException {
		init();
		Product product = (Product)sqlMapClient.queryForObject("getProduct-complex", id);
		return product;
	}

 Test类:

  

/**
	 * 测试复杂属性类型
	 * @throws SQLException
	 */
	public void getProductUseComplexType() throws SQLException{
		Product product = productDao.getProductUseComplexType(1);
		System.out.println(product);
	}

 

 结果:

id:1
description:basketball
price206.99
catId:1
catDescription:sports

 

上面的例子中,Product对象拥有一个类型为Category的category属性。因为category是复杂类型(用户定义的类型),JDBC不知道如何给它赋值。通过将category属性值和另一个mapped statement联系起来,为SQL Map引擎如何给它赋值提供了足够的信息。通过执行“getProduct”,“get-product-result”Result Map使用PRD_CAT_ID字段的值去调用“getCategory”。“get-category-result”Result Map将初始化一个Category对象并赋值给它。然后整个Category对象将赋值给Product的category属性。

 

避免N+1 Select(1:1)
上面的方法存在一个问题,就是无论何时加载一个Product,实际上都要执行两个SQL语句(分别加载Product和Category)。只加载一个 Product的情况下,这个问题似乎微不足道。但在执行一个获得10个Product的查询时,每得到一个Product都要分别执行一个加载 Category的SQL语句。结果共执行了11次查询:一次用于得到一个Product List,每得到一个Product对象都要执行另外一次查询,以获得相应的Category对象(N+1,这个例子是10+1=11)。
解决方法是,使用一个联合查询和嵌套的属性映射来代替两个查询statement。

映射文件:

 

<!--avoid N+1 select(1:1)-->
		<resultMap id="get-product-complex-promotion" class="product">
			<result property="id" column="PRD_ID"/>
			<result property="description" column="PRD_DESCRIPTION"/>
			<result property="price" column="prd_price"/>
			<result property="category.id" column="CAT_ID" />
			<result property="category.description" column="CAT_DESCRIPTION" />
		</resultMap>
		<statement id="getProduct-complex-promotion" parameterClass="int" resultMap="get-product-complex-promotion">
		<![CDATA[
			select * from t_product, t_category
			where prd_cat_id=cat_id
			and prd_id = #value#
		]]>
		</statement>

 

 DAO层:

public Product getProductUseComplexTypePromotion(int id)
			throws SQLException {
		init();
		Product product = (Product)sqlMapClient.queryForObject("getProduct-complex-promotion", id);
		return product;
	}

 

 Test类:

 

/**
	 * 测试复杂属性类型的改进(避免N+1 select)
	 * @throws SQLException
	 */
	public void getProductUseComplexTypePromotion() throws SQLException{
		Product product = productDao.getProductUseComplexType(1);
		System.out.println(product);
	}

 

结果和上面的一样。

 

延迟加载 VS 联合查询(1:1)
必须要声明的是,使用联合查询的方案并不总是最好的。假如很少有必要访问相关的对象(如Product对象的Category属性),则不用联合查询加载所有的Categor属性可能更快。对于牵涉到外部连接或没有索引字段的数据库设计时,更是如此。在这种情况下,使用延迟加载和字节码增强选项的子查询,可能性能会更好。基本的原则是,如果您需要访问相关的对象,则使用联合查询。否则,使用延迟加载和字节码增强选项的子查询。
如果您不知道选择哪种方法,别担心。您可以随时更改选择而不会影响到Java代码。上面两个例子都得到相同的结果,并使用同样的调用方法。唯一要考虑的是,如果您要缓存查询结果,则使用子查询(而不是联合查询)来缓存查询结果。

 

ibatis配置文件:

Xml代码 ibatis 之 复杂部类属性(即自定义类型的属性) ibatis 之 复杂部类属性(即自定义类型的属性)ibatis 之 复杂部类属性(即自定义类型的属性)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd">
<sqlMap namespace="Classes">

	<typeAlias alias="classes" type="com.alibaba.webx.biz.dataobject.Classes" />
	<typeAlias alias="student" type="com.alibaba.webx.biz.dataobject.Student" />
	<typeAlias alias="teacher" type="com.alibaba.webx.biz.dataobject.Teacher" />
	
	<resultMap class="student" id="studentResultMap" groupBy="id">
		<result property="id" column="stuId" />
		<result property="name" column="stuName" />
	</resultMap>	

	<resultMap class="teacher" id="teacherResultMap" groupBy="id">
		<result property="id" column="terId" />
		<result property="name" column="terName" />
	</resultMap>
	
	<resultMap class="classes" id="classesResultMap" groupBy="id">
		<result property="id" column="id" />
		<result property="name" column="name" />
		<result property="studentList" resultMap="Classes.studentResultMap"/>
		<result property="teacherList" resultMap="Classes.teacherResultMap"/>
	</resultMap>
	<select id="findAllClasses" resultMap="Classes.classesResultMap">
		SELECT c.id,c.name,s.id stuId,s.name stuName,t.id terId,t.name terName 
		  FROM class c 
		  JOIN student s 
		    ON c.id=s.classid 
		  JOIN teacher t 
		    ON c.id=t.classid 
	  ORDER BY c.id;
	</select>

</sqlMap>

 

类:

Java代码 ibatis 之 复杂部类属性(即自定义类型的属性) ibatis 之 复杂部类属性(即自定义类型的属性)ibatis 之 复杂部类属性(即自定义类型的属性)
public class Classes {
	private Integer id;
	private String name;
	private List<Student> studentList;
	private List<Teacher> teacherList;

	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public List<Student> getStudentList() {
		return studentList;
	}
	public void setStudentList(List<Student> studentList) {
		this.studentList = studentList;
	}
	public List<Teacher> getTeacherList() {
		return teacherList;
	}
	public void setTeacherList(List<Teacher> teacherList) {
		this.teacherList = teacherList;
	}

	
}

public class Student {
	private Integer id;
	private String name;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

public class Teacher {
	private Integer id;
	private String name;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

 

上面的方法可以一次性查询出所有的记录,ibatis会自动分组,这个可以解决数据量比较小的情况。

但是如果多的一端比如班级的学生数据量比较大,需要分页的话,就不行了。

如果一的一端数据量比较大,比如班级比较多,需要分页的话,我的解决方法是:首先只查询出一页数据的ids,然后再通过ids,

用in的方法查询出数据,很多人说in的性能差,但总比n+1的性能要好很多吧。