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:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user