Hello MyBatis, here is a bug for inserting objects of List which overrides hashcode method and the key uses as hashcode identifier (like id) is null.
Person overrides hashcode by id , id is null, insert List
3.5.1
5.7.1
Person.java
public class Person {
private Integer id;
private String name;
private Integer age;
private String email;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Person baseModel = (Person) o;
return id.equals(baseModel.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}
insertService.java
public void insertPersons(){
String userName = "test";
int age = 18;
String mobilePhone = "[email protected]";
Person person = new Person();
person.setAge(age);
person.setName(userName);
person.setEmail(mobilePhone);
List<Person> personList = new ArrayList<>();
personList.add(person);
try{
mapper.insertBatch(personList);
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println(person);
}
}
mapper.xml
<insert id="insertBatch" useGeneratedKeys="true"
keyProperty="id" parameterType="test.Person">
INSERT INTO user (`name`, age, email) VALUES
<foreach collection="list" item="person" separator=",">
(#{person.name}, #{person.age}, #{person.email})
</foreach>
</insert>
While we request insertBatch, mybatis will throw a 'java.lang.NullPointerException' exception. because in src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java line 174, it uses paramMap.values().stream().distinct().count() == 1 to adjust whether the count of params is 1, but the method distinct() of stream in jdk8 uses hashcode of object to do distinct, when the object overrides hashcode method with the hashcode of id, and the insert operation doesn't have any content in field id, the exception then occrupt.
But in mybatis before version 3.4.6(I have ensured), this never happened, because Jdbc3KeyGenerator in mybatis 3.4.6 didn't use distinct() method to adjust the count of param.
Anyway, in most cases, we will use this logic to insert batch datas ,hope you can fix this bug in the future.
true
java.lang.NullPointerException
You can contact me by [email protected]. Thank you!
Here is a demo project to reproduce it, https://github.com/WeiYang1005/mybatis-issue .
I found many issues the same reason, like https://github.com/mybatis/mybatis-3/issues/1716, and https://github.com/mybatis/mybatis-3/issues/1711, both because the method getAssignerForParamMap() in Jdbc3KeyGenerator.java uses distinct() of stream to compare input params, while the params contain a null id.
I think the duplicate inputs should be assessed mannualy by the user rather than in the process of insert operation of mybatis, it gives us too many borders to find the reason.
Hope you can fix it !!! Thanks! ! !
So, the custom hashCode() and equals() throw NPE ?
Okay... I will look into it.
I think your implementation of equals and hashCode method is wrong. I propose to use the java.util.Objects.equals(Object, Object) and java.util.Objects.hashCode(Object) at extending method.
There seem to be cases that an exception is thrown intentionally from within equals/hashCode.
https://stackoverflow.com/q/15152145/1261766
There is no guarantee that MyBatis always supports such implementation, but in this particular case, there is a workaround (gh-1728), so I am going to apply it.

You are right, that is not a good implementation of equals and hashcode. We have already changed the implementation, but this kind of implementation mostly are old codes, we cann't prevent others' existed mistakes :D. Thus we want to avoid such cases or get more useful feedbacks. Thanks for your apply! GL!
Hi all,
The workaround is merged. =D
Please try the latest 3.5.4-SNAPSHOT and let us know if there is any issue.
https://github.com/mybatis/mybatis-3/wiki/Maven
Most helpful comment
There seem to be cases that an exception is thrown intentionally from within equals/hashCode.
https://stackoverflow.com/q/15152145/1261766
There is no guarantee that MyBatis always supports such implementation, but in this particular case, there is a workaround (gh-1728), so I am going to apply it.