MyBatis 从入门到放弃rm -rf

分页查询

/**
 * @Author: WAI CHAN
 * @Date: 19-8-8 上午10:07
 */
public class PageQueryParam {

	// 当前页码
    @NotNull(message = "{pageQueryParam.currentIndex.notnull}")
    @Min(value = 1, message = "{pageQueryParam.currentIndex.min}" )
    private Integer currentIndex;
	
	// 每一页显示的行数
    @NotNull(message = "{management.entity.dto.PageQueryParam.pageSize.notnull}")
    @Min(value = 0, message = "{management.entity.dto.PageQueryParam.pageSize.min}")
    private Integer pageSize;

    public PageQueryParam() {
    }
 }
	...
	where ...
    order by id
    limit ${(currentIndex-1)*pageSize}, #{pageSize, jdbcType=INTEGER}

Oracle 批量操作

插入

<insert id="insertSysUserBatch" parameterType="java.util.List" useGeneratedKeys="false">
        insert ALL
        <foreach collection="list" item="item">
            into sys_user
            <trim prefix="(" suffix=")" suffixOverrides=",">
                user_id, user_name
            </trim>
            values
            <trim prefix="(" suffix=")" suffixOverrides=",">
                #{item.userId, jdbcType=VARCHAR},
                #{item.userName, jdbcType=VARCHAR}
            </trim>
        </foreach>
        SELECT 1 FROM DUAL
</insert>

主键自增

<insert id="insertNvrDeviceCaptureBatch" parameterType="java.util.List" useGeneratedKeys="false">
        insert into nvr_device_capture (id, code, msg, bucket_name, object_name, add_time, quartz_time, device_serial)
        select seq_nvr_device_capture.NEXTVAL as id, param.* from(
        <foreach collection="list" item="item" open="(" close=")" separator="union all">
            select
            #{item.code, jdbcType=VARCHAR},
            #{item.msg, jdbcType=VARCHAR},
            #{item.bucketName, jdbcType=VARCHAR},
            #{item.objectName, jdbcType=VARCHAR},
            #{item.addTime, jdbcType=DATE},
            #{item.quartzTime, jdbcType=DATE},
            #{item.deviceSerial, jdbcType=VARCHAR}
            from dual
        </foreach>
        ) param
    </insert>

更新

<update id="updateSysUserBatch" parameterType="java.util.List">
        begin
            <foreach collection="list" item="item" separator=";">
                update nvr_device
                <set>
                    <if test="item.userName != null and item.userName != ''">
                        user_name = #{item.userName},
                    </if>
                    <if>
                    	...
                    </if>
                </set>
                where user_id = #{item.userId}
            </foreach>
        ;end;
    </update>

Mysql 批量操作

查询

// UsrMapper.java
List<SysUsr> selectByIdList(@Param("idList")List<Long> idList);

<!-- UsrMapper.xml-->
<select id="selectByIdList" resultType="com.waichan.SysUsr">
	...
	from sys_usr
	where id in
	<foreach collection="idList" open="(" close=")" separator="," item="id" index="i">
		#{id}
	</foreach>
</select>

插入

// UsrMapper.java
int insertList(@Param("usrList")List<SysUsr> usrList);

<!-- UsrMapper.xml-->
<insert id="insertList'>
	insert into sys_usr(usr_name, usr_password, head_img, create_time)
	values
	<foreach collection="usrList" item="usr" speparator=",">
		(#{usr.usrName}, #{usr.usrPassword}, #{usr.headImg, jdbcType=BLOB}, #{usr.createTime, jdbcType=TIMESTAMP})
	</foreach>
</insert>

更新

// UserMapper.java
int updateUserBatch(@Param("userList")List<User> userList);

// UserMapper.xml
<update id="updateUserBatch" parameterType="java.util.List">
		update sys_user
		<trim prefix="set" suffixOverrides=",">
			<trim prefix="dept_id=case" suffix="end,">
				<foreach collection="userList" item="user">
					<if test="user.deptId != null and user.deptId != 0">
					when user_id = #{user.userId} then #{user.deptId}
					</if>
				</foreach>
			</trim>
			<trim prefix="login_name=case" suffix="end,">
				<foreach collection="userList" item="user">
					<if test="user.loginName != null and user.loginName != ''">
						when user_id = #{user.userId} then #{user.loginName}
					</if>
				</foreach>
			</trim>
	     </trim>
		where 1=1 and user_id in
		<foreach collection="userList" open="(" close=")" item="user" separator=",">
		    #{user.userId}
	    </foreach>
</update>
// 实际生成的查询语句
update sys_user
	set 
	dept_id = case user_id 
		when user_id = 1 then 1 #(dept_id = 1)
		when user_id =2 then 2 #(dept_id = 2)
	end,
	login_name = case user_id 
		when user_id = 1 then 'name1' #(login_name = 'name1')
		when user_id =2 then 'name2' #(login_name = 'name2')
	end
where 1=1 and user_id in (1, 2)

Mapper.java 接口参数

常见错误分析

// 在接口中使用多个参数但不使用@Param注解
List<SysRole> selectRolesByUsrIdAndRoleEnabled(Long usrId, Integer enabled);

<!-- Mapper.xml-->
<select id="selectRolesByUsrIdAndRoleEnabled" resultType="com.waichan.SysRole">
	...
	where u.id = #{usrId} and r.enabled = #{enabled}
</select>

抛出异常:Parameter ‘usrId’ not found. Availabel parameters are [0, 1, param1, param2]

这个时候如果将#{usrId}改为#{0}或#{param1}, #{enabled}改为#{1}或#{param2},这个方法将会被正常调用,但是很明显这样的代码可读性不高。

@Param 注解

  • 给参数配置@Param注解后,MyBatis就会自动将参数封装成Map类型,@Param注解值会作为Map中的key,因此在SQL部分就可以通过配置的注解值来使用参数。
  • 当只有一个参数的时候,可以不使用注解。因为在只有一个参数的情况下(除集合和数组外),MyBatis不关心这个参数叫什么名字就会直接把这个唯一的参数值拿来使用。
// 在接口中使用多个参数, 使用@Param注解
List<SysRole> selectRolesByUsrIdAndRoleEnabled(
			@Param("usrId")Long usrId, 
			@Param("enabled")Integer enabled);

参数为集合或者数组

这里都使用@Param注解提高代码的可读性,而不使用collection=“list” 或者 collection="array"

// UsrMapper.java
List\<SysUsr\> selectByIdList(@Param("idList")List\<Long\> idList);

<!-- UsrMapper.xml-->
<select id="selectByIdList" resultType="com.waichan.SysUsr">
	...
	from sys_usr
	where id in
	<foreach collection="idList" open="(" close=")" separator="," item="id" index="i">
		#{id}
	</foreach>
</select>

返回主键的值

/**
 * @param sysUsr
 * @return 返回执行SQL所影响的行数
 *
 **/
int insertSysUsr(SysUsr sysUsr);

使用JDBC方式返回主键自增的值

前提条件:数据库支持主键自增

  1. 返回单个主键
    useGenerateKeys 设置为true后,MyBatis会使用JDBC的getGenerateKeys方法来取出由数据库内部生成的主键。获得主键后将期赋值给keyProperty配置的id属性。

  2. 返回多个主键
    当需要设置多个属性时,使用逗号隔开,这种情况下通常还需要设置keyColumn属性,按顺序指定数据库的列,这里列的值会和keyProperty配置的属性一一对应。

<!-- 返回单个主键 -->
<insert id="insertSysUsr" useGeneratedKeys="true" keyProperty="id">
	insert into sys_usr(usr_name, head_img, create_time)
	values(#{usrName}, #{usrPassword}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>
<!-- 返回多个主键 -->
<insert id="insertSysUsr" useGeneratedKeys="true" keyProperty="id1, id2, id3" keyColumn="id_1, id_2, id_3">
	insert into sys_usr(usr_name, head_img, create_time)
	values(#{usrName}, #{usrPassword}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>

使用selectKey返回主键的值

使用<selectKey>标签来获取主键的值,这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库。需要注意的是每种数据库的实际写法存在差异,需要注意,下面是MYSQL数据库的写法。

<insert id="insertSysUsr">
	insert into sys_usr(usr_name, head_img, create_time)
	values(#{usrName}, #{usrPassword}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
	<selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">
		SELECT LAST_INSERT_ID()
	</selectKey>
</insert>

关联查询

只返回一张表(一个实体类)的数据

这是关联查询中最简单的一种情形,虽然设计多表查询但是返回结果只有同一张表或者说同一各实体类中的数据。例如:根据用户id获取用户角色,涉及sys_usr、sys_role 和 sys_usr_role三张表,但是结果只有角色sys_role的信息。

<!-- SysRoleMapper.xml -->
<select id="selectRolesByUsrId" resultType="com.waichan.SysRole">
	select
		r.id,
		r.role_name roleName
	from sys_usr u
	inner join sys_usr_role ur on ur.usr_id = u.id
	inner join sys_role r on r.id = ur.id
	where u.id = #{usrId}
</select>

返回多张表的关联查询数据

假设查询的结果不仅包含sys_role中的信息,还要包含当前用户的部分信息。

  1. 创建一个扩展对象来接收查询结果。
public class SysRoleExtend extends SysRole {
	private String usrName;
}
<!-- SysRoleExtendMapper.xml -->
<select id="selectRolesByUsrId" resultType="com.waichan.SysRoleExtend">
	select
		r.id,
		r.role_name roleName,
		u.usr_name usrName
	from sys_usr u
	inner join sys_usr_role ur on ur.usr_id = u.id
	inner join sys_role r on r.id = ur.id
	where u.id = #{usrId}
</select>

Note

推荐书籍

  1. 《Mybatis 从入门到精通》-------刘增辉著

Result Map 所包含的属性

  1. id: 必填,并且唯一。在select标签中,resultMap指定的值即为此处id所设置的值。
  2. type: 必填,用于配置查询列所映射到的Java对象类型。
  3. extends: 必填,可以配置当前的resultMap继承自其他的resultMap, 属性值为继承resultMap的id。
  4. autoMapping: 选填,可选值为true或false, 用于配置是否启用非映射自断(没有在resultMap中配置的自字段)的自动映射功能,该配置可以覆盖全局的autoMappingBehavior配置。

Result Map 包含的所有标签

  1. constructor: 配置使用构造方法注入结果,包含以下两个子标签。
    idArg: id参数,标记结果作为id(唯一值),可以帮助提高整体性能。
    arg: 注入到构造方法的一个普通结果
  2. id: 一个id结果,标记结果作为id(唯一值),可以帮助提高整体性能。
  3. reuslt: 注入到Java对象属性的普通结果。
    column: 从数据库中得到的列名,或者时列的别名。
    property: 映射到列结果的属性。可以映射简单的如usrName这样的属性,也可以映射一些复杂对象中的属性,例如address.street.number,这会通过.方式的属性嵌套赋值。
    javaType: 一个Java类的完全限定名,或一个类型别名(通过typeAlias配置或者默认的类型)。如果映射到一个JavaBean,MyBatis通常可以自动判断属性的类型。如果映射到HashMap,则需要明确地指定javaType属性。
    jdbcType: 列对应的数据库类型。JDBC类型仅仅需要对插入、更新、删除操作可能为空的列进行处理。这是JDBC jdbcType的需要,而不是MyBatis的需要。
    typeHandler: 使用这个属性可以覆盖默认的类型处理器。这个属性值是类的完全限定名或类型别名。
  4. association: 一个复杂的类型关联,许多结果将包成这种类型。
  5. collection: 复杂类型的集合。
  6. discriminator: 根据结果值来决定使用哪个结果映射。
  7. case: 基于某些值的结果映射。

标签包含的属性

  1. id: 命名空间中的唯一标识符,可用来代表这条语句。
  2. parameterType: 即将传入的语句参数的完全限定类名或别名。这个属性是可选的,因为MyBatis可以推断出传入语句的具体参数,因此不建议配置该属性。
  3. flushCache: 默认值为true, 任何时候只要语句被调用,都会清空一级缓存和二级缓存。
  4. timeout: 设置在抛出异常之前,驱动程序等待数据库返回结果的秒数。
  5. statementType: 对于STATEMENT、PREPARED、CALLABLE, MyBatis会分别使用对应的Statement、PreparedStatement、CallableStatement,默认值为PREPARED。
  6. useGenerateKeys: 默认值为false。如果设置为true, MyBatis会使用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键。
  7. keyProperty: MyBatis通过getGeneratedKeys获取主键值后将要赋值的属性名。如果希望得到多个数据库自动生成的列,属性值也可以是以逗号分隔的属性名称列表。
  8. keyColumn: 仅对INSERT 和 UPDATE 有用。通过生成的键值设置表中的列名,这个设置仅在某些数据库(如POSTGRESQL)中是必须的,当主键列不是表中的第一列时需要设置。如果希望得到多个生成的列,也可以时逗号分隔的属性名称列表。
  9. databaseId: 如果配置了databaseIdProvider,Mybatis会加载所有的不带databaseId的或匹配当前databaseId的语句。如果同时存在带databaseId和不带databaseId的语句,后者会被忽略。

resultType vs resultMap

  1. 使用resultType来设置返回结果的类型,需要在SQL中为所有列名和属性名不一致的列设置别名,通过设置别名使最终的查询结果列和resultType指定对象的属性名保持一致,进而实现自动映射。

动态SQL

  1. if
    test: 必填属性,符合OGNL要求的判度表达式,表达式的结果可以时true或false,除此之外所有的非0值都为true,只有0为false。
  2. choose (when、otherwise)
  3. trim (where、 set)
  4. foreach
    collection: 必填,值为要迭代循环的属性名。
    item: 变量名,值为从迭代对象中取出的每一个值。
    index: 索引的属性名,在集合数组情况下值为当前索引值,当迭代循环的对象时Map类型时,这个值为Map的key(键值)。
    open: 整个循环内容开头的字符串。
    close: 整个循环内容结尾的字符串。
    separator: 每次循环的分隔符。
  5. bind

OGNL用法

  1. e1 or e2
  2. e1 and e2
  3. e1 == e2 或 e1 eq e2
  4. e1 != e2 或 e1 neq e2
  5. e1 lt e2: 小于
  6. e1 lte e2: 小于等于,其他表示为gt(大于),gte (大于等于)
  7. e1 + e2、e1 * e2、e1/e2、e1 -e2、e1%e2
  8. !e 或 not e: 非,取反
  9. e.method(args): 调用对象方法
  10. e.property: 对象属性值
  11. e1[ e2 ]: 按索引取值(List、数组和Map)
  12. @class@method(args): 调用类的静态方法
  13. @class@field: 调用类的静态字段值

多个接口参数

  1. 给参数配置@Param注解后, Mybatis 就会自动将参数封装成Map类型,@Param注解值会作为Map中的key,因此在SQL部分就可以通过配置的注解值来使用参数。
  2. 当只有一个参数时可以不使用注解,是因为在这种情况下(除集合和数组外),Mybatis不关心这个参数叫什么名字就会直接把这个唯一的参数值拿来使用。

关联映射查询

在RBAC权限系统中还存在着一个用户拥有u多个角色、一个角色拥有多个权限这样复杂的嵌套关系。在面对这种关系的时候,我们可能要写多个方法分别查询这些数据,然后在组合到一起。这种处理方式特别适合用在大型系统上,由于分库分表,这种用法可以减少表之间的关联查询,方便系统进行扩展。

二级缓存试用场景

  1. 以查询为主的应用中,只有尽可能少的增、删、该操作。
  2. 绝大多数以单表操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据。
  3. 可以按业务划分对表进行分组时,如关联的表比较少,可以通过参照缓存进行配置。

类型映射

Type In JavaType In JDBC
byte[]BLOB
dateDATE
timeTIME
datetimeTIMESTAMP
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值