Skip to content

Conversation

Iceberry-qdd
Copy link

@Iceberry-qdd Iceberry-qdd commented Apr 19, 2025

Fixes #22719 .

Changes proposed in this pull request:

  • This component only determines whether a DML sql take the sharding keys. So it shouldn't change the sqlStatementContext object, the object has generatedKeyContext inner it.

  • But it'll re-generate the key and add to the generatedKeyContext#generatedValues again, because it also execute the createShardingConditions method, see lines 75 and 177.

    public List<ShardingCondition> createShardingConditions(final InsertStatementContext sqlStatementContext, final List<Object> params) {
    List<ShardingCondition> result = null == sqlStatementContext.getInsertSelectContext()
    ? createShardingConditionsWithInsertValues(sqlStatementContext, params)
    : createShardingConditionsWithInsertSelect(sqlStatementContext, params);
    appendGeneratedKeyConditions(sqlStatementContext, result);
    return result;
    }

    private void appendGeneratedKeyConditions(final InsertStatementContext sqlStatementContext, final List<ShardingCondition> shardingConditions) {
    Optional<GeneratedKeyContext> generatedKey = sqlStatementContext.getGeneratedKeyContext();
    String tableName = sqlStatementContext.getSqlStatement().getTable().map(optional -> optional.getTableName().getIdentifier().getValue()).orElse("");
    if (generatedKey.isPresent() && generatedKey.get().isGenerated() && rule.findShardingTable(tableName).isPresent()) {
    String schemaName = sqlStatementContext.getTablesContext().getSchemaName()
    .orElseGet(() -> new DatabaseTypeRegistry(sqlStatementContext.getDatabaseType()).getDefaultSchemaName(database.getName()));
    AlgorithmSQLContext algorithmSQLContext = new AlgorithmSQLContext(database.getName(), schemaName, tableName, generatedKey.get().getColumnName());
    generatedKey.get().getGeneratedValues().addAll(rule.generateKeys(algorithmSQLContext, sqlStatementContext.getValueListCount()));
    generatedKey.get().setSupportAutoIncrement(rule.isSupportAutoIncrement(tableName));
    if (rule.findShardingColumn(generatedKey.get().getColumnName(), tableName).isPresent()) {
    appendGeneratedKeyCondition(generatedKey.get(), tableName, shardingConditions);
    }
    }
    }

  • Actually, when an Insert sql be executed, and configured to use the generated key, the generatedKeyContext#generatedValues().size() will be sqlStatementContext.getValueListCount() * 2 finally, the generatedKeys actually inserted to the db is generated by audit process, origin process also generated the keys, but they'll be ignored, this can be acceptable.

  • For mybatis or other frameworks(may be), it can bring out the key from the generated keys and assign it to the entity, but it requires the element count of rs and iterator to match, or it'll throw ExecutorException.
    https://github.com/mybatis/mybatis-3/blob/5f8927deebb846b299a99baba984b7db5400b312/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java#L119-L125

mapper.xml
<insert id="insert" parameterType="com.iceberry.Order" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO tb_order (user_id, product_id, amount, count, created_time)
        VALUES (
            #{userId},
            #{productId},
            #{amount},
            #{count},
            #{createdTime}
        )
</insert>
void test_insert(Long userId, Long productId, Long count, BigDecimal amount, LocalDateTime createdTime) {
    Order order = new Order(null, userId, productId, count, amount, createdTime);
    int rows = mapper.insert(order);
    System.out.println(order.getId()); // will be the key generated by shardingSphere
    Assertions.assertEquals(1, rows);
    Assertions.assertNotNull(order.getId());
}

image
image

In the above example, the number of rs elements (2) is one more than the iterator, which causes the exception.

My solution is to store a copy of getGeneratedValues() named savedGeneratedValues before ShardingSpherePreconditions.checkNotEmpty(), and after that, reassign getGeneratedValues() in the finally block to the savedGeneratedValues.


Before committing this PR, I'm sure that I have checked the following options:

  • My code follows the code of conduct of this project.
  • I have self-reviewed the commit code.
  • I have (or in comment I request) added corresponding labels for the pull request.
  • I have passed maven check locally : ./mvnw clean install -B -T1C -Dmaven.javadoc.skip -Dmaven.jacoco.skip -e.
  • I have made corresponding changes to the documentation.
  • I have added corresponding unit tests for my changes.
  • I have updated the Release Notes of the current development version. For more details, see Update Release Note

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use auditStrategy to cause duplicate generation keyGenerator

1 participant