乐趣区

关于java:Mybatis-框架如何实现-动态-SQL-呢

MyBatis 的弱小个性之一便是它的动静 SQL。如果你有应用 JDBC 或其余相似框架的教训,你就能领会到依据不同条件拼接 SQL 语句有如许苦楚。拼接的时候要确保不能忘了必要的空格,还要留神省掉列名列表最初的逗号。利用动静 SQL 这一个性能够彻底解脱这种苦楚。它借助 ognl(相似于 jsp 外面的 el 表达式)表达式来实现动静 sql 的拼接使得十分简便。

实习 动静 SQL 的形式

  • if 条件判断
  • choose, when, otherwise 选择器应用
  • trim, where, set
  • foreach
  • 应用 Ognl 表达式

案例实操

if 条件判断

动静 SQL 通常要做的事件是有条件地蕴含 where 子句的一部分。比方:

<!– 含糊匹配 –>    
<select id=”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>

编写测试方法

@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();
•    }
}

退出移动版