Mybatis-3: ON DUPLICATE KEY UPDATE during batch insert is broken after update from 3.4.4 to 3.4.5

Created on 9 Nov 2017  ·  4Comments  ·  Source: mybatis/mybatis-3

MyBatis version

3.4.5.

Database vendor and version

mysql-5.5.48-winx64

The result is present after upgrading from 3.4.4 to 3.4.5. It was working fine before.

During a batch insert I was using ON DUPLICATE KEY UPDATE. For example:

    <insert id="createAll" parameterType="com.company.Machines">
        INSERT INTO merchant_machine (merchant_account_id, product_id, description, lock_version, created_on, updated_on) values
        <foreach collection="machineList" item="machine" separator="," close="" open="">
            (#{machine.merchantAccountId},
            #{machine.productId},
            #{machine.description},
            1,
            SYSDATE(),
            SYSDATE())
        </foreach>
        ON DUPLICATE KEY
        UPDATE
            description = #{machine.description},
        product_id = #{machine.productId},
            lock_version = 1 + lock_version,
            updated_on = SYSDATE()
    </insert>

Machines object looks like this:

    public class Machines{
    private List<Machine> machineList;

    @Override
    public List<Machine> getMachineList() {
        if (machineList == null) {
            machineList = new ArrayList<>();
        }
        return machineList;
    }

    @Override
    public void setMachineList(List<Machine> machineList) {
        this.machineList = machineList;
    }
   }

It was working as expected, inserting the list in a foreach loop and on duplicate, it was updating the rows. After updating to version 3.4.5, following exception is thrown:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'machine' in 'com.company.Machines'

Basically item name is not accessible outside foreach block. Could you fix or suggest a workaround?

invalid question

Most helpful comment

Probably, this issues related with #966.

I am not familiar with the MySQL, but I think your sql is wrong.
Could you try following solution(= using values(column_name) phrase into update statement)?

    <insert id="createAll" parameterType="com.company.Machines">
        INSERT INTO merchant_machine (merchant_account_id, product_id, description, lock_version, created_on, updated_on) values
        <foreach collection="machineList" item="machine" separator="," close="" open="">
            (#{machine.merchantAccountId},
            #{machine.productId},
            #{machine.description},
            1,
            SYSDATE(),
            SYSDATE())
        </foreach>
        ON DUPLICATE KEY
        UPDATE
-           description = #{machine.description},
+           description = values(description),
-           product_id = #{machine.productId},
+           product_id = values(product_id),
            lock_version = 1 + lock_version,
            updated_on = SYSDATE()
    </insert>

About values(column_name) , phrase see https://dev.mysql.com/doc/refman/5.6/en/insert-on-duplicate.html.

All 4 comments

@giorgimode try parameterType="list"

Probably, this issues related with #966.

I am not familiar with the MySQL, but I think your sql is wrong.
Could you try following solution(= using values(column_name) phrase into update statement)?

    <insert id="createAll" parameterType="com.company.Machines">
        INSERT INTO merchant_machine (merchant_account_id, product_id, description, lock_version, created_on, updated_on) values
        <foreach collection="machineList" item="machine" separator="," close="" open="">
            (#{machine.merchantAccountId},
            #{machine.productId},
            #{machine.description},
            1,
            SYSDATE(),
            SYSDATE())
        </foreach>
        ON DUPLICATE KEY
        UPDATE
-           description = #{machine.description},
+           description = values(description),
-           product_id = #{machine.productId},
+           product_id = values(product_id),
            lock_version = 1 + lock_version,
            updated_on = SYSDATE()
    </insert>

About values(column_name) , phrase see https://dev.mysql.com/doc/refman/5.6/en/insert-on-duplicate.html.

@kazuki43zoo is right about both the reason and the resolution.
It was wrong that a loop variable was accessible after the loop.

@kazuki43zoo is right. His suggestion works. I am wondering, was it a bug in previous version. Loop variable probably should not have been accessible outside loop

Was this page helpful?
0 / 5 - 0 ratings