]> source.dussan.org Git - iciql.git/commitdiff
Change return types for several Db methods to indicate status
authorJames Moger <james.moger@gitblit.com>
Fri, 7 Nov 2014 12:57:40 +0000 (07:57 -0500)
committerJames Moger <james.moger@gitblit.com>
Fri, 7 Nov 2014 13:06:25 +0000 (08:06 -0500)
src/main/java/com/iciql/Db.java
src/test/java/com/iciql/test/PrimitivesTest.java

index 23001d86041de3635e9c0af0aaf996df1a456d5f..ca43e63051339b940ff0d72e561616a6db5bfcc2 100644 (file)
-/*\r
- * Copyright 2004-2011 H2 Group.\r
- * Copyright 2011 James Moger.\r
- * Copyright 2012 Frédéric Gaillard.\r
- * Copyright 2012 Alex Telepov.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package com.iciql;\r
-\r
-import java.sql.Connection;\r
-import java.sql.DatabaseMetaData;\r
-import java.sql.PreparedStatement;\r
-import java.sql.ResultSet;\r
-import java.sql.SQLException;\r
-import java.sql.SQLFeatureNotSupportedException;\r
-import java.sql.Savepoint;\r
-import java.sql.Statement;\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import javax.sql.DataSource;\r
-\r
-import com.iciql.DbUpgrader.DefaultDbUpgrader;\r
-import com.iciql.Iciql.IQTable;\r
-import com.iciql.Iciql.IQVersion;\r
-import com.iciql.Iciql.IQView;\r
-import com.iciql.util.IciqlLogger;\r
-import com.iciql.util.JdbcUtils;\r
-import com.iciql.util.StringUtils;\r
-import com.iciql.util.Utils;\r
-import com.iciql.util.WeakIdentityHashMap;\r
-\r
-/**\r
- * This class represents a connection to a database.\r
- */\r
-\r
-public class Db implements AutoCloseable {\r
-\r
-       /**\r
-        * This map It holds unique tokens that are generated by functions such as\r
-        * Function.sum(..) in "db.from(p).select(Function.sum(p.unitPrice))". It\r
-        * doesn't actually hold column tokens, as those are bound to the query\r
-        * itself.\r
-        */\r
-       private static final Map<Object, Token> TOKENS;\r
-\r
-       private static final Map<String, Class<? extends SQLDialect>> DIALECTS;\r
-\r
-       private final Connection conn;\r
-       private final Map<Class<?>, TableDefinition<?>> classMap = Collections\r
-                       .synchronizedMap(new HashMap<Class<?>, TableDefinition<?>>());\r
-       private final SQLDialect dialect;\r
-       private DbUpgrader dbUpgrader = new DefaultDbUpgrader();\r
-       private final Set<Class<?>> upgradeChecked = Collections.synchronizedSet(new HashSet<Class<?>>());\r
-\r
-       private boolean skipCreate;\r
-       private boolean autoSavePoint = true;\r
-\r
-       static {\r
-               TOKENS = Collections.synchronizedMap(new WeakIdentityHashMap<Object, Token>());\r
-               DIALECTS = Collections.synchronizedMap(new HashMap<String, Class<? extends SQLDialect>>());\r
-               // can register by...\r
-               // 1. Connection class name\r
-               // 2. DatabaseMetaData.getDatabaseProductName()\r
-               DIALECTS.put("Apache Derby", SQLDialectDerby.class);\r
-               DIALECTS.put("H2", SQLDialectH2.class);\r
-               DIALECTS.put("HSQL Database Engine", SQLDialectHSQL.class);\r
-               DIALECTS.put("MySQL", SQLDialectMySQL.class);\r
-               DIALECTS.put("PostgreSQL", SQLDialectPostgreSQL.class);\r
-           DIALECTS.put("Microsoft SQL Server", SQLDialectMSSQL.class);\r
-           DIALECTS.put("SQLite", SQLDialectSQLite.class);\r
-       }\r
-\r
-       private Db(Connection conn) {\r
-               this.conn = conn;\r
-               String databaseName = null;\r
-               try {\r
-                       DatabaseMetaData data = conn.getMetaData();\r
-                       databaseName = data.getDatabaseProductName();\r
-               } catch (SQLException s) {\r
-                       throw new IciqlException(s, "failed to retrieve database metadata!");\r
-               }\r
-               dialect = getDialect(databaseName, conn.getClass().getName());\r
-               dialect.configureDialect(this);\r
-       }\r
-\r
-       /**\r
-        * Register a new/custom dialect class. You can use this method to replace\r
-        * any existing dialect or to add a new one.\r
-        *\r
-        * @param token\r
-        *            the fully qualified name of the connection class or the\r
-        *            expected result of DatabaseMetaData.getDatabaseProductName()\r
-        * @param dialectClass\r
-        *            the dialect class to register\r
-        */\r
-       public static void registerDialect(String token, Class<? extends SQLDialect> dialectClass) {\r
-               DIALECTS.put(token, dialectClass);\r
-       }\r
-\r
-       SQLDialect getDialect(String databaseName, String className) {\r
-               Class<? extends SQLDialect> dialectClass = null;\r
-               if (DIALECTS.containsKey(className)) {\r
-                       // dialect registered by connection class name\r
-                       dialectClass = DIALECTS.get(className);\r
-               } else if (DIALECTS.containsKey(databaseName)) {\r
-                       // dialect registered by database name\r
-                       dialectClass = DIALECTS.get(databaseName);\r
-               } else {\r
-                       // did not find a match, use default\r
-                       dialectClass = SQLDialectDefault.class;\r
-               }\r
-               return instance(dialectClass);\r
-       }\r
-\r
-       static <X> X registerToken(X x, Token token) {\r
-               TOKENS.put(x, token);\r
-               return x;\r
-       }\r
-\r
-       static Token getToken(Object x) {\r
-               return TOKENS.get(x);\r
-       }\r
-\r
-       static <T> T instance(Class<T> clazz) {\r
-               try {\r
-                       return clazz.newInstance();\r
-               } catch (Exception e) {\r
-                       throw new IciqlException(e);\r
-               }\r
-       }\r
-\r
-       public static Db open(String url) {\r
-               try {\r
-                       Connection conn = JdbcUtils.getConnection(null, url, null, null);\r
-                       return new Db(conn);\r
-               } catch (SQLException e) {\r
-                       throw new IciqlException(e);\r
-               }\r
-       }\r
-\r
-       public static Db open(String url, String user, String password) {\r
-               try {\r
-                       Connection conn = JdbcUtils.getConnection(null, url, user, password);\r
-                       return new Db(conn);\r
-               } catch (SQLException e) {\r
-                       throw new IciqlException(e);\r
-               }\r
-       }\r
-\r
-       public static Db open(String url, String user, char[] password) {\r
-               try {\r
-                       Connection conn = JdbcUtils.getConnection(null, url, user, password == null ? null : new String(password));\r
-                       return new Db(conn);\r
-               } catch (SQLException e) {\r
-                       throw new IciqlException(e);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Create a new database instance using a data source. This method is fast,\r
-        * so that you can always call open() / close() on usage.\r
-        *\r
-        * @param ds\r
-        *            the data source\r
-        * @return the database instance.\r
-        */\r
-       public static Db open(DataSource ds) {\r
-               try {\r
-                       return new Db(ds.getConnection());\r
-               } catch (SQLException e) {\r
-                       throw new IciqlException(e);\r
-               }\r
-       }\r
-\r
-       public static Db open(Connection conn) {\r
-               return new Db(conn);\r
-       }\r
-\r
-\r
-\r
-       /**\r
-        * Convenience function to avoid import statements in application code.\r
-        */\r
-       public void activateConsoleLogger() {\r
-               IciqlLogger.activateConsoleLogger();\r
-       }\r
-\r
-       /**\r
-        * Convenience function to avoid import statements in application code.\r
-        */\r
-       public void deactivateConsoleLogger() {\r
-               IciqlLogger.deactivateConsoleLogger();\r
-       }\r
-\r
-       public <T> void insert(T t) {\r
-               Class<?> clazz = t.getClass();\r
-               long rc = define(clazz).createIfRequired(this).insert(this, t, false);\r
-               if (rc == 0) {\r
-                       throw new IciqlException("Failed to insert {0}.  Affected rowcount == 0.", t);\r
-               }\r
-       }\r
-\r
-       public <T> long insertAndGetKey(T t) {\r
-               Class<?> clazz = t.getClass();\r
-               return define(clazz).createIfRequired(this).insert(this, t, true);\r
-       }\r
-\r
-       /**\r
-        * Merge INSERTS if the record does not exist or UPDATES the record if it\r
-        * does exist. Not all databases support MERGE and the syntax varies with\r
-        * the database.\r
-        *\r
-        * If the database does not support a MERGE syntax the dialect can try to\r
-        * simulate a merge by implementing:\r
-        * <p>\r
-        * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0)\r
-        * <p>\r
-        * iciql will check the affected row count returned by the internal merge\r
-        * method and if the affected row count = 0, it will issue an update.\r
-        * <p>\r
-        * See the Derby dialect for an implementation of this technique.\r
-        * <p>\r
-        * If the dialect does not support merge an IciqlException will be thrown.\r
-        *\r
-        * @param t\r
-        */\r
-       public <T> void merge(T t) {\r
-               Class<?> clazz = t.getClass();\r
-               TableDefinition<?> def = define(clazz).createIfRequired(this);\r
-               int rc = def.merge(this, t);\r
-               if (rc == 0) {\r
-                       rc = def.update(this, t);\r
-               }\r
-               if (rc == 0) {\r
-                       throw new IciqlException("merge failed");\r
-               }\r
-       }\r
-\r
-       public <T> int update(T t) {\r
-               Class<?> clazz = t.getClass();\r
-               return define(clazz).createIfRequired(this).update(this, t);\r
-       }\r
-\r
-       public <T> int delete(T t) {\r
-               Class<?> clazz = t.getClass();\r
-               return define(clazz).createIfRequired(this).delete(this, t);\r
-       }\r
-\r
-       public <T extends Object> Query<T> from(T alias) {\r
-               Class<?> clazz = alias.getClass();\r
-               define(clazz).createIfRequired(this);\r
-               return Query.from(this, alias);\r
-       }\r
-\r
-       @SuppressWarnings("unchecked")\r
-       public <T> int dropTable(Class<? extends T> modelClass) {\r
-               TableDefinition<T> def = (TableDefinition<T>) define(modelClass);\r
-               SQLStatement stat = new SQLStatement(this);\r
-               getDialect().prepareDropTable(stat, def);\r
-               IciqlLogger.drop(stat.getSQL());\r
-               int rc = 0;\r
-               try {\r
-                       rc = stat.executeUpdate();\r
-               } catch (IciqlException e) {\r
-                       if (e.getIciqlCode() != IciqlException.CODE_OBJECT_NOT_FOUND) {\r
-                               throw e;\r
-                       }\r
-               }\r
-               // remove this model class from the table definition cache\r
-               classMap.remove(modelClass);\r
-               // remove this model class from the upgrade checked cache\r
-               upgradeChecked.remove(modelClass);\r
-               return rc;\r
-       }\r
-\r
-       @SuppressWarnings("unchecked")\r
-       public <T> int dropView(Class<? extends T> modelClass) {\r
-               TableDefinition<T> def = (TableDefinition<T>) define(modelClass);\r
-               SQLStatement stat = new SQLStatement(this);\r
-               getDialect().prepareDropView(stat, def);\r
-               IciqlLogger.drop(stat.getSQL());\r
-               int rc = 0;\r
-               try {\r
-                       rc = stat.executeUpdate();\r
-               } catch (IciqlException e) {\r
-                       if (e.getIciqlCode() != IciqlException.CODE_OBJECT_NOT_FOUND) {\r
-                               throw e;\r
-                       }\r
-               }\r
-               // remove this model class from the table definition cache\r
-               classMap.remove(modelClass);\r
-               // remove this model class from the upgrade checked cache\r
-               upgradeChecked.remove(modelClass);\r
-               return rc;\r
-       }\r
-\r
-       public <T> List<T> buildObjects(Class<? extends T> modelClass, ResultSet rs) {\r
-               return buildObjects(modelClass, false, rs);\r
-       }\r
-\r
-       @SuppressWarnings("unchecked")\r
-       public <T> List<T> buildObjects(Class<? extends T> modelClass, boolean wildcardSelect, ResultSet rs) {\r
-               List<T> result = new ArrayList<T>();\r
-               TableDefinition<T> def = (TableDefinition<T>) define(modelClass);\r
-               try {\r
-                       int[] columns = def.mapColumns(wildcardSelect, rs);\r
-                       while (rs.next()) {\r
-                               T item = Utils.newObject(modelClass);\r
-                               def.readRow(dialect, item, rs, columns);\r
-                               result.add(item);\r
-                       }\r
-               } catch (SQLException e) {\r
-                       throw new IciqlException(e);\r
-               }\r
-               return result;\r
-       }\r
-\r
-       Db upgradeDb() {\r
-               if (!upgradeChecked.contains(dbUpgrader.getClass())) {\r
-                       // flag as checked immediately because calls are nested.\r
-                       upgradeChecked.add(dbUpgrader.getClass());\r
-\r
-                       IQVersion model = dbUpgrader.getClass().getAnnotation(IQVersion.class);\r
-                       if (model.value() == 0) {\r
-                               // try superclass\r
-                               Class<?> superClass = dbUpgrader.getClass().getSuperclass();\r
-                               if (superClass.isAnnotationPresent(IQVersion.class)) {\r
-                                       model = superClass.getAnnotation(IQVersion.class);\r
-                               }\r
-                       }\r
-                       if (model.value() > 0) {\r
-                               DbVersion v = new DbVersion();\r
-                               // (SCHEMA="" && TABLE="") == DATABASE\r
-                               DbVersion dbVersion = from(v).where(v.schemaName).is("").and(v.tableName).is("")\r
-                                               .selectFirst();\r
-                               if (dbVersion == null) {\r
-                                       // database has no version registration, but model specifies\r
-                                       // version: insert DbVersion entry and return.\r
-                                       DbVersion newDb = new DbVersion(model.value());\r
-                                       // database is an older version than the model\r
-                                       boolean success = dbUpgrader.upgradeDatabase(this, 0, newDb.version);\r
-                                       if (success) {\r
-                                               insert(newDb);\r
-                                       }\r
-                               } else {\r
-                                       // database has a version registration:\r
-                                       // check to see if upgrade is required.\r
-                                       if ((model.value() > dbVersion.version) && (dbUpgrader != null)) {\r
-                                               // database is an older version than the model\r
-                                               boolean success = dbUpgrader.upgradeDatabase(this, dbVersion.version, model.value());\r
-                                               if (success) {\r
-                                                       dbVersion.version = model.value();\r
-                                                       update(dbVersion);\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               return this;\r
-       }\r
-\r
-       <T> void upgradeTable(TableDefinition<T> model) {\r
-               if (!upgradeChecked.contains(model.getModelClass())) {\r
-                       // flag is checked immediately because calls are nested\r
-                       upgradeChecked.add(model.getModelClass());\r
-\r
-                       if (model.tableVersion > 0) {\r
-                               // table is using iciql version tracking.\r
-                               DbVersion v = new DbVersion();\r
-                               String schema = StringUtils.isNullOrEmpty(model.schemaName) ? "" : model.schemaName;\r
-                               DbVersion dbVersion = from(v).where(v.schemaName).is(schema).and(v.tableName)\r
-                                               .is(model.tableName).selectFirst();\r
-                               if (dbVersion == null) {\r
-                                       // table has no version registration, but model specifies\r
-                                       // version: insert DbVersion entry\r
-                                       DbVersion newTable = new DbVersion(model.tableVersion);\r
-                                       newTable.schemaName = schema;\r
-                                       newTable.tableName = model.tableName;\r
-                                       insert(newTable);\r
-                               } else {\r
-                                       // table has a version registration:\r
-                                       // check if upgrade is required\r
-                                       if ((model.tableVersion > dbVersion.version) && (dbUpgrader != null)) {\r
-                                               // table is an older version than model\r
-                                               boolean success = dbUpgrader.upgradeTable(this, schema, model.tableName,\r
-                                                               dbVersion.version, model.tableVersion);\r
-                                               if (success) {\r
-                                                       dbVersion.version = model.tableVersion;\r
-                                                       update(dbVersion);\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       <T> TableDefinition<T> define(Class<T> clazz) {\r
-               TableDefinition<T> def = getTableDefinition(clazz);\r
-               if (def == null) {\r
-                       upgradeDb();\r
-                       def = new TableDefinition<T>(clazz);\r
-                       def.mapFields(this);\r
-                       classMap.put(clazz, def);\r
-                       if (Iciql.class.isAssignableFrom(clazz)) {\r
-                               T t = instance(clazz);\r
-                               Iciql table = (Iciql) t;\r
-                               Define.define(def, table);\r
-                       } else if (clazz.isAnnotationPresent(IQTable.class)) {\r
-                               // annotated classes skip the Define().define() static\r
-                               // initializer\r
-                               T t = instance(clazz);\r
-                               def.mapObject(t);\r
-                       } else if (clazz.isAnnotationPresent(IQView.class)) {\r
-                               // annotated classes skip the Define().define() static\r
-                               // initializer\r
-                               T t = instance(clazz);\r
-                               def.mapObject(t);\r
-                       }\r
-               }\r
-               return def;\r
-       }\r
-\r
-       <T> boolean hasCreated(Class<T> clazz) {\r
-               return upgradeChecked.contains(clazz);\r
-       }\r
-\r
-       public synchronized void setDbUpgrader(DbUpgrader upgrader) {\r
-               if (!upgrader.getClass().isAnnotationPresent(IQVersion.class)) {\r
-                       throw new IciqlException("DbUpgrader must be annotated with " + IQVersion.class.getSimpleName());\r
-               }\r
-               this.dbUpgrader = upgrader;\r
-               upgradeChecked.clear();\r
-       }\r
-\r
-       public SQLDialect getDialect() {\r
-               return dialect;\r
-       }\r
-\r
-       public Connection getConnection() {\r
-               return conn;\r
-       }\r
-\r
-       @Override\r
-       public void close() {\r
-               try {\r
-                       conn.close();\r
-               } catch (Exception e) {\r
-                       throw new IciqlException(e);\r
-               }\r
-       }\r
-\r
-       public <A> TestCondition<A> test(A x) {\r
-               return new TestCondition<A>(x);\r
-       }\r
-\r
-       public <T> void insertAll(List<T> list) {\r
-               if (list.size() == 0) {\r
-                       return;\r
-               }\r
-               Savepoint savepoint = null;\r
-               try {\r
-                       Class<?> clazz = list.get(0).getClass();\r
-                       TableDefinition<?> def = define(clazz).createIfRequired(this);\r
-                       savepoint = prepareSavepoint();\r
-                       for (T t : list) {\r
-                               PreparedStatement ps = def.createInsertStatement(this, t, false);\r
-                               int rc = ps.executeUpdate();\r
-                               if (rc == 0) {\r
-                                       throw new IciqlException("Failed to insert {0}.  Affected rowcount == 0.", t);\r
-                               }\r
-                       }\r
-                       commit(savepoint);\r
-               } catch (SQLException e) {\r
-                       rollback(savepoint);\r
-                       throw new IciqlException(e);\r
-               } catch (IciqlException e) {\r
-                       rollback(savepoint);\r
-                       throw e;\r
-               }\r
-       }\r
-\r
-       public <T> List<Long> insertAllAndGetKeys(List<T> list) {\r
-               List<Long> identities = new ArrayList<Long>();\r
-               if (list.size() == 0) {\r
-                       return identities;\r
-               }\r
-               Savepoint savepoint = null;\r
-               try {\r
-                       Class<?> clazz = list.get(0).getClass();\r
-                       TableDefinition<?> def = define(clazz).createIfRequired(this);\r
-                       savepoint = prepareSavepoint();\r
-                       for (T t : list) {\r
-                               long key = def.insert(this,  t, true);\r
-                               identities.add(key);\r
-                       }\r
-                       commit(savepoint);\r
-               } catch (IciqlException e) {\r
-                       rollback(savepoint);\r
-                       throw e;\r
-               }\r
-               return identities;\r
-       }\r
-\r
-       public <T> void updateAll(List<T> list) {\r
-               if (list.size() == 0) {\r
-                       return;\r
-               }\r
-               Savepoint savepoint = null;\r
-               try {\r
-                       Class<?> clazz = list.get(0).getClass();\r
-                       TableDefinition<?> def = define(clazz).createIfRequired(this);\r
-                       savepoint = prepareSavepoint();\r
-                       for (T t : list) {\r
-                               def.update(this, t);\r
-                       }\r
-                       commit(savepoint);\r
-               } catch (IciqlException e) {\r
-                       rollback(savepoint);\r
-                       throw e;\r
-               }\r
-       }\r
-\r
-       public <T> void deleteAll(List<T> list) {\r
-               if (list.size() == 0) {\r
-                       return;\r
-               }\r
-               Savepoint savepoint = null;\r
-               try {\r
-                       Class<?> clazz = list.get(0).getClass();\r
-                       TableDefinition<?> def = define(clazz).createIfRequired(this);\r
-                       savepoint = prepareSavepoint();\r
-                       for (T t : list) {\r
-                               def.delete(this,  t);\r
-                       }\r
-                       commit(savepoint);\r
-               } catch (IciqlException e) {\r
-                       rollback(savepoint);\r
-                       throw e;\r
-               }\r
-       }\r
-\r
-       PreparedStatement prepare(String sql, boolean returnGeneratedKeys) {\r
-               IciqlException.checkUnmappedField(sql);\r
-               try {\r
-                       if (returnGeneratedKeys) {\r
-                               return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);\r
-                       }\r
-                       return conn.prepareStatement(sql);\r
-               } catch (SQLException e) {\r
-                       throw IciqlException.fromSQL(sql, e);\r
-               }\r
-       }\r
-\r
-       Savepoint prepareSavepoint() {\r
-               // don't change auto-commit mode.\r
-               // don't create save point.\r
-               if (!autoSavePoint || !dialect.supportsSavePoints()) {\r
-                       return null;\r
-               }\r
-               // create a savepoint\r
-               Savepoint savepoint = null;\r
-               try {\r
-                       conn.setAutoCommit(false);\r
-                       savepoint = conn.setSavepoint();\r
-               } catch (SQLFeatureNotSupportedException e) {\r
-                       // jdbc driver does not support save points\r
-               } catch (SQLException e) {\r
-                       throw new IciqlException(e, "Could not create save point");\r
-               }\r
-               return savepoint;\r
-       }\r
-\r
-       void commit(Savepoint savepoint) {\r
-               if (savepoint != null) {\r
-                       try {\r
-                               conn.commit();\r
-                               conn.setAutoCommit(true);\r
-                       } catch (SQLException e) {\r
-                               throw new IciqlException(e, "Failed to commit pending transactions");\r
-                       }\r
-               }\r
-       }\r
-\r
-       void rollback(Savepoint savepoint) {\r
-               if (savepoint != null) {\r
-                       try {\r
-                               conn.rollback(savepoint);\r
-                               conn.setAutoCommit(true);\r
-                       } catch (SQLException s) {\r
-                               throw new IciqlException(s, "Failed to rollback transactions");\r
-                       }\r
-               }\r
-       }\r
-\r
-       @SuppressWarnings("unchecked")\r
-       <T> TableDefinition<T> getTableDefinition(Class<T> clazz) {\r
-               return (TableDefinition<T>) classMap.get(clazz);\r
-       }\r
-\r
-       /**\r
-        * Run a SQL query directly against the database.\r
-        *\r
-        * Be sure to close the ResultSet with\r
-        *\r
-        * <pre>\r
-        * JdbcUtils.closeSilently(rs, true);\r
-        * </pre>\r
-        *\r
-        * @param sql\r
-        *            the SQL statement\r
-        * @param args\r
-        *            optional object arguments for x=? tokens in query\r
-        * @return the result set\r
-        */\r
-       public ResultSet executeQuery(String sql, List<?> args) {\r
-               return executeQuery(sql, args.toArray());\r
-       }\r
-\r
-       /**\r
-        * Run a SQL query directly against the database.\r
-        *\r
-        * Be sure to close the ResultSet with\r
-        *\r
-        * <pre>\r
-        * JdbcUtils.closeSilently(rs, true);\r
-        * </pre>\r
-        *\r
-        * @param sql\r
-        *            the SQL statement\r
-        * @param args\r
-        *            optional object arguments for x=? tokens in query\r
-        * @return the result set\r
-        */\r
-       public ResultSet executeQuery(String sql, Object... args) {\r
-               try {\r
-                       if (args.length == 0) {\r
-                               return conn.createStatement().executeQuery(sql);\r
-                       } else {\r
-                               PreparedStatement stat = conn.prepareStatement(sql);\r
-                               int i = 1;\r
-                               for (Object arg : args) {\r
-                                       stat.setObject(i++, arg);\r
-                               }\r
-                               return stat.executeQuery();\r
-                       }\r
-               } catch (SQLException e) {\r
-                       throw new IciqlException(e);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Run a SQL query directly against the database and map the results to the\r
-        * model class.\r
-        *\r
-        * @param modelClass\r
-        *            the model class to bind the query ResultSet rows into.\r
-        * @param sql\r
-        *            the SQL statement\r
-        * @return the result set\r
-        */\r
-       public <T> List<T> executeQuery(Class<? extends T> modelClass, String sql, List<?> args) {\r
-               return executeQuery(modelClass, sql, args.toArray());\r
-       }\r
-\r
-       /**\r
-        * Run a SQL query directly against the database and map the results to the\r
-        * model class.\r
-        *\r
-        * @param modelClass\r
-        *            the model class to bind the query ResultSet rows into.\r
-        * @param sql\r
-        *            the SQL statement\r
-        * @return the result set\r
-        */\r
-       public <T> List<T> executeQuery(Class<? extends T> modelClass, String sql, Object... args) {\r
-               ResultSet rs = null;\r
-               try {\r
-                       if (args.length == 0) {\r
-                               rs = conn.createStatement().executeQuery(sql);\r
-                       } else {\r
-                               PreparedStatement stat = conn.prepareStatement(sql);\r
-                               int i = 1;\r
-                               for (Object arg : args) {\r
-                                       stat.setObject(i++, arg);\r
-                               }\r
-                               rs = stat.executeQuery();\r
-                       }\r
-                       boolean wildcardSelect = sql.toLowerCase().startsWith("select *")\r
-                                       || sql.toLowerCase().startsWith("select distinct *");\r
-                       return buildObjects(modelClass, wildcardSelect, rs);\r
-               } catch (SQLException e) {\r
-                       throw new IciqlException(e);\r
-               } finally {\r
-                       JdbcUtils.closeSilently(rs, true);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Run a SQL statement directly against the database.\r
-        *\r
-        * @param sql\r
-        *            the SQL statement\r
-        * @return the update count\r
-        */\r
-       public int executeUpdate(String sql, Object... args) {\r
-               Statement stat = null;\r
-               try {\r
-                       int updateCount;\r
-                       if (args.length == 0) {\r
-                               stat = conn.createStatement();\r
-                               updateCount = stat.executeUpdate(sql);\r
-                       } else {\r
-                               PreparedStatement ps = conn.prepareStatement(sql);\r
-                               int i = 1;\r
-                               for (Object arg : args) {\r
-                                       ps.setObject(i++, arg);\r
-                               }\r
-                               updateCount = ps.executeUpdate();\r
-                               stat = ps;\r
-                       }\r
-                       return updateCount;\r
-               } catch (SQLException e) {\r
-                       throw new IciqlException(e);\r
-               } finally {\r
-                       JdbcUtils.closeSilently(stat);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Allow to enable/disable globally createIfRequired in TableDefinition.\r
-        * For advanced user wanting to gain full control of transactions.\r
-        * Default value is false.\r
-        * @param skipCreate\r
-        */\r
-       public void setSkipCreate(boolean skipCreate) {\r
-               this.skipCreate = skipCreate;\r
-       }\r
-\r
-       public boolean getSkipCreate() {\r
-               return this.skipCreate;\r
-       }\r
-\r
-       /**\r
-        * Allow to enable/disable usage of save point.\r
-        * For advanced user wanting to gain full control of transactions.\r
-        * Default value is false.\r
-        * @param autoSavePoint\r
-        */\r
-       public void setAutoSavePoint(boolean autoSavePoint) {\r
-               this.autoSavePoint = autoSavePoint;\r
-       }\r
-\r
-       public boolean getAutoSavePoint() {\r
-               return this.autoSavePoint;\r
-       }\r
-\r
-}\r
+/*
+ * Copyright 2004-2011 H2 Group.
+ * Copyright 2011 James Moger.
+ * Copyright 2012 Frédéric Gaillard.
+ * Copyright 2012 Alex Telepov.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.iciql;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.sql.DataSource;
+
+import com.iciql.DbUpgrader.DefaultDbUpgrader;
+import com.iciql.Iciql.IQTable;
+import com.iciql.Iciql.IQVersion;
+import com.iciql.Iciql.IQView;
+import com.iciql.util.IciqlLogger;
+import com.iciql.util.JdbcUtils;
+import com.iciql.util.StringUtils;
+import com.iciql.util.Utils;
+import com.iciql.util.WeakIdentityHashMap;
+
+/**
+ * This class represents a connection to a database.
+ */
+
+public class Db implements AutoCloseable {
+
+       /**
+        * This map It holds unique tokens that are generated by functions such as
+        * Function.sum(..) in "db.from(p).select(Function.sum(p.unitPrice))". It
+        * doesn't actually hold column tokens, as those are bound to the query
+        * itself.
+        */
+       private static final Map<Object, Token> TOKENS;
+
+       private static final Map<String, Class<? extends SQLDialect>> DIALECTS;
+
+       private final Connection conn;
+       private final Map<Class<?>, TableDefinition<?>> classMap = Collections
+                       .synchronizedMap(new HashMap<Class<?>, TableDefinition<?>>());
+       private final SQLDialect dialect;
+       private DbUpgrader dbUpgrader = new DefaultDbUpgrader();
+       private final Set<Class<?>> upgradeChecked = Collections.synchronizedSet(new HashSet<Class<?>>());
+
+       private boolean skipCreate;
+       private boolean autoSavePoint = true;
+
+       static {
+               TOKENS = Collections.synchronizedMap(new WeakIdentityHashMap<Object, Token>());
+               DIALECTS = Collections.synchronizedMap(new HashMap<String, Class<? extends SQLDialect>>());
+               // can register by...
+               // 1. Connection class name
+               // 2. DatabaseMetaData.getDatabaseProductName()
+               DIALECTS.put("Apache Derby", SQLDialectDerby.class);
+               DIALECTS.put("H2", SQLDialectH2.class);
+               DIALECTS.put("HSQL Database Engine", SQLDialectHSQL.class);
+               DIALECTS.put("MySQL", SQLDialectMySQL.class);
+               DIALECTS.put("PostgreSQL", SQLDialectPostgreSQL.class);
+           DIALECTS.put("Microsoft SQL Server", SQLDialectMSSQL.class);
+           DIALECTS.put("SQLite", SQLDialectSQLite.class);
+       }
+
+       private Db(Connection conn) {
+               this.conn = conn;
+               String databaseName = null;
+               try {
+                       DatabaseMetaData data = conn.getMetaData();
+                       databaseName = data.getDatabaseProductName();
+               } catch (SQLException s) {
+                       throw new IciqlException(s, "failed to retrieve database metadata!");
+               }
+               dialect = getDialect(databaseName, conn.getClass().getName());
+               dialect.configureDialect(this);
+       }
+
+       /**
+        * Register a new/custom dialect class. You can use this method to replace
+        * any existing dialect or to add a new one.
+        *
+        * @param token
+        *            the fully qualified name of the connection class or the
+        *            expected result of DatabaseMetaData.getDatabaseProductName()
+        * @param dialectClass
+        *            the dialect class to register
+        */
+       public static void registerDialect(String token, Class<? extends SQLDialect> dialectClass) {
+               DIALECTS.put(token, dialectClass);
+       }
+
+       SQLDialect getDialect(String databaseName, String className) {
+               Class<? extends SQLDialect> dialectClass = null;
+               if (DIALECTS.containsKey(className)) {
+                       // dialect registered by connection class name
+                       dialectClass = DIALECTS.get(className);
+               } else if (DIALECTS.containsKey(databaseName)) {
+                       // dialect registered by database name
+                       dialectClass = DIALECTS.get(databaseName);
+               } else {
+                       // did not find a match, use default
+                       dialectClass = SQLDialectDefault.class;
+               }
+               return instance(dialectClass);
+       }
+
+       static <X> X registerToken(X x, Token token) {
+               TOKENS.put(x, token);
+               return x;
+       }
+
+       static Token getToken(Object x) {
+               return TOKENS.get(x);
+       }
+
+       static <T> T instance(Class<T> clazz) {
+               try {
+                       return clazz.newInstance();
+               } catch (Exception e) {
+                       throw new IciqlException(e);
+               }
+       }
+
+       public static Db open(String url) {
+               try {
+                       Connection conn = JdbcUtils.getConnection(null, url, null, null);
+                       return new Db(conn);
+               } catch (SQLException e) {
+                       throw new IciqlException(e);
+               }
+       }
+
+       public static Db open(String url, String user, String password) {
+               try {
+                       Connection conn = JdbcUtils.getConnection(null, url, user, password);
+                       return new Db(conn);
+               } catch (SQLException e) {
+                       throw new IciqlException(e);
+               }
+       }
+
+       public static Db open(String url, String user, char[] password) {
+               try {
+                       Connection conn = JdbcUtils.getConnection(null, url, user, password == null ? null : new String(password));
+                       return new Db(conn);
+               } catch (SQLException e) {
+                       throw new IciqlException(e);
+               }
+       }
+
+       /**
+        * Create a new database instance using a data source. This method is fast,
+        * so that you can always call open() / close() on usage.
+        *
+        * @param ds
+        *            the data source
+        * @return the database instance.
+        */
+       public static Db open(DataSource ds) {
+               try {
+                       return new Db(ds.getConnection());
+               } catch (SQLException e) {
+                       throw new IciqlException(e);
+               }
+       }
+
+       public static Db open(Connection conn) {
+               return new Db(conn);
+       }
+
+
+
+       /**
+        * Convenience function to avoid import statements in application code.
+        */
+       public void activateConsoleLogger() {
+               IciqlLogger.activateConsoleLogger();
+       }
+
+       /**
+        * Convenience function to avoid import statements in application code.
+        */
+       public void deactivateConsoleLogger() {
+               IciqlLogger.deactivateConsoleLogger();
+       }
+
+       public <T> boolean insert(T t) {
+               Class<?> clazz = t.getClass();
+               long rc = define(clazz).createIfRequired(this).insert(this, t, false);
+               if (rc == 0) {
+                       throw new IciqlException("Failed to insert {0}.  Affected rowcount == 0.", t);
+               }
+               return rc == 1;
+       }
+
+       public <T> long insertAndGetKey(T t) {
+               Class<?> clazz = t.getClass();
+               return define(clazz).createIfRequired(this).insert(this, t, true);
+       }
+
+       /**
+        * Upsert INSERTS if the record does not exist or UPDATES the record if it
+        * does exist. Not all databases support MERGE and the syntax varies with
+        * the database.
+        *
+        * If the database does not support a MERGE or INSERT OR REPLACE INTO syntax
+        * the dialect can try to simulate a merge by implementing:
+        * <p>
+        * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0)
+        * <p>
+        * iciql will check the affected row count returned by the internal merge
+        * method and if the affected row count = 0, it will issue an update.
+        * <p>
+        * See the Derby dialect for an implementation of this technique.
+        * <p>
+        * If the dialect does not support merge an IciqlException will be thrown.
+        *
+        * @param t
+        */
+       public <T> void upsert(T t) {
+               Class<?> clazz = t.getClass();
+               TableDefinition<?> def = define(clazz).createIfRequired(this);
+               int rc = def.merge(this, t);
+               if (rc == 0) {
+                       rc = def.update(this, t);
+               }
+               if (rc == 0) {
+                       throw new IciqlException("upsert failed");
+               }
+       }
+
+       /**
+        * Merge INSERTS if the record does not exist or UPDATES the record if it
+        * does exist. Not all databases support MERGE and the syntax varies with
+        * the database.
+        *
+        * If the database does not support a MERGE or INSERT OR REPLACE INTO syntax
+        * the dialect can try to simulate a merge by implementing:
+        * <p>
+        * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0)
+        * <p>
+        * iciql will check the affected row count returned by the internal merge
+        * method and if the affected row count = 0, it will issue an update.
+        * <p>
+        * See the Derby dialect for an implementation of this technique.
+        * <p>
+        * If the dialect does not support merge an IciqlException will be thrown.
+        *
+        * @param t
+        */
+       public <T> void merge(T t) {
+               upsert(t);
+       }
+
+       public <T> boolean update(T t) {
+               Class<?> clazz = t.getClass();
+               return define(clazz).createIfRequired(this).update(this, t) == 1;
+       }
+
+       public <T> boolean delete(T t) {
+               Class<?> clazz = t.getClass();
+               return define(clazz).createIfRequired(this).delete(this, t) == 1;
+       }
+
+       public <T extends Object> Query<T> from(T alias) {
+               Class<?> clazz = alias.getClass();
+               define(clazz).createIfRequired(this);
+               return Query.from(this, alias);
+       }
+
+       @SuppressWarnings("unchecked")
+       public <T> boolean dropTable(Class<? extends T> modelClass) {
+               TableDefinition<T> def = (TableDefinition<T>) define(modelClass);
+               SQLStatement stat = new SQLStatement(this);
+               getDialect().prepareDropTable(stat, def);
+               IciqlLogger.drop(stat.getSQL());
+               int rc = 0;
+               try {
+                       rc = stat.executeUpdate();
+               } catch (IciqlException e) {
+                       if (e.getIciqlCode() != IciqlException.CODE_OBJECT_NOT_FOUND) {
+                               throw e;
+                       }
+               }
+               // remove this model class from the table definition cache
+               classMap.remove(modelClass);
+               // remove this model class from the upgrade checked cache
+               upgradeChecked.remove(modelClass);
+               return rc == 1;
+       }
+
+       @SuppressWarnings("unchecked")
+       public <T> boolean dropView(Class<? extends T> modelClass) {
+               TableDefinition<T> def = (TableDefinition<T>) define(modelClass);
+               SQLStatement stat = new SQLStatement(this);
+               getDialect().prepareDropView(stat, def);
+               IciqlLogger.drop(stat.getSQL());
+               int rc = 0;
+               try {
+                       rc = stat.executeUpdate();
+               } catch (IciqlException e) {
+                       if (e.getIciqlCode() != IciqlException.CODE_OBJECT_NOT_FOUND) {
+                               throw e;
+                       }
+               }
+               // remove this model class from the table definition cache
+               classMap.remove(modelClass);
+               // remove this model class from the upgrade checked cache
+               upgradeChecked.remove(modelClass);
+               return rc == 1;
+       }
+
+       public <T> List<T> buildObjects(Class<? extends T> modelClass, ResultSet rs) {
+               return buildObjects(modelClass, false, rs);
+       }
+
+       @SuppressWarnings("unchecked")
+       public <T> List<T> buildObjects(Class<? extends T> modelClass, boolean wildcardSelect, ResultSet rs) {
+               List<T> result = new ArrayList<T>();
+               TableDefinition<T> def = (TableDefinition<T>) define(modelClass);
+               try {
+                       int[] columns = def.mapColumns(wildcardSelect, rs);
+                       while (rs.next()) {
+                               T item = Utils.newObject(modelClass);
+                               def.readRow(dialect, item, rs, columns);
+                               result.add(item);
+                       }
+               } catch (SQLException e) {
+                       throw new IciqlException(e);
+               }
+               return result;
+       }
+
+       Db upgradeDb() {
+               if (!upgradeChecked.contains(dbUpgrader.getClass())) {
+                       // flag as checked immediately because calls are nested.
+                       upgradeChecked.add(dbUpgrader.getClass());
+
+                       IQVersion model = dbUpgrader.getClass().getAnnotation(IQVersion.class);
+                       if (model.value() == 0) {
+                               // try superclass
+                               Class<?> superClass = dbUpgrader.getClass().getSuperclass();
+                               if (superClass.isAnnotationPresent(IQVersion.class)) {
+                                       model = superClass.getAnnotation(IQVersion.class);
+                               }
+                       }
+                       if (model.value() > 0) {
+                               DbVersion v = new DbVersion();
+                               // (SCHEMA="" && TABLE="") == DATABASE
+                               DbVersion dbVersion = from(v).where(v.schemaName).is("").and(v.tableName).is("")
+                                               .selectFirst();
+                               if (dbVersion == null) {
+                                       // database has no version registration, but model specifies
+                                       // version: insert DbVersion entry and return.
+                                       DbVersion newDb = new DbVersion(model.value());
+                                       // database is an older version than the model
+                                       boolean success = dbUpgrader.upgradeDatabase(this, 0, newDb.version);
+                                       if (success) {
+                                               insert(newDb);
+                                       }
+                               } else {
+                                       // database has a version registration:
+                                       // check to see if upgrade is required.
+                                       if ((model.value() > dbVersion.version) && (dbUpgrader != null)) {
+                                               // database is an older version than the model
+                                               boolean success = dbUpgrader.upgradeDatabase(this, dbVersion.version, model.value());
+                                               if (success) {
+                                                       dbVersion.version = model.value();
+                                                       update(dbVersion);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return this;
+       }
+
+       <T> void upgradeTable(TableDefinition<T> model) {
+               if (!upgradeChecked.contains(model.getModelClass())) {
+                       // flag is checked immediately because calls are nested
+                       upgradeChecked.add(model.getModelClass());
+
+                       if (model.tableVersion > 0) {
+                               // table is using iciql version tracking.
+                               DbVersion v = new DbVersion();
+                               String schema = StringUtils.isNullOrEmpty(model.schemaName) ? "" : model.schemaName;
+                               DbVersion dbVersion = from(v).where(v.schemaName).is(schema).and(v.tableName)
+                                               .is(model.tableName).selectFirst();
+                               if (dbVersion == null) {
+                                       // table has no version registration, but model specifies
+                                       // version: insert DbVersion entry
+                                       DbVersion newTable = new DbVersion(model.tableVersion);
+                                       newTable.schemaName = schema;
+                                       newTable.tableName = model.tableName;
+                                       insert(newTable);
+                               } else {
+                                       // table has a version registration:
+                                       // check if upgrade is required
+                                       if ((model.tableVersion > dbVersion.version) && (dbUpgrader != null)) {
+                                               // table is an older version than model
+                                               boolean success = dbUpgrader.upgradeTable(this, schema, model.tableName,
+                                                               dbVersion.version, model.tableVersion);
+                                               if (success) {
+                                                       dbVersion.version = model.tableVersion;
+                                                       update(dbVersion);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       <T> TableDefinition<T> define(Class<T> clazz) {
+               TableDefinition<T> def = getTableDefinition(clazz);
+               if (def == null) {
+                       upgradeDb();
+                       def = new TableDefinition<T>(clazz);
+                       def.mapFields(this);
+                       classMap.put(clazz, def);
+                       if (Iciql.class.isAssignableFrom(clazz)) {
+                               T t = instance(clazz);
+                               Iciql table = (Iciql) t;
+                               Define.define(def, table);
+                       } else if (clazz.isAnnotationPresent(IQTable.class)) {
+                               // annotated classes skip the Define().define() static
+                               // initializer
+                               T t = instance(clazz);
+                               def.mapObject(t);
+                       } else if (clazz.isAnnotationPresent(IQView.class)) {
+                               // annotated classes skip the Define().define() static
+                               // initializer
+                               T t = instance(clazz);
+                               def.mapObject(t);
+                       }
+               }
+               return def;
+       }
+
+       <T> boolean hasCreated(Class<T> clazz) {
+               return upgradeChecked.contains(clazz);
+       }
+
+       public synchronized void setDbUpgrader(DbUpgrader upgrader) {
+               if (!upgrader.getClass().isAnnotationPresent(IQVersion.class)) {
+                       throw new IciqlException("DbUpgrader must be annotated with " + IQVersion.class.getSimpleName());
+               }
+               this.dbUpgrader = upgrader;
+               upgradeChecked.clear();
+       }
+
+       public SQLDialect getDialect() {
+               return dialect;
+       }
+
+       public Connection getConnection() {
+               return conn;
+       }
+
+       @Override
+       public void close() {
+               try {
+                       conn.close();
+               } catch (Exception e) {
+                       throw new IciqlException(e);
+               }
+       }
+
+       public <A> TestCondition<A> test(A x) {
+               return new TestCondition<A>(x);
+       }
+
+       public <T> void insertAll(List<T> list) {
+               if (list.size() == 0) {
+                       return;
+               }
+               Savepoint savepoint = null;
+               try {
+                       Class<?> clazz = list.get(0).getClass();
+                       TableDefinition<?> def = define(clazz).createIfRequired(this);
+                       savepoint = prepareSavepoint();
+                       for (T t : list) {
+                               PreparedStatement ps = def.createInsertStatement(this, t, false);
+                               int rc = ps.executeUpdate();
+                               if (rc == 0) {
+                                       throw new IciqlException("Failed to insert {0}.  Affected rowcount == 0.", t);
+                               }
+                       }
+                       commit(savepoint);
+               } catch (SQLException e) {
+                       rollback(savepoint);
+                       throw new IciqlException(e);
+               } catch (IciqlException e) {
+                       rollback(savepoint);
+                       throw e;
+               }
+       }
+
+       public <T> List<Long> insertAllAndGetKeys(List<T> list) {
+               List<Long> identities = new ArrayList<Long>();
+               if (list.size() == 0) {
+                       return identities;
+               }
+               Savepoint savepoint = null;
+               try {
+                       Class<?> clazz = list.get(0).getClass();
+                       TableDefinition<?> def = define(clazz).createIfRequired(this);
+                       savepoint = prepareSavepoint();
+                       for (T t : list) {
+                               long key = def.insert(this,  t, true);
+                               identities.add(key);
+                       }
+                       commit(savepoint);
+               } catch (IciqlException e) {
+                       rollback(savepoint);
+                       throw e;
+               }
+               return identities;
+       }
+
+       public <T> void updateAll(List<T> list) {
+               if (list.size() == 0) {
+                       return;
+               }
+               Savepoint savepoint = null;
+               try {
+                       Class<?> clazz = list.get(0).getClass();
+                       TableDefinition<?> def = define(clazz).createIfRequired(this);
+                       savepoint = prepareSavepoint();
+                       for (T t : list) {
+                               def.update(this, t);
+                       }
+                       commit(savepoint);
+               } catch (IciqlException e) {
+                       rollback(savepoint);
+                       throw e;
+               }
+       }
+
+       public <T> void deleteAll(List<T> list) {
+               if (list.size() == 0) {
+                       return;
+               }
+               Savepoint savepoint = null;
+               try {
+                       Class<?> clazz = list.get(0).getClass();
+                       TableDefinition<?> def = define(clazz).createIfRequired(this);
+                       savepoint = prepareSavepoint();
+                       for (T t : list) {
+                               def.delete(this,  t);
+                       }
+                       commit(savepoint);
+               } catch (IciqlException e) {
+                       rollback(savepoint);
+                       throw e;
+               }
+       }
+
+       PreparedStatement prepare(String sql, boolean returnGeneratedKeys) {
+               IciqlException.checkUnmappedField(sql);
+               try {
+                       if (returnGeneratedKeys) {
+                               return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
+                       }
+                       return conn.prepareStatement(sql);
+               } catch (SQLException e) {
+                       throw IciqlException.fromSQL(sql, e);
+               }
+       }
+
+       Savepoint prepareSavepoint() {
+               // don't change auto-commit mode.
+               // don't create save point.
+               if (!autoSavePoint || !dialect.supportsSavePoints()) {
+                       return null;
+               }
+               // create a savepoint
+               Savepoint savepoint = null;
+               try {
+                       conn.setAutoCommit(false);
+                       savepoint = conn.setSavepoint();
+               } catch (SQLFeatureNotSupportedException e) {
+                       // jdbc driver does not support save points
+               } catch (SQLException e) {
+                       throw new IciqlException(e, "Could not create save point");
+               }
+               return savepoint;
+       }
+
+       void commit(Savepoint savepoint) {
+               if (savepoint != null) {
+                       try {
+                               conn.commit();
+                               conn.setAutoCommit(true);
+                       } catch (SQLException e) {
+                               throw new IciqlException(e, "Failed to commit pending transactions");
+                       }
+               }
+       }
+
+       void rollback(Savepoint savepoint) {
+               if (savepoint != null) {
+                       try {
+                               conn.rollback(savepoint);
+                               conn.setAutoCommit(true);
+                       } catch (SQLException s) {
+                               throw new IciqlException(s, "Failed to rollback transactions");
+                       }
+               }
+       }
+
+       @SuppressWarnings("unchecked")
+       <T> TableDefinition<T> getTableDefinition(Class<T> clazz) {
+               return (TableDefinition<T>) classMap.get(clazz);
+       }
+
+       /**
+        * Run a SQL query directly against the database.
+        *
+        * Be sure to close the ResultSet with
+        *
+        * <pre>
+        * JdbcUtils.closeSilently(rs, true);
+        * </pre>
+        *
+        * @param sql
+        *            the SQL statement
+        * @param args
+        *            optional object arguments for x=? tokens in query
+        * @return the result set
+        */
+       public ResultSet executeQuery(String sql, List<?> args) {
+               return executeQuery(sql, args.toArray());
+       }
+
+       /**
+        * Run a SQL query directly against the database.
+        *
+        * Be sure to close the ResultSet with
+        *
+        * <pre>
+        * JdbcUtils.closeSilently(rs, true);
+        * </pre>
+        *
+        * @param sql
+        *            the SQL statement
+        * @param args
+        *            optional object arguments for x=? tokens in query
+        * @return the result set
+        */
+       public ResultSet executeQuery(String sql, Object... args) {
+               try {
+                       if (args.length == 0) {
+                               return conn.createStatement().executeQuery(sql);
+                       } else {
+                               PreparedStatement stat = conn.prepareStatement(sql);
+                               int i = 1;
+                               for (Object arg : args) {
+                                       stat.setObject(i++, arg);
+                               }
+                               return stat.executeQuery();
+                       }
+               } catch (SQLException e) {
+                       throw new IciqlException(e);
+               }
+       }
+
+       /**
+        * Run a SQL query directly against the database and map the results to the
+        * model class.
+        *
+        * @param modelClass
+        *            the model class to bind the query ResultSet rows into.
+        * @param sql
+        *            the SQL statement
+        * @return the result set
+        */
+       public <T> List<T> executeQuery(Class<? extends T> modelClass, String sql, List<?> args) {
+               return executeQuery(modelClass, sql, args.toArray());
+       }
+
+       /**
+        * Run a SQL query directly against the database and map the results to the
+        * model class.
+        *
+        * @param modelClass
+        *            the model class to bind the query ResultSet rows into.
+        * @param sql
+        *            the SQL statement
+        * @return the result set
+        */
+       public <T> List<T> executeQuery(Class<? extends T> modelClass, String sql, Object... args) {
+               ResultSet rs = null;
+               try {
+                       if (args.length == 0) {
+                               rs = conn.createStatement().executeQuery(sql);
+                       } else {
+                               PreparedStatement stat = conn.prepareStatement(sql);
+                               int i = 1;
+                               for (Object arg : args) {
+                                       stat.setObject(i++, arg);
+                               }
+                               rs = stat.executeQuery();
+                       }
+                       boolean wildcardSelect = sql.toLowerCase().startsWith("select *")
+                                       || sql.toLowerCase().startsWith("select distinct *");
+                       return buildObjects(modelClass, wildcardSelect, rs);
+               } catch (SQLException e) {
+                       throw new IciqlException(e);
+               } finally {
+                       JdbcUtils.closeSilently(rs, true);
+               }
+       }
+
+       /**
+        * Run a SQL statement directly against the database.
+        *
+        * @param sql
+        *            the SQL statement
+        * @return the update count
+        */
+       public int executeUpdate(String sql, Object... args) {
+               Statement stat = null;
+               try {
+                       int updateCount;
+                       if (args.length == 0) {
+                               stat = conn.createStatement();
+                               updateCount = stat.executeUpdate(sql);
+                       } else {
+                               PreparedStatement ps = conn.prepareStatement(sql);
+                               int i = 1;
+                               for (Object arg : args) {
+                                       ps.setObject(i++, arg);
+                               }
+                               updateCount = ps.executeUpdate();
+                               stat = ps;
+                       }
+                       return updateCount;
+               } catch (SQLException e) {
+                       throw new IciqlException(e);
+               } finally {
+                       JdbcUtils.closeSilently(stat);
+               }
+       }
+
+       /**
+        * Allow to enable/disable globally createIfRequired in TableDefinition.
+        * For advanced user wanting to gain full control of transactions.
+        * Default value is false.
+        * @param skipCreate
+        */
+       public void setSkipCreate(boolean skipCreate) {
+               this.skipCreate = skipCreate;
+       }
+
+       public boolean getSkipCreate() {
+               return this.skipCreate;
+       }
+
+       /**
+        * Allow to enable/disable usage of save point.
+        * For advanced user wanting to gain full control of transactions.
+        * Default value is false.
+        * @param autoSavePoint
+        */
+       public void setAutoSavePoint(boolean autoSavePoint) {
+               this.autoSavePoint = autoSavePoint;
+       }
+
+       public boolean getAutoSavePoint() {
+               return this.autoSavePoint;
+       }
+
+}
index 3d3811ee0007d1d60943d5815c420bb6914d5ba3..0e8bf20d5989779c7418c2c8e5fd78f7f57459b8 100644 (file)
@@ -79,17 +79,17 @@ public class PrimitivesTest {
 \r
                // test model update\r
                retrievedModel.myInteger = 1337;\r
-               assertEquals(1, db.update(retrievedModel));\r
-               assertEquals(1, db.delete(retrievedModel));\r
+               assertTrue(db.update(retrievedModel));\r
+               assertTrue(db.delete(retrievedModel));\r
 \r
                db.close();\r
        }\r
-       \r
+\r
        @Test\r
        public void testMultipleBooleans() {\r
                Db db = IciqlSuite.openNewDb();\r
                db.insertAll(MultipleBoolsModel.getList());\r
-               \r
+\r
                MultipleBoolsModel m = new MultipleBoolsModel();\r
                try {\r
                        db.from(m).where(m.a).is(true).select();\r