动态Mapped Statement
直接使用JDBC一个非常普遍的问题是动态SQL。使用参数值、参数本身和数据列都是动态的SQL,通常非常困难。典型的解决方法是,使用一系列if-else条件语句和一连串讨厌的字符串连接。对于这个问题,SQL Map API使用和mapped statement非常相似的结构,提供了较为优雅的方法。这里是一个简单的例子:
<select id="dynamicGetAccountList"
cacheModel="account-cache"
resultMap="account-result" >
select * from ACCOUNT
<isGreaterThan prepend="and" property="id" compareValue="0">
where ACC_ID = #id#
</isGreaterThan>
order by ACC_LAST_NAME
</select>
上面的例子中,根据参数bean“id”属性的不同情况,可创建两个可能的语句。如果参数“id”大于0,将创建下面的语句:
select * from ACCOUNT where ACC_ID = ?
或者,如果“id”参数小于等于0,将创建下面的语句:
select * from ACCOUNT
在更复杂的例子中,动态Mapped Statement的用处更明显。如下面比较复杂的例子:
<statement id="dynamicGetAccountList" resultMap="account-result" >
select * from ACCOUNT
<dynamic prepend="WHERE">
<isNotNull prepend="AND" property="firstName">
(ACC_FIRST_NAME = #firstName#
<isNotNull prepend="OR" property="lastName">
ACC_LAST_NAME = #lastName#
</isNotNull>
)
</isNotNull>
<isNotNull prepend="AND" property="emailAddress">
ACC_EMAIL like #emailAddress#
</isNotNull>
<isGreaterThan prepend="AND" property="id" compareValue="0">
ACC_ID = #id#
</isGreaterThan>
</dynamic>
order by ACC_LAST_NAME
</statement>
根据不同的条件,上面动态的语句可以产生16条不同的查询语句。使用if-else结构和字符串,会产生上百行很乱的代码。
而使用动态Statement,和在SQL的动态部位周围插入条件标签一样容易。例如:
<statement id="someName" resultMap="account-result" >
select * from ACCOUNT
<dynamic prepend="where">
<isGreaterThan prepend="and" property="id" compareValue="0">
ACC_ID = #id#
</isGreaterThan>
<isNotNull prepend=”and" property="lastName">
ACC_LAST_NAME = #lastName#
</isNotNull>
</dynamic>
order by ACC_LAST_NAME
</statement>
上面的例子中,<dynamic>元素划分出SQL语句的动态部分。动态部分可以包含任意多的条件标签元素,条件标签决定是否在语句中包含其中的SQL代码。所有的条件标签元素将根据传给动态查询Statement的参数对象的情况来工作。<dynamic>元素和条件元素都有“prepend”属性,它是动态SQL代码的一部分,在必要情况下,可以被父元素的“prepend”属性覆盖。上面的例子中,prepend属性“where”将覆盖第一个为“真”的条件元素。这对于确保生成正确的SQL语句是有必要的。例如,在第一个为“真”的条件元素中,“AND”是不需要的,事实上,加上它肯定会出错。以下小节讨论不同的条件元素,包括二元条件元素,一元条件元素和其他动态元素。
这些方法用于数据更新(即非查询语句)。这就是说,使用下面的查询方法来执行数据更新操作并不是不可能。但这显得很奇怪,并明显依赖于JDBC的驱动程序。执行update(),返回受影响数据记录的数目。
queryForObject( )方法有两个版本。一个返回查询得到的新对象,另一个用一个事先生成的对象作为参数。后者对于使用多个查询为对象赋值很有用。
queryForList( )方法也有三个版本。第一个执行一个查询并返回所有的查询结果。第二个允许指定跳过结果的数目(即开始点)和返回结果的最大数目。对于查询一个数量很大的结果集,并且不想返回所有的数据时很有用。
最后一个版本的queryForList()方法需要一个行处理器(row handler)作为输入参数。这个方法可以让您使用结果对象,而不是数据表的行和列来逐行地处理查询结果集。这个方法的参数除了例行的名称和参数对象外,还包括了一个实现了RowHandler接口的类的实例。RowHandler接口只有一个方法:
public void handleRow (Object object, List list);
对于每一行从数据库中返回的数据,RowHandler接口的方法都会被执行。这种处理数据的方法,简洁而具有扩展性。使用RowHandler的例子,请参见下面的章节。输入参数list是List接口的一个实例,并从queryForList()方法中再次返回。您可以向list对象添加零或多个结果对象。显然,如果您处理上百万的数据行,把它们全部放入一个list对象里并不是个好方法。
queryForPaginatedList( )对于要返回一个可以向前和向后翻页的数据子集,queryForPaginatedList()方法很有用,它返回一个可管理的List对象。通常用于只显示一部分查询结果的用户界面。一个常见的例子是,搜索引擎找到了10000条结果,但每次只显示其中的100条。PaginatedList接口包含了向前和向后翻页的方法(nextPage(),previousPage(),gotoPage()),并提供了检查翻页状态的方法(isFirstPage(),isMiddlePage(),isLastPage(),isPreviousPageAvailable(),getPageIndex(),getPageSize())。虽然不能从PaginatedList接口得到查询结果集的总数,但这个总数可以再执行一个简单的语句count()来得到。否则,PaginatedList接口会大大的降低性能。
queryForMap( )上面的方法将查询结果集放在List对象中,而queryForMap()方法将结果集放在一个Map对象中,这个Map对象用一个传入参数keyProperty作为key值。例如,要读入一批Employee对象,您可以将这些Employee对象放在一个用employeeNumber属性作为key值的Map对象中。Map对象的值可以是整个Employee对象,也可以是Employee对象的另一个属性,属性的名称由第二个参数valueProperty指定。例如,您可能只是需要一个Map对象,用员工号作为key值,员工姓名作为value值。不要把它和用Map作为结果对象的概念混淆。这个方法可以使用Java Bean和Map(或基本类型的包装类,但不可能这样用)作为结果对象。
直接使用JDBC一个非常普遍的问题是动态SQL。使用参数值、参数本身和数据列都是动态的SQL,通常非常困难。典型的解决方法是,使用一系列if-else条件语句和一连串讨厌的字符串连接。对于这个问题,SQL Map API使用和mapped statement非常相似的结构,提供了较为优雅的方法。这里是一个简单的例子:
<select id="dynamicGetAccountList"
cacheModel="account-cache"
resultMap="account-result" >
select * from ACCOUNT
<isGreaterThan prepend="and" property="id" compareValue="0">
where ACC_ID = #id#
</isGreaterThan>
order by ACC_LAST_NAME
</select>
上面的例子中,根据参数bean“id”属性的不同情况,可创建两个可能的语句。如果参数“id”大于0,将创建下面的语句:
select * from ACCOUNT where ACC_ID = ?
或者,如果“id”参数小于等于0,将创建下面的语句:
select * from ACCOUNT
在更复杂的例子中,动态Mapped Statement的用处更明显。如下面比较复杂的例子:
<statement id="dynamicGetAccountList" resultMap="account-result" >
select * from ACCOUNT
<dynamic prepend="WHERE">
<isNotNull prepend="AND" property="firstName">
(ACC_FIRST_NAME = #firstName#
<isNotNull prepend="OR" property="lastName">
ACC_LAST_NAME = #lastName#
</isNotNull>
)
</isNotNull>
<isNotNull prepend="AND" property="emailAddress">
ACC_EMAIL like #emailAddress#
</isNotNull>
<isGreaterThan prepend="AND" property="id" compareValue="0">
ACC_ID = #id#
</isGreaterThan>
</dynamic>
order by ACC_LAST_NAME
</statement>
根据不同的条件,上面动态的语句可以产生16条不同的查询语句。使用if-else结构和字符串,会产生上百行很乱的代码。
而使用动态Statement,和在SQL的动态部位周围插入条件标签一样容易。例如:
<statement id="someName" resultMap="account-result" >
select * from ACCOUNT
<dynamic prepend="where">
<isGreaterThan prepend="and" property="id" compareValue="0">
ACC_ID = #id#
</isGreaterThan>
<isNotNull prepend=”and" property="lastName">
ACC_LAST_NAME = #lastName#
</isNotNull>
</dynamic>
order by ACC_LAST_NAME
</statement>
上面的例子中,<dynamic>元素划分出SQL语句的动态部分。动态部分可以包含任意多的条件标签元素,条件标签决定是否在语句中包含其中的SQL代码。所有的条件标签元素将根据传给动态查询Statement的参数对象的情况来工作。<dynamic>元素和条件元素都有“prepend”属性,它是动态SQL代码的一部分,在必要情况下,可以被父元素的“prepend”属性覆盖。上面的例子中,prepend属性“where”将覆盖第一个为“真”的条件元素。这对于确保生成正确的SQL语句是有必要的。例如,在第一个为“真”的条件元素中,“AND”是不需要的,事实上,加上它肯定会出错。以下小节讨论不同的条件元素,包括二元条件元素,一元条件元素和其他动态元素。
这些方法用于数据更新(即非查询语句)。这就是说,使用下面的查询方法来执行数据更新操作并不是不可能。但这显得很奇怪,并明显依赖于JDBC的驱动程序。执行update(),返回受影响数据记录的数目。
queryForObject( )方法有两个版本。一个返回查询得到的新对象,另一个用一个事先生成的对象作为参数。后者对于使用多个查询为对象赋值很有用。
queryForList( )方法也有三个版本。第一个执行一个查询并返回所有的查询结果。第二个允许指定跳过结果的数目(即开始点)和返回结果的最大数目。对于查询一个数量很大的结果集,并且不想返回所有的数据时很有用。
最后一个版本的queryForList()方法需要一个行处理器(row handler)作为输入参数。这个方法可以让您使用结果对象,而不是数据表的行和列来逐行地处理查询结果集。这个方法的参数除了例行的名称和参数对象外,还包括了一个实现了RowHandler接口的类的实例。RowHandler接口只有一个方法:
public void handleRow (Object object, List list);
对于每一行从数据库中返回的数据,RowHandler接口的方法都会被执行。这种处理数据的方法,简洁而具有扩展性。使用RowHandler的例子,请参见下面的章节。输入参数list是List接口的一个实例,并从queryForList()方法中再次返回。您可以向list对象添加零或多个结果对象。显然,如果您处理上百万的数据行,把它们全部放入一个list对象里并不是个好方法。
queryForPaginatedList( )对于要返回一个可以向前和向后翻页的数据子集,queryForPaginatedList()方法很有用,它返回一个可管理的List对象。通常用于只显示一部分查询结果的用户界面。一个常见的例子是,搜索引擎找到了10000条结果,但每次只显示其中的100条。PaginatedList接口包含了向前和向后翻页的方法(nextPage(),previousPage(),gotoPage()),并提供了检查翻页状态的方法(isFirstPage(),isMiddlePage(),isLastPage(),isPreviousPageAvailable(),getPageIndex(),getPageSize())。虽然不能从PaginatedList接口得到查询结果集的总数,但这个总数可以再执行一个简单的语句count()来得到。否则,PaginatedList接口会大大的降低性能。
queryForMap( )上面的方法将查询结果集放在List对象中,而queryForMap()方法将结果集放在一个Map对象中,这个Map对象用一个传入参数keyProperty作为key值。例如,要读入一批Employee对象,您可以将这些Employee对象放在一个用employeeNumber属性作为key值的Map对象中。Map对象的值可以是整个Employee对象,也可以是Employee对象的另一个属性,属性的名称由第二个参数valueProperty指定。例如,您可能只是需要一个Map对象,用员工号作为key值,员工姓名作为value值。不要把它和用Map作为结果对象的概念混淆。这个方法可以使用Java Bean和Map(或基本类型的包装类,但不可能这样用)作为结果对象。