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