MyBatis 04 实战
增删改查实现
在实际使用中,MyBatis 的使用遵从一定的规范。
常用的增删改查的 MyBatis 实现如下:
Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.sail.mapper.UserMapper">
<resultMap id="resultMap" type="user">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
</resultMap>
<sql id="selectAll">
select id,
name,
pwd
from user
</sql>
<select id="selectList" parameterType="user" resultMap="resultMap">
<include refid="selectAll"/>
<where>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
<if test="pwd != null and pwd != ''">
and pwd like concat('%', #{pwd}, '%')
</if>
</where>
limit #{startIndex}, #{pageSize}
</select>
<select id="pageByRowBounds" parameterType="user" resultMap="resultMap">
<include refid="selectAll"/>
<where>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
<if test="pwd != null and pwd != ''">
and pwd like concat('%', #{pwd}, '%')
</if>
</where>
</select>
<select id="selectOne" parameterType="int" resultMap="resultMap">
<include refid="selectAll"/>
where id = #{id}
</select>
<insert id="insert" parameterType="user">
insert into user
<trim prefix="(" suffixOverrides="," suffix=")">
<if test="name != null">name,</if>
<if test="pwd != null">pwd,</if>
</trim>
<trim prefix="values (" suffixOverrides="," suffix=")">
<if test="name != null">#{name},</if>
<if test="pwd != null">#{pwd},</if>
</trim>
</insert>
<insert id="insertBatch" parameterType="list">
insert into user
(name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.name}, #{user.pwd})
</foreach>
</insert>
<update id="update" parameterType="user">
update user
<trim prefix="set" suffixOverrides=",">
<if test="name != null">name = #{name},</if>
<if test="pwd != null">pwd = #{pwd},</if>
</trim>
where id = #{id}
</update>
<update id="updateBatch1" parameterType="list">
insert into user
(id, name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.id}, #{user.name}, #{user.pwd})
</foreach>
on duplicate key update
name = values(name), pwd = values(pwd)
</update>
<update id="updateBatch2" parameterType="list">
replace into user
(id, name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.id}, #{user.name}, #{user.pwd})
</foreach>
</update>
<delete id="delete" parameterType="int">
delete from user
where id = #{id}
</delete>
<delete id="deleteBatch" parameterType="string">
delete from user
where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>
Mapper.java
public interface UserMapper {
/**
* 列表
* @return 用户列表
* @param user 用户
*/
List<User> selectList(User user);
/**
* 通过RowBounds分页
* @return 用户列表
* @param user 用户
*/
List<User> pageByRowBounds(User user);
/**
* 单项
* @param id 主键
* @return 用户
*/
User selectOne(int id);
/**
* 插入
* @param user 用户
* @return 插入数量
*/
int insert(User user);
/**
* 批量插入
* @param userList 用户列表
* @return 插入数量
*/
int insertBatch(List<User> userList);
/**
* 更新
* @param user 用户
* @return 更新数量
*/
int update(User user);
/**
* 批量更新1
* @param userList 用户列表
* @return 更新数量
*/
int updateBatch1(List<User> userList);
/**
* 批量更新2
* @param userList 用户列表
* @return 更新数量
*/
int updateBatch2(List<User> userList);
/**
* 删除
* @param id 主键
* @return 删除数量
*/
int delete(int id);
/**
* 批量删除
* @param ids 主键数组
* @return 删除数量
*/
int deleteBatch(int[] ids);
}
Test.java
public class MyBatis {
/**
* 查询列表
*/
@Test
public void selectList() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
int pageNum = 1;
int pageSize = 2;
user.setStartIndex((pageNum - 1) * pageSize);
user.setPageSize(pageSize);
List<User> userList = userMapper.selectList(user);
System.out.println(userList);
sqlSession.close();
}
/**
* RowBounds分页
*/
@Test
public void pageByRowBounds() {
SqlSession sqlSession = MybatisUtils.getSession();
User user = new User();
int pageNum = 1;
int pageSize = 2;
RowBounds rowBounds = new RowBounds((pageNum - 1) * pageSize, pageSize);
List<User> userList = sqlSession.selectList("cn.sail.mapper.UserMapper.pageByRowBounds", null, rowBounds);
System.out.println(userList);
sqlSession.close();
}
/**
* 查询单项
*/
@Test
public void selectOne() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectOne(1);
System.out.println(user);
sqlSession.close();
}
/**
* 插入
*/
@Test
public void insert() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("sail");
user.setPwd("123456");
int insert = userMapper.insert(user);
System.out.println(insert);
sqlSession.commit();
sqlSession.close();
}
/**
* 批量插入
*/
@Test
public void insertBatch() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setName("sail1");
user.setPwd("123456");
userList.add(user);
user = new User();
user.setName("sail2");
user.setPwd("123456");
userList.add(user);
int insertBatch = userMapper.insertBatch(userList);
System.out.println(insertBatch);
sqlSession.commit();
sqlSession.close();
}
/**
* 更新
*/
@Test
public void update() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(6);
user.setName("sail");
user.setPwd("12345678");
int update = userMapper.update(user);
System.out.println(update);
sqlSession.commit();
sqlSession.close();
}
/**
* 批量更新1
*/
@Test
public void updateBatch1() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setId(11);
user.setName("sail1");
user.setPwd("1234567");
userList.add(user);
user = new User();
user.setId(12);
user.setName("sail2");
user.setPwd("1234567");
userList.add(user);
int updateBatch1 = userMapper.updateBatch1(userList);
System.out.println(updateBatch1);
sqlSession.commit();
sqlSession.close();
}
/**
* 批量更新2
*/
@Test
public void updateBatch2() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setId(11);
user.setName("sail1");
user.setPwd("12345678");
userList.add(user);
user = new User();
user.setId(12);
user.setName("sail2");
user.setPwd("12345678");
userList.add(user);
int updateBatch2 = userMapper.updateBatch2(userList);
System.out.println(updateBatch2);
sqlSession.commit();
sqlSession.close();
}
/**
* 删除
*/
@Test
public void delete() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int delete = userMapper.delete(6);
System.out.println(delete);
sqlSession.commit();
sqlSession.close();
}
/**
* 批量删除
*/
@Test
public void deleteBatch() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int[] ids = {9, 10};
int deleteBatch = userMapper.deleteBatch(ids);
System.out.println(deleteBatch);
sqlSession.commit();
sqlSession.close();
}
}
resultMap标签
<resultMap id="resultMap" type="user">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
</resultMap>
resultMap 中,column 是数据库表的列名 , property 是对应实体类的属性名。
resultMap 元素是 MyBatis 中最重要最强大的元素。它可以省略大量的赋值代码,并可以灵活的给字段取别名,且可以复用。
resultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。
sql片段
<sql id="selectAll">
select id,
name,
pwd
from user
</sql>
sql 片段一般编写通用性的 SQL 语句,比如全字段查询,过滤条件封装等。
最好基于单表来定义 sql 片段,提高片段的可重用性。
在 sql 片段中不要包括 where。
sql 片段是需要用 include 标签引用的,如以上的 sql 片段可以用如下的 include 标签引用:
<include refid="selectAll"/>
引用 sql 片段时,如果 refid 指定的不在本文件中,那么需要在前面加上 namespace。
列表分页查询
<select id="selectList" parameterType="user" resultMap="resultMap">
<include refid="selectAll"/>
<where>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
<if test="pwd != null and pwd != ''">
and pwd like concat('%', #{pwd}, '%')
</if>
</where>
limit #{startIndex}, #{pageSize}
</select>
/**
* 列表
* @return 用户列表
* @param user 用户
*/
List<User> selectList(User user);
/**
* 查询列表
*/
@Test
public void selectList() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
int pageNum = 1;
int pageSize = 2;
user.setStartIndex((pageNum - 1) * pageSize);
user.setPageSize(pageSize);
List<User> userList = userMapper.selectList(user);
System.out.println(userList);
sqlSession.close();
}
列表查询往往是一个页面需要编写的第一个 SQL,也往往是数据量最大、语句最复杂的 SQL。
parameterType 这里使用的是配置文件中取的别名,对应实体类。
resultMap 这里使用的是前面定义的 resultMap。
如果用 resultType ,则需要指定具体的类或者 MyBatis 默认的基本数据类型。
MyBatis 默认的基本数据类型有:int、string、long、map。
where 标签会知道如果它包含的标签中有返回值的话,它就插入一个 where 。此外,如果标签返回的内容是以 and 或 or 开头的,则它会剔除掉。
if 标签用于判断参数是否有值,有值则拼接标签中的 SQL 语句,没有值则不拼接,可以提高 SQL 查询效率和避免传值为 null 的语法错误。
#{} 用于传递参数,会默认在首尾拼接 ‘ 。
${} 用于传递直接量,即不会在首尾拼接 ’ ,常用于拼接 SQL 语句。
limit 处没有进行参数有值判断,所以 startIndex 和 pageSize 必须有值,不然语句会报错。
以上是常用的分页查询处理方式,也可以用 Java 代码实现分页,如RowBounds分页:
<select id="pageByRowBounds" parameterType="user" resultMap="resultMap">
<include refid="selectAll"/>
<where>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
<if test="pwd != null and pwd != ''">
and pwd like concat('%', #{pwd}, '%')
</if>
</where>
</select>
/**
* 通过RowBounds分页
* @return 用户列表
* @param user 用户
*/
List<User> pageByRowBounds(User user);
@Test
public void pageByRowBounds() {
SqlSession sqlSession = MybatisUtils.getSession();
User user = new User();
int pageNum = 1;
int pageSize = 2;
RowBounds rowBounds = new RowBounds((pageNum - 1) * pageSize, pageSize);
List<User> userList = sqlSession.selectList("cn.sail.mapper.UserMapper.pageByRowBounds", null, rowBounds);
System.out.println(userList);
sqlSession.close();
}
此种分页方式的代码较为繁琐,且由于进行了封装,效率也不如上面的方式,不推荐使用。
查询单项
<select id="selectOne" parameterType="int" resultMap="resultMap">
<include refid="selectAll"/>
where id = #{id}
</select>
/**
* 单项
* @param id 主键
* @return 用户
*/
User selectOne(int id);
/**
* 查询单项
*/
@Test
public void selectOne() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectOne(1);
System.out.println(user);
sqlSession.close();
}
查询单项常用于详情查看和修改时赋值。
由于主键项是没有做是否有值判断的,因此必须有值,是否会造成语法错误。
由于单项查询的返回值往往是一个类,因此要注意非空判断,避免空指针异常。
插入
<insert id="insert" parameterType="user">
insert into user
<trim prefix="(" suffixOverrides="," suffix=")">
<if test="name != null">name,</if>
<if test="pwd != null">pwd,</if>
</trim>
<trim prefix="values (" suffixOverrides="," suffix=")">
<if test="name != null">#{name},</if>
<if test="pwd != null">#{pwd},</if>
</trim>
</insert>
/**
* 插入
* @param user 用户
* @return 插入数量
*/
int insert(User user);
@Test
public void insert() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("sail");
user.setPwd("123456");
int insert = userMapper.insert(user);
System.out.println(insert);
sqlSession.commit();
sqlSession.close();
}
插入涉及到数据变更,在实际使用中建议加上 @Transactional(rollbackFor = Exception.class)
注解,在出错时会进行回滚,避免造成数据错误。
主键值往往是自增的,插入时一般不需要设置主键值。
trim 标签可以用 suffixOverrides 属性过滤掉 SQL 语句尾部多余的符号,也可以用 prefix 拼接标签中开头的语句,用 suffix 标签拼接标签中结尾的语句。
批量插入
<insert id="insertBatch" parameterType="list">
insert into user
(name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.name}, #{user.pwd})
</foreach>
</insert>
/**
* 批量插入
* @param userList 用户列表
* @return 插入数量
*/
int insertBatch(List<User> userList);
@Test
public void insertBatch() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setName("sail1");
user.setPwd("123456");
userList.add(user);
user = new User();
user.setName("sail2");
user.setPwd("123456");
userList.add(user);
int insertBatch = userMapper.insertBatch(userList);
System.out.println(insertBatch);
sqlSession.commit();
sqlSession.close();
}
foreach标签
- collection:指定输入对象中的集合属性。
- item:每次遍历生成的对象。
- open:开始遍历时的拼接字符串。
- close:结束时拼接的字符串。
- separator:遍历对象之间需要拼接的字符串。
修改
<update id="update" parameterType="user">
update user
<trim prefix="set" suffixOverrides=",">
<if test="name != null">name = #{name},</if>
<if test="pwd != null">pwd = #{pwd},</if>
</trim>
where id = #{id}
</update>
/**
* 更新
* @param user 用户
* @return 更新数量
*/
int update(User user);
@Test
public void update() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(6);
user.setName("sail");
user.setPwd("12345678");
int update = userMapper.update(user);
System.out.println(update);
sqlSession.commit();
sqlSession.close();
}
参数中主键的值是必须的。
更新涉及到数据变更,在实际使用中建议加上 @Transactional(rollbackFor = Exception.class)
注解,在出错时会进行回滚,避免造成数据错误。
由于一般会进行修改字段的非空判断,所以当一个字段有值改为无值时,由于无值的参数被非空判断拦截,是修改不成功的,如果有此需求则需要单独定义没有字段非空判断的 SQL 语句。
批量更新1
<update id="updateBatch1" parameterType="list">
insert into user
(id, name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.id}, #{user.name}, #{user.pwd})
</foreach>
on duplicate key update
name = values(name), pwd = values(pwd)
</update>
/**
* 批量更新1
* @param userList 用户列表
* @return 更新数量
*/
int updateBatch1(List<User> userList);
@Test
public void updateBatch1() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setId(11);
user.setName("sail1");
user.setPwd("1234567");
userList.add(user);
user = new User();
user.setId(12);
user.setName("sail2");
user.setPwd("1234567");
userList.add(user);
int updateBatch1 = userMapper.updateBatch1(userList);
System.out.println(updateBatch1);
sqlSession.commit();
sqlSession.close();
}
on duplicate key update,是基于主键(PRIMARY KEY)或唯一索引(UNIQUE INDEX)使用的。
如果已存在该唯一标示或主键就更新,如果不存在该唯一标示或主键则作为新行插入。
批量更新2
<update id="updateBatch2" parameterType="list">
replace into user
(id, name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.id}, #{user.name}, #{user.pwd})
</foreach>
</update>
/**
* 批量更新2
* @param userList 用户列表
* @return 更新数量
*/
int updateBatch2(List<User> userList);
@Test
public void updateBatch2() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setId(11);
user.setName("sail1");
user.setPwd("12345678");
userList.add(user);
user = new User();
user.setId(12);
user.setName("sail2");
user.setPwd("12345678");
userList.add(user);
int updateBatch2 = userMapper.updateBatch2(userList);
System.out.println(updateBatch2);
sqlSession.commit();
sqlSession.close();
}
replace into 跟 insert into 的用法完全一样,但是它带有更新功能。
如果发现表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插入新的数据。否则,直接插入新数据。
它是先删除数据,然后再插入,如果当前的数据库用户没有删除权限,是不能使用replace into的。
删除
<delete id="delete" parameterType="int">
delete from user
where id = #{id}
</delete>
/**
* 删除
* @param id 主键
* @return 删除数量
*/
int delete(int id);
@Test
public void delete() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int delete = userMapper.delete(6);
System.out.println(delete);
sqlSession.commit();
sqlSession.close();
}
参数中主键是必须的。
删除涉及到数据变更,在实际使用中建议加上 @Transactional(rollbackFor = Exception.class)
注解,在出错时会进行回滚,避免造成数据错误。
批量删除
<delete id="deleteBatch" parameterType="string">
delete from user
where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
/**
* 批量删除
* @param ids 主键数组
* @return 删除数量
*/
int deleteBatch(int[] ids);
@Test
public void deleteBatch() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int[] ids = {9, 10};
int deleteBatch = userMapper.deleteBatch(ids);
System.out.println(deleteBatch);
sqlSession.commit();
sqlSession.close();
}
以上就是常用的增删改查语句,在实际使用中,为效率考虑,建议尽量使用单表进行增删改查,那些复杂的关联在代码中处理。
补充
@Param
@Param 注解用于给方法参数起一个名字。以下是总结的使用原则:
- 在方法只接受一个参数的情况下,可以不使用 @Param。
- 在方法接受多个参数的情况下,建议一定要使用 @Param 注解给参数命名。
- 如果参数是 JavaBean, 则不能使用 @Param。
- 不使用 @Param 注解时,参数只能有一个,并且是 Javabean。
注解写SQL
mybatis 最初配置信息是基于 XML,映射语句(SQL)也是定义在 XML 中的。而到 MyBatis 3 提供了新的基于注解的配置。
注解主要分成 :
- @select ()
- @update ()
- @Insert ()
- @delete ()
可以在注解中编写 SQL 语句实现与 XML 同样的效果。
利用注解开发就不需要mapper.xml映射文件了。
Java 注解的的表达力和灵活性十分有限,稍微复杂一点的 SQL 语句用注解进行编写会非常困难,因此不建议使用。
多对一和一对多处理
多对一
多对一的理解:
- 多个学生对应一个老师。
- 如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师。
按查询嵌套处理
<select id="getStudents" resultMap="StudentTeacher">
select *
from student
</select>
<resultMap id="StudentTeacher" type="Student">
<!-- association关联属性:
property属性名
column在多的一方的表中的列名
column="{key=value,key=value}"
其实就是键值对的形式,key是传给下个sql的取值名称,value是上个sql查询的字段名。
javaType属性类型
select引用查询结果 -->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
select *
from teacher
where id = #{id}
</select>
按结果嵌套处理
<select id="getStudents2" resultMap="StudentTeacher2" >
select s.id sid,
s.name sname,
t.name tname
from student s, teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<!--关联对象property 关联对象在Student实体类中的属性-->
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
按照查询进行嵌套处理就像SQL中的子查询
按照结果进行嵌套处理就像SQL中的联表查询
多对一的情况用 XML 处理较为复杂,建议在代码中进行处理,逻辑会清晰很多,由于不会关联表,查询效率也会大大提高。
一对多
一对多的理解:
- 一个老师拥有多个学生。
- 如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)。
按查询嵌套处理
<select id="getTeacher2" resultMap="TeacherStudent2">
select *
from teacher
where id = #{id}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<!--column是一对多的外键 , 写的是一的主键的列名-->
<collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select *
from student
where tid = #{id}
</select>
按结果嵌套处理
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,
s.name sname,
t.name tname,
t.id tid
from student s,teacher t
where s.tid = t.id
and t.id = #{id}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<result property="tid" column="tid" />
</collection>
</resultMap>
总结
association:关联
collection:集合
association 是用于一对一和多对一,而collection是用于一对多的关系
JavaType和ofType都是用来指定对象类型的
JavaType 是用来指定pojo中属性的类型
ofType 指定的是映射到list集合属性中pojo的类型。
choose语句
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,它类似于 if-else 语句。
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select *
from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>