zoukankan      html  css  js  c++  java
  • 《Mybatis从入门到精通》读书笔记(二)

    第三章. Mybatis 注解方式的基本用法

    表字段和Java属性字段映射的方式:

    1. SQL语句中列取别名

    2. 设置mapUnderscoreToCamelCase = true(下划线转驼峰)

    3. resultMap映射

    注解方式应用场景不多,不做过多介绍,具体可以参考原书或mybatis官方文档。

    第四章. Mybatis 动态SQL

    以下是Mybatis动态SQL在XML中支持的几种标签:

    • if
    • choose(when、otherwise)
    • trim(where、set)
    • foreach
    • bind

    4.1. if

    判断条件property != null或property == null:适用于任何类型的字段,用于判断属性值是否为空。

    判断条件property != ' '或property == ' ':仅适用于String类型的字段,用于判断属性值是否为空字符串。

    // 在OGNL表达式中,这两个判断的顺序不会影响判断的结果,也不会有空指针异常
    <if test="userName != null and userName != ''">

    4.2. choose

    用来实现 if .. .else if ... else ... 的逻辑

    <select id="selectByIdOrUserName" resultType="tk.mybatis.simple.model.SysUser">
        select id, 
        	user_name userName, 
            user_password userPassword,
            user_email userEmail,
        from sys_user
        where 1 = 1
    	<choose>
    		<when test="id != null">
    		and id = #{id}
    		</when>
    		<when test="userName != null and userName != ''">
    		and user_name = #{userName}
    		</when>
    		<otherwise>
    		limit 0
    		</otherwise>
    	</choose>
    </select>

    4.3. where、set、trim

    where标签的作用:如果该标签包含的元素中有返回值,就插入一个where;如果where后面的字符串是以AND或OR开头的,就将它们剔除。

    <select id="selectByUser" resultType="tk.mybatis.simple.model.SysUser">
        select 
            id, 
        	user_name userName, 
            user_password userPassword,
            user_email userEmail,
        from sys_user
        <where>
    	    <if test="userName != '' and userName != null">
    	       and user_name like concat('%', #{userName}, '%')
    	    </if>
    	    <if test="userEmail != '' and userEmail != null">
    	       and user_email = #{userEmail}
    	    </if>
        </where>
    </select>

    set标签的作用:如果该标签包含的元素中有返回值,就插入一个set;如果set后面的字符串是以逗号结尾的,就将这个逗号剔除。

    <update id="updateByIdSelective">
    	update sys_user 
    	<set>
    		<if test="userName != null and userName != ''">
    		user_name = #{userName},
    		</if>
    		<if test="userPassword != null and userPassword != ''">
    		user_password = #{userPassword},
    		</if>
    		<if test="userEmail != null and userEmail != ''">
    		user_email = #{userEmail},
    		</if>
    		<if test="userInfo != null and userInfo != ''">
    		user_info = #{userInfo},
    		</if>
    		<if test="headImg != null">
    		head_img = #{headImg, jdbcType=BLOB},
    		</if>
    		<if test="createTime != null">
    		create_time = #{createTime, jdbcType=TIMESTAMP},
    		</if>
    		id = #{id},
    	</set>
    	where id = #{id}
    </update>	

    where和set标签的功能都可以用trim标签来实现,并且在底层就是通过TrimSqlNode实现的。

    where标签对应trim的实现如下:

    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        ...
    </trim>

    这里的AND和OR后面的空格不能省略,为了避免匹配到andes、orders等单词。

    实际的prefixOverrides包含“AND”、“OR”、“AND ”、“OR ”、“AND ”、“OR ”、“AND ”、“OR ”,不仅仅是上面提到的两个带空格的前缀。

    set标签对应trim的实现如下:

    <trim prefix="SET" suffixOverrides=",">
        ...
    </trim>

    trim标签有如下属性:

    prefix:当trim元素内包含内容时,会给内容增加prefix指定的前缀。
    
    prefixOverrides:当trim元素内包含内容时,会把内容中匹配的前缀字符串去掉。
    
    suffix:当trim元素内包含内容时,会给内容增加suffix指定的后缀。
    
    suffixOverrides:当trim元素内包含内容时,会把内容中匹配的后缀字符串去掉。

    4.4. foreach

    SQL语句中有时会使用IN关键字,例如id in (1, 2, 3)。可以使用${id}方式直接获取值,但这种写法不能防止SQL注入,想避免SQL注入就需要用#{}的写法,这时就要配合foreach标签来满足需求。

    foreach可以对数组、Map或实现了Iterable接口(如List、Set)的对象进行遍历。数组在处理时会转换为List对象,因此foreach遍历的对象可以分为两大类:Iterable类型和Map类型。

    4.4.1. foreach实现in集合

    List<SysUser> selectByIdList(List<Long> idList);
    <select id="selectByIdList" resultType="tk.mybatis.simple.model.SysUser">
        select id, 
        	user_name userName, 
            user_password userPassword,
            user_email userEmail,
            user_info userInfo,
            head_img headImg,
            create_time createTime
        from sys_user
    	where id in
    	<foreach collection="list" open="(" close=")" separator="," item="id" index="i">
    		#{id}
    	</foreach>
    </select>

    foreach包含以下属性:

    collection:必填,值为要迭代循环的属性名。这个属性值的情况有很多。
    index:索引的属性名,在集合、数组情况下值为当前索引值,当迭代循环的对象是Map类型时,这个值为Map的key(键值)
    item:变量名,值为从迭代对象中取出的每一个值。
    open:整个循环内容开头的字符串。
    close:整个循环内容结尾的字符串。
    separator:每次循环的分隔符。

    collection的属性设置分为以下几种情况:

    1. 只有一个数组参数或集合参数

    以下代码是DefaultSQLSession中的方法,也是默认情况下的处理逻辑。

    private Object wrapCollection(Object object) {
        DefaultSqlSession.StrictMap map;
        if (object instanceof Collection) {
            map = new DefaultSqlSession.StrictMap();
            map.put("collection", object);
            if (object instanceof List) {
                map.put("list", object);
            }
            return map;
        } else if (object != null && object.getClass().isArray()) {
            map = new DefaultSqlSession.StrictMap();
            map.put("array", object);
            return map;
        } else {
            return object;
        }
    }

    总结下来就是:

    当传入参数为集合collection的子类(Set、List):collection = "collection"

    当传入参数为List:collection = "collection" 或 collection = "list"

    当传入参数为Array:collection = "array" 

    这里是传入数组或集合类型参数的默认名字。推荐使用@Param来指定参数的名字,这时collection就设置为通过@Param注解指定的名字。

    2. 有多个参数

    当有多个参数的时候,要使用@Param注解给每个参数指定一个名字,否则在SQL中使用参数时就会不方便,因此将collection设置为@Param注解指定的名字即可。

    3. 参数是Map类型

    使用Map和使用@Param注解方式类似,将collection指定为对应Map中的key即可。

    如果要循环所传入的Map,推荐使用@Param注解指定名字,此时可将collection设置为指定的名字,如果不想指定名字,就使用默认值_parameter。

    4. 参数是一个对象

    这种情况下指定为对象的属性名即可。当使用对象内多层嵌套的对象时,使用属性.属性(集合和数组使用下标取值)的方式可以指定深层的属性值。

    4.4.2. foreach实现批量插入

    如果数据库支持批量插入,就可以通过foreach来实现。批量插入是SQL-92新增的特性,目前支持的数据库有DB2、SQL Server 2008及以上版本、PostgreSQL 8.2及以上版本、MySQL、SQLite 3.7.11及以上版本、H2。批量插入的语法如下:

    INSERT INTO tablename (column-a, [column-b, ...])
    
    VALUES ('values-1a',['value-1b',... ]),
    
     ('values-2a',['value-2b',... ]),
    
    ...
    <insert id="insertList">
    	insert into sys_user(
    		user_name, user_password,user_email,
    		user_info, head_img, create_time)
    	values
    	<foreach collection="list" item="user" separator=",">
    		(
    		#{user.userName}, #{user.userPassword},#{user.userEmail},
    		#{user.userInfo}, #{user.headImg, jdbcType=BLOB}, #{user.createTime, jdbcType=TIMESTAMP})
    	</foreach>
    </insert>

    从Mybatis 3.3.1版本开始,Mybatis开始支持批量新增回写主键值的功能,到目前为止,可以完美支持该功能的仅有MySQL数据库。要在MySQL中实现批量插入返回自增主键值,只需要在原来基础上进行如下修改:

    <insert id="insertList" useGeneratedKeys="true" keyProperty="id">

    4.4.3. foreach实现动态UPDATE

    <update id="updateByMap">
    	update sys_user 
    	set 
    	<foreach collection="_parameter" item="val" index="key" separator=",">
    		${key} = #{val}
    	</foreach>
    	where id = #{id}
    </update>
    @Test
    public void testUpdateByMap(){
    	SqlSession sqlSession = getSqlSession();
    	try {
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		//从数据库查询 1 个 user 对象
    		Map<String, Object> map = new HashMap<String, Object>();
    		map.put("id", 1L);
    		map.put("user_email", "test@mybatis.tk");
    		map.put("user_password", "12345678");
    		//更新数据
    		userMapper.updateByMap(map);
    		//根据当前 id 查询修改后的数据
    		SysUser user = userMapper.selectById(1L);
    		Assert.assertEquals("test@mybatis.tk", user.getUserEmail());
    	} finally {
    		//为了不影响数据库中的数据导致其他测试失败,这里选择回滚
    		sqlSession.rollback();
    		//不要忘记关闭 sqlSession
    		sqlSession.close();
    	}
    }

    4.4.4. OGNL用法

    • e1 or e2
    • e1 and e2
    • e1 == e2,e1 eq e2
    • e1 != e2,e1 neq e2
    • e1 lt e2:小于
    • e1 lte e2:小于等于,其他gt(大于),gte(大于等于)
    • e1 in e2
    • e1 not in e2
    • e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
    • !e,not e:非,求反
    • e.method(args)调用对象方法
    • e.property对象属性值
    • e1[ e2 ]按索引取值,List,数组和Map
    • @class@method(args)调用类的静态方法
    • @class@field调用类的静态字段值

    表达式1~4是最常用的4中情况。另外有时候需要判断一个集合是否为空时,可能会出现如下判断。

    <if test="list != null and list.size() > 0">
    	<!--其他-->
    </if>

    在这种用法中,list.size()是调用对象的方法,> 0是和数字进行比较。

    表达式10、11两种情况也特别常见,而且可以多层嵌套使用。假设User类型的属性user中有一个Address类型的属性名为addr,在Address中还有一个属性zipcode,可以通过user.addr.zipcode直接使用zipcode的值。假设Map类型的属性为map,我们可以通过map['userName']或map.userName来获取map中的key为userName的值,这里一定要注意,不管userName的值是不是null,必须保证userName这个key存在,否则就会报错。

    表达式12通常用于简化一些校验,或者进行更特殊的校验,例如if中常见的判断可以写成如下这样。

    <if test="@tk.mybatis.util.StringUtil@isNotEmpty(userName)">
        and user_name like concat('%', #{userName}, '%')
    </if>

    假设只是在测试的时候想知道映射XML中方法执行的参数,可以在XML方法标签中添加如下方法:

    <select id="selectByUser" resultType="tk.mybatis.simple.model.SysUser">
    	<bind name="print" value="@tk.mybatis.util.StringUtil@print(_parameter)"/>
        select id, 
        	user_name userName, 
            user_password userPassword,
            user_email userEmail,
            user_info userInfo,
            head_img headImg,
            create_time createTime
        from sys_user
        <where>
    	    <if test="@tk.mybatis.util.StringUtil@isNotEmpty(userName)">
    		    and user_name like concat('%', #{userName}, '%')
    	    </if>
    	    <if test="userEmail != '' and userEmail != null">
    	    	and user_email = #{userEmail}
    	    </if>
        </where>
    </select>

    其中StringUtil类如下:

    package tk.mybatis.util;
    
    public class StringUtil {
    	
    	public static boolean isEmpty(String str){
    		return str == null || str.length() == 0;
    	}
    	
    	public static boolean isNotEmpty(String str){
    		return !isEmpty(str);
    	}
    	
    	public static void print(Object parameter){
    		System.out.println(parameter);
    	}
    	
    }
    
  • 相关阅读:
    Java 数据库操作oracle增删改查,通用封装基于hashmap
    Python 自动化paramiko操作linux使用shell命令,以及文件上传下载linux与windows之间的实现
    Java利用 ganymedssh2build.jar来上传文件到linux以及下载linux文件以及执行linux shell命令
    Java Calendar and SimpleDateFormat 时间模块
    Java 读取properties
    Java java httpclient4.5 进行http,https通过SSL安全验证跳过,封装接口请求 get,post(formdata,json)封装,文件上传下载
    Python 基于request库的get,post,delete,封装
    更法第一 (zz)
    北京将投资707亿元建三条地铁新线 (zz.IS2120@BG57IV3)
    fgetws 讀取Unicode文件 (zz.IS2120@BG57IV3)
  • 原文地址:https://www.cnblogs.com/codestarer/p/13635559.html
Copyright © 2011-2022 走看看