protected String buildLockKey(TableRecords rowsIncludingPK) {
        if (rowsIncludingPK.size() == 0) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(rowsIncludingPK.getTableMeta().getTableName());
        sb.append(":");
        int rowSequence = 0;
        List<Map<String, Field>> pksRows = rowsIncludingPK.pkRows();
        List<String> primaryKeysOnlyName = rowsIncludingPK.getTableMeta().getPrimaryKeyOnlyName();
        for (Map<String, Field> rowMap : pksRows) {
            int pkSplitIndex = 0;
            for (String pkName : primaryKeysOnlyName) {
                if (pkSplitIndex > 0) {
                    sb.append("_");
                }
                Object pkVal = rowMap.get(pkName).getValue();
                validPk(String.valueOf(pkVal));
                sb.append(pkVal);
                pkSplitIndex++;
            }
            rowSequence++;
            if (rowSequence < pksRows.size()) {
                sb.append(",");
            }
        }
        return sb.toString();
    }
@Test
    public void testBuildLockKey() {
        //build expect data
        String tableName = "test_name";
        String fieldOne = "1";
        String fieldTwo = "2";
        String split1 = ":";
        String split2 = ",";
        String pkColumnName="id";
        //test_name:1,2
        String buildLockKeyExpect = tableName + split1 + fieldOne + split2 + fieldTwo;
        // mock field
        Field field1 = mock(Field.class);
        when(field1.getValue()).thenReturn(fieldOne);
        Field field2 = mock(Field.class);
        when(field2.getValue()).thenReturn(fieldTwo);
        List<Map<String,Field>> pkRows =new ArrayList<>();
        pkRows.add(Collections.singletonMap(pkColumnName, field1));
        pkRows.add(Collections.singletonMap(pkColumnName, field2));

        // mock tableMeta
        TableMeta tableMeta = mock(TableMeta.class);
        when(tableMeta.getTableName()).thenReturn(tableName);
        when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList(new String[]{pkColumnName}));
        // mock tableRecords
        TableRecords tableRecords = mock(TableRecords.class);
        when(tableRecords.getTableMeta()).thenReturn(tableMeta);
        when(tableRecords.size()).thenReturn(pkRows.size());
        when(tableRecords.pkRows()).thenReturn(pkRows);
        // mock executor
        BaseTransactionalExecutor executor = mock(BaseTransactionalExecutor.class);
        when(executor.buildLockKey(tableRecords)).thenCallRealMethod();
        when(executor.getTableMeta()).thenReturn(tableMeta);
        assertThat(executor.buildLockKey(tableRecords)).isEqualTo(buildLockKeyExpect);
    }