From 91a9f8209062c58b476585e117f5893e7660b7c5 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sun, 10 Nov 2019 17:48:05 +0900 Subject: [PATCH] Avoid invoking equals/hashCode when assigning generated keys When detecting single parameter case, it now checks parameter name instead of equality of the parameter objects. This should fix gh-1719. A caveat : when there is only one parameter and its name is 'param2' (for whatever reason), `keyProperty` must include the parameter name i.e. `keyProperty="param2.xx"`. --- .../executor/keygen/Jdbc3KeyGenerator.java | 15 +++- .../ibatis/reflection/ParamNameResolver.java | 4 +- .../submitted/keygen/CountryMapper.java | 8 ++ .../ibatis/submitted/keygen/CountryMapper.xml | 9 ++- .../keygen/Jdbc3KeyGeneratorTest.java | 34 +++++++++ .../ibatis/submitted/keygen/NpeCountry.java | 75 +++++++++++++++++++ 6 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 src/test/java/org/apache/ibatis/submitted/keygen/NpeCountry.java diff --git a/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java b/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java index 5b115d87e43..755e13d3587 100644 --- a/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java +++ b/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import org.apache.ibatis.binding.MapperMethod.ParamMap; import org.apache.ibatis.executor.Executor; @@ -35,6 +36,7 @@ import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.reflection.ArrayUtil; import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.ParamNameResolver; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.defaults.DefaultSqlSession.StrictMap; import org.apache.ibatis.type.JdbcType; @@ -47,6 +49,8 @@ */ public class Jdbc3KeyGenerator implements KeyGenerator { + private static final String SECOND_GENERIC_PARAM_NAME = ParamNameResolver.GENERIC_NAME_PREFIX + "2"; + /** * A shared instance. * @@ -171,7 +175,10 @@ private void assignKeysToParamMap(Configuration configuration, ResultSet rs, Res private Entry getAssignerForParamMap(Configuration config, ResultSetMetaData rsmd, int columnPosition, Map paramMap, String keyProperty, String[] keyProperties, boolean omitParamName) { - boolean singleParam = paramMap.values().stream().distinct().count() == 1; + Set keySet = paramMap.keySet(); + // A caveat : if the only parameter has {@code @Param("param2")} on it, + // it must be referenced with param name e.g. 'param2.x'. + boolean singleParam = !keySet.contains(SECOND_GENERIC_PARAM_NAME); int firstDot = keyProperty.indexOf('.'); if (firstDot == -1) { if (singleParam) { @@ -180,10 +187,10 @@ private Entry getAssignerForParamMap(Configuration config, 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()); + + keySet); } String paramName = keyProperty.substring(0, firstDot); - if (paramMap.containsKey(paramName)) { + if (keySet.contains(paramName)) { String argParamName = omitParamName ? null : paramName; String argKeyProperty = keyProperty.substring(firstDot + 1); return entry(paramName, new KeyAssigner(config, rsmd, columnPosition, argParamName, argKeyProperty)); @@ -193,7 +200,7 @@ private Entry getAssignerForParamMap(Configuration config, 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()); + + keySet); } } diff --git a/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java index 6fbe5816213..ed5bad5e1a5 100644 --- a/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java +++ b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2018 the original author or authors. + * Copyright 2009-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ public class ParamNameResolver { - private static final String GENERIC_NAME_PREFIX = "param"; + public static final String GENERIC_NAME_PREFIX = "param"; /** *

diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java index 46b36016319..f21eff19ee8 100644 --- a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java +++ b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java @@ -22,6 +22,7 @@ import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.reflection.ParamNameResolver; public interface CountryMapper { @@ -100,4 +101,11 @@ int insertMultiParams_keyPropertyWithWrongParamName(@Param("country") Country co @Options(useGeneratedKeys = true, keyProperty = "country.id") @Insert({ "insert into country (countryname,countrycode) values ('a','A'), ('b', 'B')" }) int tooManyGeneratedKeysParamMap(@Param("country") Country country, @Param("someId") Integer someId); + + int insertWeirdCountries(List list); + + // If the only parameter has a name 'param2', keyProperty must include the prefix 'param2.'. + @Options(useGeneratedKeys = true, keyProperty = ParamNameResolver.GENERIC_NAME_PREFIX + "2.id") + @Insert({ "insert into country (countryname,countrycode) values (#{param2.countryname},#{param2.countrycode})" }) + int singleParamWithATrickyName(@Param(ParamNameResolver.GENERIC_NAME_PREFIX + "2") Country country); } diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml index fa5624f1cf8..29dca2fb953 100644 --- a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml +++ b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml @@ -1,7 +1,7 @@