演习 动态 SQL 的办法if条件判断choose, when, otherwise 选择器利用trim, where, setforeach利用Ognl表达式案例实操if条件判断
动态 SQL 常日要做的事情是有条件地包含 where 子句的一部分。比如:
<!-- 模糊匹配 --> <select id=34;queryUserByuserName" parameterType="string" resultType="user"> select id,userName,userPwd from user where 1=1 <if test="userName!=null and userName!=''"> and userName like '%#{userName}%' </if></select>
利用if标签便是加一个test属性作为判断, 如果有多个条件组合判断的话用and, or连接
实现方法
@Override public List<User> queryUserByUserName(String userName) { List<User> users=null; SqlSession session=null; try { session=sqlSessionFactory.openSession(); Map map=new HashMap(); //map 参数 map.put("userName",userName); users=session.selectList("com.xxx.mapper.UserMapper.queryUserByUserName", map); } catch (Exception e) { e.printStackTrace(); }finally{ if(null!=session){ session.close(); } } return users; }
运行结果, sql自动判断并且拼接上了
choose, when, otherwise 选择器利用
我们不想用到所有的条件语句,而只想从中择其一二。针对这种情形,MyBatis 供应了 choose 元素,它有点像 Java 中的 switch 语句
<select id="queryUserByParams" parameterType="map" resultType="user"> select id,userPwd <choose> <when test="nation!=null and nation!=''"> ,userName </when> <otherwise> ,realName </otherwise> </choose> from user where userName like '%${userName}%' <if test="phone!=null and phone!=''"> and phone like '%${phone}%' </if></select>
这条语句的意思便是说 如果我传进nation不为空就查userName的值, 否则是realName的值
@Test public void test16(){ UserDao userDao=new UserDaoImpl(sqlSessionFactory); List<User> list=userDao.queryUserByParams("", null, "xxx"); for(User u:list){ System.out.println(u); } }
trim, where, set
前面几个例子已经适宜地办理了一个臭名昭著的动态 SQL 问题, 然后我们再来看第一条的配置
<select id="findUserByUserName" resultMap="RM_User" > select userId, userName, password from user where userName like '%${userName}%' <if test="phone != null and phone != ''" > and phone like '%${phone}%' </if> </select>
如果我把 userName like '%${userName}%'这个语句也用if做个判断
<select id="findUserByUserName" resultMap="RM_User" > select userId, userName, password from user where <if test="userName != null and userName != ''" > userName like '%${userName}%' </if> <if test="phone != null and phone != ''" > and phone like '%${phone}%' </if></select>
这样的话我们预测一下 打印的sql该当是
select userId, userName, password from user where
很明显这条sql会报错
那为理解决这个问题呢, 我们利用<where></where>标签
<select id="queryUserByParams" parameterType="map" resultType="user"> select id,userPwd,phone <choose> <when test="nation!=null and nation!=''"> ,userName </when> <otherwise> ,realName </otherwise> </choose>from user<where> <if test="userName !=null and userName !=''"> userName like '%${userName}%' </if> <if test="phone!=null and phone!=''"> and phone like '%${phone}%' </if> </where></select>
编写测试类
@Test public void test16(){ UserDao userDao=new UserDaoImpl(sqlSessionFactory); List<User> list=userDao.queryUserByParams("", "", ""); for(User u:list){ System.out.println(u); } }
where 元素知道只有在一个以上的if条件有值的情形下才去插入“WHERE”子句。而且,若末了的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。就像上面的配置如果我phone有值, userName没值的话 where也知道要将phone 前面的and去掉
但如果 where 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元向来定制我们想要的功能。比如,和 where 元素等价的自定义 trim 元素为:
<select id="queryUserByParams" parameterType="map" resultType="user"> select id,userPwd,phone <choose> <when test="nation!=null and nation!=''"> ,userName </when> <otherwise> ,realName </otherwise> </choose> from user <trim prefix="where" prefixOverrides="and |or" > <if test="userName !=null and userName !=''"> userName like '%${userName}%' </if> <if test="phone!=null and phone!=''"> and phone like '%${phone}%' </if> </trim> </select>
这样的效果跟<where></where>效果是一样的
prefixOverrides 属性会忽略通过管道分隔的文本序列(把稳此例中的空格也是必要的)。它带来的结果便是所有在 prefixOverrides 属性中指定的内容将被移除,并且插入 prefix 属性中指定的内容。
对付update语句, 我们采取<set></set>去设置值
<update id="updateUserById" parameterType="user"> update user <set> <if test="userName!=null"> userName=#{userName}, </if> <if test="userPwd!=null"> userPwd=#{userPwd}, </if> </set> where id=#{id} </update>
编写测试方法
@Test public void test17(){ UserDao userDao=new UserDaoImpl(sqlSessionFactory); User user=userDao.queryUserById(6); user.setUserPwd(null); user.setUserName("xxx06"); userDao.updateUserById(user); }
若你对等价的自定义 trim 元素的样子感兴趣,那这就该当是它的真面孔:
<update id="updateUserById" parameterType="user"> update user <trim prefix="set" suffixOverrides="," > <!-- 此时利用后缀肃清, --> <if test="userName!=null"> userName=#{userName}, </if> <if test="userPwd!=null"> userPwd=#{userPwd}, </if> </trim> where id=#{id} </update>
这个效果和set是同等的
foreach
动态 SQL 的其余一个常用的必要操作是须要对一个凑集进行遍历,常日是在构建 IN 条件语句或者是批量插入。比如:
<select id="findUserByUserName" resultMap="RM_User" > select userId, userName, password from user <where> <if test="userNameList != null" > userName in <foreach item="item" index="index" collection="userNameList"open="(" separator="," close=")"> #{item} </foreach> </if> </where> </select>
须要视频配套资料或其他资料+我们小姐姐V lezijie007(加好友暗号 98 ,不备注不加)编写测试方法
@Test public void testFindUserByUserName() { InputStream is = MybatisSecondaryCacheTest.class.getClassLoader().getResourceAsStream("mybatis.xml"); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is); SqlSession session = sessionFactory.openSession(); // 创建参数 Map<String, Object> params = new HashMap<>(); // 创建以string数组然后转化成list String[] userName = new String[]{"Tonygogo", "hello", "哈哈哈"}; params.put("userNameList", Arrays.asList(userName)); // string数组转list, key的名称要与映射文件中的变量名要一贯 List<User> users = session.selectList("findUserByUserName", params); System.out.println("查询结果: " + users.toString()); }
利用Ognl表达式
我们在上面的映命中, 如果用if去判断一个值是否为空或者是空字符串时我们是这样做的test="userName != null and userName !='' "这样写起来比较繁芜, 为此我们采取Ognl表达式@Ognl@isNotEmpty(userName)去判断。
利用ognl表达式时我们要在根目录的包下面加上Ognl的一个Java类, 这里面会有各种各样的判断比如为空判断@Ognl@isEmpty(userName),不为空判断 @Ognl@isNotEmpty(userName), 是否是空字符串@Ognl@isBlank(userName), 不为空字符串@Ognl@isNotBlank(userName)等等
我们常用的可能便是这四个,它只是方便我们去做一些操作,实际中也会用到
import java.lang.reflect.Array; import java.util.Collection;import java.util.Map; / Ognl工具类,紧张是为了在ognl表达式访问静态方法时可以减少长长的类名称编写 Ognl访问静态方法的表达式为: @class@method(args) 示例利用: <pre> <if test="@Ognl@isNotEmpty(userId)"> and user_id = #{userId} </if> </pre> / public class Ognl { / 可以用于判断String,Map,Collection,Array是否为空 @param o @return / @SuppressWarnings("rawtypes") public static boolean isEmpty(Object o) throws IllegalArgumentException { if(o == null) return true; if(o instanceof String) { if(((String)o).length() == 0){ return true; } } else if(o instanceof Collection) { if(((Collection)o).isEmpty()){ return true; } } else if(o.getClass().isArray()) { if(Array.getLength(o) == 0){ return true; } } else if(o instanceof Map) { if(((Map)o).isEmpty()){ return true; } }else { return false; // throw new IllegalArgumentException("Illegal argument type,must be : Map,Collection,Array,String. but was:"+o.getClass()); } return false; } / 可以用于判断 Map,Collection,String,Array是否不为空 @param c @return / public static boolean isNotEmpty(Object o) { return !isEmpty(o); } public static boolean isNotBlank(Object o) { return !isBlank(o); } public static boolean isBlank(Object o) { if(o == null) return true; if(o instanceof String) { String str = (String)o; return isBlank(str); } return false; } public static boolean isBlank(String str) { if(str == null || str.length() == 0) { return true; } for (int i = 0; i < str.length(); i++) { if (!Character.isWhitespace(str.charAt(i))) { return false; } } return true; } }
扩展表明形式动态sql
除了xml 配置能够支持动态 sql 外,MyBatis供应了各种表明如@InsertProvider,@UpdateProvider,@DeleteProvider和@SelectProvider,来帮助构建动态SQL语句,然后让MyBatis实行这些SQL语句。
public interface AccountDao { / \ 添加账户记录 \ 添加字符串sql由AccountProvider 类addAccount方法供应 \ 返回影响行数 \ @param account \ @return / @InsertProvider(method="addAccount",type=AccountProvider.class) public int addAcccount(Account account); / \ 添加账户记录 \ 添加字符串sql由AccountProvider 类addAccount方法供应 \ 返回主键 \ @param account \ @return / @InsertProvider(method="addAccount",type=AccountProvider.class) @Options(useGeneratedKeys=true,keyColumn="id") public int addAcccount02(Account account); / \ 根据id查询账户记录 \ 查询字符串sql由AccountProvider 类queryAccountById方法供应 \ @param id \ @return / @SelectProvider(method="queryAccountById",type=AccountProvider.class) public Account queryAccountById(@Param("id")int id); / \ 多条件查询账户记录 \ 查询字符串sql由AccountProvider 类queryAccountByParams方法供应 \ @param aname \ @param type \ @param time \ @return / @SelectProvider(method="queryAccountByParams",type=AccountProvider.class) public List<Account> queryAccountByParams(@Param("aname")String aname,@Param("type")String type,@Param("time")String time); / \ 更新账户记录 \ 更新字符串sql由AccountProvider 类updateAccountById方法供应 \ @param account \ @return / @UpdateProvider(method="updateAccount",type=AccountProvider.class) public int updateAccountById(Account account); / \ 根据id删除账户记录 \ 删除字符串sql由AccountProvider 类deleteAccount方法供应 \ @param id \ @return / @DeleteProvider(method="deleteAccount",type=AccountProvider.class) public int deleteAccountById(@Param("id")int id); } public class AccountProvider { / \ 返回添加账户记录sql字符串 \ @param account \ @return / public String addAccount(final Account account){ return new SQL(){{ INSERT_INTO("account"); VALUES("aname","#{aname}"); VALUES("type", "#{type}"); VALUES("remark","#{remark}"); VALUES("money", "#{money}"); VALUES("user_id", "#{userId}"); VALUES("create_time","#{createTime}"); VALUES("update_time", "#{updateTime}"); }}.toString(); } / \ 返回根据id查询账户记录sql字符串 \ @param id \ @return / public String queryAccountById(@Param("id")int id){ return new SQL(){{ SELECT("id,aname,type,remark,create_time as createTime,update_time as updateTime,user_id as userId"); FROM("account"); WHERE(" id=#{id} "); }}.toString(); } / \ 返回多条件查询sql字符串 \ @param aname \ @param type \ @param time \ @return / public String queryAccountByParams(@Param("aname") final String aname,@Param("type")final String type,@Param("time")final String time){ String sql= new SQL(){{ SELECT("id,aname,type,remark,create_time as createTime,update_time as updateTime,user_id as userId"); FROM("account"); WHERE(" 1=1 "); if(!StringUtils.isNullOrEmpty(aname)){ AND(); WHERE(" aname like concat('%',#{aname},'%') "); } if(!StringUtils.isNullOrEmpty(type)){ AND(); WHERE(" type =#{type}"); } if(!StringUtils.isNullOrEmpty(time)){ AND(); WHERE(" create_time <=#{time}"); } }}.toString(); return sql; } / \ 返回更新账户记录sql字符串 \ @param account \ @return / public String updateAccount(Account account){ return new SQL(){{ UPDATE(" account"); SET("aname=#{aname}"); SET("type=#{type}"); WHERE("id=#{id}"); }}.toString(); } / \ 返回删除账户记录sql字符串 \ @param id \ @return / public String deleteAccount(@Param("id")int id){ return new SQL(){{ DELETE_FROM("account"); WHERE("id=#{id}"); }}.toString(); } }