我爱编程MyBatis

MyBatis XML使用方式

2018-05-15  本文已影响0人  lihongyan

内容:


select用法

MyBatis的select用法,重点是resultMap、resultType的使用

先上一段XML中的代码:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
                    "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="tk.mybatis.simple.mapper.UserMapper">

    <resultMap id="userMap" type="tk.mybatis.simple.model.SysUser">
        <id property="id" column="id"/>
        <result property="userName" column="user_name"/>
        <result property="userPassword" column="user_password"/>
        <result property="userEmail" column="user_email"/>
        <result property="userInfo" column="user_info"/>
        <result property="headImg" column="head_img" jdbcType="BLOB"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
    </resultMap>
    
    <select id="selectById" resultMap="userMap">
        select user_name,
            user_email,
            user_info
        from sys_user 
        where id = #{id}
    </select>
</mapper>

需要注意的内容:

resultMap标签用于配置Java对象的属性和查询结果列的对应关系,通过resultMap中配置的column和property可以将查询列的值映射到对象的属性上。

resultMap标签包含的所有属性有:

resultMap包含的标签有:

id标签和result标签包含的属性值:

需要注意的内容:

<setting name="mapUnderscoreToCamelCase" value="true" />

constructor标签包含的子标签有:

constructor中的idArg、arg分别对应着resultMap中的id、result标签,它们的含义相同,只是注入方式不同

考虑一种情况:在使用resultType的前提下,假设有用户表、角色表、权限表

第一种简单的情形:根据用户id获取用户拥有的所有角色,返回的结果为角色集合,结果只有角色的信息,不包含额外的其他字段信息,这时直接使用resultType="SysRole"即可。

第二种情形:根据用户id获取用户拥有的所有角色,并且需要查询出用户名和用户邮件。

public class SysRoleExtend extends SysRole {
    private String userName;
    private String userEmail;
}

这样就可以将resultType设置为SysRoleExtend即可。

<select id="selectRolesByUserId" resultType="tk.mybatis.simple.model.SysRole">
    select 
        r.id, 
        r.role_name roleName, 
        r.enabled,
        r.create_by createBy,
        r.create_time createTime,
        u.user_name as "user.userName",
        u.user_email as "user.userEmail"
    from sys_user u
    inner join sys_user_role ur on u.id = ur.user_id
    inner join sys_role r on ur.role_id = r.id
    where u.id = #{userId}
</select>

user是SysRole中刚刚增加的属性,userName和userEmail是SysUser对象中的属性,通过这种方式可以直接将值赋给user字段中的属性


insert用法

insert的用法比较简单,只有让它返回生成的主键值时,由于不同数据库的主键生成方式不同,这种情况会有一些复杂。

insert标签包含的属性有:

简单的insert

上一段简单的XML代码:

<insert id="insert">
    insert into sys_user(
        user_name, user_password, user_email, 
        user_info, head_img, create_time)
    values(
        #{userName}, #{userPassword}, #{userEmail}, 
        #{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>

此处的<insert>中的SQL就是一个简单的INSERT语句,将所有的列都列举出来,在values中通过#{property}的方式参数中取出属性的值。为了防止类型错误,对于一些特殊的数据类型,建议制定具体的jdbcType值。例如,headImg制定BLOB类型,createTime制定TIMESTAMP类型。

返回主键

上述语句默认返回影响的行数,而不是生成的主键。如果想要返回生成的主键,可以使用如下的两种方法

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

这种方式适用于能够提供主键自增的数据库中,例如MySQL、SQL Server

直接上代码:

<insert id="insert2" useGeneratedKeys="true" keyProperty="id">
    insert into sys_user(
        user_name, user_password, 
        <if test="userEmail != null">
            <if test="userEmail != ''">
            user_email, 
            </if>
        </if>
        user_info, head_img, create_time)
    values(
        #{userName}, #{userPassword}, 
        <if test="userEmail != null">
            <if test="userEmail != ''">
            #{userEmail}, 
            </if>
        </if>
        #{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>

通过useGeneratedKeys和keyProperty可以实现自增的需求。useGeneratedKeys设置为true,MyBatis使用JDBC的getGeneratedKeys方法获取数据库生成的主键,并将结果赋值给keyProperty指定的对象属性值。当需要获取多个数据库自动生成的值时,使用逗号隔开keyProperty的属性值,并设置keyColumn属性,按顺序指定数据库的列,这里要求keyColumn中列的顺序与keyProperty中属性的顺序一一对应。

使用selectKey返回主键的值

对于不提供主键自增功能的数据库,我们需要先使用序列得到一个值,然后将这个值赋给id,再将数据插入数据库,MyBatis提供了<selectKey>标签来完成该需求。这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库。

mysql
<insert id="insert3">
    insert into sys_user(
        user_name, user_password, user_email, 
        user_info, head_img, create_time)
    values(
        #{userName}, #{userPassword}, #{userEmail}, 
        #{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
    <selectKey keyColumn="id" keyProperty="id" resultType="long" order="AFTER">
        SELECT LAST_INSERT_ID()
    </selectKey>
</insert>
oracle
<insert id="insertOracle">
    <selectKey keyColumn="id" keyProperty="id" resultType="long" order="BEFORE">
        SELECT SEQ_USER.nextval from dual
    </selectKey>
    insert into sys_user(
        id, user_name, user_password, user_email, 
        user_info, head_img, create_time)
    values(
        #{id}, #{userName}, #{userPassword}, #{userEmail}, 
        #{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>

下面列举一些在<selectKey>标签中常用的SQL


update用法

基本的update用法,比较复杂的update用法是结合<if>标签来更新不为null的值,以及使用<foreach>实现的批量更新,以上两种用法需要使用MyBatis的动态SQL。

<update id="updateById">
    update sys_user 
    set user_name = #{userName},
        user_password = #{userPassword},
        user_email = #{userEmail},
        user_info = #{userInfo},
        head_img = #{headImg, jdbcType=BLOB},
        create_time = #{createTime, jdbcType=TIMESTAMP}
    where id = #{id}
</update>

delete用法

<delete id="deleteById">
    delete from sys_user where id = #{id}
</delete>

对应的Mapper.java中的方法可以为:

deleteById(Long id);

// 或

deleteById(SysUser user);

多个接口参数

在实际应用中经常会遇到使用多个参数的情况。一种方法是,将多个参数合并到一个JavaBean中,并使用这个JavaBean作为接口方法的参数。这种方法使用起来很方便,但不适合全部的情况,因为不能只为了两三个参数去创建新的JavaBean。因此,对于参数比较少的情况,还有两种方式可以采用:使用Map类型作为参数;使用@Param注解。

使用Map类型作为参数的方法,就是在Map中通过key来映射XML中SQL使用的参数值名字,value用来存放参数值,需要多个参数时,通过Map的key-value方式传递参数值,由于这种方式还需要自己手动创建Map以及对参数进行赋值,其实并不简洁。所以在实际使用时,当有多个参数时,推荐使用@Param的方式。接下来先介绍不使用@Param注解时,使用多个参数的问题

<select id="selectByUser" 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 1 = 1
        and user_id = #{userId}
        and enabled = #{enabled}
</select>

对应的接口方法为:

SysUser selectByUser(Long userId, String enabled);

调用selectByUser方法时,MyBatis会抛出一下异常:

Parameter 'userId' not found
Available parameters are [0, 1, param1, param2]

这个错误标示,XML可用的参数只有0、1、param1、param2,没有userId。没有0和1,param1和param2都是MyBatis根据参数位置自定义的名字。这时如果将XML中的#{userId}改为#{0}或#{param1},将#{enabled}改为#{1}或#{param2},selectByUser方法就会被正常调用了,在实际使用时并不建议这么做。

接下来介绍使用@Param的情况

修改接口方法为:

SysUser selectByUser(@Param("userId")Long userId, @Param("enabled") String enabled);

这时的XML文件中对应的SQL的可用参数编程了[userId, enabled, param1, param2],如果把#{userId}变成#{param1},#{enabled}变成#{param2}方法也能正常执行。

实际上,给参数配置@Param注解后,MyBatis就会自动将参数封装成Map类型,@Param注解值会作为Map中的key,参数值作为Map中的value。

当只有一个参数的时候,MyBatis不关系这个参数叫什么名字就会直接把这个唯一的参数值拿来使用。

以上内容使用的多个参数都是基本类型或包装类型,当参数是对象类型时,情况略有不同,假设接口方法为

SysUser selectByUser(@Param("user")SysUser user, @Param("role") SysRole role);

则XML中也需要做对应的修改:

<select id="selectByUser" resultType="tk.mybatis.simple.model.SysUser">
    select id, 
        u.user_name userName, 
        u.user_password userPassword,
        u.user_email userEmail,
        u.user_info userInfo,
        u.head_img headImg,
        u.create_time createTime
    from sys_user u 
        inner join sys_user_role ur on u.user_id = ur.user_id
        inner join sys_role r on ur.role_id = r.role_id
    where 1 = 1
        and u.user_id = #{user.userId}
        and r.enabled = #{role.enabled}
</select>

当参数是对象是,就不能直接使用#{userId}和#{enabled}了,而是要通过点取值方式使用#{user.id}和#{role.enabled}从两个JavaBean中取出指定属性的值。


动态代理

为什么Mapper接口没有实现类却能被正常调用呢?这是因为MyBatis在Mapper接口上使用了动态代理的机制

假设存在如下Mapper接口:

public interface UserMapper{
    List<SysUser> selectAll();
}

并且存在如下关联的XML:

<select id="selectAll" 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
</select>

使用Java动态代理方式创建一个代理类:

public class MapperProxy<T> implements InvocationHandler {
    private Class<T> mapperInterface;
    
    private SqlSession sqlSession;
    
    public MapperProxy(Class<T> mapperInterface, SqlSession sqlSession){
        this.mapperInterface = mapperInterface;
        this.sqlSession = sqlSession;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Objectp[] args) throws Throwable{
        // 需要代理的接口中的方法
        String methodName = mapperInterface.getCanonicalName() + "." + method.getName();
        
        // 将接口中的方法代理到SqlSession中的对应方法
        List<T> list = sqlSession.selectList(methodName);
        
        // 返回结果
        return list;
    }
}

测试代码如下:

// 构建代理类
MapperProxy userMapperProxy = new MapperProxy(UserMapper.class, sqlSession);

// 获取UserMapper接口的代理类
UserMapper userMapper = (UserMapper)Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(),
        new Class[]{ UserMapper.class },
        userMapperProxy
    );
    
// 调用UserMapper接口中的selectAll()方法,实际上被代理到了SqlSession中的方法
List<SysUser> users = userMapper.selectAll();

上一篇 下一篇

猜你喜欢

热点阅读