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>
先看 <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(); }
}
基础测试类
现在在 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>
注意看下面这段代码,和 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 接口中添加以下方法。
/**
* 根据主键更新
*
* @param sysUser
* @return
* */
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();
}
}
下面给出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();
}
}
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
最新文章
- ADO.NET数据库操作助手类
- oracle 变量赋值
- sql server分页
- 如何给桌面搬家(Win XP)
- linux源码阅读笔记 jmpi指令(转)
- Magento强大的配置系统
- 20169210《Linux内核原理与分析》第九周作业
- easyui tab 加载iframe 高度问题
- Android开发手记(14) 使用MediaPlayer播放mp3
- java 笔试
- SQL Server索引进阶:第十级,索引内部结构
- javascript定义类和类的实现
- [Lua]Mac系统上安装Lua环境
- thinkphp 3.2 发送邮件(Phpmailer)
- c#$用法
- spring-boot-actuator报错Full authentication is required to access this resource
- TopK
- kubernetes 实战4_命令_Configure Pods and Containers
- Ugly Number II leetcode java
- spark 学习_rdd常用操作
热门文章
- SHELL学习笔记三
- 倍增求LCA(最近公共祖先)
- HTTP TCP UDP ICMP IP ARP 协议详解(10.15 第二十一天)
- js中数字的4种遍历方式
- ContentProvider ContentResolver ContentObserver 内容:提供、访问、监听
- 对于AVL树和红黑树的理解
- 快速幂的类似问题(51Nod 1008 N的阶乘 mod P)
- 启动mysql遇到问题Can&#39;t connect to local MySQL server through socket &#39;/tmp/mysql.sock&#39; (2)
- jQuery选择器全解析
- 手机与Arduino蓝牙串口通讯实验及完整例程