2.3、MyBatis的XML基本用法——insert用法
2.3、MyBatis的XML基本用法——insert用法
insert比较简单,除了需要返回主键时,不同数据库的方式有所不同
1、简单用法
添加接口方法

/** * 新增用户 * * @param sysUser * @return */ int insert(SysUser sysUser);
添加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>
测试代码

@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()); } finally { // 为了不影响数据库中的数据导致其他测试失败,这里选择回滚 // 由于默认的 sqlSessionFactory.openSession() 是不自动提交的, // 因此不手动执行 commit 也不会提交到数据库 sqlSession.rollback(); // 不要忘记关闭 sqlSession sqlSession.close(); } }
<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的语句,后者会被忽略。
此处<insert>中的SQL就是一个简单的INSERT语句,将所有的列都列举出来,在values中通过#{property}方式从参数中取出属性的值。
为了防止类型错误,对于一些特殊的数据类型,建议指定具体的jdbcType值。例如headImg指定BLOB类型,createTime指定TIMESTAMP类型。
特别说明!
BLOB对应的类型是ByteArrayInputStream,就是二进制数据流。
由于数据库区分date、time、datetime类型,但是Java中一般都使用java.util.Date类型。因此为了保证数据类型的正确,需要手动指定日期类型,date、time、datetime对应的JDBC类型分别为DATE、TIME、TIMESTAMP。
2、使用JDBC方式返回主键自增的值
适用于数据库本身可以设置字段为自增的情况
接口方法

/** * 新增用户 - 使用 useGeneratedKeys 方式 * * @param sysUser * @return */ int insert2(SysUser sysUser);
XML配置,主要和上面例子增加了useGeneratedKeys="true" keyProperty="id"

<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后,MyBatis会使用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键。获得主键值后将其赋值给keyProperty配置的id属性。当需要设置多个属性时,使用逗号隔开,这种情况下通常还需要设置keyColumn属性,按顺序指定数据库的列,这里列的值会和keyProperty配置的属性一一对应
测试代码

@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.setHeadImg(new byte[] { 1, 2, 3 }); user.setCreateTime(new Date()); int result = userMapper.insert2(user); // 只插入 1 条数据 Assert.assertEquals(1, result); // 因为 id 回写,所以 id 不为 null Assert.assertNotNull(user.getId()); } finally { sqlSession.commit(); // 不要忘记关闭 sqlSession sqlSession.close(); } }
3、使用selectKey返回主键的值
这种方式既适用于2的情况,更适用于数据库不支持自增列,而是通过序列得到一个值,然后赋值给id再插入数据库的情况
接口方法

/** * 新增用户 - 使用 selectKey 方式 * * @param sysUser * @return */ int insert3(SysUser sysUser);
XML配置
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" resultType="long" keyProperty="id" order="AFTER"> SELECT LAST_INSERT_ID() </selectKey> </insert>
Oracle数据库

<!-- Oracle 的例子,查询多个列的时候需要 keyColumn --> <insert id="insertOracle"> <selectKey keyColumn="id" resultType="long" keyProperty="id" 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标签的keyColumn、keyProperty和上面useGeneratedKeys的用法含义相同,这里的resultType用于设置返回值类型。order属性的设置和使用的数据库有关。在MySQL数据库中,order属性设置的值是AFTER,因为当前记录的主键值在insert语句执行成功后才能获取到。而在Oracle数据库中,order的值要设置为BEFORE,这是因为Oracle中需要先从序列获取值,然后将值作为主键插入到数据库中
Oracle方式的INSERT语句中明确写出了id列和值#{id},因为执行selectKey中的语句后id就有值了,我们需要把这个序列值作为主键值插入到数据库中,所以必须指定id列,如果不指定这一列,数据库就会因为主键不能为空而抛出异常
而selectKey标签的写法在前后并无所谓,影响执行顺序的只是order
不同数据库生成id的语句不同
DB2使用VALUESIDENTITY_VAL_LOCAL()。
MYSQL使用SELECTLAST_INSERT_ID()。
SQLSERVER使用SELECTSCOPE_IDENTITY()。
CLOUDSCAPE使用VALUESIDENTITY_VAL_LOCAL()。
DERBY使用VALUESIDENTITY_VAL_LOCAL()。
HSQLDB使用CALLIDENTITY()。
SYBASE使用SELECT@@IDENTITY。
DB2_MF使用SELECTIDENTITY_VAL_LOCAL()FROMSYSIBM.SYSDUMMY1。
INFORMIX使用selectdbinfo('sqlca.sqlerrd1')fromsystableswheretabid=1