MyBatis从入门到精通(第2章):MyBatis XML方式的基本用法【insert用法、update用法、delete用法】

2.4  insert 用法

2.4.1  简单的 insert方法

在接口 UserMapper.java 中添加如下方法。

    /**
     * 新增用户
     * @param sysUser
     * @return
     */
    int insert(SysUser sysUser);

然后打开对应的 UserMapper.xml 文件,添加如下代码。

    <insert id="insert">
        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>
View Code

先看 <insert> 元素,这个标签包含如下属性

· id:命名空间中的唯一标识符,可用来代表这条语句

· parameterType :即将传入的语句参数的完全限定类名或别名这个属性是可选的,因为 MyBatis 可以推断出传入语句的具体参数,因此不建议配置该属性

· flushCache 默认值为 true,任何时候只要语句被调用,都会清空一级缓存和二级缓存

· timeout:设置在抛出异常之前,驱动程序等待数据库返回请求结果的秒数

· statementType :对于 STATEMENT 、PREPARED、CALLABLE,MyBatis 会分别使用对应的 Statement PreparedStatement CallableStatement ,默认值为 PREPARED

· useGeneratedKeys :默认值为 false如果设置为 true,MyBatis 会使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键

· keyProperty :MyBatis 通过 getGeneratedKeys 获取主键值后将要赋值的属性名。如果希望得到多个数据库自动生成的列,属性值也可以是以逗号分隔的属性名称列表

· keyColumn :仅对 INSERT 和 UPDATE 有用。通过生成的键值设置表中的列名,这个设置仅在某些数据库(如 PostgreSQL )中是必须的当主键列不是表中的第一列时需要设置如果希望得到多个生成的列,也可以是逗号分隔

· databaseId :如果配置了 databaseIdProvider (4.6 节有详细配置方法,MyBatis 会加载所有的不带 databaseId 的或匹配当前 databaseId 的语句。如果同时存在带 databaseId 和不带 databaseId 的语句,后者会被忽略

在values中通过  #{ property } 方式从(实体类对象的)参数中取出属性的值。为了防止类型错误,对于一些特殊的数据类型,建议指定具体的 jdbcType 值。例如 headImg 指定 BLOB 类型, createTime 指定 TIMESTAMP 类型

特别说明:

  1 )BLOB对应的类型是 ByteArrayInputStream,就是二进制数据流。

  2 )由于数据库区分date、time、datetime类型,但是在Java中一般都使用java.util.Date类型。因此为了保证数据类型的正确,需要手动指定日期类型。date、time、datetime对应的JDBC类型分别为DATE、TIME、TIMESTAMP。   

 BaseMapperTest

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

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;

/**
 * 基础测试类
 */

public class BaseMapperTest {
    private static SqlSessionFactory sqlSessionFactory;

    @BeforeClass
    public static void init()  {
        try {
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            reader.close();
        } catch (IOException ignore) {
            ignore.printStackTrace();
        }
    }

    public SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();

    }
}
View Code 基础测试类

 现在在 UserMapperTest 测试类中增加一个方法来测试这个 insert 方法,代码如下。现在在 UserMapperTest 测试类中增加一个方法来测试这个 insert 方法,代码如下

    @Test
    public void testInsert(){
        SqlSession sqlSession = getSqlSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //创建一个 user 对象
            SysUser user = new SysUser();
            user.setUserName("test1");
            user.setUserPassword("123456");
            user.setUserEmail("test@mybatis.tk");
            user.setUserInfo("test info");
            //正常情况下应该读入一张图片存到 byte 数组中
            user.setHeadImg(new byte[]{1,2,3});
            user.setCreateTime(new Date());
            //将新建的对象插入数据库中,特别注意,这里的返回值 result 是执行的 SQL 影响的行数
            int result = userMapper.insert(user);
            //只插入 1 条数据
            Assert.assertEquals(1, result);
            //id 为 null,我们没有给 id 赋值,并且没有配置回写 id 的值
            Assert.assertNull(user.getId()); //引用数据类型未赋值默认为null
        } finally {
            //为了不影响数据库中的数据导致其他测试失败,这里选择回滚
            //由于默认的 sqlSessionFactory.openSession() 是不自动提交的,
            //因此不手动执行 commit 也不会提交到数据库
            sqlSession.rollback();
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }

    }
public void testInsert()  

 

  数据库的datatime类型可以存储DATE(时间部分默认为00:00:00)和 TIMESTAMP两种类型的时间。

  上面接口中对应的方法   int  insert( SysUser sysUser)   ,这个int类型返回值是执行的SQL影响的行数,这个值和日志中的 Updatas:1 是一致的。

 


如何获得主键的值呢?下面提供两种方法,基本上可以涵盖所有数据库的不同情况

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

在使用主键自增(如 MySQL SQL Server 数据库)时,插入数据库后可能需要得到自增的主键值,然后使用这个值进行一些其他的操作

现在添加一个insert2方法,首先在UserMapper接口中增加insert2方法。

    /**
     * 新增用户 - 使用 useGeneratedKeys 方式
     *
     * @param sysUser
     * @return
     */
    int insert2(SysUser sysUser);

然后在XML映射文件中新增一个 insert2 方法。

    <insert id="insert2" useGeneratedKeys="true" keyProperty="id">
        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>
useGeneratedKeys="true" 后,当需要设置多个属性值时,使用逗号隔开,还需要设置 keyColumn属性,按顺序指定数据库的列 ,这里列的值会和 keyProperty配置的属性一一对应。
由于要使用数据库返回的主键值,所以 SQL 上下两部分的列中去掉了 id 列和对应的#{id}属性


下面写一个测试,验证是否返回了SysUser的主键值,在测试类 UserMapperTest中添加如下代码。
    @Test
    public void testInsert2(){
        SqlSession sqlSession = getSqlSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //创建一个 user 对象
            SysUser user = new SysUser();
            user.setUserName("test1");
            user.setUserPassword("123456");
            user.setUserEmail("test@mybatis.tk");
            user.setUserInfo("test info");
            user.setCreateTime(new Date());
            int result = userMapper.insert2(user);
            //只插入1条数据
            Assert.assertEquals(1,result);
            //因为id回写,所以id不为null
            Assert.assertNotNull(user.getId());
        } finally {
            sqlSession.rollback();
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }
    }
public void testInsert2()

 

2.4.3 使用 selectKey 返回主键的值 

有些数据库(如 Oracle)不提供主键自增的功能,而是使用序列得到一个值,然后将这个值赋给 id,再将数据插入数据库。对于这种情况,可以采用另外一种方式:使用 <selectKey> 标签来获取主键的值,这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库

 先来看一下MySQL的例子。

 在接口和XML中再新增一个insert3方法, UserMapper接口的代码如下。

    /**
     * 新增用户 - 使用 selectKey 方式
     * 
     * @param sysUser
     * @return
     */
    int insert3(SysUser sysUser);

不同之处在 UserMapper.xml 文件中的代码:新添加了<selectKey>标签。

    <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" resultType="long" keyProperty="id" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
    </insert>
View Code

注意看下面这段代码,和 insert2 相比增加了 selectKey 标签

 

 

这里的 resultType 用于设置返回值类型。order 属性的设置和使用的数据库有关。在 MySQL 数据库中,order 属性设置的值是 AFTER,因为当前记录的主键值在 insert 语句执行成功后才能获取到

而在 Oracle 数据库中,order 的值要设置为 BEFORE,这是因为 Oracle 中需要先从序列获取值,然后将值作为主键插入到数据库中

selectKey元素放置的上、下位置不会影响最终执行的结果。这么写仅仅是为了符合实际的执行顺序,看起来更直观而已

在 Oracle 示例中 SELECT SEQ _ ID.nextval from dual 是一个获取序列的 SQL 语句

 · DB2 使用 VALUES IDENTITY _VAL_LOCAL()。

· MYSQL 使用 SELECT LAST _INSERT_ID()。

· SQLSERVER 使用 SELECT SCOPE _IDENTITY()。

· CLOUDSCAPE 使用 VALUES IDENTITY _VAL_LOCAL()。

· DERBY 使用 VALUES IDENTITY _VAL_LOCAL()。

· HSQLDB 使用 CALL IDENTITY ()。

· SYBASE 使用 SELECT@@IDENTITY 。

· DB2_MF 使用 SELECT IDENTITY _VAL_LOCAL() FROM SYSIBM.SYSDUMMY 1。

· INFORMIX 使用 select dbinfo ( 'sqlca.sqlerrd 1') from systables where tabid= 1。 
以下是其他一些支持主键自增的数据库配置 selectKey 中回写主键的 SQL。

 


 

2.5 update 用法 

先来看一个简单的通过主键更新数据的 update 方法的例子。在 UserMapper 接口中添加以下方法

1     /**
2     * 根据主键更新
3     *
4     * @param sysUser
5     * @return
6     * */
7     int updateById(SysUser sysUser);  //这里的参数 sysUser是要更新的数据
在接口对应的 UserMapper.xml中添加如下代码。
    <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>

这个方法的 SQL 很简单,下面写一个简单的测试来验证一下。在 UserMapperTest 中添加如下代码

@Test
    public void testUpdateById(){
        SqlSession sqlSession = getSqlSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //从数据库查询 1 个 user 对象
            SysUser user = userMapper.selectById(1L);
            //当前 userName 为 admin
            Assert.assertEquals("admin", user.getUserName());
            //修改用户名
            user.setUserName("admin_test");
            //修改邮箱
            user.setUserEmail("test@mybatis.tk");
            //更新数据,特别注意,这里的返回值 result 是执行的 SQL 影响的行数
            int result = userMapper.updateById(user);
            //只更新 1 条数据
            Assert.assertEquals(1, result);
            //根据当前 id 查询修改后的数据
            user = userMapper.selectById(1L);
            //修改后的名字 admin_test
            Assert.assertEquals("admin_test", user.getUserName());
        } finally {
            //为了不影响数据库中的数据导致其他测试失败,这里选择回滚
            //由于默认的 sqlSessionFactory.openSession() 是不自动提交的,
            //因此不手动执行 commit 也不会提交到数据库
            sqlSession.rollback();
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }
    }
public void testUpdateById()

 还可以通过修改 UPDATE 语句中的 WHERE 条件来更新一条或一批数据。基本的 update 用法就这么简单,更复杂的情况在后面的动态 SQL 章节中会进行讲解

 


 

2.6  delete用法

delete 同 update 类似,下面也用一个简单的例子说明。在 UserMapper 中添加一个简单的例子,代码如下

    /**
     * 通过主键删除
     *
     * @param id
     * @return
     */
    int deleteById(Long id);

根据主键删除数据的时候,如果主键只有一个字段,那么就可以和这个方法一样使用一个参数id ,这个方法对应UserMapper.xml中的代码如下。

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

注意接口中 int deleteById (Long id;方法的参数类型为 Long id,如果将参数类型修改如下,也是正确的

 

对于以上的接口,在 UserMapperTest中编写一个测试方法,代码如下。

    @Test
    public void testDeleteById(){
        SqlSession sqlSession = getSqlSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //从数据库查询 1 个 user 对象,根据 id = 1 查询
            SysUser user1 = userMapper.selectById( 1L);
            //现在还能查询出 user 对象
            Assert.assertNotNull(user1);
            //调用方法删除
            Assert.assertEquals(1, userMapper.deleteById(user1));
            //再次查询,这时应该没有值,为 null
            Assert.assertNull(userMapper.selectById(1L));

/*            //使用 SysUser 参数再做一遍测试,根据 id = 1001  查询
            SysUser user2 = userMapper.selectById(1001L);
            //现在还能查询出 user 对象
            Assert.assertNotNull(user2);
            //调用方法删除,注意这里使用参数为 user2
            Assert.assertEquals(1, userMapper.deleteById(user2));
            //再次查询,这时应该没有值,为 null
            Assert.assertNull(userMapper.selectById(1001L));
            //使用 SysUser 参数再做一遍测试*/
        } finally {
            //为了不影响数据库中的数据导致其他测试失败,这里选择回滚
            //由于默认的 sqlSessionFactory.openSession() 是不自动提交的,
            //因此不手动执行 commit 也不会提交到数据库
            sqlSession.rollback();
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }
    }
View Code

下面给出deleteById方法输出的操作日志。

 

以上是一个简单的 delete 方法示例,在动态 SQL 章节中还会介绍一些更复杂的用法


 

2.7 多个接口参数的用法

上述CURD案例中的方法的参数只有一个:一种是基本类型,另一种是JavaBean。

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

现在,在接口方法的参数前添加 @Param注解,代码如下。

    /**
     * 根据用户 id 和 角色的 enabled 状态获取用户的角色
     *
     * @param userId
     * @param enabled
     * @return
     */
    List<SysRole> selectRolesByUserIdAndRoleEnabled(
                @Param("userId")Long userId,
                @Param("enabled")Integer enabled   );

 

这个接口方法对应的 UserMapper.xml中的代码如下。

    <select id="selectRolesByUserIdAndRoleEnabled" resultType="SysRole">
        select
            r.id,
            r.role_name roleName,
            r.enabled,
            r.create_by createBy,
            r.create_time createTime
        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} and r.enabled = #{enabled}
    </select>

在 UserMapperTest 中添加如下代码进行测试。

    @Test
    public void testSelectRolesByUserIdAndRoleEnabled(){
        SqlSession sqlSession = getSqlSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //调用 selectRolesByUserIdAndRoleEnabled 方法查询用户的角色
            List<SysRole> roleList = userMapper.selectRolesByUserIdAndRoleEnabled(1L, null);
            //结果不为空
            Assert.assertNotNull(roleList);
            //角色数量大于 0 个
            Assert.assertTrue(roleList.size() > 0);
        } finally {
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }
    }
View Code Test

    /**
     * 根据用户 id 和 角色的 enabled 状态获取用户的角色
     *
     * @param user
     * @param role
     * @return
     */
    List<SysRole> selectRolesByUserIdAndRoleEnabledBean(
                @Param("user") SysUser user,
                @Param("role") SysRole role            );
    <select id="selectRolesByUserIdAndRoleEnabledBean" resultType="SysRole">
        select
            r.id,
            r.role_name roleName,
            r.enabled,
            r.create_by createBy,
            r.create_time createTime
        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 = #{user.id} and r.enabled = #{role.enabled}
    </select>
select id="selectRolesByUserIdAndRoleEnabledBean

 

    @Test
    public void testSelectRolesByUserIdAndRoleEnabledBean(){
        SqlSession sqlSession = getSqlSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //调用 selectRolesByUserIdAndRoleEnabled 方法查询用户的角色
            SysUser user = new SysUser();
            user.setId(1L);
            SysRole role = new SysRole();
            role.setEnabled(1);
            List<SysRole> roleList = userMapper.selectRolesByUserIdAndRoleEnabledBean(user, role);
            //结果不为空
            Assert.assertNotNull(roleList);
            //角色数量大于 0 个
            Assert.assertTrue(roleList.size() > 0);
        } finally {
            //不要忘记关闭 sqlSession
            sqlSession.close();
        }
    }
public void testSelectRolesByUserIdAndRoleEnabledBean

=================================================================================================

end

posted @ 2019-07-28 15:40  Marlon康  阅读(505)  评论(0编辑  收藏  举报