mybatis主键生成策略
不设置主键值,也不获取数据库生成的主键值的情况,`为了保持代码的通用性。
·
KeyGenerator
public interface KeyGenerator {
// 在执行数据更新前生成主键
void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
// 在执行数据更新后生成主键
void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
}
Jdbc3KeyGenerator
<!--useGeneratedKeys="true"代表我们希望在插入数据库之后获取生成的主键值-->
<insert id="insertOne" parameterMap="parameterMap1515" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into test_extend_and_automapping (`myname`, `myage`)
values (#{myname}, #{myage})
</insert>
/**
* @author Clinton Begin
* @author Kazuki Shimizu
* 在插入数据库之后获取生成的主键值
*/
public class Jdbc3KeyGenerator implements KeyGenerator {
/**
* A shared instance.
*
* @since 3.4.3
*/
public static final Jdbc3KeyGenerator INSTANCE = new Jdbc3KeyGenerator();
//所以该方法不需要实现
@Override
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
// do nothing
}
@Override
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
processBatch(ms, stmt, parameter);
}
/**
*
* @param ms 代表一个完整的sql
* @param stmt 代表一个 selectKey对象
* @param parameter 存储传入参数信息的集合
* 有可能是这这样的 如可能:TestExtendAndAutoMapping{id=null, myname='7868c188-241e-491a-b951-9624efc2044c', myage=24}
*/
public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {
//获取selectkey标签的keyProperty属性 一个selectKey只会有一个,这里只有一个
final String[] keyProperties = ms.getKeyProperties();
if (keyProperties == null || keyProperties.length == 0) {
return;
}
//获取JDBC的ResultSet对象,封装有数据库生成的主键信息(因此,mybaits最终还是要依赖于JDBC的机制)
try (ResultSet rs = stmt.getGeneratedKeys()) {
//获取全局配置对象
final Configuration configuration = ms.getConfiguration();
//获取结果集中元数据信息,并且元数据个数要大于等于keyProperty的数量
if (rs.getMetaData().getColumnCount() >= keyProperties.length) {
Object soleParam = getSoleParameter(parameter);
//获取参数值
if (soleParam != null) {//只有一个参数情况
assignKeysToParam(configuration, rs, keyProperties, soleParam);
} else {//多个参数的情况
assignKeysToOneOfParams(configuration, rs, keyProperties, (Map<?, ?>) parameter);
}
}
} catch (Exception e) {
throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
}
}
protected void assignKeysToOneOfParams(final Configuration configuration, ResultSet rs, final String[] keyProperties,
Map<?, ?> paramMap) throws SQLException {
// Assuming 'keyProperty' includes the parameter name. e.g. 'param.id'.
int firstDot = keyProperties[0].indexOf('.');
if (firstDot == -1) {
throw new ExecutorException(
"Could not determine which parameter to assign generated keys to. "
+ "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
+ "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are "
+ paramMap.keySet());
}
String paramName = keyProperties[0].substring(0, firstDot);
Object param;
if (paramMap.containsKey(paramName)) {
param = paramMap.get(paramName);
} else {
throw new ExecutorException("Could not find parameter '" + paramName + "'. "
+ "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
+ "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are "
+ paramMap.keySet());
}
// Remove param name from 'keyProperty' string. e.g. 'param.id' -> 'id'
String[] modifiedKeyProperties = new String[keyProperties.length];
for (int i = 0; i < keyProperties.length; i++) {
if (keyProperties[i].charAt(firstDot) == '.' && keyProperties[i].startsWith(paramName)) {
modifiedKeyProperties[i] = keyProperties[i].substring(firstDot + 1);
} else {
throw new ExecutorException("Assigning generated keys to multiple parameters is not supported. "
+ "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
+ "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are "
+ paramMap.keySet());
}
}
assignKeysToParam(configuration, rs, modifiedKeyProperties, param);
}
private void assignKeysToParam(final Configuration configuration, ResultSet rs, final String[] keyProperties,
Object param)
throws SQLException {
//类型注册器
final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
//获取结果集元数据对象
final ResultSetMetaData rsmd = rs.getMetaData();
// Wrap the parameter in Collection to normalize the logic.
Collection<?> paramAsCollection = null;//最后都转化为Collection
if (param instanceof Object[]) {//参数是数组
paramAsCollection = Arrays.asList((Object[]) param);
} else if (!(param instanceof Collection)) {//是集合
paramAsCollection = Arrays.asList(param);
} else {
paramAsCollection = (Collection<?>) param;
}
TypeHandler<?>[] typeHandlers = null;
for (Object obj : paramAsCollection) {
if (!rs.next()) {
break;
}
//将传入参数封装为MetaObject方便操作,比如操作其属性值等 利用反射 给主键赋值
MetaObject metaParam = configuration.newMetaObject(obj);
if (typeHandlers == null) {
//获取类型处理器 以便赋值操作
typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd);
}
//填充主键值到对象中
populateKeys(rs, metaParam, keyProperties, typeHandlers);
}
}
private Object getSoleParameter(Object parameter) {
//ParamMap,StrictMap 是DefaultSqlSession的静态内部类,都继承了HashMap
//判断参数的类型不是HashMap 则直接返回
if (!(parameter instanceof ParamMap || parameter instanceof StrictMap)) {
return parameter;
}
//如果是Map,则遍历
Object soleParam = null;
for (Object paramValue : ((Map<?, ?>) parameter).values()) {
if (soleParam == null) {//如果soleParam==null,则将paramValue赋值给如果soleParam
soleParam = paramValue;
} else if (soleParam != paramValue) {//如果soleParam != paramValue,则将soleParam置为null,并且返回soleParam
soleParam = null;
break;
}
}
return soleParam;
}
private TypeHandler<?>[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam, String[] keyProperties, ResultSetMetaData rsmd) throws SQLException {
TypeHandler<?>[] typeHandlers = new TypeHandler<?>[keyProperties.length];
for (int i = 0; i < keyProperties.length; i++) {
if (metaParam.hasSetter(keyProperties[i])) {
Class<?> keyPropertyType = metaParam.getSetterType(keyProperties[i]);
typeHandlers[i] = typeHandlerRegistry.getTypeHandler(keyPropertyType, JdbcType.forCode(rsmd.getColumnType(i + 1)));
} else {
throw new ExecutorException("No setter found for the keyProperty '" + keyProperties[i] + "' in '"
+ metaParam.getOriginalObject().getClass().getName() + "'.");
}
}
return typeHandlers;
}
private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers) throws SQLException {
for (int i = 0; i < keyProperties.length; i++) {//其实keyProperties长度是1
String property = keyProperties[i];
TypeHandler<?> th = typeHandlers[i];
if (th != null) {
Object value = th.getResult(rs, i + 1);
//利用反射调用set方法赋值
metaParam.setValue(property, value);
}
}
}
}
SelectKeyGenerator
/**
* @author Clinton Begin
* @author Jeff Butler
*
* <insert id="insertByQueryParamter" parameterMap="myParameterMap">
<selectKey keyProperty="id" keyColumn="id"
resultType="java.lang.String" order="BEFORE">
select uuid()
</selectKey>
INSERT INTO `test_selectkey`
(`id`,
`myname`,
`myage`)
VALUES (#{id},
#{myname},
#{myage})
</insert>
<insert id="insertOneSelectKeyAfter" parameterMap="parameterMap1515">
<selectKey keyProperty="id"
keyColumn="id"
order="AFTER"
resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into test_extend_and_automapping (`myname`, `myage`)
values (#{myname}, #{myage})
</insert>
order=after executeBefore = ture
order=before executeBefore = false
SelectKeyGenerator 可以在操作数据库之前或之后获取主键
executeBefore = false则调用processBefore 在操作数据库之前获取主键 通过select uuid()生成主键值,并设置到对应的属性中
executeBefore = ture 则调用processAfter 在操作数据库之后获取主键 由数据库生成的主键
*/
public class SelectKeyGenerator implements KeyGenerator {
public static final String SELECT_KEY_SUFFIX = "!selectKey";
private final boolean executeBefore;
private final 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) {
processGeneratedKeys(executor, ms, parameter);
}
}
@Override
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
if (!executeBefore) {
processGeneratedKeys(executor, ms, parameter);
}
}
private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
try {
if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
// 获取配置的属性值,即<selectKey>标签中配置的keyProperty="id"
String[] keyProperties = keyStatement.getKeyProperties();//这里的keyProperties.length =1 只有一个属性
//获取全局配置
final Configuration configuration = ms.getConfiguration();
//将属性封装成metaObject 方便赋值操作
final MetaObject metaParam = configuration.newMetaObject(parameter);
if (keyProperties != null) {
// Do not close keyExecutor.
// The transaction will be closed by parent executor.
//获取执行器
Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
//执行<selectKey>中的sql 获取主键值 这里的主键值也只有一个
List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
if (values.size() == 0) {
throw new ExecutorException("SelectKey returned no data.");
} else if (values.size() > 1) {
throw new ExecutorException("SelectKey returned more than one value.");
} else {
MetaObject metaResult = configuration.newMetaObject(values.get(0));
if (keyProperties.length == 1) {//这里只考虑一个值的情况
//使用mybatis,实体类中 可以没有getter方法,但是不能没有setter,因为赋值操作是通过反射完成的,setValue,如果没有setter方法,则报错
if (metaResult.hasGetter(keyProperties[0])) {//判断主键属性是否有getter方法
// 如果是传入的对象的当前属性有getter方法,一般获取的是
// 字符串,所有肯定没有
setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
} else {
// 设置属性值
// metaParam:用户传入的参数对象
// keyProperties[0]:要设置的属性值,即id
// values.get(0):生成的主键值
// 将生成的主键值设置到传入参数对象的属性中
// 如下是设置前,设置后的值:
// 设置前:{"myage":19,"myname":"379bff59-eb6d-4b03-95d4-571379e9d13f"}
// 设置后:{"id":"db6fbe60-07af-11ec-978a-1b6197d25533","myage":19,"myname":"379bff59-eb6d-4b03-95d4-571379e9d13f"}
// no getter for the property - maybe just a single value object
// so try that
setValue(metaParam, keyProperties[0], values.get(0));
}
} else {
handleMultipleProperties(keyProperties, metaParam, metaResult);
}
}
}
}
} catch (ExecutorException e) {
throw e;
} catch (Exception e) {
throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
}
}
//这里应该是处理联合主键
private void handleMultipleProperties(String[] keyProperties,
MetaObject metaParam, MetaObject metaResult) {
String[] keyColumns = keyStatement.getKeyColumns();
if (keyColumns == null || keyColumns.length == 0) {
// no key columns specified, just use the property names
for (String keyProperty : keyProperties) {
setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
}
} else {
if (keyColumns.length != keyProperties.length) {
throw new ExecutorException("If SelectKey has key columns, the number must match the number of key properties.");
}
for (int i = 0; i < keyProperties.length; i++) {
setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
}
}
}
private void setValue(MetaObject metaParam, String property, Object value) {
if (metaParam.hasSetter(property)) {
//通过反射调用setter方法赋值
metaParam.setValue(property, value);
} else {
throw new ExecutorException("No setter found for the keyProperty '" + property + "' in " + metaParam.getOriginalObject().getClass().getName() + ".");
}
}
}
NoKeyGenerator
不设置主键值,也不获取数据库生成的主键值的情况,`为了保持代码的通用性
public class NoKeyGenerator implements KeyGenerator {
public static final NoKeyGenerator INSTANCE = new NoKeyGenerator();
@Override
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
// Do Nothing
}
@Override
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
// Do Nothing
}
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)