Mybatis-3: Possible NullPointerException when assigning generated keys to objects with custom hashCode() method

Created on 7 Nov 2019  Â·  8Comments  Â·  Source: mybatis/mybatis-3

Summary

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 in mybatis leads to NullPointerException because dinstinct compare with id which is null.

MyBatis version

3.5.1

Database vendor and version

5.7.1

Test case or example project


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>

Steps to reproduce

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.

Expected result

true

Actual result

java.lang.NullPointerException

You can contact me by [email protected]. Thank you!

bug

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.


All 8 comments

Thank you for the report, @WeiYang1005 .
Could you add a full stack trace?
An executable demo project (like these) is even better. ;)
And please use three ticks ` for code blocks. See the guide for syntax highlighting, etc..

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

Was this page helpful?
0 / 5 - 0 ratings