Use Insert
为方便理解,本章涉及示例代码已上传至 gitee
==>获取示例代码请点击这里。。。
拉取示例代码时,请拉取所有分支,master 分支只是做了示例的初始化
首先先来实现一个简单的 Insert 查询:
public interface SysUserMapper {
// other methods ...
Integer insertInfo(SysUser sysUser);
}
<insert id="insertInfo" parameterType="SysUser" >
insert into sys_user(
id,
user_name,
user_password,
user_info,
user_email,
head_img,
create_time
)
value
(
#{id},
#{userName},
#{userPassword},
#{userInfo},
#{userEmail},
#{headImg,jdbcType = BLOB},
#{createTime,jdbcType = TIMESTAMP}
);
</insert>
介绍一下 insert 标签里的属性:
- id : 命名空间中的唯一标识符,同 select 标签的 ID 属性性质相同
- parameterType : 传入参数的完全限定名或别名,可选
- flushCahe : 默认值为 true,表示当该条语句被调用,都会清空 MyBatis 的一级和二级缓存
- timeout : 设置在抛出异常之前,驱动程序等待数据库返回请求结果的秒数
- statementType : 对于 statement、prepared、callable,MyBatis 会分别使用对应的 Statement、PreparedStatment、CallableStatement,默认值是 Prepared
- useGeneratedKeys : 默认值为 false,如果设置为 true,MyBatis 会使用 JDBC 的 getGeneratedKeys() 方法取出数据库内部生成的主键
- keyPreparty : MyBatis 通过 JDBC 的 getGeneratedKeys() 方法获取主键后,将要赋值的实体类属性名;如果希望得到多个数据库自动生成的列,属性值是以逗号分隔的属性名称列表
- keyColumn : 仅对 Insert 和 Update 有用,该设置仅在某些数据库中必须(PostgreSQL),当主键列不是表中的第一列时需要设置,如果希望得到多个生成的列, 也可以是逗号分隔的属性名称列表
- databaseId : 如果配置了 databaseIdProvider (后续会介绍该属性的详细情况),MyBatis 会加载所有的不带 databaseId 的或匹配当前 databaseId 的语句,如果同时存在带 databaseId 和不带 databaseId 的语句,后者会被忽略
介绍完 insert 标签的属性,再来说一下使用 insert 标签最常用的返回插入数据的主键自增值:
- 使用 JDBC 方式返回主键自增值(注:该方式只适用于主键自增的数据库)
在项目中新增如下代码:
public interface SysUserMapper {
// other methods ...
Integer insertInfoNotId(SysUser sysUser);
}
<insert id="insertInfoNotId" parameterType="SysUser" useGeneratedKeys="true" keyProperty="id">
insert into sys_user(
user_name,
user_password,
user_info,
user_email,
head_img,
create_time
)
value
(
#{userName},
#{userPassword},
#{userInfo},
#{userEmail},
#{headImg,jdbcType = BLOB},
#{createTime,jdbcType = TIMESTAMP}
);
</insert>
通过与 insert id=“insertInfo” 对比,发现 insert id=“insertInfoNotId” 只是新增了两个属性:useGeneratedKeys=“true” keyProperty=“id” ;这两个属性的设置表示:MyBatis 会使用 JDBC 的 getGeneratedKeys() 方法来取出由数据库内部生成的主键,获得主键值后,将其赋值给 keyProperty 配置的 id 属性,当需要设置多个属性时,使用逗号隔开,这种情况下还需要设置 keyColumn 属性,keyColumn 设置的数据库列名与 keyProperty 配置的属性一一对应。
大概说一下 MyBatis 通过 JDBC 获取自增主键的流程:
PreparedStatementHandler 类的 update(Statement statement) 方法源码如下:
@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
关键的获取数据库生成的主键的步骤就在
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
方法中,有兴趣的可以打断点进去跟着走一遍这里只贴出关键的步骤:
Jdbc3KeyGenerator.class
private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers) throws SQLException {
for (int i = 0; i < keyProperties.length; i++) {
String property = keyProperties[i];
TypeHandler<?> th = typeHandlers[i];
if (th != null) {
// 具体的获取 数据库生成的 主键代码
Object value = th.getResult(rs, i + 1);
metaParam.setValue(property, value);
}
}
}
之后,便能可以查看到实体对象的 id 属性的值了。
- 使用 selectKey 标签返回主键的值(不论数据库是否提供主键自增功能,均适用)
public interface SysUserMapper {
// other methods ...
Integer insertUseSelectKey(SysUser sysUser);
}
<insert id="insertUseSelectKey" parameterType="SysUser" useGeneratedKeys="true" keyProperty="id">
insert into sys_user(user_name,user_password,user_info,user_email,head_img,create_time)
value(#{userName},#{userPassword},#{userInfo},#{userEmail},#{headImg,jdbcType = BLOB},#{createTime,jdbcType = TIMESTAMP});
<selectKey keyProperty="id" keyColumn="id" order="AFTER" resultType="long">
<!-- 用于获取数据库中最后插入的数据的 ID 值 -->
select last_insert_id()
</selectKey>
</insert>
介绍一下 selectKey 标签中的属性值:
- keyColumn 和 keyProperty 与 useGenertedKeys 中的用法相同
- resultType : 用于设置返回值类型
- order : 根据数据库种类不同而不同,MySQL 数据库,设置为 after,Oracle 数据库设置为 before
- MySQL 数据库中当前记录的主键值只有在 insert 语句执行完后才能获得,所以 order = “after”
- Oracle 数据库中则需要先从序列获取值,然后将值作为主键插入,所以 order = “before” ;并且 insert 的 sql 语句中明确写出 id 字段和值,如果 sql 中没有 明确写出 id 字段和值,则有可能会因为主键不能为空而抛出异常
selectKey 标签的执行流程和 useGenertedKeys 相同,只不过
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
中 keyGenerator 属性的实例变成了 SelectKeyGenerator 类型,在 SelectKeyGenerator 的
processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter);
方法中,会有如下的代码段:
List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
它会去执行 selectKey 标签中的 SQL 语句 : select last_insert_id();以获得最后一次插入的数据的 id 值。