1.目的
这一章的目的主要是插入语句以后返回插入记录的id,因为插入语句可分为要返回记录id,不要返回记录id的以及不同数据源类型执行的时机也不同(如:oracle不支持主键,需要先插入序列再增加,Mysql支持主键增加一条记录就会有索引)。
如下图,insert里包含selectKey,由selectKey去执行查询此次新增的id记录,我们看到selectKey标签上的属性有keyProperty、order、resultType。
- keyProperty这个是把返回索引的索引放入id里
- order为after则是在insert后执行selectKey
- resultType则是查询返回的类型,示例里为Long
我们最主要的目的就是解析这样的selectKey,然后存入MappedStatement里到时执行时使用,执行Update时则执行selectKey的方法,最后在activity实体id赋值执行完的记录id。
2.设计说明
标黄色的是修改的,其余都是新增的方法。
1.构建时(builder)
在构建(builder)时,我们需要解析下selectKey的标签,而selectKey是在语句里,所以需要更改XMLStatementBuilder类添加解析selectKey的标签。最后把解析好的标签放入到addMappedStatement(),这里的动作是两次addMappedStatement(),为什么是两次呢?ps:可以看下面两个图。
- 第一次,SqlCommandType是“SELECT”,代表selectKey本身的语句,需要存储一次MappedStatement
- 第二次,SqlCommandType是“INSERT”,代表是selectKey父级的INSERT标签,它下面要包含这个selectKey的MappedStatement信息,然后如果有selectKey则创建SelectKeyGenerator对象,需要再一次存储MappedStatement。
ps此处看不懂可以调试全局看下,多看几遍就懂了为什么这样设计了。
2.selectKey执行时(executor keygen)
2.1 定义接口:
针对上述情况,需要定义接口KeyGenerator,定义方法为processBefore()(针对Oracle不支持主键的)和processAfter()(支持主键的,如MySql)
2.2 实现接口:
NoKenGenerator和SelectKeyGenerator,不要求返回记录的就执行NoKenGenerator(实现方法什么都不操作)。SelectKeyGenerator的实现则是执行查询索引操作,并将结果添加到insert的操作的实体里,这样就得到了索引。
3.执行时(executor)
我们存储到MappedStatement以后就要流转到执行时,因为SelectKey是查询,所以在Executor时
添加了重载的query()方法。
BaseExecutor实现新的query()方法,拿到了sql语句后,调用原有的doQuery,由SimpleExecutor类实现,此时就会调用BaseStatementHandler构造方法,这里新添加了generateKeys()方法,主要是检查下是否需要在insert前执行selectKey(如:Mysql是insert后,Oracle是insert前)。
执行了新增方法时,则需要添加调用KeyGenerator的processAfter()方法,这个方法依然要检查是否在insert后执行selectKey。
4.结果集的更改(resultset)
由于之前的结果集是只处理bean对象, 一般返回selectKey需要的基本类型,如ID是Long,所以这里需要添加createPrimitiveResultObject()方法获取类型处理器。
在createResultObject()此方法里则需要判断是否是基本类型,如果基本类型则调用createPrimitiveResultObject()。其余类型则还按之前代码走。
5.connection,连接的更改
由于要两个语句用一个连接如果两个连接则无法返回id索引,所以需要在获取连接处判断下是否有连接,如果有就不创建,没有就创建。
3.代码
3.1 selectKey执行操作
包:package cn.bugstack.mybatis.executor.keygen;
添加接口KeyGenerator,主要是用来执行SelectKey查询操作的,此接口定义了两个方法,
processBefore:是在不支持主键需要获取序列时调用,执行insert语句前调用
processAfter:是在支持主键在执行insert语句后嗲用
public interface KeyGenerator {
/**
* 针对Sequence主键而言,在执行insert sql前必须指定一个主键值给要插入的记录,
* 如Oracle、DB2,KeyGenerator提供了processBefore()方法。
*/
void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
/**
* 针对自增主键的表,在插入时不需要主键,而是在插入过程自动获取一个自增的主键,
* 比如MySQL、PostgreSQL,KeyGenerator提供了processAfter()方法
*/
void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
}
实现类SelectKeyGenera,有selectKey时实现执行selectKey的逻辑。
// step13新增
public class SelectKeyGenerator implements KeyGenerator {
public static final String SELECT_KEY_SUFFIX = "!selectKey";
private boolean executeBefore;
private MappedStatement keyStatement;
public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {
this.executeBefore = executeBefore;
this.keyStatement = keyStatement;
}
@Override
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
if (executeBefore) {
processGe