Another Zell Depot patch:

- refactoring query construction;
- modified collection lookup to first look up the matching primary keys, then
check the cache for hits then look up the remainder from the database;
- other small renamery and cleanup.
This commit is contained in:
Michael Bayne
2007-07-12 20:02:54 +00:00
parent e472fc6de8
commit 083cb788d8
32 changed files with 1219 additions and 664 deletions
@@ -1,322 +0,0 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.depot.annotation.Computed;
import com.samskivert.jdbc.depot.clause.FieldOverride;
import com.samskivert.jdbc.depot.clause.ForUpdate;
import com.samskivert.jdbc.depot.clause.FromOverride;
import com.samskivert.jdbc.depot.clause.GroupBy;
import com.samskivert.jdbc.depot.clause.Join;
import com.samskivert.jdbc.depot.clause.Limit;
import com.samskivert.jdbc.depot.clause.OrderBy;
import com.samskivert.jdbc.depot.clause.QueryClause;
import com.samskivert.jdbc.depot.clause.Where;
/**
* Encapsulates a non-modifying query of persistent objects.
*/
public abstract class ConstructedQuery<T>
implements Query<T>
{
/**
* Maps a class referenced by this query to its associated table.
*
* This method is called by individual clauses.
*/
public String getTableName (Class cl)
{
return _classMap.get(cl).getTableName();
}
/**
* Maps a class referenced by this query to the abbreviation used for its associated table. The
* abbreviation is transientand only has meaning within the scope of this particular query.
*
* This method is called by individual clauses.
*/
public String getTableAbbreviation (Class<? extends PersistentRecord> cl)
{
int ix = _classList.indexOf(cl);
if (ix < 0) {
throw new IllegalArgumentException("Unknown persistence class: " + cl);
}
return "T" + (ix+1);
}
// from Query
public CacheKey getCacheKey ()
{
if (_where != null && _where instanceof CacheKey) {
return (CacheKey) _where;
}
return null;
}
/**
* Translates the data in this Query object into a SQL query suited to instantiate a persistent
* object given the supplied key and clauses. These clauses are assumed to contain no
* conflicts.
*
* If no key is supplied all instances will be loaded.
*/
public PreparedStatement createQuery (Connection conn)
throws SQLException
{
if (_mainType == null) { // internal error
throw new RuntimeException("createQuery() called with _mainClass == null");
}
// we need to know if there's a entity-level @Computed annotation
Computed entityComputed = _mainType.getAnnotation(Computed.class);
// and we need to look up per-field information on our marshaller
DepotMarshaller<?> mainMarshaller = _classMap.get(_mainType);
StringBuilder query = new StringBuilder("select ");
boolean skip = true;
for (String field : mainMarshaller.getFieldNames()) {
if (!skip) {
query.append(", ");
}
skip = false;
// see if there's a field override
FieldOverride clause = _disMap.get(field);
if (clause != null) {
clause.appendClause(this, query);
continue;
}
// figure out the class we're selecting from unless we're otherwise overriden:
// for a concrete record, simply use the corresponding table; for a computed one,
// default to the shadowed concrete record, or null if there isn't one
Class<? extends PersistentRecord> tableClass =
entityComputed == null ? _mainType : entityComputed.shadowOf();
// handle the field-level @Computed annotation, if there is one
Computed fieldComputed = mainMarshaller.getFieldMarshaller(field).getComputed();
if (fieldComputed != null) {
// check if the computed field has a literal SQL definition
if (fieldComputed.fieldDefinition().length() > 0) {
query.append(fieldComputed.fieldDefinition()).append(" as ").append(field);
continue;
}
// or if we can simply ignore the field
if (!fieldComputed.required()) {
skip = true;
continue;
}
// else see if there's an overriding shadowOf definition
if (fieldComputed.shadowOf() != null) {
tableClass = fieldComputed.shadowOf();
}
}
// if we get this far we hopefully have a table to select from
if (tableClass != null) {
String tableName = getTableAbbreviation(tableClass);
query.append(tableName).append(".").append(field);
continue;
}
// else owie
throw new SQLException(
"Persistent field has no definition [class=" + _mainType +
", field=" + field + "]");
}
if (_fromOverride != null) {
_fromOverride.appendClause(this, query);
} else if (mainMarshaller.getTableName() != null) {
query.append(" from ").append(mainMarshaller.getTableName());
query.append(" as ").append(getTableAbbreviation(_mainType));
} else {
throw new SQLException("Query on @Computed entity with no FromOverrideClause.");
}
for (Join clause : _joinClauses) {
clause.appendClause(this, query);
}
if (_where != null) {
_where.appendClause(this, query);
}
if (_groupBy != null) {
_groupBy.appendClause(this, query);
}
if (_orderBy != null) {
_orderBy.appendClause(this, query);
}
if (_limit != null) {
_limit.appendClause(this, query);
}
if (_forUpdate != null) {
_forUpdate.appendClause(this, query);
}
PreparedStatement pstmt = conn.prepareStatement(query.toString());
int argIdx = 1;
for (Join clause : _joinClauses) {
argIdx = clause.bindArguments(pstmt, argIdx);
}
if (_where != null) {
argIdx = _where.bindArguments(pstmt, argIdx);
}
if (_groupBy != null) {
argIdx = _groupBy.bindArguments(pstmt, argIdx);
}
if (_orderBy != null) {
argIdx = _orderBy.bindArguments(pstmt, argIdx);
}
if (_limit != null) {
argIdx = _limit.bindArguments(pstmt, argIdx);
}
if (_forUpdate != null) {
argIdx = _forUpdate.bindArguments(pstmt, argIdx);
}
return pstmt;
}
/**
* Creates a new Query object to generate one or more instances of the specified persistent
* class, as dictated by the key and query clauses. A persistence context is supplied for
* instantiation of marshallers, which may trigger table creations and schema migrations.
*/
protected ConstructedQuery (PersistenceContext ctx, Class<? extends PersistentRecord> type,
QueryClause... clauses)
throws PersistenceException
{
_mainType = type;
Set<Class<? extends PersistentRecord>> classSet =
new HashSet<Class<? extends PersistentRecord>>();
if (type != null) {
classSet.add(type);
}
for (QueryClause clause : clauses) {
if (clause instanceof Where) {
if (_where != null) {
throw new IllegalArgumentException(
"Query can't contain multiple Where clauses.");
}
_where = (Where) clause;
} else if (clause instanceof FromOverride) {
if (_fromOverride != null) {
throw new IllegalArgumentException(
"Query can't contain multiple FromOverride clauses.");
}
_fromOverride = (FromOverride) clause;
classSet.addAll(_fromOverride.getClassSet());
} else if (clause instanceof Join) {
_joinClauses.add((Join) clause);
classSet.addAll(((Join) clause).getClassSet());
} else if (clause instanceof FieldOverride) {
_disMap.put(((FieldOverride) clause).getField(),
((FieldOverride) clause));
} else if (clause instanceof OrderBy) {
if (_orderBy != null) {
throw new IllegalArgumentException(
"Query can't contain multiple OrderBy clauses.");
}
_orderBy = (OrderBy) clause;
} else if (clause instanceof GroupBy) {
if (_groupBy != null) {
throw new IllegalArgumentException(
"Query can't contain multiple GroupBy clauses.");
}
_groupBy = (GroupBy) clause;
} else if (clause instanceof Limit) {
if (_limit != null) {
throw new IllegalArgumentException(
"Query can't contain multiple Limit clauses.");
}
_limit = (Limit) clause;
} else if (clause instanceof ForUpdate) {
if (_forUpdate != null) {
throw new IllegalArgumentException(
"Query can't contain multiple For Update clauses.");
}
_forUpdate = (ForUpdate) clause;
}
}
_classMap = new HashMap<Class, DepotMarshaller>();
for (Class<? extends PersistentRecord> c : classSet) {
_classMap.put(c, ctx.getMarshaller(c));
}
_classList = new ArrayList<Class<? extends PersistentRecord>>(classSet);
}
/** The persistent class to instantiate for the results. */
protected Class<? extends PersistentRecord> _mainType;
/** A list of referenced classes, used to generate table abbreviations. */
protected List<Class<? extends PersistentRecord>> _classList;
/** Classes mapped to marshallers, used for table names and field lists. */
protected Map<Class, DepotMarshaller> _classMap;
/** Persistent class fields mapped to field override clauses. */
protected Map<String, FieldOverride> _disMap = new HashMap<String, FieldOverride>();
/** The from override clause, if any. */
protected FromOverride _fromOverride;
/** The where clause. */
protected Where _where;
/** A list of join clauses, each potentially referencing a new class. */
protected List<Join> _joinClauses = new ArrayList<Join>();
/** The order by clause, if any. */
protected OrderBy _orderBy;
/** The group by clause, if any. */
protected GroupBy _groupBy;
/** The limit clause, if any. */
protected Limit _limit;
/** The For Update clause, if any. */
protected ForUpdate _forUpdate;
}
@@ -2,7 +2,7 @@
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006 Michael Bayne, Pär Winzell
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
@@ -239,7 +239,7 @@ public class DepotMarshaller<T extends PersistentRecord>
// add the primary key, if we have one
if (hasPrimaryKey()) {
_declarations.add("PRIMARY KEY (" + getPrimaryKeyColumns() + ")");
_declarations.add("PRIMARY KEY (" + getJoinedKeyColumns() + ")");
}
// if we did not find a schema version field, complain
@@ -282,6 +282,19 @@ public class DepotMarshaller<T extends PersistentRecord>
return (_pkColumns != null);
}
/**
* Return the names of the columns that constitute the primary key of our associated
* persistent record.
*/
public String[] getPrimaryKeyColumns ()
{
String[] pkcols = new String[_pkColumns.size()];
for (int ii = 0; ii < pkcols.length; ii ++) {
pkcols[ii] = _pkColumns.get(ii).getColumnName();
}
return pkcols;
}
/**
* Returns a key configured with the primary key of the supplied object. If all the fields are
* null, this method returns null. An exception is thrown if some of the fields are null and
@@ -353,6 +366,29 @@ public class DepotMarshaller<T extends PersistentRecord>
return new Key<T>(_pclass, columns, values);
}
/**
* Creates a primary key record for the type of object handled by this marshaller, using the
* supplied result set.
*/
public Key<T> makePrimaryKey (ResultSet rs)
throws SQLException
{
if (!hasPrimaryKey()) {
throw new UnsupportedOperationException(
getClass().getName() + " does not define a primary key");
}
Comparable[] values = new Comparable[_pkColumns.size()];
for (int ii = 0; ii < _pkColumns.size(); ii++) {
Object keyValue = _pkColumns.get(ii).getFromSet(rs);
if (!(keyValue instanceof Comparable)) {
throw new IllegalArgumentException("Key field must be Comparable [field=" +
_pkColumns.get(ii).getColumnName() + "]");
}
values[ii] = (Comparable) keyValue;
}
return makePrimaryKey(values);
}
/**
* Returns true if this marshaller has been initialized ({@link #init} has been called), its
* migrations run and it is ready for operation. False otherwise.
@@ -387,7 +423,7 @@ public class DepotMarshaller<T extends PersistentRecord>
}
throw new SQLException("ResultSet missing field: " + fm.getField().getName());
}
fm.getValue(rs, po);
fm.writeToObject(rs, po);
}
return po;
@@ -427,7 +463,7 @@ public class DepotMarshaller<T extends PersistentRecord>
PreparedStatement pstmt = conn.prepareStatement(insert.toString());
int idx = 0;
for (String field : _columnFields) {
_fields.get(field).setValue(po, pstmt, ++idx);
_fields.get(field).readFromObject(po, pstmt, ++idx);
}
return pstmt;
@@ -507,10 +543,10 @@ public class DepotMarshaller<T extends PersistentRecord>
idx = 0;
// bind the update arguments
for (String field : modifiedFields) {
_fields.get(field).setValue(po, pstmt, ++idx);
_fields.get(field).readFromObject(po, pstmt, ++idx);
}
// now bind the key arguments
key.bindArguments(pstmt, ++idx);
key.bindClauseArguments(pstmt, ++idx);
return pstmt;
} catch (SQLException sqe) {
@@ -553,7 +589,7 @@ public class DepotMarshaller<T extends PersistentRecord>
pstmt.setObject(++idx, value);
}
// now bind the key arguments
key.bindArguments(pstmt, ++idx);
key.bindClauseArguments(pstmt, ++idx);
return pstmt;
}
@@ -568,7 +604,7 @@ public class DepotMarshaller<T extends PersistentRecord>
StringBuilder query = new StringBuilder("delete from " + getTableName());
key.appendClause(null, query);
PreparedStatement pstmt = conn.prepareStatement(query.toString());
key.bindArguments(pstmt, 1);
key.bindClauseArguments(pstmt, 1);
return pstmt;
}
@@ -594,7 +630,7 @@ public class DepotMarshaller<T extends PersistentRecord>
key.appendClause(null, update);
PreparedStatement pstmt = conn.prepareStatement(update.toString());
key.bindArguments(pstmt, 1);
key.bindClauseArguments(pstmt, 1);
return pstmt;
}
@@ -745,7 +781,7 @@ public class DepotMarshaller<T extends PersistentRecord>
// add or remove the primary key as needed
if (hasPrimaryKey() && !indexColumns.containsKey("PRIMARY")) {
String pkdef = "primary key (" + getPrimaryKeyColumns() + ")";
String pkdef = "primary key (" + getJoinedKeyColumns() + ")";
log.info("Adding primary key to " + getTableName() + ": " + pkdef);
ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " add " + pkdef));
@@ -888,13 +924,9 @@ public class DepotMarshaller<T extends PersistentRecord>
}
}
protected String getPrimaryKeyColumns ()
protected String getJoinedKeyColumns ()
{
String[] pkcols = new String[_pkColumns.size()];
for (int ii = 0; ii < pkcols.length; ii ++) {
pkcols[ii] = _pkColumns.get(ii).getColumnName();
}
return StringUtil.join(pkcols, ", ");
return StringUtil.join(getPrimaryKeyColumns(), ", ");
}
protected void updateVersion (Connection conn, int version)
@@ -2,7 +2,7 @@
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006 Michael Bayne, Pär Winzell
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
@@ -22,10 +22,8 @@ package com.samskivert.jdbc.depot;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -34,6 +32,9 @@ import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.depot.Modifier.*;
import com.samskivert.jdbc.depot.clause.FieldOverride;
import com.samskivert.jdbc.depot.clause.FromOverride;
import com.samskivert.jdbc.depot.clause.Join;
import com.samskivert.jdbc.depot.clause.QueryClause;
import com.samskivert.jdbc.depot.clause.Where;
import com.samskivert.util.ArrayUtil;
@@ -113,104 +114,44 @@ public class DepotRepository
protected <T extends PersistentRecord> T load (Class<T> type, QueryClause... clauses)
throws PersistenceException
{
final DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
return _ctx.invoke(new ConstructedQuery<T>(_ctx, type, clauses) {
public T invoke (Connection conn) throws SQLException {
PreparedStatement stmt = createQuery(conn);
try {
T result = null;
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
result = marsh.createObject(rs);
}
// TODO: if (rs.next()) issue warning?
rs.close();
return result;
} finally {
JDBCUtil.close(stmt);
}
}
// from Query
public void updateCache (PersistenceContext ctx, T result) {
CacheKey key = getCacheKey();
if (key == null) {
// no row-specific cache key was given
if (result == null || !marsh.hasPrimaryKey()) {
return;
}
// if we can, create a key from what was actually returned
key = marsh.getPrimaryKey(result);
}
ctx.cacheStore(key, result != null ? result.clone() : null);
}
// from Query
public T transformCacheHit (CacheKey key, T value)
{
// we do not want to return a reference to the actual cached entity
if (value == null) {
return null;
}
@SuppressWarnings("unchecked") T cvalue = (T) value.clone();
return cvalue;
}
});
return _ctx.invoke(new FindOneQuery<T>(_ctx, type, clauses));
}
/**
* Loads all persistent objects that match the specified key.
* Loads all persistent objects that match the specified clauses.
*
* We have two strategies for doing this: one performs the query as-is, the second executes
* two passes: first fetching only key columns and consulting the cache for each such key;
* then, in the second pass, fetching the full entity only for keys that were not found in
* the cache.
*
* The more complex strategy could save a lot of data shuffling. On the other hand, its
* complexity is an inherent drawback, and it does execute two separate database queries
* for what the simple method does in one.
*/
protected <T extends PersistentRecord> List<T> findAll (
Class<T> type, QueryClause... clauses)
protected <T extends PersistentRecord> List<T> findAll (Class<T> type, QueryClause... clauses)
throws PersistenceException
{
final DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
return _ctx.invoke(new ConstructedQuery<ArrayList<T>>(_ctx, type, clauses) {
public ArrayList<T> invoke (Connection conn) throws SQLException {
PreparedStatement stmt = createQuery(conn);
try {
ArrayList<T> results = new ArrayList<T>();
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
results.add(marsh.createObject(rs));
}
return results;
DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
boolean useExplicit = (marsh.getTableName() == null) || !marsh.hasPrimaryKey();
} finally {
JDBCUtil.close(stmt);
}
}
// from Query
public void updateCache (PersistenceContext ctx, ArrayList<T> result) {
if (marsh.hasPrimaryKey()) {
for (T bit : result) {
ctx.cacheStore(marsh.getPrimaryKey(bit), bit.clone());
}
}
}
// from Query
public ArrayList<T> transformCacheHit (CacheKey key, ArrayList<T> bits)
{
if (bits == null) {
return bits;
}
ArrayList<T> result = new ArrayList<T>();
for (T bit : bits) {
if (bit != null) {
@SuppressWarnings("unchecked") T cbit = (T) bit.clone();
result.add(cbit);
} else {
result.add(null);
}
}
return result;
}
});
// TODO: This is a very conservative approach where all complicated queries are handled
// TODO: with the traditional single-pass query. The double pass algorithm can handle a
// TODO: much larger class of queries, but only after some engine upgrades: specifically,
// TODO: the field references used in the WHERE clause of the Entity query in WithCache
// TODO: cannot be the trivial expansion currently used in the Key class, but must rather
// TODO: undergo the same treatment SELECT fields currently do in SQLQueryBuilder. This
// TODO: is probably best done with an additional method in QueryBuilderContext, or perhaps
// TODO: a much larger switch to using a visitor pattern for generating the SQL of the
// TODO: clause hierarchies.
for (QueryClause clause : clauses) {
useExplicit |= (clause instanceof FieldOverride ||
clause instanceof Join ||
clause instanceof FromOverride);
}
return _ctx.invoke(useExplicit ?
new FindAllQuery.Explicitly<T>(_ctx, type, clauses) :
new FindAllQuery.WithCache<T>(_ctx, type, clauses));
}
/**
@@ -0,0 +1,106 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.samskivert.io.PersistenceException;
/**
* Primarily an implementation of {@link QueryBuilderContext}, this class holds the persistent
* classes used in the context of a single query and maps them to {@link DepotMarshaller}
* instances as well as table names and table abbreviations.
*
* The main motivation for breaking this functionality out into its own class is to encapsulate
* the operation that throws {@link PersistenceException} as separate from the operations that
* throw {@link SQLException}.
*/
public class DepotTypes<T>
implements QueryBuilderContext<T>
{
public DepotTypes (PersistenceContext ctx,
Class<? extends PersistentRecord> main,
Set<Class<? extends PersistentRecord>> others)
throws PersistenceException
{
_mainType = main;
_classMap = new HashMap<Class, DepotMarshaller>();
for (Class<? extends PersistentRecord> c : others) {
_classMap.put(c, ctx.getMarshaller(c));
}
_classList = new ArrayList<Class<? extends PersistentRecord>>(others);
}
public Class<? extends PersistentRecord> getMainType ()
{
return _mainType;
}
// from ConstructedQuery
public String getTableName (Class<? extends PersistentRecord> cl)
{
return _classMap.get(cl).getTableName();
}
// from ConstructedQuery
public String getTableAbbreviation (Class<? extends PersistentRecord> cl)
{
int ix = _classList.indexOf(cl);
if (ix < 0) {
throw new IllegalArgumentException("Unknown persistence class: " + cl);
}
return "T" + (ix+1);
}
public DepotMarshaller getMarshaller (Class cl)
{
return _classMap.get(cl);
}
public String getMainTableName ()
{
return getTableName(_mainType);
}
public DepotMarshaller getMainMarshaller ()
{
return getMarshaller(_mainType);
}
public String getMainTableAbbreviation ()
{
return getTableAbbreviation(_mainType);
}
/** The persistent class to instantiate for the results. */
protected Class<? extends PersistentRecord> _mainType;
/** A list of referenced classes, used to generate table abbreviations. */
protected List<Class<? extends PersistentRecord>> _classList;
/** Classes mapped to marshallers, used for table names and field lists. */
protected Map<Class, DepotMarshaller> _classMap;
}
@@ -2,7 +2,7 @@
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006 Michael Bayne, Pär Winzell
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
@@ -21,7 +21,6 @@
package com.samskivert.jdbc.depot;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -37,7 +36,6 @@ import java.sql.Timestamp;
import com.samskivert.jdbc.depot.annotation.Column;
import com.samskivert.jdbc.depot.annotation.Computed;
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
import com.samskivert.jdbc.depot.annotation.GenerationType;
import com.samskivert.jdbc.depot.annotation.Id;
import com.samskivert.util.StringUtil;
@@ -47,7 +45,7 @@ import com.samskivert.util.StringUtil;
*
* @see DepotMarshaller
*/
public abstract class FieldMarshaller
public abstract class FieldMarshaller<T>
{
/**
* Creates and returns a field marshaller for the specified field. Throws an exception if the
@@ -152,19 +150,49 @@ public abstract class FieldMarshaller
return _columnDefinition;
}
/**
* Reads and returns this field from the result set.
*/
public abstract T getFromSet (ResultSet rs)
throws SQLException;
/**
* Sets the specified column of the given prepared statement to the given value.
*/
public abstract void writeToStatement (PreparedStatement ps, int column, T value)
throws SQLException;
/**
* Reads this field from the given persistent object.
*/
public abstract T getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException;
/**
* Writes the given value to the given persistent value.
*/
public abstract void writeToObject (Object po, T value)
throws IllegalArgumentException, IllegalAccessException;
/**
* Reads the value of our field from the persistent object and sets that
* value into the specified column of the supplied prepared statement.
*/
public abstract void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException;
public void readFromObject (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException
{
writeToStatement(ps, column, getFromObject(po));
}
/**
* Reads the specified column from the supplied result set and writes it to
* the appropriate field of the persistent object.
*/
public abstract void getValue (ResultSet rset, Object po)
throws SQLException, IllegalAccessException;
public void writeToObject (ResultSet rset, Object po)
throws SQLException, IllegalAccessException
{
writeToObject(po, getFromSet(rset));
}
/**
* Returns the type used in the SQL column definition for this field.
@@ -254,113 +282,161 @@ public abstract class FieldMarshaller
_columnDefinition = builder.toString();
}
protected static class BooleanMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setBoolean(column, _field.getBoolean(po));
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.setBoolean(po, rs.getBoolean(getColumnName()));
}
protected static class BooleanMarshaller extends FieldMarshaller<Boolean> {
public String getColumnType () {
return "TINYINT";
}
public Boolean getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return _field.getBoolean(po);
}
public Boolean getFromSet (ResultSet rs)
throws SQLException {
return rs.getBoolean(getColumnName());
}
public void writeToObject (Object po, Boolean value)
throws IllegalArgumentException, IllegalAccessException {
_field.setBoolean(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, Boolean value)
throws SQLException {
ps.setBoolean(column, value);
}
}
protected static class ByteMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setByte(column, _field.getByte(po));
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.setByte(po, rs.getByte(getColumnName()));
}
protected static class ByteMarshaller extends FieldMarshaller<Byte> {
public String getColumnType () {
return "TINYINT";
}
public Byte getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return _field.getByte(po);
}
public Byte getFromSet (ResultSet rs)
throws SQLException {
return rs.getByte(getColumnName());
}
public void writeToObject (Object po, Byte value)
throws IllegalArgumentException, IllegalAccessException {
_field.setByte(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, Byte value)
throws SQLException {
ps.setByte(column, value);
}
}
protected static class ShortMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setShort(column, _field.getShort(po));
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.setShort(po, rs.getShort(getColumnName()));
}
protected static class ShortMarshaller extends FieldMarshaller<Short> {
public String getColumnType () {
return "SMALLINT";
}
public Short getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return _field.getShort(po);
}
public Short getFromSet (ResultSet rs)
throws SQLException {
return rs.getShort(getColumnName());
}
public void writeToObject (Object po, Short value)
throws IllegalArgumentException, IllegalAccessException {
_field.setShort(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, Short value)
throws SQLException {
ps.setShort(column, value);
}
}
protected static class IntMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setInt(column, _field.getInt(po));
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.setInt(po, rs.getInt(getColumnName()));
}
protected static class IntMarshaller extends FieldMarshaller<Integer> {
public String getColumnType () {
return "INTEGER";
}
public Integer getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return _field.getInt(po);
}
public Integer getFromSet (ResultSet rs)
throws SQLException {
return rs.getInt(getColumnName());
}
public void writeToObject (Object po, Integer value)
throws IllegalArgumentException, IllegalAccessException {
_field.setInt(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, Integer value)
throws SQLException {
ps.setInt(column, value);
}
}
protected static class LongMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setLong(column, _field.getLong(po));
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.setLong(po, rs.getLong(getColumnName()));
}
protected static class LongMarshaller extends FieldMarshaller<Long> {
public String getColumnType () {
return "BIGINT";
}
public Long getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return _field.getLong(po);
}
public Long getFromSet (ResultSet rs)
throws SQLException {
return rs.getLong(getColumnName());
}
public void writeToObject (Object po, Long value)
throws IllegalArgumentException, IllegalAccessException {
_field.setLong(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, Long value)
throws SQLException {
ps.setLong(column, value);
}
}
protected static class FloatMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setFloat(column, _field.getFloat(po));
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.setFloat(po, rs.getFloat(getColumnName()));
}
protected static class FloatMarshaller extends FieldMarshaller<Float> {
public String getColumnType () {
return "FLOAT";
}
public Float getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return _field.getFloat(po);
}
public Float getFromSet (ResultSet rs)
throws SQLException {
return rs.getFloat(getColumnName());
}
public void writeToObject (Object po, Float value)
throws IllegalArgumentException, IllegalAccessException {
_field.setFloat(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, Float value)
throws SQLException {
ps.setFloat(column, value);
}
}
protected static class DoubleMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setDouble(column, _field.getDouble(po));
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.setDouble(po, rs.getDouble(getColumnName()));
}
protected static class DoubleMarshaller extends FieldMarshaller<Double> {
public String getColumnType () {
return "DOUBLE";
}
public Double getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return _field.getDouble(po);
}
public Double getFromSet (ResultSet rs)
throws SQLException {
return rs.getDouble(getColumnName());
}
public void writeToObject (Object po, Double value)
throws IllegalArgumentException, IllegalAccessException {
_field.setDouble(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, Double value)
throws SQLException {
ps.setDouble(column, value);
}
}
protected static class ObjectMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setObject(column, _field.get(po));
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.set(po, rs.getObject(getColumnName()));
}
protected static class ObjectMarshaller extends FieldMarshaller<Object> {
public String getColumnType () {
Class<?> ftype = _field.getType();
if (ftype.equals(Byte.class)) {
@@ -392,37 +468,69 @@ public abstract class FieldMarshaller
"Don't know how to create SQL for " + ftype + ".");
}
}
public Object getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return _field.get(po);
}
public Object getFromSet (ResultSet rs)
throws SQLException {
return rs.getObject(getColumnName());
}
public void writeToObject (Object po, Object value)
throws IllegalArgumentException, IllegalAccessException {
_field.set(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, Object value)
throws SQLException {
ps.setObject(column, value);
}
}
protected static class ByteArrayMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setBytes(column, (byte[])_field.get(po));
protected static class ByteArrayMarshaller extends FieldMarshaller<byte[]> {
public byte[] getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return (byte[]) _field.get(po);
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.set(po, rs.getBytes(getColumnName()));
public byte[] getFromSet (ResultSet rs)
throws SQLException {
return rs.getBytes(getColumnName());
}
public void writeToObject (Object po, byte[] value)
throws IllegalArgumentException, IllegalAccessException {
_field.set(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, byte[] value)
throws SQLException {
ps.setBytes(column, value);
}
public String getColumnType () {
return "VARBINARY";
}
}
protected static class IntArrayMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setObject(column, _field.get(po));
protected static class IntArrayMarshaller extends FieldMarshaller<int[]> {
public int[] getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return (int[]) _field.get(po);
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
_field.set(po, rs.getObject(getColumnName()));
public int[] getFromSet (ResultSet rs)
throws SQLException {
return (int[]) rs.getObject(getColumnName());
}
public void writeToObject (Object po, int[] value)
throws IllegalArgumentException, IllegalAccessException {
_field.set(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, int[] value)
throws SQLException {
ps.setObject(column, value);
}
public String getColumnType () {
return "BLOB";
}
}
protected static class ByteEnumMarshaller extends FieldMarshaller {
protected static class ByteEnumMarshaller extends FieldMarshaller<ByteEnum> {
public ByteEnumMarshaller (Class clazz) {
try {
_factmeth = clazz.getMethod("fromByte", new Class[] { Byte.TYPE });
@@ -437,22 +545,33 @@ public abstract class FieldMarshaller
}
}
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setInt(column, ((ByteEnum)_field.get(po)).toByte());
}
public void getValue (ResultSet rs, Object po)
throws SQLException, IllegalAccessException {
try {
_field.set(po, _factmeth.invoke(null, rs.getByte(getColumnName())));
} catch (InvocationTargetException ite) {
throw new RuntimeException(ite);
}
}
public String getColumnType () {
return "TINYINT";
}
public ByteEnum getFromObject (Object po)
throws IllegalArgumentException, IllegalAccessException {
return (ByteEnum) _field.get(po);
}
public ByteEnum getFromSet (ResultSet rs)
throws SQLException {
try {
return (ByteEnum) _factmeth.invoke(null, rs.getByte(getColumnName()));
} catch (SQLException se) {
throw se;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void writeToObject (Object po, ByteEnum value)
throws IllegalArgumentException, IllegalAccessException {
_field.set(po, value);
}
public void writeToStatement (PreparedStatement ps, int column, ByteEnum value)
throws SQLException {
ps.setByte(column, value.toByte());
}
protected Method _factmeth;
}
@@ -0,0 +1,219 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.depot.operator.Logic.*;
import com.samskivert.jdbc.depot.clause.FieldOverride;
import com.samskivert.jdbc.depot.clause.Join;
import com.samskivert.jdbc.depot.clause.QueryClause;
import com.samskivert.jdbc.depot.clause.Where;
import com.samskivert.jdbc.depot.expression.SQLExpression;
import com.samskivert.util.ArrayUtil;
/**
* This class implements the functionality required by {@link DepotRepository#findAll): fetch
* a collection of persistent objects using one of two included strategies.
*/
public abstract class FindAllQuery<T extends PersistentRecord>
implements Query<List<T>>
{
/**
* The two-pass collection query implementation. {@see DepotRepository#findAll} for details.
*/
public static class WithCache<T extends PersistentRecord> extends FindAllQuery<T>
{
public WithCache (PersistenceContext ctx, Class<T> type, QueryClause[] clauses)
throws PersistenceException
{
super(ctx, type);
_types = SQLQueryBuilder.getDepotTypes(ctx, type, clauses);
_clauses = clauses;
}
public List<T> invoke (Connection conn) throws SQLException {
SQLQueryBuilder<T> keyFieldQuery = new SQLQueryBuilder<T>(
_ctx, _types, _marsh.getPrimaryKeyColumns(), _clauses);
PreparedStatement stmt = keyFieldQuery.prepare(conn);
Map<Key<T>, T> entities = new HashMap<Key<T>, T>();
List<Key<T>> allKeys = new ArrayList<Key<T>>();
List<Key<T>> fetchKeys = new ArrayList<Key<T>>();
try {
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Key<T> key = _marsh.makePrimaryKey(rs);
allKeys.add(key);
// TODO: All this cache fiddling needs to move to PersistenceContext?
Cache cache = _ctx.getCache(key.getCacheId());
Element hit = cache.get(key.getCacheKey());
if (hit != null) {
@SuppressWarnings("unchecked") T value = (T) hit.getValue();
if (value != null) {
@SuppressWarnings("unchecked") T newValue = (T) value.clone();
entities.put(key, newValue);
continue;
}
}
fetchKeys.add(key);
}
} finally {
JDBCUtil.close(stmt);
}
if (fetchKeys.size() > 0) {
int jj = 0;
QueryClause[] newClauses = new QueryClause[_clauses.length + 1];
// a select subset of query clauses are preserved for the entity query
for (int ii = 0; ii < _clauses.length; ii ++) {
if (_clauses[ii] instanceof Join || _clauses[ii] instanceof FieldOverride) {
newClauses[jj ++] = _clauses[ii];
}
}
SQLExpression[] keyArray = fetchKeys.toArray(new SQLExpression[0]);
// add our special key-matching where clause
newClauses[jj ++] = new Where(new Or(keyArray));
newClauses = ArrayUtil.splice(newClauses, jj);
// build the new query
SQLQueryBuilder<T> entityQuery = new SQLQueryBuilder<T>(
_ctx, _types, _marsh.getFieldNames(), newClauses);
// and execute it
stmt = entityQuery.prepare(conn);
try {
ResultSet rs = stmt.executeQuery();
for (Key<T> key : fetchKeys) {
if (!rs.next()) {
throw new SQLException("Expecting more rows in result set.");
}
entities.put(key, _marsh.createObject(rs));
}
} finally {
JDBCUtil.close(stmt);
}
}
List<T> result = new ArrayList<T>();
for (Key<T> key : allKeys) {
result.add(entities.get(key));
}
return result;
}
protected DepotTypes<T> _types;
protected QueryClause[] _clauses;
}
/**
* The single-pass collection query implementation. {@see DepotRepository#findAll} for details.
*/
public static class Explicitly<T extends PersistentRecord> extends FindAllQuery<T>
{
public Explicitly (PersistenceContext ctx, Class<T> type, QueryClause[] clauses)
throws PersistenceException
{
super(ctx, type);
DepotTypes<List<T>> types = SQLQueryBuilder.getDepotTypes(ctx, type, clauses);
_builder = new SQLQueryBuilder<List<T>>(ctx, types, _marsh.getFieldNames(), clauses);
}
public List<T> invoke (Connection conn) throws SQLException {
PreparedStatement stmt = _builder.prepare(conn);
List<T> result = new ArrayList<T>();
try {
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
result.add(_marsh.createObject(rs));
}
} finally {
JDBCUtil.close(stmt);
}
return result;
}
protected SQLQueryBuilder<List<T>> _builder;
}
public FindAllQuery (PersistenceContext ctx, Class<T> type)
throws PersistenceException
{
_ctx = ctx;
_marsh = _ctx.getMarshaller(type);
}
// from Query
public CacheKey getCacheKey ()
{
return null;
}
// from Query
public void updateCache (PersistenceContext ctx, List<T> result) {
if (_marsh.hasPrimaryKey()) {
for (T bit : result) {
ctx.cacheStore(_marsh.getPrimaryKey(bit), bit.clone());
}
}
}
// from Query
public List<T> transformCacheHit (CacheKey key, List<T> bits)
{
if (bits == null) {
return bits;
}
List<T> result = new ArrayList<T>();
for (T bit : bits) {
if (bit != null) {
@SuppressWarnings("unchecked") T cbit = (T) bit.clone();
result.add(cbit);
} else {
result.add(null);
}
}
return result;
}
protected PersistenceContext _ctx;
protected DepotMarshaller<T> _marsh;
}
@@ -0,0 +1,100 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.depot.clause.QueryClause;
/**
* The implementation of {@link DepotRepository#find) functionality.
*/
public class FindOneQuery<T extends PersistentRecord>
implements Query<T>
{
public FindOneQuery (PersistenceContext ctx, Class<T> type, QueryClause[] clauses)
throws PersistenceException
{
_marsh = ctx.getMarshaller(type);
DepotTypes<T> types = SQLQueryBuilder.getDepotTypes(ctx, type, clauses);
_builder = new SQLQueryBuilder<T>(ctx, types, _marsh.getFieldNames(), clauses);
}
// from Query
public CacheKey getCacheKey ()
{
if (_builder.where != null && _builder.where instanceof CacheKey) {
return (CacheKey) _builder.where;
}
return null;
}
// from Query
public T invoke (Connection conn) throws SQLException {
PreparedStatement stmt = _builder.prepare(conn);
try {
T result = null;
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
result = _marsh.createObject(rs);
}
// TODO: if (rs.next()) issue warning?
rs.close();
return result;
} finally {
JDBCUtil.close(stmt);
}
}
// from Query
public void updateCache (PersistenceContext ctx, T result) {
CacheKey key = getCacheKey();
if (key == null) {
// no row-specific cache key was given
if (result == null || !_marsh.hasPrimaryKey()) {
return;
}
// if we can, create a key from what was actually returned
key = _marsh.getPrimaryKey(result);
}
ctx.cacheStore(key, (result != null) ? result.clone() : null);
}
// from Query
public T transformCacheHit (CacheKey key, T value)
{
if (value == null) {
return null;
}
// we do not want to return a reference to the actual cached entity so we clone it
@SuppressWarnings("unchecked") T cvalue = (T) value.clone();
return cvalue;
}
protected DepotMarshaller<T> _marsh;
protected SQLQueryBuilder<T> _builder;
}
+27 -16
View File
@@ -32,17 +32,18 @@ import java.util.Map;
import com.samskivert.jdbc.depot.annotation.Id;
import com.samskivert.jdbc.depot.clause.Where;
import com.samskivert.jdbc.depot.expression.SQLExpression;
import com.samskivert.util.StringUtil;
/**
* A special form of {@link Where} clause that uniquely specifices a single database row and
* A special form of {@link Where} clause that uniquely specifies a single database row and
* thus also a single persistent object. Because it implements both {@link CacheKey} and
* {@link CacheInvalidator} it also uniquely indexes into the cache and knows how to invalidate
* itself upon modification. This class is created by many {@link DepotMarshaller} methods as
* a convenience, and may also be instantiated explicitly.
*/
public class Key<T extends PersistentRecord> extends Where
implements CacheKey, CacheInvalidator, Serializable
implements SQLExpression, CacheKey, CacheInvalidator, Serializable
{
/**
* Constructs a new single-column {@code Key} with the given value.
@@ -109,18 +110,33 @@ public class Key<T extends PersistentRecord> extends Where
}
// from QueryClause
public Collection<Class<? extends PersistentRecord>> getClassSet ()
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
{
ArrayList<Class<? extends PersistentRecord>> set =
new ArrayList<Class<? extends PersistentRecord>>();
set.add(_pClass);
return set;
classSet.add(_pClass);
}
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
builder.append(" where ");
appendExpression(query, builder);
}
// from QueryClause
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
for (int ii = 0; ii < _values.length; ii ++) {
if (_values[ii] != null) {
pstmt.setObject(argIdx ++, _values[ii]);
}
}
return argIdx;
}
// from SQLExpression
public void appendExpression (QueryBuilderContext<?> query, StringBuilder builder)
{
String[] keyFields = getKeyFields(_pClass);
for (int ii = 0; ii < keyFields.length; ii ++) {
if (ii > 0) {
@@ -130,16 +146,11 @@ public class Key<T extends PersistentRecord> extends Where
}
}
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
// from SQLExpression
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
for (int ii = 0; ii < _values.length; ii ++) {
if (_values[ii] != null) {
pstmt.setObject(argIdx ++, _values[ii]);
}
}
return argIdx;
return bindClauseArguments(pstmt, argIdx);
}
// from CacheKey
@@ -85,16 +85,13 @@ public class MultiKey<T extends PersistentRecord> extends Where
}
// from QueryClause
public Collection<Class<? extends PersistentRecord>> getClassSet ()
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
{
ArrayList<Class<? extends PersistentRecord>> set =
new ArrayList<Class<? extends PersistentRecord>>();
set.add(_pClass);
return set;
classSet.add(_pClass);
}
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
builder.append(" where ");
boolean first = true;
@@ -121,7 +118,7 @@ public class MultiKey<T extends PersistentRecord> extends Where
}
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
for (Map.Entry entry : _map.entrySet()) {
@@ -257,7 +257,8 @@ public class PersistenceContext
if (listeners != null && listeners.size() > 0) {
for (CacheListener<?> listener : listeners) {
Log.debug("cacheInvalidate: cascading [listener=" + listener + "]");
@SuppressWarnings("unchecked") CacheListener<T> casted = (CacheListener<T>)listener;
@SuppressWarnings("unchecked")
CacheListener<T> casted = (CacheListener<T>)listener;
casted.entryCached(key, entry, oldEntry);
}
}
@@ -29,12 +29,19 @@ import java.sql.SQLException;
public interface Query<T>
{
/**
* Returns the {@link CacheKey} associated with this query, if relevant, or null.
* Any query may elect to utilize the built-in cache by returning a non-null {@link CacheKey}
* in this method. This is done automatically by the {@link DepotRepository} when looking up
* single entities by primary key, but even entire collections can be cached under a single
* key.
*
* Great care must be taken to invalidate such cached collections when their constituent
* entities are invalidated. This is generally done using {@link CacheListener} and
* {@link CacheInvalidator}.
*/
public CacheKey getCacheKey ();
/**
* Performs the actual JDBC operations associated with this query.
* Performs the actual JDBC operations associated with this query.
*/
public T invoke (Connection conn)
throws SQLException;
@@ -0,0 +1,43 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
/**
* This type is used during the construction of SQL queries to map persistent classes
* to table names and table abbreviations. Its primary implementation is {@link DepotTypes}.
*/
public interface QueryBuilderContext<T>
{
/**
* Maps a class referenced by this query to its associated table.
*
* This method is called by individual clauses.
*/
public String getTableName (Class<? extends PersistentRecord> cl);
/**
* Maps a class referenced by this query to the abbreviation used for its associated table. The
* abbreviation is meaningful only within the scope of this particular query.
*
* This method is called by individual clauses.
*/
public String getTableAbbreviation (Class<? extends PersistentRecord> cl);
}
@@ -0,0 +1,288 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.depot.annotation.Computed;
import com.samskivert.jdbc.depot.clause.FieldOverride;
import com.samskivert.jdbc.depot.clause.ForUpdate;
import com.samskivert.jdbc.depot.clause.FromOverride;
import com.samskivert.jdbc.depot.clause.GroupBy;
import com.samskivert.jdbc.depot.clause.Join;
import com.samskivert.jdbc.depot.clause.Limit;
import com.samskivert.jdbc.depot.clause.OrderBy;
import com.samskivert.jdbc.depot.clause.QueryClause;
import com.samskivert.jdbc.depot.clause.Where;
/**
* Builds actual SQL given a main persistent type and some {@link QueryClause} objects.
*/
public class SQLQueryBuilder<T>
{
/** The from override clause, if any. */
public FromOverride fromOverride;
/** The where clause. */
public Where where;
/** A list of join clauses, each potentially referencing a new class. */
public List<Join> joinClauses = new ArrayList<Join>();
/** The order by clause, if any. */
public OrderBy orderBy;
/** The group by clause, if any. */
public GroupBy groupBy;
/** The limit clause, if any. */
public Limit limit;
/** The For Update clause, if any. */
public ForUpdate forUpdate;
/**
* Constructs a {@link DepotTypes} object for your convenience given similar arguments used to
* construct a {@link SQLQueryBuilder}. This method mainly offloads the task of interrogating
* the various clauses for what persistent classes they introduce into the query.
*/
public static <T> DepotTypes<T> getDepotTypes (
PersistenceContext ctx, Class<? extends PersistentRecord> type, QueryClause... clauses)
throws PersistenceException
{
Set<Class<? extends PersistentRecord>> classSet =
new HashSet<Class<? extends PersistentRecord>>();
if (type != null) {
classSet.add(type);
}
for (QueryClause clause : clauses) {
if (clause != null) {
clause.addClasses(classSet);
}
}
return new DepotTypes<T>(ctx, type, classSet);
}
/**
* Creates a new Query object to generate one or more instances of the specified persistent
* class, as dictated by the key and query clauses. A persistence context is supplied for
* instantiation of marshallers, which may trigger table creations and schema migrations.
*/
public SQLQueryBuilder (PersistenceContext ctx, DepotTypes<T> types, String[] fields,
QueryClause... clauses)
{
_builder = new StringBuilder("select ");
_types = types;
// iterate over the clauses and sort them into the different types we understand
for (QueryClause clause : clauses) {
if (clause == null) {
continue;
}
if (clause instanceof Where) {
if (where != null) {
throw new IllegalArgumentException(
"Query can't contain multiple Where clauses.");
}
where = (Where) clause;
} else if (clause instanceof FromOverride) {
if (fromOverride != null) {
throw new IllegalArgumentException(
"Query can't contain multiple FromOverride clauses.");
}
fromOverride = (FromOverride) clause;
} else if (clause instanceof Join) {
joinClauses.add((Join) clause);
} else if (clause instanceof FieldOverride) {
_disMap.put(((FieldOverride) clause).getField(),
((FieldOverride) clause));
} else if (clause instanceof OrderBy) {
if (orderBy != null) {
throw new IllegalArgumentException(
"Query can't contain multiple OrderBy clauses.");
}
orderBy = (OrderBy) clause;
} else if (clause instanceof GroupBy) {
if (groupBy != null) {
throw new IllegalArgumentException(
"Query can't contain multiple GroupBy clauses.");
}
groupBy = (GroupBy) clause;
} else if (clause instanceof Limit) {
if (limit != null) {
throw new IllegalArgumentException(
"Query can't contain multiple Limit clauses.");
}
limit = (Limit) clause;
} else if (clause instanceof ForUpdate) {
if (forUpdate != null) {
throw new IllegalArgumentException(
"Query can't contain multiple For Update clauses.");
}
forUpdate = (ForUpdate) clause;
}
}
Computed entityComputed = _types.getMainType().getAnnotation(Computed.class);
// iterate over the fields we're filling in and figure out whence each one comes
boolean skip = true;
for (String field : fields) {
if (!skip) {
_builder.append(", ");
}
skip = false;
// first, see if there's a field override
FieldOverride clause1 = _disMap.get(field);
if (clause1 != null) {
clause1.appendClause(_types, _builder);
continue;
}
// figure out the class we're selecting from unless we're otherwise overriden:
// for a concrete record, simply use the corresponding table; for a computed one,
// default to the shadowed concrete record, or null if there isn't one
Class<? extends PersistentRecord> tableClass =
entityComputed == null ? _types.getMainType() : entityComputed.shadowOf();
// handle the field-level @Computed annotation, if there is one
Computed fieldComputed =
_types.getMainMarshaller().getFieldMarshaller(field).getComputed();
if (fieldComputed != null) {
// check if the computed field has a literal SQL definition
if (fieldComputed.fieldDefinition().length() > 0) {
_builder.append(fieldComputed.fieldDefinition()).append(" as ").append(field);
continue;
}
// or if we can simply ignore the field
if (!fieldComputed.required()) {
skip = true;
continue;
}
// else see if there's an overriding shadowOf definition
if (fieldComputed.shadowOf() != null) {
tableClass = fieldComputed.shadowOf();
}
}
// if we get this far we hopefully have a table to select from
if (tableClass != null) {
String tableName = _types.getTableAbbreviation(tableClass);
_builder.append(tableName).append(".").append(field);
continue;
}
// else owie
throw new IllegalArgumentException(
"Persistent field has no definition [class=" + _types.getMainType() +
", field=" + field + "]");
}
}
public PreparedStatement prepare (Connection conn)
throws SQLException
{
if (fromOverride != null) {
fromOverride.appendClause(_types, _builder);
} else if (_types.getMainTableName() != null) {
_builder.append(" from ").append(_types.getMainTableName()).
append(" as ").append(_types.getMainTableAbbreviation());
} else {
throw new SQLException("Query on @Computed entity with no FromOverrideClause.");
}
for (Join clause : joinClauses) {
clause.appendClause(_types, _builder);
}
if (where != null) {
where.appendClause(_types, _builder);
}
if (groupBy != null) {
groupBy.appendClause(_types, _builder);
}
if (orderBy != null) {
orderBy.appendClause(_types, _builder);
}
if (limit != null) {
limit.appendClause(_types, _builder);
}
if (forUpdate != null) {
forUpdate.appendClause(_types, _builder);
}
PreparedStatement pstmt = conn.prepareStatement(_builder.toString());
int argIdx = 1;
for (Join clause : joinClauses) {
argIdx = clause.bindClauseArguments(pstmt, argIdx);
}
if (where != null) {
argIdx = where.bindClauseArguments(pstmt, argIdx);
}
if (groupBy != null) {
argIdx = groupBy.bindClauseArguments(pstmt, argIdx);
}
if (orderBy != null) {
argIdx = orderBy.bindClauseArguments(pstmt, argIdx);
}
if (limit != null) {
argIdx = limit.bindClauseArguments(pstmt, argIdx);
}
if (forUpdate != null) {
argIdx = forUpdate.bindClauseArguments(pstmt, argIdx);
}
return pstmt;
}
/** A StringBuilder to hold the constructed SQL. */
protected StringBuilder _builder;
/** The class that maps persistent classes to marshallers. */
protected DepotTypes<T> _types;
/** Persistent class fields mapped to field override clauses. */
protected Map<String, FieldOverride> _disMap = new HashMap<String, FieldOverride>();
}
@@ -25,13 +25,13 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
/**
* Marks a field as computed, meaning it is ignored for schema purposes and it does not directly
* correspond to a column in a table, thus its value must be overridden in the {@link
* ConstructedQuery}.
* QueryBuilderContext}.
*/
@Retention(value=RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.TYPE })
@@ -20,7 +20,6 @@
package com.samskivert.jdbc.depot.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -20,8 +20,12 @@
package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.expression.ColumnExp;
import com.samskivert.jdbc.depot.expression.LiteralExp;
@@ -65,7 +69,7 @@ public class FieldOverride extends QueryClause
}
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
_override.appendExpression(query, builder);
builder.append(" as ").append(_field);
@@ -20,7 +20,12 @@
package com.samskivert.jdbc.depot.clause;
import com.samskivert.jdbc.depot.ConstructedQuery;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
/**
* Represents a FOR UPDATE clause.
@@ -28,7 +33,7 @@ import com.samskivert.jdbc.depot.ConstructedQuery;
public class ForUpdate extends QueryClause
{
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
builder.append(" for update ");
}
@@ -20,11 +20,13 @@
package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
/**
@@ -53,13 +55,13 @@ public class FromOverride extends QueryClause
}
// from QueryClause
public Collection<Class<? extends PersistentRecord>> getClassSet ()
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
{
return _fromClasses;
classSet.addAll(_fromClasses);
}
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
builder.append(" from " );
for (int ii = 0; ii < _fromClasses.size(); ii++) {
@@ -74,4 +76,5 @@ public class FromOverride extends QueryClause
/** The classes of the tables we're selecting from. */
protected ArrayList<Class<? extends PersistentRecord>> _fromClasses =
new ArrayList<Class<? extends PersistentRecord>>();
}
@@ -22,8 +22,10 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.expression.SQLExpression;
/**
@@ -37,7 +39,7 @@ public class GroupBy extends QueryClause
}
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
builder.append(" group by ");
for (int ii = 0; ii < _values.length; ii++) {
@@ -49,11 +51,11 @@ public class GroupBy extends QueryClause
}
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
for (int ii = 0; ii < _values.length; ii++) {
argIdx = _values[ii].bindArguments(pstmt, argIdx);
argIdx = _values[ii].bindExpressionArguments(pstmt, argIdx);
}
return argIdx;
}
@@ -22,15 +22,14 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.expression.ColumnExp;
import com.samskivert.jdbc.depot.expression.SQLExpression;
import com.samskivert.jdbc.depot.operator.Conditionals.*;
import com.samskivert.jdbc.depot.operator.SQLOperator;
/**
* Represents a JOIN.
@@ -55,19 +54,16 @@ public class Join extends QueryClause
_joinCondition = new Equals(primary, join);
}
public Join (Class<? extends PersistentRecord> joinClass, SQLOperator joinCondition)
public Join (Class<? extends PersistentRecord> joinClass, SQLExpression joinCondition)
{
_joinClass = joinClass;
_joinCondition = joinCondition;
}
// from QueryClause
public Collection<Class<? extends PersistentRecord>> getClassSet ()
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
{
ArrayList<Class<? extends PersistentRecord>> set =
new ArrayList<Class<? extends PersistentRecord>>();
set.add(_joinClass);
return set;
classSet.add(_joinClass);
}
/**
@@ -80,7 +76,7 @@ public class Join extends QueryClause
}
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
switch (_type) {
case INNER:
@@ -99,10 +95,10 @@ public class Join extends QueryClause
}
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return _joinCondition.bindArguments(pstmt, argIdx);
return _joinCondition.bindExpressionArguments(pstmt, argIdx);
}
/** Indicates the type of join to be performed. */
@@ -112,5 +108,5 @@ public class Join extends QueryClause
protected Class<? extends PersistentRecord> _joinClass;
/** The condition used to join in the new table. */
protected SQLOperator _joinCondition;
protected SQLExpression _joinCondition;
}
@@ -22,8 +22,10 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
/**
* Represents a LIMIT/OFFSET clause, for pagination.
@@ -37,13 +39,13 @@ public class Limit extends QueryClause
}
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
builder.append(" limit ? offset ? ");
}
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
pstmt.setObject(argIdx++, _count);
@@ -22,8 +22,10 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.expression.ColumnExp;
import com.samskivert.jdbc.depot.expression.LiteralExp;
import com.samskivert.jdbc.depot.expression.SQLExpression;
@@ -83,7 +85,7 @@ public class OrderBy extends QueryClause
}
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
builder.append(" order by ");
for (int ii = 0; ii < _values.length; ii++) {
@@ -96,11 +98,11 @@ public class OrderBy extends QueryClause
}
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
for (int ii = 0; ii < _values.length; ii++) {
argIdx = _values[ii].bindArguments(pstmt, argIdx);
argIdx = _values[ii].bindExpressionArguments(pstmt, argIdx);
}
return argIdx;
}
@@ -2,7 +2,7 @@
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006 Michael Bayne, Pär Winzell
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
@@ -24,7 +24,7 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
/**
@@ -33,26 +33,24 @@ import com.samskivert.jdbc.depot.PersistentRecord;
public abstract class QueryClause
{
/**
* Return a set of all persistent classes referenced by this clause. The default implementation
* returns null to indicate that no classes are rererenced.
* Adds all persistent classes referenced by this clause, if any, to the supplied set.
*/
public Collection<Class<? extends PersistentRecord>> getClassSet ()
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
{
return null;
}
/**
* Construct the SQL form of this query clause. The implementor is expected to call methods on
* the Query object to e.g. resolve current table abbreviations associated with classes.
*/
public abstract void appendClause (ConstructedQuery<?> query, StringBuilder builder);
/**
* Bind any objects that were referenced in the generated SQL. For each ? that appears in the
* Constructs the SQL form of this query clause. The implementor is expected to call methods on
* the Query object to e.g. resolve current table abbreviations associated with classes.
*/
public abstract void appendClause (QueryBuilderContext<?> query, StringBuilder builder);
/**
* Binds any objects that were referenced in the generated SQL. For each ? that appears in the
* SQL, precisely one parameter must be claimed and bound in this method, and argIdx
* incremented and returned. The default implementation binds nothing.
*/
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return argIdx;
@@ -22,16 +22,17 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.clause.QueryClause;
import com.samskivert.jdbc.depot.expression.ColumnExp;
import com.samskivert.jdbc.depot.expression.SQLExpression;
import com.samskivert.jdbc.depot.expression.ValueExp;
import com.samskivert.jdbc.depot.operator.Conditionals.Equals;
import com.samskivert.jdbc.depot.operator.Conditionals.IsNull;
import com.samskivert.jdbc.depot.operator.Logic.And;
import com.samskivert.jdbc.depot.operator.SQLOperator;
/**
* Represents a where clause: the condition can be any comparison operator or logical combination
@@ -80,28 +81,28 @@ public class Where extends QueryClause
this(toCondition(columns, values));
}
public Where (SQLOperator condition)
public Where (SQLExpression condition)
{
_condition = condition;
}
// from QueryClause
public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
{
builder.append(" where ");
_condition.appendExpression(query, builder);
}
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return _condition.bindArguments(pstmt, argIdx);
return _condition.bindExpressionArguments(pstmt, argIdx);
}
protected static SQLOperator toCondition (ColumnExp[] columns, Comparable[] values)
protected static SQLExpression toCondition (ColumnExp[] columns, Comparable[] values)
{
SQLOperator[] comparisons = new SQLOperator[columns.length];
SQLExpression[] comparisons = new SQLExpression[columns.length];
for (int ii = 0; ii < columns.length; ii ++) {
comparisons[ii] = (values[ii] == null) ? new IsNull(columns[ii]) :
new Equals(columns[ii], new ValueExp(values[ii]));
@@ -109,5 +110,5 @@ public class Where extends QueryClause
return new And(comparisons);
}
protected SQLOperator _condition;
protected SQLExpression _condition;
}
@@ -23,7 +23,7 @@ package com.samskivert.jdbc.depot.expression;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
/**
@@ -52,7 +52,7 @@ public class ColumnExp
}
// from SQLExpression
public void appendExpression (ConstructedQuery<?> query, StringBuilder builder)
public void appendExpression (QueryBuilderContext<?> query, StringBuilder builder)
{
if (pClass == null || query == null) {
builder.append(pColumn);
@@ -63,7 +63,7 @@ public class ColumnExp
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return argIdx;
@@ -23,7 +23,7 @@ package com.samskivert.jdbc.depot.expression;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
/**
* An expression for a function, e.g. FLOOR(blah).
@@ -41,7 +41,7 @@ public class FunctionExp
}
// from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder)
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
{
builder.append(_function);
builder.append("(");
@@ -55,11 +55,11 @@ public class FunctionExp
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
for (int ii = 0; ii < _arguments.length; ii ++) {
argIdx = _arguments[ii].bindArguments(pstmt, argIdx);
argIdx = _arguments[ii].bindExpressionArguments(pstmt, argIdx);
}
return argIdx;
}
@@ -23,7 +23,7 @@ package com.samskivert.jdbc.depot.expression;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
/**
* An expression for things we don't support natively, e.g. COUNT(*).
@@ -38,13 +38,13 @@ public class LiteralExp
}
// from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder)
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
{
builder.append(_text);
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return argIdx;
@@ -23,7 +23,7 @@ package com.samskivert.jdbc.depot.expression;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
/**
* Represents an SQL expression, e.g. column name, function, or constant.
@@ -34,13 +34,13 @@ public interface SQLExpression
* Construct the SQL form of this expression. The implementor is invited to call methods on the
* Query object to e.g. resolve the current table abbreviations associated with classes.
*/
public void appendExpression (ConstructedQuery<?> query, StringBuilder builder);
public void appendExpression (QueryBuilderContext<?> query, StringBuilder builder);
/**
* Bind any objects that were referenced in the generated SQL. For each ? that appears in the
* SQL, precisely one parameter must be claimed and bound in this method, and argIdx
* incremented and returned.
*/
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException;
}
@@ -23,7 +23,7 @@ package com.samskivert.jdbc.depot.expression;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
/**
* A Java value that is bound as a parameter to the query, e.g. 1 or 'abc'.
@@ -37,13 +37,13 @@ public class ValueExp
}
// from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder)
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
{
builder.append("?");
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
pstmt.setObject(argIdx ++, _value);
@@ -24,7 +24,7 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.expression.ColumnExp;
import com.samskivert.jdbc.depot.expression.SQLExpression;
@@ -57,14 +57,14 @@ public abstract class Conditionals
}
// from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder)
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
{
_column.appendExpression(query, builder);
builder.append(" is null");
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return argIdx;
@@ -233,7 +233,7 @@ public abstract class Conditionals
}
// from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder)
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
{
_column.appendExpression(query, builder);
builder.append(" in (");
@@ -247,7 +247,7 @@ public abstract class Conditionals
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
for (int ii = 0; ii < _values.length; ii++) {
@@ -286,7 +286,7 @@ public abstract class Conditionals
}
// from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder)
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
{
builder.append("match(");
int idx = 0;
@@ -312,7 +312,7 @@ public abstract class Conditionals
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
pstmt.setString(argIdx++, _query);
@@ -23,7 +23,8 @@ package com.samskivert.jdbc.depot.operator;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.expression.SQLExpression;
import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator;
/**
@@ -38,7 +39,7 @@ public abstract class Logic
*/
public static class Or extends MultiOperator
{
public Or (SQLOperator... conditions)
public Or (SQLExpression... conditions)
{
super(conditions);
}
@@ -55,7 +56,7 @@ public abstract class Logic
*/
public static class And extends MultiOperator
{
public And (SQLOperator... conditions)
public And (SQLExpression... conditions)
{
super(conditions);
}
@@ -73,14 +74,14 @@ public abstract class Logic
public static class Not
implements SQLOperator
{
public Not (SQLOperator condition)
public Not (SQLExpression condition)
{
super();
_condition = condition;
}
// from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder)
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
{
builder.append(" not (");
_condition.appendExpression(query, builder);
@@ -88,12 +89,12 @@ public abstract class Logic
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return _condition.bindArguments(pstmt, argIdx);
return _condition.bindExpressionArguments(pstmt, argIdx);
}
protected SQLOperator _condition;
protected SQLExpression _condition;
}
}
@@ -23,7 +23,7 @@ package com.samskivert.jdbc.depot.operator;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.QueryBuilderContext;
import com.samskivert.jdbc.depot.expression.SQLExpression;
import com.samskivert.jdbc.depot.expression.ValueExp;
@@ -40,14 +40,14 @@ public interface SQLOperator extends SQLExpression
public abstract static class MultiOperator
implements SQLOperator
{
public MultiOperator (SQLOperator... conditions)
public MultiOperator (SQLExpression ... conditions)
{
super();
_conditions = conditions;
}
// from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder)
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
{
for (int ii = 0; ii < _conditions.length; ii++) {
if (ii > 0) {
@@ -60,11 +60,11 @@ public interface SQLOperator extends SQLExpression
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
for (int ii = 0; ii < _conditions.length; ii++) {
argIdx = _conditions[ii].bindArguments(pstmt, argIdx);
argIdx = _conditions[ii].bindExpressionArguments(pstmt, argIdx);
}
return argIdx;
}
@@ -74,7 +74,7 @@ public interface SQLOperator extends SQLExpression
*/
protected abstract String operator ();
protected SQLOperator[] _conditions;
protected SQLExpression[] _conditions;
}
/**
@@ -94,7 +94,7 @@ public interface SQLOperator extends SQLExpression
}
// from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder)
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
{
_lhs.appendExpression(query, builder);
builder.append(operator());
@@ -102,11 +102,11 @@ public interface SQLOperator extends SQLExpression
}
// from SQLExpression
public int bindArguments (PreparedStatement pstmt, int argIdx)
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
argIdx = _lhs.bindArguments(pstmt, argIdx);
argIdx = _rhs.bindArguments(pstmt, argIdx);
argIdx = _lhs.bindExpressionArguments(pstmt, argIdx);
argIdx = _rhs.bindExpressionArguments(pstmt, argIdx);
return argIdx;
}