使用autoGenerated键的Groovy addBatch / executeBatch

时间:2015-10-06 13:48:07

标签: groovy

在使用Groovy SQL的 withBatch 方法时,是否有人检索过数据库插入的自动生成的密钥?我有以下代码

def Sql target = ...//database connection
target.withBatch { ps ->
    insertableStuff.each { ps.addBatch ( it ) }

    ps.executeBatch()

    def results = ps.getGeneratedKeys()  //what do I do with this? 
}

我们正在使用DB2,并且我已经使用单个语句/结果集成功测试了 getGeneratedKeys 方法,但是一旦我将该流程包装成批处理,我就不确定是什么对象我正在处理。

根据IBM,可以得到结果,但他们的例子是使用标准的JDBC对象,而不是groovy的对象。有什么想法吗?

3 个答案:

答案 0 :(得分:0)

我把Groovy SQL的东西拿出来看看我是否可以得到一些工作,我想确保DB2 for z / OS实际上支持该功能,并且能够获得生成的值。我正在使用IBM's example,但是我必须添加一些额外的代码来处理IBM示例正在使用的转换。

SQL target = ...//get database connection
def preparedStatement = target.connection.prepareStatement(statement, ['ISN'] as String[])
ResultSet[] resultSets = ((DB2PreparedStatement) (ps.getDelegate().getDelegate())).getDBGeneratedKeys()
resultSets.each { ResultSet results ->
    while(results.next()) {
        println results.getInt(1)
    }
}

所以...这有点笨重,但它的功能。不幸的是,通过自己控制语句,我丢失了Groovy通常为我做的所有参数映射。

我正在查看groovy的Sql源代码,可以看到他们明确告诉数据库连接不处理参数的位置,所以我想我会向Sql.metaClass添加一个新的方法,传入自动生成的列名称列表或其他东西,以使其更加可口。

我还想知道是否有办法让getGeneratedKeys方法正常工作,这样我就不必进行所有的投射。至少,一种实用的方法来为我安全地处理铸件。

try {
    withinBatch = true;
    PreparedStatement statement = (PreparedStatement) getAbstractStatement(new CreatePreparedStatementCommand(0), connection, sql);
    configure(statement);
    psWrapper = new BatchingPreparedStatementWrapper(statement, indexPropList, batchSize, LOG, this);
    closure.call(psWrapper);
    return psWrapper.executeBatch();
} catch (SQLException e) {

createNewPreparedStatement(0)可防止创建可返回自动生成密钥的语句。

答案 1 :(得分:0)

为了确保我没有疯狂,我再次尝试了'getGeneratedKeys'方法,并且声明我知道有效并且没有结果(见下文)。我不得不递归地遍历结果以找到IBM类。所以...不是我最喜欢的代码,它非常脆弱,但它很实用。现在我只需要看看我是否还能以某种方式使用 withBatch 方法,我显然需要覆盖一些东西。

println 'print using getGeneratedKeys'
def results = preparedStatement.getGeneratedKeys()
while (results.next()) {
    println SqlGroovyMethods.toRowResult(results)
}

println 'print using delegate processing'
println getGeneratedKeys(preparedStatement)

private List getGeneratedKeys(PreparedStatement statement) {
    switch (statement) {
        case DelegatingStatement:
            return getGeneratedKeys(DelegatingStatement.cast(statement).getDelegate())

        case DB2PreparedStatement:
            ResultSet[] resultSets = DB2PreparedStatement.cast(statement).getDBGeneratedKeys()

            List keys = []
            resultSets.each { ResultSet results ->
                while (results.next()) {
                    keys << SqlGroovyMethods.toRowResult(results)
                }
            }
            return keys

        default:
            return [SqlGroovyMethods.toRowResult(statement.getGeneratedKeys())]
    }
}

---- Console Output ----
print using getGeneratedKeys
print using delegate processing
[[KEY:7391], [KEY:7392]]

答案 2 :(得分:0)

好的,让它运转起来。我不得不入侵Groovy SQL类,并且有一些我不能做的事情,因为Groovy类中的方法是私有的,所以这个实现不支持cachedStatements,isWithinBatch方法将无法运行正确地在闭包中,并且无法访问已更新的行数。

在基础Groovy代码中看到一些变体很高兴,可能有一个扩展点,你放入自己的处理程序(因为你不希望在基础Groovy代码中使用IBM特定的东西),但至少我现在有一个可行的解决方案。

public class SqlWithGeneratedKeys extends Sql {
    public SqlWithGeneratedKeys(Sql parent) {
        super(parent);
    }
    public List<GroovyRowResult> withBatch(String pSql, String [] keys, Closure closure) throws SQLException {
        return this.withBatch(0, pSql, keys, closure);
    }

    public List<GroovyRowResult> withBatch(int batchSize, String pSql, String [] keys, Closure closure) throws SQLException {
        final Connection connection = this.createConnection();
        List<Tuple> indexPropList = null;

        final SqlWithParams preCheck = this.buildSqlWithIndexedProps(pSql);
        BatchingPreparedStatementWrapper psWrapper = null;

        String sql = pSql;
        if (preCheck != null) {
            indexPropList = new ArrayList<Tuple>();
            for (final Object next : preCheck.getParams()) {
                indexPropList.add((Tuple) next);
            }
            sql = preCheck.getSql();
        }

        PreparedStatement statement = null;
        try {
            statement = connection.prepareStatement(sql, keys);
            this.configure(statement);
            psWrapper = new BatchingPreparedStatementWrapper(statement, indexPropList, batchSize, LOG, this);
            closure.call(psWrapper);
            psWrapper.executeBatch();

            return this.getGeneratedKeys(statement);
        } catch (final SQLException e) {
            LOG.warning("Error during batch execution of '" + sql + "' with message: " + e.getMessage());
            throw e;
        } finally {
            BaseDBServices.closeDBElements(connection, statement, null);
        }
    }

    protected List<GroovyRowResult> getGeneratedKeys(Statement statement) throws SQLException {
        if (statement instanceof DelegatingStatement) {
            return this.getGeneratedKeys(DelegatingStatement.class.cast(statement).getDelegate());

        } else if (statement instanceof DB2PreparedStatement) {
            final ResultSet[] resultSets = DB2PreparedStatement.class.cast(statement).getDBGeneratedKeys();

            final List<GroovyRowResult> keys = new ArrayList<GroovyRowResult>();
            for (final ResultSet results : resultSets) {
                while (results.next()) {
                    keys.add(SqlGroovyMethods.toRowResult(results));
                }
            }
            return keys;
        }
        return Arrays.asList(SqlGroovyMethods.toRowResult(statement.getGeneratedKeys()));
    }
}

打电话给它很干净。

println new SqlWithGeneratedKeys(target).withBatch(statement, ['ISN'] as String[]) { ps ->
    rows.each {
        ps.addBatch(it)
    }
}