Mybatis XML方式的基本用法

创建项目

  • 创建Maven项目
  • 添加Mybatis依赖
<!--mybatis-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.3</version>
</dependency>
  • 添加Log4j、JUnit和Mysql依赖
<!--mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
</dependency>

<!--测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

<!--日志-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.26</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

配置Mybatis

创建配置文件

在src/main/resources下创建mabatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <!--指定使用log4j输出日志-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>

    <typeAliases>
        <!--配置包的别名
           这样在使用类的时候, 不需要写包名部分,只写类型即可
        -->
        <package name="com._54programer.xml.domain"/>
    </typeAliases>

    <!--数据库配置-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="UNPOOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--方式1
            配置完整路径的映射配置文件-->
        <mapper resource="com/_54programer/xml/mapper/UserMapper.xml"/>
        <mapper resource="com/_54programer/xml/mapper/RoleMapper.xml"/>
        <!--方式2
            这种配置方式, 会先去查找com._54programer.xml.mapper包下所以的接口
            然后循环所有接口, 将接口com._54programer.xml.mapper.UserMapper转为还com/_54programer/xml/mapper/UserMapper.xml
            然后搜索xml资源, 搜到就解析xml
        -->
        <!-- 
        <package name="com._54programer.xml.mapper"/> -->
    </mappers>

</configuration>
创建数据库表、实体类和映射文件
  • 数据库表
CREATE TABLE `sys_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `user_name` varchar(50) DEFAULT NULL COMMENT '用户名',
  `user_password` varchar(50) DEFAULT NULL COMMENT '密码',
  `user_email` varchar(50) DEFAULT 'test@qq.com' COMMENT '邮箱',
  `user_info` text DEFAULT NULL COMMENT '简介',
  `head_img` blob DEFAULT NULL COMMENT '头像',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表' |
CREATE TABLE `sys_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  `role_name` varchar(50) DEFAULT NULL COMMENT '角色名',
  `enabled` int(11) DEFAULT NULL COMMENT '有效标志',
  `create_by` bigint(20) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色表' |
CREATE TABLE `sys_user_role` (
  `user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
  `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色关联表'
  • 实体类
/**
 * 用户表
 */
public class SysUser implements Serializable {
    /**
     * 用户ID
     * 不要使用基本数据类型
     * 原因: 如果使用long id, 那么id默认为0, 如果在动态语句中使用 id != null进行判断, 结果总会为true
     */
    private Long id;
    /**
     * 用户名
     */
    private String userName;
    /**
     * 密码
     */
    private String userPassword;
    /**
     * 邮箱
     */
    private String userEmail;
    /**
     * 简介
     */
    private String userInfo;
    /**
     * 头像
     * byte[] 这个类型一般对应数据库中的BLOB、LONGVARBINARY以及一些二进制流有关的字段类型
     */
    private byte[] headImg;
    /**
     * 创建时间
     */
    private Date createTime;

    省略getter、setter方法
}
/**
 * 角色表
 */
public class SysRole implements Serializable {
    /**
     * 角色ID
     */
    private Long id;
    /**
     * 角色名
     */
    private String roleName;
    /**
     * 有效标志
     */
    private Integer enabled;
    /**
     * 创建人
     */
    private String createBy;
    /**
     * 创建时间
     */
    private Date createTime;
    省略getter、setter方法
}
  • 映射文件

/resources/com/_54programer/xml/mapper/UserMapper.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="com._54programer.xml.mapper.UserMapper">

</mapper>

/resources/com/_54programer/xml/mapper/RoleMapper.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="com._54programer.xml.mapper.RoleMapper">

</mapper>
  • 配置Log4j

/resources/log4j.properties

# 全局配置
log4j.rootLogger=ERROR, stdout

# mybatis日志配置
# mybatis日志最低级别是TRACE,在这个日志级别下,会输出sql执行的详细信息,特别适合在开发时使用
log4j.logger.com._54programer.xml.mapper=TRACE

# 控制台输出配置
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

到这个我们的Mybatis环境就配置好了

使用Mybatis进行增删该查

Select用法
  • 根据ID查询用户信息
package com._54programer.xml.mapper;
import com._54programer.xml.domain.SysUser;

public interface UserMapper {
    SysUser selectById(Long id);
}
<?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">
<!--
    namespace: 命名空间, 通过命名空间关联对应接口
-->
<mapper namespace="com._54programer.xml.mapper.UserMapper">
    <!--
        resultMap: 用于配置java对象的属性和查询结果列的对应关系
        id: 跟select中resultMap对应
        type: java对象类型
        property: java对象属性
        column: 数据库列名
        jdbcType: 列对应的数据库类型
    -->
    <resultMap id="userMap" type="com._54programer.xml.domain.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: 唯一标示符,通过id关联接口中的方法
        resultMap: 设置返回值的类型和映射关系
        #{id}: mybatis中预编译参数的一种方式, id是参数名
    -->
    <select id="selectById" resultMap="userMap">
        select * from sys_user where id = #{id}
    </select>
</mapper>
package com._54programer.xml.mapper;

import com._54programer.xml.domain.SysUser;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;

import java.io.IOException;
import java.io.Reader;

public class UserMapperTest {

    private static SqlSessionFactory sqlSessionFactory;

    @BeforeClass
    public static void init(){
        try {
            //1.通过Resources工具类将mybatis-config.xml读入Reader
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            //2.创建SqlSessionFactory, 在创建SqlSessionFactory的过程中
            //首先解析mybatis-config.xml配置文件, 然后根据mappers配置读取全部的Mapper.xml进行具体的解析
            //解析完成后, 创建SqlSessionFactory就具有所有的属性配置和执行sql的信息
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void TestSelectById(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        SysUser sysUser = mapper.selectById(1L);
        sqlSession.close();
    }
}

运行输出:
DEBUG [main] - ==>  Preparing: select * from sys_user where id = ? 
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <==        Row: 1, admin, 123456, admin@qq.com, <<BLOB>>, <<BLOB>>, 2016-06-07 01:11:12.0
DEBUG [main] - <==      Total: 1
  • 查询全部用户信息
public interface UserMapper {
    List<SysUser> selectAll();
}
<?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="com._54programer.xml.mapper.UserMapper">

    <!--
        resultType: 返回值类型
    -->
    <select id="selectAll" resultType="com._54programer.xml.domain.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>

</mapper>
@Test
public void TestSelectAll(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<SysUser> sysUsers = mapper.selectAll();
    sqlSession.close();
}

输出:
DEBUG [main] - ==>  Preparing: select id, user_name userName, user_password userPassword, user_email userEmail, user_info userInfo, head_img headImg, create_time createTime from sys_user 
DEBUG [main] - ==> Parameters: 
TRACE [main] - <==    Columns: id, userName, userPassword, userEmail, userInfo, headImg, createTime
TRACE [main] - <==        Row: 1, admin, 123456, admin@qq.com, <<BLOB>>, <<BLOB>>, 2016-06-07 01:11:12.0
TRACE [main] - <==        Row: 1001, test, 123456, test@qq.com, <<BLOB>>, <<BLOB>>, 2016-06-07 00:00:00.0
DEBUG [main] - <==      Total: 2

Mybatis可以自动将以下画线方式命名的数据库列映射到Java对象的驼峰式命名属性中。所以我们还可以这样写

<select id="selectAll" resultType="com._54programer.xml.domain.SysUser">
    select id,
        user_name,
        user_password,
        user_email,
        user_info,
        head_img,
        create_time
        from sys_user
</select>
@Test
public void TestSelectAll(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<SysUser> sysUsers = mapper.selectAll();
    sqlSession.close();
}

输出:
DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_email, user_info, head_img, create_time from sys_user 
DEBUG [main] - ==> Parameters: 
TRACE [main] - <==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <==        Row: 1, admin, 123456, admin@qq.com, <<BLOB>>, <<BLOB>>, 2016-06-07 01:11:12.0
TRACE [main] - <==        Row: 1001, test, 123456, test@qq.com, <<BLOB>>, <<BLOB>>, 2016-06-07 00:00:00.0
DEBUG [main] - <==      Total: 2
  • 多表关联查询

方式1: 重新定义一个继承SysUser的扩展类SysUserExtend

public class SysUserExtend extends SysUser {
    private String roleName;

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}
public interface UserMapper {
    List<SysUserExtend> selectUserRole(Long id);
}
<select id="selectUserRole" resultType="com._54programer.xml.domain.SysUserExtend">
    SELECT
    u.id, u.user_email, u.user_info, u.user_name, u.user_password, u.head_img, u.create_time,
    r.role_name
    from sys_user u
    LEFT JOIN sys_user_role ur on u.id = ur.user_id
    LEFT JOIN sys_role r on r.id = ur.role_id
    WHERE u.id = #{id}
</select>
@Test
public void TestselectUserRole(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<SysUserExtend> sysUserExtends = mapper.selectUserRole(1L);
    sqlSession.close();
}

输出:
DEBUG [main] - ==>  Preparing: SELECT u.id, u.user_email, u.user_info, u.user_name, u.user_password, u.head_img, u.create_time, r.role_name from sys_user u LEFT JOIN sys_user_role ur on u.id = ur.user_id LEFT JOIN sys_role r on r.id = ur.role_id WHERE u.id = ? 
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <==    Columns: id, user_email, user_info, user_name, user_password, head_img, create_time, role_name
TRACE [main] - <==        Row: 1, admin@qq.com, <<BLOB>>, admin, 123456, <<BLOB>>, 2016-06-07 01:11:12.0, 管理员
TRACE [main] - <==        Row: 1, admin@qq.com, <<BLOB>>, admin, 123456, <<BLOB>>, 2016-06-07 01:11:12.0, 普通用户
DEBUG [main] - <==      Total: 2

方式2: 在SysUser中增加SysRole对象

public class SysUser implements Serializable {

    private Long id;
    private String userName;
    private String userPassword;
    private String userEmail;
    private String userInfo;
    private byte[] headImg;
    private Date createTime;

    //在SysUser中增加SysRole对象
    private SysRole role;

public interface UserMapper {
    List<SysUser> selectUserRole(Long id);
}
<select id="selectUserRole" resultType="com._54programer.xml.domain.SysUser">
    SELECT
    u.id, u.user_email, u.user_info, u.user_name, u.user_password, u.head_img, u.create_time,
    r.role_name as "role.roleName"
    from sys_user u
    LEFT JOIN sys_user_role ur on u.id = ur.user_id
    LEFT JOIN sys_role r on r.id = ur.role_id
    WHERE u.id = #{id}
</select>
@Test
public void TestselectUserRole(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.selectUserRole(1L);
    sqlSession.close();
}

输出:
DEBUG [main] - ==>  Preparing: SELECT u.id, u.user_email, u.user_info, u.user_name, u.user_password, u.head_img, u.create_time, r.role_name as "role.roleName" from sys_user u LEFT JOIN sys_user_role ur on u.id = ur.user_id LEFT JOIN sys_role r on r.id = ur.role_id WHERE u.id = ? 
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <==    Columns: id, user_email, user_info, user_name, user_password, head_img, create_time, role.roleName
TRACE [main] - <==        Row: 1, admin@qq.com, <<BLOB>>, admin, 123456, <<BLOB>>, 2016-06-07 01:11:12.0, 管理员
TRACE [main] - <==        Row: 1, admin@qq.com, <<BLOB>>, admin, 123456, <<BLOB>>, 2016-06-07 01:11:12.0, 普通用户
DEBUG [main] - <==      Total: 2
Insert
  • 插入一条数据
public interface UserMapper {
    int addUser(SysUser sysUser);
}
<insert id="addUser">
    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>
@Test
public void TestAddUser(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    SysUser sysUser = new SysUser();
    sysUser.setCreateTime(new Date());
    sysUser.setUserEmail("test1@qq.com");
    sysUser.setUserInfo("test1...");
    sysUser.setUserName("sn");
    sysUser.setUserPassword("123456");
    mapper.addUser(sysUser);
    sqlSession.commit();
    sqlSession.close();
}

输出:
DEBUG [main] - ==>  Preparing: insert into sys_user( id, user_name, user_password, user_email, user_info, head_img, create_time) values( ?, ?, ?, ?, ?, ?, ? ) 
DEBUG [main] - ==> Parameters: null, sn(String), 123456(String), test1@qq.com(String), test1...(String), null, 2019-11-19 13:41:03.726(Timestamp)
DEBUG [main] - <==    Updates: 1
  • 使用JDBC方式返回主键ID
    这种回写主键的方法只适用于支持主键自增的数据库。有些数据库(如 Oracle )不提供主键自增的功能。
public interface UserMapper {
    int addUserGetId(SysUser sysUser);
}
<!--
    useGeneratedKeys: 设为true, Mybatis会使用JDBC的getGeneratedKeys方法来获取数据库内部生成的主键
    获得主键后将其赋值给keyProperty配置的id属性
-->
<insert id="addUserGetId" useGeneratedKeys="true" keyProperty="id">
    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>
@Test
public void TestaddUserGetId(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    SysUser sysUser = new SysUser();
    sysUser.setCreateTime(new Date());
    sysUser.setUserEmail("test1@qq.com");
    sysUser.setUserInfo("test1...");
    sysUser.setUserName("sn");
    sysUser.setUserPassword("123456");
    mapper.addUserGetId(sysUser);
    //输出新增记录的ID
    System.out.println("主键ID: "+sysUser.getId());
    sqlSession.commit();
    sqlSession.close();
}
输出:
DEBUG [main] - ==>  Preparing: insert into sys_user( id, user_name, user_password, user_email, user_info, head_img, create_time) values( ?, ?, ?, ?, ?, ?, ? ) 
DEBUG [main] - ==> Parameters: null, sn(String), 123456(String), test1@qq.com(String), test1...(String), null, 2019-11-19 13:50:06.426(Timestamp)
DEBUG [main] - <==    Updates: 1
主键ID: 1037
  • 使用selectKey返回主键ID
    这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库。
<!--
    resultType: 返回值类型
    order: order属性的设置和使用的数据库有关, 在mysql数据库中, order属性值设置AFTER,
           因为当前记录的主键值在insert语句执行成功后才能获取, 在Oracle, order的值要
           设置为BEFORE, 因为Oracle中需要先从序列获取值然后再作为主键插入到数据库中
-->
<insert id="addUserGetId">
    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}
    )
    <selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">
        select last_insert_id()
    </selectKey>
</insert>
@Test
public void TestaddUserGetId(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    SysUser sysUser = new SysUser();
    sysUser.setCreateTime(new Date());
    sysUser.setUserEmail("test1@qq.com");
    sysUser.setUserInfo("test1...");
    sysUser.setUserName("sn");
    sysUser.setUserPassword("123456");
    mapper.addUserGetId(sysUser);
    //输出新增记录的ID
    System.out.println(sysUser.getId());
    sqlSession.commit();
    sqlSession.close();
}

输出:
DEBUG [main] - ==>  Preparing: insert into sys_user( id, user_name, user_password, user_email, user_info, head_img, create_time) values( ?, ?, ?, ?, ?, ?, ? ) 
DEBUG [main] - ==> Parameters: null, sn(String), 123456(String), test1@qq.com(String), test1...(String), null, 2019-11-19 14:00:07.414(Timestamp)
DEBUG [main] - <==    Updates: 1
DEBUG [main] - ==>  Preparing: select last_insert_id() 
DEBUG [main] - ==> Parameters: 
TRACE [main] - <==    Columns: last_insert_id()
TRACE [main] - <==        Row: 1038
DEBUG [main] - <==      Total: 1
1038
Update
public interface UserMapper {
    int updateUserById(SysUser sysUser);
}
<update id="updateUserById">
    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>
@Test
public void TestUpdateUserById(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    SysUser sysUser = new SysUser();
    sysUser.setId(1037L);
    sysUser.setCreateTime(new Date());
    sysUser.setUserEmail("test2@qq.com");
    sysUser.setUserInfo("test2...");
    sysUser.setUserName("sn");
    sysUser.setUserPassword("123456789");
    int result = mapper.updateUserById(sysUser);
    System.out.println(result);
    sqlSession.commit();
    sqlSession.close();
}
输出:
DEBUG [main] - ==>  Preparing: update sys_user set user_name = ?, user_password = ?, user_email = ?, user_info = ?, head_img = ?, create_time = ? where id = ? 
DEBUG [main] - ==> Parameters: sn(String), 123456789(String), test2@qq.com(String), test2...(String), null, 2019-11-19 14:13:50.226(Timestamp), 1037(Long)
DEBUG [main] - <==    Updates: 1
1
Delete
public interface UserMapper {
    int deleteUserById(Long id);
}
<update id="deleteUserById">
    delete from sys_user where id = #{id}
</update>
@Test
public void TestDeleteUserById(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int result = mapper.deleteUserById(1037L);
    System.out.println(result);
    sqlSession.commit();
    sqlSession.close();
}
输出:
DEBUG [main] - ==>  Preparing: delete from sys_user where id = ? 
DEBUG [main] - ==> Parameters: 1037(Long)
DEBUG [main] - <==    Updates: 1
1
多参数

我们尝试下传多个参数进行数据查询

public interface UserMapper {
    SysUser findUserByIdAndUsername(Long id, String user_name);
}
<select id="findUserByIdAndUsername" resultType="com._54programer.xml.domain.SysUser">
    select id,
        user_name,
        user_password,
        user_email,
        user_info,
        head_img,
        create_time
        from sys_user
        where id = #{id} and user_name = #{user_name}
</select>
@Test
public void TestFindByIdAndUsername(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    SysUser sysUser = mapper.findUserByIdAndUsername(1L, "admin");
    sqlSession.close();
}

运行:
报错了!!!
org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: 
Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]

怎么解决? 在参数钱添加@Param注解即可。

public interface UserMapper {
    SysUser findUserByIdAndUsername(@Param("id") Long id, @Param("user_name") String user_name);
}

再测试,这次成功了

DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_email, user_info, head_img, create_time from sys_user where id = ? and user_name = ? 
DEBUG [main] - ==> Parameters: 1(Long), admin(String)
TRACE [main] - <==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <==        Row: 1, admin, 123456, admin@qq.com, <<BLOB>>, <<BLOB>>, 2016-06-07 01:11:12.0
DEBUG [main] - <==      Total: 1

给参数配置@Param注解后,Mybatis就会自动将参数封装成Map类型,@param会作为Map中的key,因此sql就可以通过配置的注解值来使用参数。

Mybatis动态SQL

if
  • 查询条件
<!--
    test: 必须属性, 属性值是一个符合OGNL要求的判断表达式
    userName != null : 适用于任何类型, 用于判断属性值是否为空
    userName != '' : 仅适用于字符串类型, 用于判断是否为空字符串
    and or : 相当于 && ||
-->
<select id="selectByUser" resultType="com._54programer.xml.domain.SysUser">
    select id,
        user_name,
        user_password,
        user_email,
        user_info,
        head_img,
        create_time
        from sys_user
        where 1=1
        <if test="userName != null and userName != ''">
            and user_name like concat('%', #{userName}, '%')
        </if>
        <if test="userEmail != null and userEmail != ''">
            and user_email = #{userEmail}
        </if>
</select>
  • 更新字段
<!--
    每个if标签的sql语句后面都有一个逗号
    如果全部为null或'', 就会报错, 所以在最后拼接了一个id = #{id}
-->
<update id="updateUserById">
    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}
    where id = #{id}
</update>
choose
<!--
    choose when otherwise
    可以让逻辑更为严密
    加入userName=null或'', 会查询出所有用户的信息
-->
<select id="selectByUser" resultType="com._54programer.xml.domain.SysUser">
    select id,
    user_name,
    user_password,
    user_email,
    user_info,
    head_img,
    create_time
    from sys_user
    where 1=1
    <choose>
        <when test="userName != null and userName != ''">
            and user_name = #{userName}
        </when>
        <otherwise>
            and 1=2
        </otherwise>
    </choose>
</select>
where
<!--
    where里都不成立, 不会拼接where 1=1
    where里有条件成立, 会自动拼接where 1=1
-->
<select id="selectByUser" resultType="com._54programer.xml.domain.SysUser">
    select id,
    user_name,
    user_password,
    user_email,
    user_info,
    head_img,
    create_time
    from sys_user
    <where>
        <if test="userName != null and userName != ''">
            and user_name like concat('%', #{userName}, '%')
        </if>
        <if test="userEmail != null and userEmail != ''">
            and user_email = #{userEmail}
        </if>
    </where>
</select>
foreach
  • in查询
<!--
    collection: 循环迭代的属性名
    item: 变量名,值为从法代对象中取出的每一个值。
    index: 索引的属性名
    open: 整个循环内容开头的字符串
    close: 整个循环内容结尾的字符串
    separator: 每次循环的分隔符
-->
<select id="selectByID" resultType="com._54programer.xml.domain.SysUser">
    select id,
        user_name,
        user_password,
        user_email,
        user_info,
        head_img,
        create_time
        from sys_user
        where id in
        <foreach collection="list" open="(" close=")" separator="," item="id" index="i">
            #{id}
        </foreach>
</select>
  • 批量插入
<insert id="addUser">
    insert into sys_user(
        id, user_name, user_password, user_email, user_info, head_img, create_time)
    values(
    <foreach collection="list" item="user" separator=",">
        #{user.id}, #{user.userName}, #{user.userPassword}, #{user.userEmail},
        #{user.userInfo}, #{user.headImg, jdbcType=BLOB}, #{user.createTime, jdbcType=TIMESTAMP}
    </foreach>
    )
</insert>
  • 动态更新
<!--
    MyBati在内部的上下文中默认值_parameter
-->
<update id="updateUserById">
    update sys_user
    set
    <foreach collection="_parameter" item="val" index="key" separator=",">
        ${key} = #{val}
    </foreach>
    where id = #{id}
</update>

Mapper接口动态代理实现原理

我们可能会有一个疑问,为什么Mapper接口没有实现类却能被正常使用?
这是因为Mybatis在Mapper接口上使用了动态代理。

这是我们刚才定义的一个Mapper接口:

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

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

public class MyMapperProxy<T> implements InvocationHandler {

    private Class<T> mapperInterface;
    private SqlSession sqlSession;

    public MyMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
        this.mapperInterface = mapperInterface;
        this.sqlSession = sqlSession;
    }

    /**
     * 这里只是简单实现
     * 不考虑接口方法中的参数和方法的返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        List<T> list = sqlSession.selectList(mapperInterface.getCanonicalName() + "." + method.getName());
        return list;
    }

}
@Test
public void Test1(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    MyMapperProxy userMapperProxy = new MyMapperProxy(UserMapper.class, sqlSession);
    UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{UserMapper.class}, userMapperProxy);
    userMapper.selectAll();
    sqlSession.close();
}

输出:
DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_email, user_info, head_img, create_time from sys_user 
DEBUG [main] - ==> Parameters: 
TRACE [main] - <==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <==        Row: 1, admin, 123456, admin@qq.com, <<BLOB>>, <<BLOB>>, 2016-06-07 01:11:12.0
TRACE [main] - <==        Row: 1001, test, 123456, test@qq.com, <<BLOB>>, <<BLOB>>, 2016-06-07 00:00:00.0
TRACE [main] - <==        Row: 1036, sn, 123456, test1@qq.com, <<BLOB>>, <<BLOB>>, 2019-11-19 13:41:03.0
TRACE [main] - <==        Row: 1038, sn, 123456, test1@qq.com, <<BLOB>>, <<BLOB>>, 2019-11-19 14:00:07.0
DEBUG [main] - <==      Total: 4

从代理类中可以看到,当调用一个接口的方法时,会先通过接口的全限定名称和当前调用的方法名的组合得到这个方法id ,这个 id 的值就是映射 XML中namespace 和具体方法 id的组合。所以可以在代理方法中使用 sqlSession 以命名空间的方式调用方法。通过这种方式可以将接口和 XML 文件中的方法关联起来。这种代理方式和常规代理的不同之处在于,这里没有对某个具体类进行代理,而是通过代理转化成了对其他代码的调用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容