1. insert用法

1.1 简单的insert方法

假如现在我们想新增一个用户,该如何操作呢?

首先,在接口SysUserMapper中添加如下方法。

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

然后打开对应的SysUserMapper.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>

特别说明:

1)为了防止类型错误,对于一些特殊的数据类型,建议指定具体的jdbcType值。例如headImg指定BLOB类型,createTime指定TIMESTAMP类型。

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

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

在SysUserMapperTest测试类中添加如下代码,测试下insert()方法。

@Test
public void testInsert() {
SqlSession sqlSession = getSqlSession(); try {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class); SysUser sysUser = new SysUser();
sysUser.setUserName("test1");
sysUser.setUserPassword("123456");
sysUser.setUserEmail("test@mybatis.tk");
sysUser.setUserInfo("test info");
// 正常情况下应该读入一张图片保存到byte数组中
sysUser.setHeadImg(new byte[]{1, 2, 3});
sysUser.setCreateTime(new Date()); // 这里的返回值result是执行的SQL影响的行数
int result = sysUserMapper.insert(sysUser);
// 只插入1条数据
Assert.assertEquals(1, result);
// id为null,没有给id赋值,并且没有配置回写id的值
Assert.assertNull(sysUser.getId());
} finally {
// 为了不影响其他测试,这里选择回滚
// 默认的sqlSessionFactory.openSession()是不自动提交的
// 因此不手动执行commit也不会提交到数据库
sqlSession.rollback();
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, test1(String), 123456(String), test@mybatis.tk(String), test info(String), java.io.ByteArrayInputStream@544a2ea6(ByteArrayInputStream), 2019-07-02 13:09:07.822(Timestamp)

DEBUG [main] - <== Updates: 1

现在我们修改下createTime指定的jdbcType类型,直观的理解下jdbcType值的作用。

createTime,jdbcType=DATE

再次运行测试方法,日志中createTime字段的值如下。

2019-07-02(Date)

再次修改createTime指定的jdbcType类型为TIME。

createTime,jdbcType=TIME

再次运行测试方法,发现报如下错误:

报错的原因是,数据库中的字段类型为datetime,但是这里只有time部分的值。

通过上面的测试,说明数据库的datetime类型可以存储DATE(时间部分默认为00:00:00)和TIMESTAMP这两种类型的时间,不能存储TIME类型的时间。

1.2 返回主键值(JDBC方式)

在1.1的例子中,新增完数据,我们并没有拿到数据库中自增的id值,但有些场景中,我们需要先拿到数据库中自增的值,然后再处理其余的逻辑,那么如何拿到数据库中的自增的id值呢?

首先,在接口SysUserMapper中添加方法如下。

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

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

<insert id="insertUseGeneratedKeys" 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设置为ture后,MyBatis会使用JDBC的getGeneratedKeys()方法来取出由数据库内部生成的主键。获取到主键后将其赋值给keyProperty配置的id属性。

在SysUserMapperTest测试类中添加如下代码,测试新增的insertUseGeneratedKeys()方法。

@Test
public void testInsertUseGeneratedKeys() {
SqlSession sqlSession = getSqlSession(); try {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class); SysUser sysUser = new SysUser();
sysUser.setUserName("test1");
sysUser.setUserPassword("123456");
sysUser.setUserEmail("test@mybatis.tk");
sysUser.setUserInfo("test info");
// 正常情况下应该读入一张图片保存到byte数组中
sysUser.setHeadImg(new byte[]{1, 2, 3});
sysUser.setCreateTime(new Date()); // 这里的返回值result是执行的SQL影响的行数
int result = sysUserMapper.insertUseGeneratedKeys(sysUser);
// 只插入1条数据
Assert.assertEquals(1, result);
// 因为id回写,所以id不为null
Assert.assertNotNull(sysUser.getId());
} finally {
sqlSession.rollback();
sqlSession.close();
}
}

运行该测试方法,测试通过,输出日志如下。

DEBUG [main] - ==> Preparing: INSERT INTO sys_user(user_name, user_password, user_email, user_info, head_img, create_time) VALUES (?,?,?,?,?,?)

DEBUG [main] - ==> Parameters: test1(String), 123456(String), test@mybatis.tk(String), test info(String), java.io.ByteArrayInputStream@544a2ea6(ByteArrayInputStream), 2019-07-02 14:02:22.506(Timestamp)

DEBUG [main] - <== Updates: 1

1.3 返回主键值(selectKey方式)

1.2中回写主键的方法只适用于支持主键自增的数据库。

但有些数据库(比如Oracle)不提供主键自增的功能,而是使用序列得到一个值,然后将这个值赋给id,再将数据插入到数据库。

对于这种情况,就可以采用selectKey方式,因为selectKey方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库。

我们先来看下MySql的例子。

首先,在接口SysUserMapper中添加如下方法。

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

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

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

和1.2相比,这里的语句多了selectKey标签,其中:

  • keyColumn:主键的数据库列名。
  • resultType:返回值类型。
  • keyProperty:主键对应的属性名。
  • order:该属性的设置和使用的数据库有关,如果使用的是MySql数据库,设置的值是AFTER,因为当前记录的主键值在insert语句执行成功后才能获取到。如果使用的是Oracle数据库,设置的值是BEFORE,因为Oracle中需要先从序列获取值,然后将值作为主键插入到数据库中。

如果数据库是Oracle的话,语句如下(因为环境问题,以下代码我并未验证,有兴趣的同学可以自己试下)。

<insert id="insertUseSelectKey">
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="BEFORE">
SELECT SEQ_ID.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>

2. update用法

假如我们现在希望通过主键id来更新用户信息,该如何操作呢?

首先,在接口SysUserMapper中添加如下方法。

/**
* 根据主键更新
*
* @param sysUser
* @return
*/
int updateById(SysUser sysUser);

然后,打开对应的SysUserMapper.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>

最后在SysUserMapperTest测试类中,添加如下测试方法。

@Test
public void testUpdateById() {
SqlSession sqlSession = getSqlSession(); try {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
SysUser sysUser = sysUserMapper.selectById(1L); Assert.assertEquals("admin", sysUser.getUserName()); sysUser.setUserName("admin_test");
sysUser.setUserEmail("admin_test@mybatis.tk");
sysUser.setUserInfo("test info");
// 正常情况下应该读入一张图片保存到byte数组中
sysUser.setHeadImg(new byte[]{1, 2, 3});
sysUser.setCreateTime(new Date()); // 这里的返回值result是执行的SQL影响的行数
int result = sysUserMapper.updateById(sysUser);
// 只更新1条数据
Assert.assertEquals(1, result); sysUser = sysUserMapper.selectById(1L);
Assert.assertEquals("admin_test", sysUser.getUserName());
Assert.assertEquals("admin_test@mybatis.tk", sysUser.getUserEmail());
} finally {
sqlSession.rollback();
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: admin_test(String), 123456(String), admin_test@mybatis.tk(String), test info(String), java.io.ByteArrayInputStream@78186a70(ByteArrayInputStream), 2019-07-02 14:57:34.792(Timestamp), 1(Long)

DEBUG [main] - <== Updates: 1

3. delete用法

假如我们现在希望通过主键id来删除用户信息,该如何操作呢?

首先,在接口SysUserMapper中添加如下方法。

/**
* 根据主键删除
*
* @param id
* @return
*/
int deleteById(Long id); /**
* 根据对象的主键删除
*
* @param sysUser
* @return
*/
int deleteBySysUser(SysUser sysUser);

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

<delete id="deleteById">
DELETE FROM sys_user WHERE id = #{id}
</delete>
<delete id="deleteBySysUser">
DELETE FROM sys_user WHERE id = #{id}
</delete>

最后在SysUserMapperTest测试类中,添加如下测试方法。

@Test
public void testDeleteById() {
SqlSession sqlSession = getSqlSession(); try {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
SysUser sysUser = sysUserMapper.selectById(1L);
Assert.assertNotNull(sysUser); // 这里是直接根据id删除
int result = sysUserMapper.deleteById(1L);
// 只删除1条数据
Assert.assertEquals(1, result); Assert.assertNull(sysUserMapper.selectById(1L)); SysUser sysUser2 = sysUserMapper.selectById(1001L);
Assert.assertNotNull(sysUser2); // 这里是根据对象的id属性删除
Assert.assertEquals(1, sysUserMapper.deleteBySysUser(sysUser2)); Assert.assertNull(sysUserMapper.selectById(1001L));
} finally {
sqlSession.rollback();
sqlSession.close();
}
}

运行测试方法,测试通过,输出的部分日志如下。

DEBUG [main] - ==> Preparing: DELETE FROM sys_user WHERE id = ?

DEBUG [main] - ==> Parameters: 1(Long)

DEBUG [main] - <== Updates: 1

4. 源码及参考

源码地址:https://github.com/zwwhnly/mybatis-action.git,欢迎下载。

刘增辉《MyBatis从入门到精通》

原创不易,如果觉得文章能学到东西的话,欢迎点个赞、评个论、关个注,这是我坚持写作的最大动力。

如果有兴趣,欢迎添加我的微信:zwwhnly,等你来聊技术、职场、工作等话题(PS:我是一名奋斗在上海的程序员)。

MyBatis从入门到精通(四):MyBatis XML方式的基本用法之增删改的更多相关文章

  1. MyBatis从入门到精通:使用XML方式(映射文件之类的)

    2.3节笔记部分: package tk.mybatis.simple; public class Temp { } /* 2.2 使用XML方式 MyBatis使用了Java的动态代理可以直接通过接 ...

  2. MyBatis从入门到精通(二):MyBatis XML方式的基本用法之Select

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. 明确需求 书中提到的需求是一个基 ...

  3. MyBatis从入门到精通(三):MyBatis XML方式的基本用法之多表查询

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. 多表查询 上篇博客中,我们示例的 ...

  4. MyBatis从入门到精通(十四):在MyBatis中使用类型处理器

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解在MyBatis中如何 ...

  5. MyBatis从入门到精通(2):MyBatis XML方式的基本用法

    本章将通过完成权限管理的常见业务来学习 MyBatis XML方式的基本用法 2.1一个简单的权限控制需求 权限管理的需求: 一个用户拥有若干角色,一个角色拥有若干权限,权限就是对某个模块资源的某种操 ...

  6. MyBatis从入门到精通(一):MyBatis入门

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. MyBatis简介 ​ 2001 ...

  7. MyBatis基础入门《十四》ResultMap子元素(association )

    MyBatis基础入门<十四>ResultMap子元素(association ) 1. id: >> 一般对应数据库中改行的主键ID,设置此项可以提高Mybatis的性能 2 ...

  8. MyBatis从入门到精通(五):MyBatis 注解方式的基本用法

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. @Select 注解 1.1 使 ...

  9. MyBatis从入门到精通(六):MyBatis动态Sql之if标签的用法

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解如何使用if标签生成动 ...

随机推荐

  1. 面向对象举例(一) —— 顶点(vertex)、边(edge)与图(graph)

    Graph: class Graph(dict): def __init__(self, vs=[], es=[]): for v in vs: self.add_vertex(v) for e in ...

  2. Rust 2017 调查报告:学习曲线是最大痛点(最大的问题是这门语言太偏底层了,现在做底层的少了。还有C这个绕不过去的存在)

    Rust 官方在社区上做了一次调查,以了解用户如何看待 Rust 的发展.调查共收到 5368 份回复,其中有 大约 2/3 的是 Rust 用户,剩下的 1/3 是非 Rust 用户,调查结果如下. ...

  3. Windows下程序打包发布时的小技巧(使用Dependency Walker侦测不理想,改用VS自带的dumpbin则万无一失,还可查看dll导出的函数)

    Windows下开发的应用程序在发布时,需要将其依赖的一些动态链接库一起打进安装包里面去.这个时候,快速确定这个程序到底依赖哪些动态链接库变得非常重要.很久以前写过一篇关于Qt程序安装包制作的博客,里 ...

  4. Android数组和开发List之间的转换

    1.List转换到一个数组.(这里List它是实体是ArrayList) 转让ArrayList的toArray方法. toArray public <T> T[] toArray(T[] ...

  5. Qt SQLite 批量插入优化(SQLite默认将每条语句看成单独的事务)good

    使用SQLite存储数据时发现插入速度太慢,程序跑了将近五分钟才插入了不到三千条.上网查资料才发现,SQLite这种文件数据库与MySql机制不一样,每条事务都有打开和关闭文件的步骤,SQLite默认 ...

  6. 如何获取app配置文件内容

    App.config: <appSettings> <add key="FCPConnection" value="Data Source=192.16 ...

  7. vs2015 生成 cordova 页面中文乱码

    原文:vs2015 生成 cordova 页面中文乱码 1.用VS2015新创建Cordova项目,启动运行index.html 中文显示乱码 解决方案: 1.使用text/html通用解析编码utf ...

  8. SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database)

    原文:SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database) 一.本文所涉及的内容(Contents) 本文所涉及的内容(C ...

  9. Inno Setup制作最简单的安装程序

    目标就是[把exe程序放到制定目录,然后自动把工程需要的dll放到system32目录下,自动注册注册表.] 实现上述需求,用Inno Setup可以非常方便快捷实现. 安装Inno Setup. 点 ...

  10. Cindy components(配色很不错)

    https://sourceforge.net/projects/tcycomponents/