Index: C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/impl/SqlMapExecutorDelegate.java =================================================================== --- C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/impl/SqlMapExecutorDelegate.java (revision 630661) +++ C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/impl/SqlMapExecutorDelegate.java (working copy) @@ -450,7 +450,10 @@ } if (selectKeyStatement != null && selectKeyStatement.isAfter()) { - generatedKey = executeSelectKey(session, trans, ms, param); + // TODO (Brian) added isGenerated() switch and set the key value on with PROBE + if(!selectKeyStatement.isGenerated()) { + generatedKey = executeSelectKey(session, trans, ms, param); + } } autoCommitTransaction(session, autoStart); @@ -461,6 +464,9 @@ return generatedKey; } + // FIXME (Brian) the underlying problem here is that the SelectKeyStatement is treated as a separate query with + // no access to the PreparedStatement from the insert + // TODO (Brian) need to be PROBE to set property private Object executeSelectKey(SessionScope session, Transaction trans, MappedStatement ms, Object param) throws SQLException { Object generatedKey = null; RequestScope request; Index: C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/execution/SqlExecutor.java =================================================================== --- C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/execution/SqlExecutor.java (revision 630661) +++ C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/execution/SqlExecutor.java (working copy) @@ -15,24 +15,36 @@ */ package com.ibatis.sqlmap.engine.execution; +import java.sql.BatchUpdateException; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient; +import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate; import com.ibatis.sqlmap.engine.mapping.parameter.BasicParameterMapping; import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMap; import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMapping; import com.ibatis.sqlmap.engine.mapping.result.ResultMap; import com.ibatis.sqlmap.engine.mapping.result.ResultObjectFactoryUtil; +import com.ibatis.sqlmap.engine.mapping.statement.DefaultRowHandler; +import com.ibatis.sqlmap.engine.mapping.statement.InsertStatement; import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement; import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback; +import com.ibatis.sqlmap.engine.mapping.statement.SelectKeyStatement; import com.ibatis.sqlmap.engine.scope.ErrorContext; import com.ibatis.sqlmap.engine.scope.RequestScope; import com.ibatis.sqlmap.engine.scope.SessionScope; -import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient; -import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate; -import com.ibatis.sqlmap.engine.mapping.statement.DefaultRowHandler; -import java.sql.*; -import java.util.ArrayList; -import java.util.List; - /** * Class responsible for executing the SQL */ @@ -79,13 +91,56 @@ request.getParameterMap().setParameters(request, ps, parameters); errorContext.setMoreInfo("Check the statement (update failed)."); ps.execute(); + if(request.getStatement() instanceof InsertStatement) { // FIXME (Brian) addition, hooking into statement execute + InsertStatement is = (InsertStatement)request.getStatement(); + if(is.getSelectKeyStatement() != null + && is.getSelectKeyStatement().isGenerated()) { + getGeneratedKeys(request, is, ps); + } + } rows = ps.getUpdateCount(); } finally { closeStatement(request.getSession(), ps); } return rows; } + + // FIXME (Brian) getGeneratedKeys() + private void getGeneratedKeys(RequestScope request, InsertStatement is, PreparedStatement ps) throws SQLException { + SelectKeyStatement selectKeyStatement = is.getSelectKeyStatement(); + ResultSet rs = ps.getGeneratedKeys(); + try { + Class resultClass = selectKeyStatement.getResultMap().getResultClass(); + System.out.println("resultClass: " + resultClass.getName()); + // TODO (Brian) get GeneratedKey object, get columns from resultset, set on parameter/result class of request + if(rs.next()) { + Map generatedKeys = new HashMap(); + + ResultSetMetaData rsmd = rs.getMetaData(); + + int columns = rs.getMetaData().getColumnCount(); + System.out.println("generated " + columns + " keys"); + for(int i=1; i<=columns; i++) + { + + // TODO column name I'm getting back is 'ID' for a primary key called 'USER_ID' + // is this a sql server thing? + + // TODO we may need to pass in String[] columnNames when we create the PreparedStatement + + System.out.println("key " + i + " column is " + rs.getMetaData().getColumnName(i)); + System.out.println("key " + i + " value is " + rs.getInt(i)); + //selectKeyStatement.getResultMap().getResults(request, rs); + //handleResults(request, rs, 0, 10, request.RowHandlerCallback callback) + } + //generatedKey = rs.getObject(selectKeyStatement.getKeyProperty()); + } + } finally { + rs.close(); + } + } + /** * Adds a statement to a batch * @@ -486,15 +541,31 @@ } } + // FIXME (Brian) this will set RETURN_GENERATED_KEYS on every prepared statement if the database supports it + // should be ignored if the driver/database determines it's a select + // this was done because the sql statements are being cached by the sql, so we didn't want identical sql statements + // (where one wants generated keys and one doesn't) generating a cache race condition + + // FIXME (Brian) at this point we should know the names of the columns we're interested in, so calling + // conn.prepareStatement(sql, columnNames[]) might be better below + + // FIXME (Brian) there doesn't appear to be any non-intrusive, efficient way to cache the connection metadata, so + // the supportsGetGenerateKeys() is commented out below private static PreparedStatement prepareStatement(SessionScope session, Connection conn, String sql) throws SQLException { SqlMapExecutorDelegate delegate = ((ExtendedSqlMapClient)session.getSqlMapExecutor()).getDelegate(); + PreparedStatement ps = null; if (session.hasPreparedStatementFor(sql)) { - return session.getPreparedStatement((sql)); + ps = session.getPreparedStatement((sql)); } else { - PreparedStatement ps = conn.prepareStatement(sql); + //if(conn.getMetaData().supportsGetGeneratedKeys()) { + //ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + ps = conn.prepareStatement(sql, new String[]{"USER_ID"}); + //} else { + // ps = conn.prepareStatement(sql, Statement.NO_GENERATED_KEYS); + //} session.putPreparedStatement(delegate, sql, ps); - return ps; } + return ps; } private CallableStatement prepareCall(SessionScope session, Connection conn, String sql) throws SQLException { Index: C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/builder/xml/SqlStatementParser.java =================================================================== --- C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/builder/xml/SqlStatementParser.java (revision 630661) +++ C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/builder/xml/SqlStatementParser.java (working copy) @@ -204,7 +204,7 @@ isDynamic = parseDynamicTags(n, dynamic, sqlBuffer, isDynamic, false); if (statement instanceof InsertStatement) { InsertStatement insertStatement = ((InsertStatement) statement); - SelectKeyStatement selectKeyStatement = findAndParseSelectKeyStatement(n, statement); + SelectKeyStatement selectKeyStatement = findAndParseSelectKeyStatement(n, statement); // FIXME findAndParseSelectKey() called insertStatement.setSelectKeyStatement(selectKeyStatement); } @@ -325,6 +325,7 @@ } } else if (child.getNodeType() == Node.ELEMENT_NODE && "selectKey".equals(child.getNodeName())) { + // FIXME (Brian) jump in here and substitute GeneratedKeyStatement? (or do it below)? selectKeyStatement = new SelectKeyStatement(); hasType = parseSelectKey(child, insertStatement, selectKeyStatement); break; @@ -348,7 +349,7 @@ */ private boolean parseSelectKey(Node node, GeneralStatement insertStatement, SelectKeyStatement selectKeyStatement) { vars.errorCtx.setActivity("parsing a select key"); - + // TODO check for 'generated' type, don't check SQL statement if true // get attributes Properties attributes = NodeletUtils.parseAttributes(node, vars.properties); String keyPropName = attributes.getProperty("keyProperty"); @@ -372,6 +373,12 @@ hasType = true; selectKeyStatement.setAfter("post".equals(type)); } + + // TODO: jump in here, check for type='generated', + if("generated".equals(type)) { + selectKeyStatement.setGenerated(true); + selectKeyStatement.setAfter(true); + } try { if (resultClassName != null) { @@ -393,7 +400,9 @@ // process SQL statement, including inline parameter maps vars.errorCtx.setMoreInfo("Check the select key SQL statement."); - processSqlStatement(node, selectKeyStatement); + if(!selectKeyStatement.isGenerated()) { + processSqlStatement(node, selectKeyStatement); + } BasicResultMap resultMap; resultMap = new AutoResultMap(vars.client.getDelegate(), false); Index: C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/builder/xml/sql-map-2.dtd =================================================================== --- C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/builder/xml/sql-map-2.dtd (revision 630661) +++ C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/builder/xml/sql-map-2.dtd (working copy) @@ -156,7 +156,7 @@ Index: C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapping/statement/GeneralStatement.java =================================================================== --- C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapping/statement/GeneralStatement.java (revision 630661) +++ C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapping/statement/GeneralStatement.java (working copy) @@ -75,7 +75,7 @@ errorContext.setActivity("executing mapped statement"); errorContext.setMoreInfo("Check the statement or the result map."); - rows = sqlExecuteUpdate(request, trans.getConnection(), sqlString, parameters); + rows = sqlExecuteUpdate(request, trans.getConnection(), sqlString, parameters); // FIXME (Brian) sqlExecuteUpdate errorContext.setMoreInfo("Check the output parameters."); if (parameterObject != null) { Index: C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapping/statement/SelectKeyStatement.java =================================================================== --- C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapping/statement/SelectKeyStatement.java (revision 630661) +++ C:/development/ibatis/java_release_2.3.0-677/mapper/mapper2/src/com/ibatis/sqlmap/engine/mapping/statement/SelectKeyStatement.java (working copy) @@ -26,6 +26,10 @@ private String keyProperty; private boolean after; + + private boolean generated; // FIXME (Brian) added generated flag + public boolean isGenerated() { return generated; } + public void setGenerated(boolean generated) { this.generated = generated; } public String getKeyProperty() { return keyProperty;