/** * Executes a batch on <code>java.sql.PreparedStatement</code>. * * @param ps <code>java.sql.PreparedStatement</code> to execute batch on. * * @return an array of update counts containing one element for each command in the batch. * The elements of the array are ordered according to the order in which commands were added to the batch. * * @see <a href="http://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#executeBatch()">Statement#executeBatch()</a> */ public int[] executeBatch(PreparedStatement ps){ try { int[] counters = ps.executeBatch(); ps.clearParameters(); return counters; } catch (SQLException e) { throw new DBException(e); } }
/** * Provides connection from current thread. * * @return connection from current thread. */ public Connection connection() { Connection connection = ConnectionsAccess.getConnection(name); if (connection == null) { throw new DBException("there is no connection '" + name + "' on this thread, are you sure you opened it?"); } return connection; }
/** * Creates a <code>java.sql.PreparedStatement</code> to be used in batch executions later. * * @param parametrizedStatement Example of a statement: <code>INSERT INTO employees VALUES (?, ?)</code>. * @return instance of <code>java.sql.PreparedStatement</code> with compiled query. */ public PreparedStatement startBatch(String parametrizedStatement){ try { return connection().prepareStatement(parametrizedStatement); } catch (SQLException e) { throw new DBException(e); } }
/** * Adds a batch statement using given <code>java.sql.PreparedStatement</code> and parameters. * @param ps <code>java.sql.PreparedStatement</code> to add batch to. * @param params parameters for the query in <code>java.sql.PreparedStatement</code>. Parameters will be * set on the statement in the same order as provided here. */ public void addBatch(PreparedStatement ps, Object... params) { try { setParameters(ps, params); ps.addBatch(); } catch (SQLException e) { throw new DBException(e); } }
/** * Commits local transaction. */ public void commitTransaction() { try { Connection c = ConnectionsAccess.getConnection(name); if (c == null) { throw new DBException("Cannot commit transaction, connection '" + name + "' not available"); } c.commit(); LogFilter.log(LOGGER, LogLevel.DEBUG, "Transaction committed"); } catch (SQLException ex) { throw new DBException(ex.getMessage(), ex); } }
/** * Opens local transaction. */ public void openTransaction() { try { Connection c = ConnectionsAccess.getConnection(name); if (c == null) { throw new DBException("Cannot open transaction, connection '" + name + "' not available"); } c.setAutoCommit(false); LogFilter.log(LOGGER, LogLevel.DEBUG, "Transaction opened"); } catch (SQLException ex) { throw new DBException(ex.getMessage(), ex); } }
/** * Rolls back local transaction. */ public void rollbackTransaction() { try { Connection c = ConnectionsAccess.getConnection(name); if (c == null) { throw new DBException("Cannot rollback transaction, connection '" + name + "' not available"); } c.rollback(); LogFilter.log(LOGGER, LogLevel.DEBUG, "Transaction rolled back"); } catch (SQLException ex) { throw new DBException(ex.getMessage(), ex); } }
private void checkExistingConnection(String name) { if (null != ConnectionsAccess.getConnection(name)) { throw new DBException("Cannot open a new connection because existing connection is still on current thread, name: " + name + ", connection instance: " + connection() + ". This might indicate a logical error in your application."); } }
/** * Applies additional criteria to scopes defined in the model. * * @param subquery additional criteria. * @param params dynamic parameters for the subquery. Similar to {@link Model#where(String, Object...)}. * @return a list of records filtered by all supplied scopes as well as additional criteria. */ public <T extends Model> LazyList<T> where(String subquery, Object... params) { StringBuilder query; if(subquery.equals("*")){ query = new StringBuilder(); }else { query = new StringBuilder(subquery); query.append(" AND "); } for (int i = 0; i < scopes.size(); i++) { String scope = scopes.get(i); if(!ModelDelegate.getScopes(modelClass.getName()).containsKey(scope)){ throw new DBException(String.format("Scope '%s' is not defined in model '%s'.", scope, modelClass.getName())); } String scopeQuery = ModelDelegate.getScopes(modelClass.getName()).get(scope); query.append(scopeQuery); if (i < (scopes.size() - 1)) { query.append(" AND "); } } return ModelDelegate.where((Class<T>) modelClass, query.toString(), params); }
/** * This method exists to force immediate load from DB. Example; * <code> Person.find("name = ?", "Smith").load();</code>. * It is not possible to call other methods after load(). The load() method should be the last to be called in the chain: * <code> Person.find("name = ?", "Smith").limit(10).load();</code>. * This: will generate exception: <code> Person.find("name = ?", "Smith").load().limit();</code>. * * @return fully loaded list. */ //TODO: write test, and also test for exception. public <E extends Model> LazyList<E> load() { if (hydrated()) { throw new DBException("load() must be the last on the chain of methods"); } hydrate(); return (LazyList<E>) this; }
protected String getTableName(Class<? extends Model> modelClass) { init(MetaModel.getDbName(modelClass)); String tableName = metaModels.getTableName(modelClass); if (tableName == null) { throw new DBException("failed to find metamodel for " + modelClass + ". Are you sure that a corresponding table exists in DB?"); } return tableName; }
public void with(RowListener listener){ try { processRS(listener); } catch(SQLException e) { throw new DBException(e); } finally { //TODO: shouldn't these be closed in the same scope they were created? closeQuietly(rs); closeQuietly(s); } }
/** * Executes a raw query and returns an instance of {@link RowProcessor}. Use it in the following pattern: * <pre> * Base.find("select first_name, last_name from really_large_table").with(new RowListenerAdapter() { public void onNext(Map row) { ///write your code here Object o1 = row.get("first_name"); Object o2 = row.get("last_name"); } }); </pre> * * @param query raw SQL. * @param params list of parameters if query is parametrized. * @return instance of <code>RowProcessor</code> which has with() method for convenience. */ public RowProcessor find(String query, Object ... params) { //TODO: count ? signs and number of params, throw exception if do not match if(query.indexOf('?') == -1 && params.length != 0) throw new IllegalArgumentException("you passed arguments, but the query does not have placeholders: (?)"); //TODO: cache prepared statements here too PreparedStatement ps; ResultSet rs; try { ps = createStreamingPreparedStatement(query); setParameters(ps, params); rs = ps.executeQuery(); return new RowProcessor(rs, ps); } catch (SQLException e) { throw new DBException(query, params, e); } }
/** * Executes DML. Use it for inserts and updates. * * @param query raw DML. * @return number of rows afected by query. */ public int exec(String query){ long start = System.currentTimeMillis(); Statement s = null; try { s = connection().createStatement(); int count = s.executeUpdate(query); LogFilter.logQuery(LOGGER, query, null, start); return count; } catch (SQLException e) { logException("Query failed: " + query, e); throw new DBException(query, null, e); } finally { closeQuietly(s); } }
/** * Executes parametrized DML - will contain question marks as placeholders. * * @param query query to execute - will contain question marks as placeholders. * @param params query parameters. * @return number of records affected. */ public int exec(String query, Object... params){ if(query.indexOf('?') == -1) throw new IllegalArgumentException("query must be parametrized"); long start = System.currentTimeMillis(); PreparedStatement ps = null; try { ps = connection().prepareStatement(query); setParameters(ps, params); int count = ps.executeUpdate(); LogFilter.logQuery(LOGGER, query, params, start); return count; } catch (SQLException e) { logException("Failed query: " + query, e); throw new DBException(query, params, e); } finally { closeQuietly(ps); } }
/** * This method returns the value of the first column of the first row. * If query results have more than one column or more than one row, they will be ignored. * * @param query query * @param params parameters * @return fetched value, or null if query did not fetch anything. */ public Object firstCell(final String query, Object... params) { PreparedStatement ps = null; ResultSet rs = null; try { Object result = null; long start = System.currentTimeMillis(); ps = connection().prepareStatement(query); setParameters(ps, params); rs = ps.executeQuery(); if (rs.next()) { result = rs.getObject(1); } LogFilter.logQuery(LOGGER, query, params, start); return result; } catch (SQLException e) { throw new DBException(query, params, e); } finally { closeQuietly(rs); closeQuietly(ps); } }
/** * Closes connection and detaches it from current thread. * @param suppressWarning true to not display a warning in case of a problem (connection not there) */ public void close(boolean suppressWarning) { try { Connection connection = ConnectionsAccess.getConnection(name); if(connection == null){ throw new DBException("cannot close connection '" + name + "' because it is not available"); } StatementCache.instance().cleanStatementCache(connection); connection.close(); LogFilter.log(LOGGER, LogLevel.DEBUG, "Closed connection: {}", connection); } catch (Exception e) { if (!suppressWarning) { LOGGER.warn("Could not close connection! MUST INVESTIGATE POTENTIAL CONNECTION LEAK!", e); } } finally { ConnectionsAccess.detach(name); // let's free the thread from connection } }
/** * Executes a raw query and calls instance of <code>RowListener</code> with every row found. * Use this method for very large result sets. * * @param sql raw SQL query. * @param listener client listener implementation for processing individual rows. */ public void find(String sql, RowListener listener) { Statement s = null; ResultSet rs = null; try { s = createStreamingStatement(); rs = s.executeQuery(sql); RowProcessor p = new RowProcessor(rs, s); p.with(listener); } catch (SQLException e) { throw new DBException(sql, null, e); } finally { closeQuietly(rs); closeQuietly(s); } }
/** * Detaches a connection from current thread and returns an instance of it. This method does not close a connection. * Use it for cases of advanced connection management, such as integration with Spring Framework. * * @return instance of a connection detached from current thread by name passed to constructor. */ public Connection detach() { Connection connection = ConnectionsAccess.getConnection(name); try { if (connection == null) { throw new DBException("cannot detach connection '" + name + "' because it is not available"); } ConnectionsAccess.detach(name); // let's free the thread from connection StatementCache.instance().cleanStatementCache(connection); } catch (DBException e) { LogFilter.log(LOGGER, LogLevel.ERROR, "Could not close connection! MUST INVESTIGATE POTENTIAL CONNECTION LEAK!", e); } return connection; }
/** * This method will open a connection defined in the file 'database.properties' located at * root of classpath. The connection picked up from the file is defined by <code>ACTIVE_ENV</code> * environment variable or <code>active_env</code> system property. * If this variable is not defined, it defaults to 'development' environment. * * <p></p> * If there is JUnit on classpath, this method assumes it is running under test, and defaults to 'test'. * * @see Configuration#getEnvironment() */ public DB open(){ Configuration config = Registry.instance().getConfiguration(); ConnectionSpec spec = config.getCurrentConnectionSpec(); if(spec == null){ throw new DBException("Could not find configuration in a property file for environment: " + config.getEnvironment() + ". Are you sure you have a database.properties file configured?"); } return open(spec); }