Index: C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/AutoAdapter.java =================================================================== --- C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/AutoAdapter.java (revision 405312) +++ C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/AutoAdapter.java (working copy) ..-77,6 +77,7 @@ import org.objectstyle.cayenne.dba.frontbase.FrontBaseSniffer; import org.objectstyle.cayenne.dba.hsqldb.HSQLDBSniffer; import org.objectstyle.cayenne.dba.ingres.IngresSniffer; +import org.objectstyle.cayenne.dba.msaccess.MSAccessSniffer; import org.objectstyle.cayenne.dba.mysql.MySQLSniffer; import org.objectstyle.cayenne.dba.openbase.OpenBaseSniffer; import org.objectstyle.cayenne.dba.oracle.OracleSniffer; ..-105,7 +106,8 @@ new MySQLSniffer(), new PostgresSniffer(), new OracleSniffer(), new SQLServerSniffer(), new HSQLDBSniffer(), new DB2Sniffer(), new SybaseSniffer(), new DerbySniffer(), new OpenBaseSniffer(), - new FirebirdSniffer(), new FrontBaseSniffer(), new IngresSniffer() + new FirebirdSniffer(), new FrontBaseSniffer(), new IngresSniffer(), + new MSAccessSniffer() }; /** Index: C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessActionBuilder.java =================================================================== --- C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessActionBuilder.java (revision 0) +++ C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessActionBuilder.java (revision 0) ..-0,0 +1,31 @@ +package org.objectstyle.cayenne.dba.msaccess; + +import org.objectstyle.cayenne.access.jdbc.BatchAction; +import org.objectstyle.cayenne.dba.DbAdapter; +import org.objectstyle.cayenne.dba.JdbcActionBuilder; +import org.objectstyle.cayenne.map.EntityResolver; +import org.objectstyle.cayenne.query.BatchQuery; +import org.objectstyle.cayenne.query.SQLAction; + +public class MSAccessActionBuilder extends JdbcActionBuilder +{ + + public MSAccessActionBuilder(DbAdapter adapter, EntityResolver resolver) + { + super(adapter, resolver); + } + + + public SQLAction batchAction(BatchQuery query) { + // check run strategy... + + // optimistic locking is not supported in batches due to JDBC driver limitations + boolean useOptimisticLock = query.isUsingOptimisticLocking(); + + boolean runningAsBatch = !useOptimisticLock && adapter.supportsBatchUpdates(); + BatchAction action = new MSAccessBatchAction(query, adapter, entityResolver); + action.setBatch(runningAsBatch); + return action; + } + +} Index: C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessAdapter.java =================================================================== --- C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessAdapter.java (revision 0) +++ C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessAdapter.java (revision 0) ..-0,0 +1,22 @@ +package org.objectstyle.cayenne.dba.msaccess; + +import org.objectstyle.cayenne.access.DataNode; +import org.objectstyle.cayenne.dba.JdbcAdapter; +import org.objectstyle.cayenne.query.Query; +import org.objectstyle.cayenne.query.SQLAction; + +/** + **/ +public class MSAccessAdapter extends JdbcAdapter { + + + public MSAccessAdapter() + { + setSupportsGeneratedKeys(true); + } + public SQLAction getAction(Query query, DataNode node) { + return query.createSQLAction(new MSAccessActionBuilder(this, node + .getEntityResolver())); + } + + } \ No newline at end of file Index: C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessBatchAction.java =================================================================== --- C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessBatchAction.java (revision 0) +++ C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessBatchAction.java (revision 0) ..-0,0 +1,143 @@ +package org.objectstyle.cayenne.dba.msaccess; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Collections; +import java.util.Map; + +import org.objectstyle.cayenne.CayenneException; +import org.objectstyle.cayenne.access.OperationObserver; +import org.objectstyle.cayenne.access.OptimisticLockException; +import org.objectstyle.cayenne.access.QueryLogger; +import org.objectstyle.cayenne.access.ResultIterator; +import org.objectstyle.cayenne.access.jdbc.BatchAction; +import org.objectstyle.cayenne.access.jdbc.JDBCResultIterator; +import org.objectstyle.cayenne.access.jdbc.RowDescriptor; +import org.objectstyle.cayenne.access.trans.BatchQueryBuilder; +import org.objectstyle.cayenne.dba.DbAdapter; +import org.objectstyle.cayenne.map.EntityResolver; +import org.objectstyle.cayenne.query.BatchQuery; +import org.objectstyle.cayenne.query.DeleteBatchQuery; +import org.objectstyle.cayenne.query.UpdateBatchQuery; + +/** + * Extracts generated primary key for MS Access databases. Note that only a single + * generated column is supported. + * + */ +public class MSAccessBatchAction extends BatchAction { + + public MSAccessBatchAction(BatchQuery batchQuery, DbAdapter adapter, + EntityResolver entityResolver) + { + super(batchQuery, adapter, entityResolver); + } + + protected void runAsIndividualQueries(Connection connection, BatchQueryBuilder queryBuilder, OperationObserver delegate, boolean generatesKeys) throws SQLException, Exception + { + + boolean isLoggable = QueryLogger.isLoggable(); + boolean useOptimisticLock = query.isUsingOptimisticLocking(); + + String queryStr = queryBuilder.createSqlString(query); + + // log batch SQL execution + QueryLogger.logQuery(queryStr, Collections.EMPTY_LIST); + + // run batch queries one by one + query.reset(); + + // here a bit of woodoo to support generated keys.... + PreparedStatement statement = connection.prepareStatement(queryStr); + try { + while (query.next()) { + if (isLoggable) { + QueryLogger.logQueryParameters("bind", queryBuilder + .getParameterValues(query)); + } + + queryBuilder.bindParameters(statement, query); + + int updated = statement.executeUpdate(); + if (useOptimisticLock && updated != 1) { + + Map snapshot = Collections.EMPTY_MAP; + if (query instanceof UpdateBatchQuery) { + snapshot = ((UpdateBatchQuery) query).getCurrentQualifier(); + } + else if (query instanceof DeleteBatchQuery) { + snapshot = ((DeleteBatchQuery) query).getCurrentQualifier(); + } + + throw new OptimisticLockException( + query.getDbEntity(), + queryStr, + snapshot); + } + + delegate.nextCount(query, updated); + + if (generatesKeys) + { + String findPrimKey = "SELECT..IDENTITY"; + QueryLogger.logQuery(findPrimKey, Collections.EMPTY_LIST); + + Statement fetchPrimKey = connection.createStatement(); + try + { + fetchPrimKey.executeQuery(findPrimKey); + processGeneratedKeys(fetchPrimKey, delegate); + } finally + { + try + { + fetchPrimKey.close(); + } catch (Throwable e) + { + } + } + } + + if (isLoggable) { + QueryLogger.logUpdateCount(updated); + } + } + } + finally { + try { + statement.close(); + } + catch (Exception e) { + } + } + } + + /** + * Implements generated keys extraction supported by MS Access + * + */ + protected void processGeneratedKeys(Statement statement, OperationObserver observer) + throws SQLException, CayenneException { + + /* + * The only difference between JDBC 3.0 & MS Access extraction for this fn. + * is that we invoke getResultSet() here on the query that found the + * latest primary key insterted. + */ + ResultSet keysRS = statement.getResultSet(); + RowDescriptor descriptor = new RowDescriptor(keysRS, getAdapter() + .getExtendedTypes()); + ResultIterator iterator = new JDBCResultIterator( + null, + null, + keysRS, + descriptor, + 0); + + observer.nextGeneratedDataRows(query, iterator); + } + +} Index: C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessSniffer.java =================================================================== --- C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessSniffer.java (revision 0) +++ C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/MSAccessSniffer.java (revision 0) ..-0,0 +1,22 @@ +package org.objectstyle.cayenne.dba.msaccess; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import org.objectstyle.cayenne.dba.DbAdapter; +import org.objectstyle.cayenne.dba.DbAdapterFactory; + +public class MSAccessSniffer implements DbAdapterFactory { + + public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException { + String dbName = md.getDatabaseProductName(); + if (dbName == null || dbName.toUpperCase().indexOf("ACCESS") < 0) { + return null; + } + + MSAccessAdapter adapter = new MSAccessAdapter(); + + adapter.setSupportsGeneratedKeys(true); + return adapter; + } +} Index: C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/package.html =================================================================== --- C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/package.html (revision 0) +++ C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/package.html (revision 0) ..-0,0 +1,5 @@ + + +Microsoft Access DbAdapter. + + \ No newline at end of file Index: C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/types.xml =================================================================== --- C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/types.xml (revision 0) +++ C:/workspace/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/dba/msaccess/types.xml (revision 0) ..-0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +