1. 程式人生 > 其它 >Too many keys are generated. There are only 1 target objects.

Too many keys are generated. There are only 1 target objects.

技術標籤:MyBatisSpring Boot

Too many keys are generated. There are only 1 target objects.

記一次由spring boot升級導致的問題解決。

版本資訊:
mybatis-spring-boot-starter : 2.1.4
mybatis-spring: 2.0.6
mybatis : 3.5.6
mysql-connector-java: 8.0.22

錯誤日誌:

Caused by: org.apache.ibatis.executor.ExecutorException: Too many keys are generated. There are only 1 target objects. You either specified a wrong 'keyProperty'
or encountered a driver bug like #1523. at org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.assignKeysToParam(Jdbc3KeyGenerator.java:121) ~[mybatis-3.5.5.jar:3.5.5] at org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.assignKeys(Jdbc3KeyGenerator.java:104) ~[mybatis-3.5.5.jar:3.5.5] at org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.processBatch(
Jdbc3KeyGenerator.java:85) ~[mybatis-3.5.5.jar:3.5.5] at org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.processAfter(Jdbc3KeyGenerator.java:71) ~[mybatis-3.5.5.jar:3.5.5] at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:51) ~[mybatis-3.5.5.jar:3.5.5]
at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:74) ~[mybatis-3.5.5.jar:3.5.5] at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:50) ~[mybatis-3.5.5.jar:3.5.5] at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117) ~[mybatis-3.5.5.jar:3.5.5] at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76) ~[mybatis-3.5.5.jar:3.5.5] at com.jd.eps.eis.product.core.interceptor.TenantInterceptor.intercept(TenantInterceptor.java:80) ~[eis-core-service-2.0.0-SNAPSHOT.jar:?] at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61) ~[mybatis-3.5.5.jar:3.5.5] at com.sun.proxy.$Proxy184.update(Unknown Source) ~[?:?] at com.jd.eps.eis.product.core.interceptor.DataPermissionInterceptor.intercept(DataPermissionInterceptor.java:75) ~[eis-core-service-2.0.0-SNAPSHOT.jar:?] at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61) ~[mybatis-3.5.5.jar:3.5.5] at com.sun.proxy.$Proxy184.update(Unknown Source) ~[?:?] at sun.reflect.GeneratedMethodAccessor277.invoke(Unknown Source) ~[?:?] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_171] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_171] at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:63) ~[mybatis-3.5.5.jar:3.5.5] at com.sun.proxy.$Proxy184.update(Unknown Source) ~[?:?] at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197) ~[mybatis-3.5.5.jar:3.5.5] at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:184) ~[mybatis-3.5.5.jar:3.5.5] at sun.reflect.GeneratedMethodAccessor416.invoke(Unknown Source) ~[?:?] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_171] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_171] at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:426) ~[mybatis-spring-2.0.5.jar:2.0.5] ... 119 more

跟蹤日誌發現是在執行批量插入時,使用了useGeneratedKeys=“true”,因為是批量操作,所以返回多個key,但是在設定的時候發現只有一個物件可以設定,因此報錯。
org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator#processBatch

public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {
  final String[] keyProperties = ms.getKeyProperties();
  if (keyProperties == null || keyProperties.length == 0) {
    return;
  }
  try (ResultSet rs = stmt.getGeneratedKeys()) {
    final ResultSetMetaData rsmd = rs.getMetaData();
    final Configuration configuration = ms.getConfiguration();
    if (rsmd.getColumnCount() < keyProperties.length) {
      // Error?
    } else {
      assignKeys(configuration, rs, rsmd, keyProperties, parameter);
    }
  } catch (Exception e) {
    throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
  }
}

private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties,
      Object parameter) throws SQLException {
  if (parameter instanceof ParamMap || parameter instanceof StrictMap) {
    // Multi-param or single param with @Param
    assignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter);
  } else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty()
      && ((ArrayList<?>) parameter).get(0) instanceof ParamMap) {
    // Multi-param or single param with @Param in batch operation
    assignKeysToParamMapList(configuration, rs, rsmd, keyProperties, (ArrayList<ParamMap<?>>) parameter);
  } else {
    // Single param without @Param
    assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter);
  }
}

原因一:
自己開發的Mybatis外掛對parameter型別做了轉換,使原本的MapperMethod.ParamMap型別變成了HashMap型別,導致執行assignKeys方法時走了else的邏輯。

解決方法:
對MapperMethod.ParamMap型別單獨處理。

// 如果引數為空,建立空Map
 if (parameterObject == null) {
     paramMap = new HashMap<>();
 } else if (parameterObject instanceof MapperMethod.ParamMap) {
     paramMap = new MapperMethod.ParamMap<>();
     paramMap.putAll((Map) parameterObject);
 } else if (parameterObject instanceof Map) {
     // 解決不可變Map的情況
     paramMap = new HashMap<>();
     paramMap.putAll((Map) parameterObject);
 } else {
     // do sth.
 }

原因二:
指定keyProperty時應包含list的別名資訊,如keyProperty=“list.id”。

int insertConfigDetails(@Param("list") List<ConfigDetailDO> configDetailDOS);
<insert id="insertConfigDetails" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="list.id">
</insert>