Exciting new additions:
1. ColumnExp is now parameterized on the type of the column it represents. This
enables item number two.
2. The beginnings of a very primitive implementation of individual field
selection, which looks like:
List<Tuple2<Integer,String>> results = from(FooRecord.class).where(
FooRecord.ID.in(someIds)).select(FooRecord.ID, FooRecord.NAME);
I don't advise using this new functionality just yet, because it's very
primitive and will probably break if you try to do anything fancy. Plus it
doesn't interact with the cache at all yet.
However, I wanted to get the typed ColumnExp bits in there so that I can
migrate all the existing projects over and get that painful business out of the
way. Note for the jumpy: migration in this case means eliminating a bunch of
unchecked type usage warnings, rather than behavioral changes. The existing
Depot functionality continues to work exactly as is. The types are not used
except for in the new individual field selection code.
One bit of code that will break is calls to updatePartial() which used to take
a Map<ColumnExp, ?> and now take a Map<ColumnExp<?>, ?>. Since those were
already in generics land, they won't just fail with an unchecked type warning,
the compiler will reject the old call (the calls are binary compatible, so
unrecompiled code will still work fine). Fortunately the Map-taking
updatePartial is not called that much outside of MSOY, which I have already
migrated to fully parameterized ColumnExp land.
This commit is contained in:
@@ -34,7 +34,7 @@ public class CountRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<CountRecord> _R = CountRecord.class;
|
||||
public static final ColumnExp COUNT = colexp(_R, "count");
|
||||
public static final ColumnExp<Integer> COUNT = colexp(_R, "count");
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** The computed count. */
|
||||
|
||||
@@ -337,13 +337,20 @@ public abstract class DepotRepository
|
||||
return _ctx.invoke(new FindAllKeysQuery<T>(_ctx, type, forUpdate, clauses));
|
||||
}
|
||||
|
||||
// public <T extends PersistentRecord> List<Object> findAll (
|
||||
// Class<T> type, Iterable<? extends QueryClause> clauses)
|
||||
// throws DatabaseException
|
||||
// {
|
||||
// return _ctx.invoke(new FindAllQuery.ForColumns<T>(_ctx, type, clauses, column));
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns a builder that can be used to construct a query in a fluent style. For example:
|
||||
* {@code from(FooRecord.class).where(ID.greaterThan(25)).ascending(SIZE).select()}
|
||||
*/
|
||||
public <T extends PersistentRecord> QueryBuilder<T> from (Class<T> type)
|
||||
{
|
||||
return new QueryBuilder<T>(this, type);
|
||||
return new QueryBuilder<T>(_ctx, this, type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,7 +429,7 @@ public abstract class DepotRepository
|
||||
*
|
||||
* @throws DatabaseException if any problem is encountered communicating with the database.
|
||||
*/
|
||||
public <T extends PersistentRecord> int update (T record, final ColumnExp... modifiedFields)
|
||||
public <T extends PersistentRecord> int update (T record, final ColumnExp<?>... modifiedFields)
|
||||
throws DatabaseException
|
||||
{
|
||||
@SuppressWarnings("unchecked") Class<T> pClass = (Class<T>) record.getClass();
|
||||
@@ -451,7 +458,7 @@ public abstract class DepotRepository
|
||||
* @throws DatabaseException if any problem is encountered communicating with the database.
|
||||
*/
|
||||
public <T extends PersistentRecord> int updatePartial (
|
||||
Key<T> key, ColumnExp field, Object value, Object... more)
|
||||
Key<T> key, ColumnExp<?> field, Object value, Object... more)
|
||||
throws DatabaseException
|
||||
{
|
||||
return updatePartial(key.getPersistentClass(), key, key, field, value, more);
|
||||
@@ -471,7 +478,8 @@ public abstract class DepotRepository
|
||||
* values that duplicate another row already in the database.
|
||||
* @throws DatabaseException if any problem is encountered communicating with the database.
|
||||
*/
|
||||
public <T extends PersistentRecord> int updatePartial (Key<T> key, Map<ColumnExp, ?> updates)
|
||||
public <T extends PersistentRecord> int updatePartial (
|
||||
Key<T> key, Map<? extends ColumnExp<?>, ?> updates)
|
||||
throws DatabaseException
|
||||
{
|
||||
return updatePartial(key.getPersistentClass(), key, key, updates);
|
||||
@@ -498,15 +506,15 @@ public abstract class DepotRepository
|
||||
* @throws DatabaseException if any problem is encountered communicating with the database.
|
||||
*/
|
||||
public <T extends PersistentRecord> int updatePartial (
|
||||
Class<T> type, final WhereClause key, CacheInvalidator invalidator,
|
||||
Map<ColumnExp, ?> updates)
|
||||
Class<T> type, WhereClause key, CacheInvalidator invalidator,
|
||||
Map<? extends ColumnExp<?>, ?> updates)
|
||||
throws DatabaseException
|
||||
{
|
||||
// separate the arguments into keys and values
|
||||
final ColumnExp[] fields = new ColumnExp[updates.size()];
|
||||
final ColumnExp<?>[] fields = new ColumnExp<?>[updates.size()];
|
||||
final SQLExpression[] values = new SQLExpression[fields.length];
|
||||
int ii = 0;
|
||||
for (Map.Entry<ColumnExp, ?> entry : updates.entrySet()) {
|
||||
for (Map.Entry<? extends ColumnExp<?>, ?> entry : updates.entrySet()) {
|
||||
fields[ii] = entry.getKey();
|
||||
values[ii++] = makeValue(entry.getValue());
|
||||
}
|
||||
@@ -536,16 +544,16 @@ public abstract class DepotRepository
|
||||
*/
|
||||
public <T extends PersistentRecord> int updatePartial (
|
||||
Class<T> type, final WhereClause key, CacheInvalidator invalidator,
|
||||
ColumnExp field, Object value, Object... more)
|
||||
ColumnExp<?> field, Object value, Object... more)
|
||||
throws DatabaseException
|
||||
{
|
||||
// separate the updates into keys and values
|
||||
final ColumnExp[] fields = new ColumnExp[1+more.length/2];
|
||||
final ColumnExp<?>[] fields = new ColumnExp<?>[1+more.length/2];
|
||||
final SQLExpression[] values = new SQLExpression[fields.length];
|
||||
fields[0] = field;
|
||||
values[0] = makeValue(value);
|
||||
for (int ii = 1, idx = 0; ii < fields.length; ii++) {
|
||||
fields[ii] = (ColumnExp)more[idx++];
|
||||
fields[ii] = (ColumnExp<?>)more[idx++];
|
||||
values[ii] = makeValue(more[idx++]);
|
||||
}
|
||||
return updatePartial(type, key, invalidator, fields, values);
|
||||
@@ -572,7 +580,7 @@ public abstract class DepotRepository
|
||||
*/
|
||||
public <T extends PersistentRecord> int updatePartial (
|
||||
Class<T> type, final WhereClause key, CacheInvalidator invalidator,
|
||||
ColumnExp[] fields, SQLExpression[] values)
|
||||
ColumnExp<?>[] fields, SQLExpression[] values)
|
||||
throws DatabaseException
|
||||
{
|
||||
requireNotComputed(type, "updatePartial");
|
||||
|
||||
@@ -73,30 +73,32 @@ public class Key<T extends PersistentRecord> extends WhereClause
|
||||
/**
|
||||
* Creates a single column key.
|
||||
*/
|
||||
public static <T extends PersistentRecord> Key<T> newKey (
|
||||
Class<T> pClass, ColumnExp ix, Comparable<?> val)
|
||||
public static <T extends PersistentRecord, V extends Comparable<V>> Key<T> newKey (
|
||||
Class<T> pClass, ColumnExp<V> ix, V val)
|
||||
{
|
||||
return new Key<T>(pClass, new ColumnExp[] { ix }, new Comparable[] { val });
|
||||
return new Key<T>(pClass, new ColumnExp<?>[] { ix }, new Comparable<?>[] { val });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a two column key.
|
||||
*/
|
||||
public static <T extends PersistentRecord> Key<T> newKey (
|
||||
Class<T> pClass, ColumnExp ix1, Comparable<?> val1, ColumnExp ix2, Comparable<?> val2)
|
||||
public static <T extends PersistentRecord, V1 extends Comparable<V1>, V2 extends Comparable<V2>>
|
||||
Key<T> newKey (Class<T> pClass, ColumnExp<V1> ix1, V1 val1, ColumnExp<V2> ix2, V2 val2)
|
||||
{
|
||||
return new Key<T>(pClass, new ColumnExp[] { ix1, ix2 }, new Comparable[] { val1, val2 });
|
||||
return new Key<T>(pClass, new ColumnExp<?>[] { ix1, ix2 },
|
||||
new Comparable<?>[] { val1, val2 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a three column key.
|
||||
*/
|
||||
public static <T extends PersistentRecord> Key<T> newKey (
|
||||
Class<T> pClass, ColumnExp ix1, Comparable<?> val1, ColumnExp ix2, Comparable<?> val2,
|
||||
ColumnExp ix3, Comparable<?> val3)
|
||||
public static <T extends PersistentRecord, V1 extends Comparable<V1>,
|
||||
V2 extends Comparable<V2>, V3 extends Comparable<V3>>
|
||||
Key<T> newKey (Class<T> pClass, ColumnExp<V1> ix1, V1 val1, ColumnExp<V2> ix2, V2 val2,
|
||||
ColumnExp<V3> ix3, V3 val3)
|
||||
{
|
||||
return new Key<T>(pClass, new ColumnExp[] { ix1, ix2, ix3 },
|
||||
new Comparable[] { val1, val2, val3 });
|
||||
return new Key<T>(pClass, new ColumnExp<?>[] { ix1, ix2, ix3 },
|
||||
new Comparable<?>[] { val1, val2, val3 });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +128,7 @@ public class Key<T extends PersistentRecord> extends WhereClause
|
||||
/**
|
||||
* Constructs a new multi-column {@code Key} with the given values.
|
||||
*/
|
||||
public Key (Class<T> pClass, ColumnExp[] fields, Comparable<?>[] values)
|
||||
public Key (Class<T> pClass, ColumnExp<?>[] fields, Comparable<?>[] values)
|
||||
{
|
||||
this(pClass, toCanonicalOrder(pClass, fields, values));
|
||||
}
|
||||
@@ -198,7 +200,7 @@ public class Key<T extends PersistentRecord> extends WhereClause
|
||||
*/
|
||||
public void toShortString (StringBuilder builder)
|
||||
{
|
||||
ColumnExp[] keyFields = DepotUtil.getKeyFields(_pClass);
|
||||
ColumnExp<?>[] keyFields = DepotUtil.getKeyFields(_pClass);
|
||||
for (int ii = 0; ii < keyFields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
builder.append(":");
|
||||
@@ -243,14 +245,14 @@ public class Key<T extends PersistentRecord> extends WhereClause
|
||||
}
|
||||
|
||||
protected static Comparable<?>[] toCanonicalOrder (
|
||||
Class<? extends PersistentRecord> pClass, ColumnExp[] fields, Comparable<?>[] values)
|
||||
Class<? extends PersistentRecord> pClass, ColumnExp<?>[] fields, Comparable<?>[] values)
|
||||
{
|
||||
if (fields.length != values.length) {
|
||||
throw new IllegalArgumentException("Field and Value arrays must be of equal length.");
|
||||
}
|
||||
|
||||
// look up the cached primary key fields for this object
|
||||
ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
ColumnExp<?>[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
|
||||
// fast path!
|
||||
if (fields.length == 1 && keyFields.length == 1 && keyFields[0].equals(fields[0])) {
|
||||
@@ -258,7 +260,7 @@ public class Key<T extends PersistentRecord> extends WhereClause
|
||||
}
|
||||
|
||||
// build a local map of field name -> field value
|
||||
Map<ColumnExp, Comparable<?>> map = Maps.newHashMap();
|
||||
Map<ColumnExp<?>, Comparable<?>> map = Maps.newHashMap();
|
||||
for (int ii = 0; ii < fields.length; ii++) {
|
||||
map.put(fields[ii], values[ii]);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public abstract class KeySet<T extends PersistentRecord> extends WhereClause
|
||||
return new EmptyKeySet<T>(pClass);
|
||||
}
|
||||
|
||||
ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
ColumnExp<?>[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
if (keyFields.length == 1) {
|
||||
Comparable<?> first = keys.iterator().next().getValues()[0];
|
||||
Comparable<?>[] keyArray;
|
||||
@@ -99,7 +99,7 @@ public abstract class KeySet<T extends PersistentRecord> extends WhereClause
|
||||
public static <T extends PersistentRecord> KeySet<T> newSimpleKeySet (
|
||||
Class<T> pClass, Collection<? extends Comparable<?>> keys)
|
||||
{
|
||||
ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
ColumnExp<?>[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
if (keyFields.length != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot create KeySet using simple keys for record with non-simple primary key " +
|
||||
|
||||
@@ -35,7 +35,7 @@ import com.samskivert.util.Tuple;
|
||||
*/
|
||||
class MultiKeySet<T extends PersistentRecord> extends KeySet<T>
|
||||
{
|
||||
public MultiKeySet (Class<T> pClass, ColumnExp[] keyFields, Comparable<?>[][] keys)
|
||||
public MultiKeySet (Class<T> pClass, ColumnExp<?>[] keyFields, Comparable<?>[][] keys)
|
||||
{
|
||||
super(pClass);
|
||||
_keys = keys;
|
||||
@@ -207,5 +207,5 @@ class MultiKeySet<T extends PersistentRecord> extends KeySet<T>
|
||||
}
|
||||
|
||||
protected Comparable<?>[][] _keys;
|
||||
protected ColumnExp[] _keyFields;
|
||||
protected ColumnExp<?>[] _keyFields;
|
||||
}
|
||||
|
||||
@@ -20,39 +20,28 @@
|
||||
|
||||
package com.samskivert.depot;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.samskivert.depot.expression.ColumnExp;
|
||||
import com.samskivert.depot.impl.DepotUtil;
|
||||
import com.samskivert.depot.impl.QueryResult;
|
||||
|
||||
/**
|
||||
* The base class for all persistent records used in Depot. Persistent records must be cloneable
|
||||
* and serializable; this class is used to enforce those requirements.
|
||||
*/
|
||||
public class PersistentRecord
|
||||
implements Cloneable, Serializable
|
||||
public class PersistentRecord extends QueryResult
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<PersistentRecord> _R = PersistentRecord.class;
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
@Override // from Object
|
||||
public PersistentRecord clone ()
|
||||
{
|
||||
try {
|
||||
return (PersistentRecord) super.clone();
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
throw new AssertionError(cnse); // this should never happen since we are Cloneable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a column expression for this class with the specified field name. Used by the
|
||||
* autogenerated column expression constants.
|
||||
*/
|
||||
protected static ColumnExp colexp (Class<? extends PersistentRecord> clazz, String fieldName)
|
||||
protected static <T> ColumnExp<T> colexp (
|
||||
Class<? extends PersistentRecord> clazz, String fieldName)
|
||||
{
|
||||
return new ColumnExp(clazz, fieldName);
|
||||
return new ColumnExp<T>(clazz, fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +57,7 @@ public class PersistentRecord
|
||||
/**
|
||||
* Register the key fields for a subclass. Called by autogenerated code.
|
||||
*/
|
||||
protected static void registerKeyFields (ColumnExp... fields)
|
||||
protected static void registerKeyFields (ColumnExp<?>... fields)
|
||||
{
|
||||
DepotUtil.registerKeyFields(fields);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.google.common.collect.Lists;
|
||||
import com.samskivert.depot.clause.*;
|
||||
import com.samskivert.depot.expression.ColumnExp;
|
||||
import com.samskivert.depot.expression.SQLExpression;
|
||||
import com.samskivert.depot.impl.ColumnSet;
|
||||
import com.samskivert.depot.impl.FindAllQuery;
|
||||
|
||||
/**
|
||||
* The root of a fluent mechanism for constructing queries. Obtain an instance via {@link
|
||||
@@ -37,8 +39,9 @@ import com.samskivert.depot.expression.SQLExpression;
|
||||
public class QueryBuilder<T extends PersistentRecord>
|
||||
implements Cloneable
|
||||
{
|
||||
public QueryBuilder (DepotRepository repo, Class<T> pclass)
|
||||
public QueryBuilder (PersistenceContext ctx, DepotRepository repo, Class<T> pclass)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_repo = repo;
|
||||
_pclass = pclass;
|
||||
}
|
||||
@@ -104,7 +107,7 @@ public class QueryBuilder<T extends PersistentRecord>
|
||||
* Configures a {@link Where} clause that selects rows where the supplied column equals the
|
||||
* supplied value.
|
||||
*/
|
||||
public QueryBuilder<T> where (ColumnExp column, Comparable<?> value)
|
||||
public <V extends Comparable<? super V>> QueryBuilder<T> where (ColumnExp<V> column, V value)
|
||||
{
|
||||
return where(new Where(column, value));
|
||||
}
|
||||
@@ -113,8 +116,8 @@ public class QueryBuilder<T extends PersistentRecord>
|
||||
* Configures a {@link Where} clause that selects rows where both supplied columns equal both
|
||||
* supplied values.
|
||||
*/
|
||||
public QueryBuilder<T> where (ColumnExp index1, Comparable<?> value1,
|
||||
ColumnExp index2, Comparable<?> value2)
|
||||
public <V1 extends Comparable<? super V1>, V2 extends Comparable<? super V2>>
|
||||
QueryBuilder<T> where (ColumnExp<V1> index1, V1 value1, ColumnExp<V2> index2, V2 value2)
|
||||
{
|
||||
return where(new Where(index1, value1, index2, value2));
|
||||
}
|
||||
@@ -133,7 +136,7 @@ public class QueryBuilder<T extends PersistentRecord>
|
||||
/**
|
||||
* Adds a {@link Join} clause configured with the supplied left and right columns.
|
||||
*/
|
||||
public QueryBuilder<T> join (ColumnExp left, ColumnExp right)
|
||||
public QueryBuilder<T> join (ColumnExp<?> left, ColumnExp<?> right)
|
||||
{
|
||||
return join(new Join(left, right));
|
||||
}
|
||||
@@ -151,7 +154,7 @@ public class QueryBuilder<T extends PersistentRecord>
|
||||
* Adds a {@link Join} clause configured with the supplied left and right columns and join
|
||||
* type.
|
||||
*/
|
||||
public QueryBuilder<T> join (ColumnExp left, ColumnExp right, Join.Type type)
|
||||
public QueryBuilder<T> join (ColumnExp<?> left, ColumnExp<?> right, Join.Type type)
|
||||
{
|
||||
return join(new Join(left, right).setType(type));
|
||||
}
|
||||
@@ -279,7 +282,7 @@ public class QueryBuilder<T extends PersistentRecord>
|
||||
/**
|
||||
* Adds a {@link FieldDefinition} clause.
|
||||
*/
|
||||
public QueryBuilder<T> fieldDef (ColumnExp field, SQLExpression override)
|
||||
public QueryBuilder<T> fieldDef (ColumnExp<?> field, SQLExpression override)
|
||||
{
|
||||
return fieldDef(new FieldDefinition(field, override));
|
||||
}
|
||||
@@ -348,6 +351,24 @@ public class QueryBuilder<T extends PersistentRecord>
|
||||
return _repo.load(CountRecord.class, _cache, getClauseArray()).count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns just the supplied column from the rows matching the query.
|
||||
*/
|
||||
public <V> List<V> select (ColumnExp<V> column)
|
||||
{
|
||||
return _ctx.invoke(new FindAllQuery.ForColumns<T,V>(
|
||||
_ctx, ColumnSet.create(_pclass, column), getClauses()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns just the supplied columns from the rows matching the query.
|
||||
*/
|
||||
public <V1, V2> List<Tuple2<V1,V2>> select (ColumnExp<V1> col1, ColumnExp<V2> col2)
|
||||
{
|
||||
return _ctx.invoke(new FindAllQuery.ForColumns<T,Tuple2<V1,V2>>(
|
||||
_ctx, ColumnSet.create(_pclass, col1, col2), getClauses()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the records that match the configured query clauses. Note that only the where
|
||||
* clauses are used to evaluate a deletion. Attempts to use other clauses will result in
|
||||
@@ -455,6 +476,7 @@ public class QueryBuilder<T extends PersistentRecord>
|
||||
requireNull(_forUpdate, "ForUpdate clause not supported by delete.");
|
||||
}
|
||||
|
||||
protected final PersistenceContext _ctx;
|
||||
protected final DepotRepository _repo;
|
||||
protected final Class<T> _pclass;
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ public abstract class SchemaMigration extends Modifier
|
||||
*/
|
||||
public static class Rename extends SchemaMigration
|
||||
{
|
||||
public Rename (int targetVersion, String oldColumnName, ColumnExp newColumn) {
|
||||
public Rename (int targetVersion, String oldColumnName, ColumnExp<?> newColumn) {
|
||||
super(targetVersion);
|
||||
_oldColumnName = oldColumnName;
|
||||
_fieldName = newColumn.name;
|
||||
@@ -131,7 +131,7 @@ public abstract class SchemaMigration extends Modifier
|
||||
*/
|
||||
public static class Retype extends SchemaMigration
|
||||
{
|
||||
public Retype (int targetVersion, ColumnExp column) {
|
||||
public Retype (int targetVersion, ColumnExp<?> column) {
|
||||
super(targetVersion);
|
||||
_fieldName = column.name;
|
||||
}
|
||||
@@ -170,7 +170,7 @@ public abstract class SchemaMigration extends Modifier
|
||||
*/
|
||||
public static class Add extends SchemaMigration
|
||||
{
|
||||
public Add (int targetVersion, ColumnExp column, String defaultValue) {
|
||||
public Add (int targetVersion, ColumnExp<?> column, String defaultValue) {
|
||||
super(targetVersion);
|
||||
_fieldName = column.name;
|
||||
_defaultValue = defaultValue;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Depot library - a Java relational persistence library
|
||||
// Copyright (C) 2006-2010 Michael Bayne and 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.depot;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.samskivert.depot.impl.QueryResult;
|
||||
|
||||
/**
|
||||
* Contains a two column result.
|
||||
*/
|
||||
public class Tuple2<A,B> implements Serializable
|
||||
{
|
||||
public final A a;
|
||||
public final B b;
|
||||
|
||||
public Tuple2 (A a, B b)
|
||||
{
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return "[" + a + "," + b + "]";
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public @interface Index
|
||||
* If this is defined, a static method must be defined on the record that provides the index
|
||||
* configuration. The method must match one of the following two signatures:
|
||||
* <pre>
|
||||
* public static ColumnExp[] indexName ()
|
||||
* public static ColumnExp<?>[] indexName ()
|
||||
* public static List<Tuple<SQLExpression, OrderBy.Order>> indexName ()
|
||||
* </pre>
|
||||
* The first form will result in a simple multicolum index being created
|
||||
|
||||
@@ -46,7 +46,7 @@ public class FieldDefinition implements QueryClause
|
||||
|
||||
public FieldDefinition (String field, Class<? extends PersistentRecord> pClass, String pCol)
|
||||
{
|
||||
this(field, new ColumnExp(pClass, pCol));
|
||||
this(field, new ColumnExp<Object>(pClass, pCol));
|
||||
}
|
||||
|
||||
public FieldDefinition (String field, SQLExpression override)
|
||||
@@ -55,7 +55,7 @@ public class FieldDefinition implements QueryClause
|
||||
_definition = override;
|
||||
}
|
||||
|
||||
public FieldDefinition (ColumnExp field, SQLExpression override)
|
||||
public FieldDefinition (ColumnExp<?> field, SQLExpression override)
|
||||
{
|
||||
_field = field.name;
|
||||
_definition = override;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class FieldOverride extends FieldDefinition
|
||||
super(field, override);
|
||||
}
|
||||
|
||||
public FieldOverride (ColumnExp field, SQLExpression override)
|
||||
public FieldOverride (ColumnExp<?> field, SQLExpression override)
|
||||
{
|
||||
super(field, override);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class Join implements QueryClause
|
||||
/** Indicates the join type to be used. The default is INNER. */
|
||||
public static enum Type { INNER, LEFT_OUTER, RIGHT_OUTER }
|
||||
|
||||
public Join (ColumnExp primary, ColumnExp join)
|
||||
public Join (ColumnExp<?> primary, ColumnExp<?> join)
|
||||
{
|
||||
_joinClass = join.getPersistentClass();
|
||||
_joinCondition = new Equals(primary, join);
|
||||
|
||||
@@ -43,7 +43,7 @@ public class SelectClause
|
||||
* class for rows that match the supplied clauses.
|
||||
*/
|
||||
public SelectClause (Class<? extends PersistentRecord> pClass,
|
||||
ColumnExp[] columns, QueryClause... clauses)
|
||||
ColumnExp<?>[] columns, QueryClause... clauses)
|
||||
{
|
||||
this(pClass, columns, Arrays.asList(clauses));
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class SelectClause
|
||||
* Creates a new select clause, selecting the supplied columns from the specified persistent
|
||||
* class for rows that match the supplied clauses.
|
||||
*/
|
||||
public SelectClause (Class<? extends PersistentRecord> pClass, ColumnExp[] columns,
|
||||
public SelectClause (Class<? extends PersistentRecord> pClass, ColumnExp<?>[] columns,
|
||||
Iterable<? extends QueryClause> clauses)
|
||||
{
|
||||
_pClass = pClass;
|
||||
@@ -133,7 +133,7 @@ public class SelectClause
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public ColumnExp[] getFields ()
|
||||
public ColumnExp<?>[] getFields ()
|
||||
{
|
||||
return _fields;
|
||||
}
|
||||
@@ -231,7 +231,7 @@ public class SelectClause
|
||||
protected Class<? extends PersistentRecord> _pClass;
|
||||
|
||||
/** The persistent fields to select. */
|
||||
protected ColumnExp[] _fields;
|
||||
protected ColumnExp<?>[] _fields;
|
||||
|
||||
/** The from override clause, if any. */
|
||||
protected FromOverride _fromOverride;
|
||||
|
||||
@@ -37,26 +37,28 @@ import com.samskivert.depot.impl.operator.IsNull;
|
||||
*/
|
||||
public class Where extends WhereClause
|
||||
{
|
||||
public Where (ColumnExp column, Comparable<?> value)
|
||||
public <V extends Comparable<? super V>> Where (ColumnExp<V> column, V value)
|
||||
{
|
||||
this(new ColumnExp[] { column }, new Comparable<?>[] { value });
|
||||
this(new ColumnExp<?>[] { column }, new Comparable<?>[] { value });
|
||||
}
|
||||
|
||||
public Where (ColumnExp index1, Comparable<?> value1,
|
||||
ColumnExp index2, Comparable<?> value2)
|
||||
public <V1 extends Comparable<? super V1>, V2 extends Comparable<? super V2>> Where (
|
||||
ColumnExp<V1> index1, V1 value1, ColumnExp<V2> index2, V2 value2)
|
||||
{
|
||||
this(new ColumnExp[] { index1, index2 }, new Comparable<?>[] { value1, value2 });
|
||||
this(new ColumnExp<?>[] { index1, index2 }, new Comparable<?>[] { value1, value2 });
|
||||
}
|
||||
|
||||
public Where (ColumnExp index1, Comparable<?> value1,
|
||||
ColumnExp index2, Comparable<?> value2,
|
||||
ColumnExp index3, Comparable<?> value3)
|
||||
public <V1 extends Comparable<? super V1>, V2 extends Comparable<? super V2>,
|
||||
V3 extends Comparable<? super V3>>
|
||||
Where (ColumnExp<V1> index1, V1 value1,
|
||||
ColumnExp<V2> index2, V2 value2,
|
||||
ColumnExp<V3> index3, V3 value3)
|
||||
{
|
||||
this(new ColumnExp[] { index1, index2, index3 },
|
||||
this(new ColumnExp<?>[] { index1, index2, index3 },
|
||||
new Comparable<?>[] { value1, value2, value3 });
|
||||
}
|
||||
|
||||
public Where (ColumnExp[] columns, Comparable<?>[] values)
|
||||
public Where (ColumnExp<?>[] columns, Comparable<?>[] values)
|
||||
{
|
||||
this(toCondition(columns, values));
|
||||
}
|
||||
@@ -90,7 +92,7 @@ public class Where extends WhereClause
|
||||
return String.valueOf(_condition);
|
||||
}
|
||||
|
||||
protected static SQLExpression toCondition (ColumnExp[] columns, Comparable<?>[] values)
|
||||
protected static SQLExpression toCondition (ColumnExp<?>[] columns, Comparable<?>[] values)
|
||||
{
|
||||
SQLExpression[] comparisons = new SQLExpression[columns.length];
|
||||
for (int ii = 0; ii < columns.length; ii ++) {
|
||||
|
||||
@@ -30,7 +30,7 @@ import com.samskivert.depot.impl.FragmentVisitor;
|
||||
* An expression that unambiguously identifies a field of a class, for example
|
||||
* <code>GameRecord.itemId</code>.
|
||||
*/
|
||||
public class ColumnExp extends FluentExp
|
||||
public class ColumnExp<T> extends FluentExp
|
||||
{
|
||||
/** The name of the column we reference. */
|
||||
public final String name;
|
||||
@@ -47,13 +47,13 @@ public class ColumnExp extends FluentExp
|
||||
* expression. This is useful for "casting" a column expression from a parent class to a
|
||||
* derived class.
|
||||
*/
|
||||
public ColumnExp as (Class<? extends PersistentRecord> oClass)
|
||||
public ColumnExp<T> as (Class<? extends PersistentRecord> oClass)
|
||||
{
|
||||
return new ColumnExp(oClass, name);
|
||||
return new ColumnExp<T>(oClass, name);
|
||||
}
|
||||
|
||||
/** Returns a {@link Join} on this column and the supplied target. */
|
||||
public Join join (ColumnExp join)
|
||||
public Join join (ColumnExp<?> join)
|
||||
{
|
||||
return new Join(this, join);
|
||||
}
|
||||
@@ -84,9 +84,9 @@ public class ColumnExp extends FluentExp
|
||||
@Override // from Object
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
return (other instanceof ColumnExp) &&
|
||||
((ColumnExp)other)._pClass.equals(_pClass) &&
|
||||
((ColumnExp)other).name.equals(this.name);
|
||||
return (other instanceof ColumnExp<?>) &&
|
||||
((ColumnExp<?>)other)._pClass.equals(_pClass) &&
|
||||
((ColumnExp<?>)other).name.equals(this.name);
|
||||
}
|
||||
|
||||
@Override // from Object
|
||||
|
||||
@@ -147,7 +147,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
public Void visit (Key.Expression key)
|
||||
{
|
||||
Class<? extends PersistentRecord> pClass = key.getPersistentClass();
|
||||
ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
ColumnExp<?>[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
Comparable<?>[] values = key.getValues();
|
||||
for (int ii = 0; ii < keyFields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
@@ -241,7 +241,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
return null;
|
||||
}
|
||||
|
||||
public Void visit (ColumnExp columnExp)
|
||||
public Void visit (ColumnExp<?> columnExp)
|
||||
{
|
||||
appendRhsColumn(columnExp.getPersistentClass(), columnExp);
|
||||
return null;
|
||||
@@ -373,7 +373,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
// while expanding column names in the SELECT query, do aliasing and expansion
|
||||
_enableAliasing = _enableOverrides = true;
|
||||
|
||||
for (ColumnExp field : selectClause.getFields()) {
|
||||
for (ColumnExp<?> field : selectClause.getFields()) {
|
||||
// write column to a temporary buffer
|
||||
StringBuilder saved = _builder;
|
||||
_builder = new StringBuilder();
|
||||
@@ -457,7 +457,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
appendTableAbbreviation(pClass);
|
||||
_builder.append(" set ");
|
||||
|
||||
ColumnExp[] fields = updateClause.getFields();
|
||||
ColumnExp<?>[] fields = updateClause.getFields();
|
||||
Object pojo = updateClause.getPojo();
|
||||
SQLExpression[] values = updateClause.getValues();
|
||||
for (int ii = 0; ii < fields.length; ii ++) {
|
||||
@@ -503,7 +503,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
{
|
||||
if (!_allowComplexIndices) {
|
||||
for (Tuple<SQLExpression, Order> field : createIndexClause.getFields()) {
|
||||
if (!(field.left instanceof ColumnExp)) {
|
||||
if (!(field.left instanceof ColumnExp<?>)) {
|
||||
log.warning("This database can't handle complex indexes. Aborting creation.",
|
||||
"ixName", createIndexClause.getName());
|
||||
return null;
|
||||
@@ -757,7 +757,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
}
|
||||
|
||||
protected Void bindField (
|
||||
Class<? extends PersistentRecord> pClass, ColumnExp field, Object pojo)
|
||||
Class<? extends PersistentRecord> pClass, ColumnExp<?> field, Object pojo)
|
||||
{
|
||||
final DepotMarshaller<?> marshaller = _types.getMarshaller(pClass);
|
||||
_bindables.add(newBindable(marshaller, field, pojo));
|
||||
@@ -781,7 +781,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
// equivalent of an lvalue; something that can appear to the left of an equals sign.
|
||||
// We do not prepend this identifier with a table abbreviation, nor do we expand
|
||||
// field overrides, shadowOf declarations, or the like: it is just a column name.
|
||||
protected void appendLhsColumn (Class<? extends PersistentRecord> type, ColumnExp field)
|
||||
protected void appendLhsColumn (Class<? extends PersistentRecord> type, ColumnExp<?> field)
|
||||
{
|
||||
// TODO: nix type and use the class from the supplied ColumnExp
|
||||
DepotMarshaller<?> dm = _types.getMarshaller(type);
|
||||
@@ -796,7 +796,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
|
||||
// Appends an expression for the given field on the given persistent record; this can
|
||||
// appear in a SELECT list, in WHERE clauses, etc, etc.
|
||||
protected void appendRhsColumn (Class<? extends PersistentRecord> type, ColumnExp field)
|
||||
protected void appendRhsColumn (Class<? extends PersistentRecord> type, ColumnExp<?> field)
|
||||
{
|
||||
DepotMarshaller<?> dm = _types.getMarshaller(type);
|
||||
if (dm == null) {
|
||||
@@ -903,11 +903,11 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
Object pojo = insertClause.getPojo();
|
||||
DepotMarshaller<?> marsh = _types.getMarshaller(pClass);
|
||||
Set<String> idFields = insertClause.getIdentityFields();
|
||||
ColumnExp[] fields = marsh.getColumnFieldNames();
|
||||
ColumnExp<?>[] fields = marsh.getColumnFieldNames();
|
||||
|
||||
_builder.append("(");
|
||||
boolean comma = false;
|
||||
for (ColumnExp field : fields) {
|
||||
for (ColumnExp<?> field : fields) {
|
||||
if (idFields.contains(field.name)) {
|
||||
continue;
|
||||
}
|
||||
@@ -920,7 +920,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
_builder.append(") values (");
|
||||
|
||||
comma = false;
|
||||
for (ColumnExp field : fields) {
|
||||
for (ColumnExp<?> field : fields) {
|
||||
if (idFields.contains(field.name)) {
|
||||
continue;
|
||||
}
|
||||
@@ -945,7 +945,7 @@ public abstract class BuildVisitor implements FragmentVisitor<Void>
|
||||
}
|
||||
|
||||
protected static Bindable newBindable (
|
||||
final DepotMarshaller<?> marshaller, final ColumnExp field, final Object pojo)
|
||||
final DepotMarshaller<?> marshaller, final ColumnExp<?> field, final Object pojo)
|
||||
{
|
||||
return new Bindable() {
|
||||
public void doBind (Connection conn, PreparedStatement stmt, int argIx)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Depot library - a Java relational persistence library
|
||||
// Copyright (C) 2006-2010 Michael Bayne and 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.depot.impl;
|
||||
|
||||
import com.samskivert.depot.PersistentRecord;
|
||||
import com.samskivert.depot.Tuple2;
|
||||
import com.samskivert.depot.expression.ColumnExp;
|
||||
|
||||
/**
|
||||
* Contains a set of columns which are to be selected from a query result.
|
||||
*/
|
||||
public abstract class ColumnSet<T extends PersistentRecord,R>
|
||||
{
|
||||
public static <T extends PersistentRecord, V> ColumnSet<T,V> create (
|
||||
Class<T> ptype, ColumnExp<V> column)
|
||||
{
|
||||
return new ColumnSet<T, V>(ptype, new ColumnExp[] { column }) {
|
||||
public V createObject (Object[] results) {
|
||||
@SuppressWarnings("unchecked") V result = (V)results[0];
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T extends PersistentRecord, V1, V2> ColumnSet<T,Tuple2<V1,V2>> create (
|
||||
Class<T> ptype, ColumnExp<V1> col1, ColumnExp<V2> col2)
|
||||
{
|
||||
return new ColumnSet<T, Tuple2<V1,V2>>(ptype, new ColumnExp[] { col1, col2 }) {
|
||||
public Tuple2<V1,V2> createObject (Object[] results) {
|
||||
@SuppressWarnings("unchecked") V1 r1 = (V1)results[0];
|
||||
@SuppressWarnings("unchecked") V2 r2 = (V2)results[1];
|
||||
return new Tuple2<V1,V2>(r1, r2);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public final Class<T> ptype;
|
||||
public final ColumnExp<?>[] columns;
|
||||
|
||||
public abstract R createObject (Object[] results);
|
||||
|
||||
protected ColumnSet (Class<T> ptype, ColumnExp<?>[] columns)
|
||||
{
|
||||
this.ptype = ptype;
|
||||
this.columns = columns;
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ import static com.samskivert.depot.Log.log;
|
||||
* Handles the marshalling and unmarshalling of persistent instances to JDBC primitives ({@link
|
||||
* PreparedStatement} and {@link ResultSet}).
|
||||
*/
|
||||
public class DepotMarshaller<T extends PersistentRecord>
|
||||
public class DepotMarshaller<T extends PersistentRecord> implements QueryMarshaller<T,T>
|
||||
{
|
||||
/** The name of a private static field that must be defined for all persistent object classes.
|
||||
* It is used to handle schema migration. If automatic schema migration is not desired, define
|
||||
@@ -119,7 +119,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
boolean seenIdentityGenerator = false;
|
||||
|
||||
// introspect on the class and create marshallers and indices for persistent fields
|
||||
List<ColumnExp> fields = Lists.newArrayList();
|
||||
List<ColumnExp<?>> fields = Lists.newArrayList();
|
||||
ListMultimap<String, Tuple<SQLExpression, Order>> namedFieldIndices =
|
||||
ArrayListMultimap.create();
|
||||
ListMultimap<String, Tuple<SQLExpression, Order>> uniqueNamedFieldIndices =
|
||||
@@ -146,7 +146,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
|
||||
FieldMarshaller<?> fm = FieldMarshaller.createMarshaller(field);
|
||||
_fields.put(field.getName(), fm);
|
||||
ColumnExp fieldColumn = new ColumnExp(_pClass, field.getName());
|
||||
ColumnExp<?> fieldColumn = new ColumnExp<Object>(_pClass, field.getName());
|
||||
fields.add(fieldColumn);
|
||||
|
||||
// check to see if this is our primary key
|
||||
@@ -245,7 +245,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
|
||||
// generate our full list of fields/columns for use in queries
|
||||
_allFields = fields.toArray(new ColumnExp[fields.size()]);
|
||||
_allFields = fields.toArray(new ColumnExp<?>[fields.size()]);
|
||||
|
||||
// now check for @Entity annotations on the entire superclass chain
|
||||
Class<? extends PersistentRecord> iterClass = pClass.asSubclass(PersistentRecord.class);
|
||||
@@ -254,7 +254,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
if (entity != null) {
|
||||
// add any indices needed for uniqueness constraints
|
||||
for (UniqueConstraint constraint : entity.uniqueConstraints()) {
|
||||
ColumnExp[] colExps = new ColumnExp[constraint.fields().length];
|
||||
ColumnExp<?>[] colExps = new ColumnExp<?>[constraint.fields().length];
|
||||
int ii = 0;
|
||||
for (String field : constraint.fields()) {
|
||||
FieldMarshaller<?> fm = _fields.get(field);
|
||||
@@ -262,7 +262,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown unique constraint field: " + field);
|
||||
}
|
||||
colExps[ii ++] = new ColumnExp(_pClass, field);
|
||||
colExps[ii ++] = new ColumnExp<Object>(_pClass, field);
|
||||
}
|
||||
_indexes.add(buildIndex(constraint.name(), true, colExps));
|
||||
}
|
||||
@@ -316,7 +316,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
/**
|
||||
* Returns all the persistent fields of our class, in definition order.
|
||||
*/
|
||||
public ColumnExp[] getFieldNames ()
|
||||
public ColumnExp<?>[] getFieldNames ()
|
||||
{
|
||||
return _allFields;
|
||||
}
|
||||
@@ -324,7 +324,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
/**
|
||||
* Returns all the persistent fields that correspond to concrete table columns.
|
||||
*/
|
||||
public ColumnExp[] getColumnFieldNames ()
|
||||
public ColumnExp<?>[] getColumnFieldNames ()
|
||||
{
|
||||
return _columnFields;
|
||||
}
|
||||
@@ -374,11 +374,11 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
* Return the names of the columns that constitute the primary key of our associated persistent
|
||||
* record.
|
||||
*/
|
||||
public ColumnExp[] getPrimaryKeyFields ()
|
||||
public ColumnExp<?>[] getPrimaryKeyFields ()
|
||||
{
|
||||
ColumnExp[] pkcols = new ColumnExp[_pkColumns.size()];
|
||||
ColumnExp<?>[] pkcols = new ColumnExp<?>[_pkColumns.size()];
|
||||
for (int ii = 0; ii < pkcols.length; ii ++) {
|
||||
pkcols[ii] = new ColumnExp(_pClass, _pkColumns.get(ii).getField().getName());
|
||||
pkcols[ii] = new ColumnExp<Object>(_pClass, _pkColumns.get(ii).getField().getName());
|
||||
}
|
||||
return pkcols;
|
||||
}
|
||||
@@ -533,10 +533,10 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
|
||||
// figure out the list of fields that correspond to actual table columns and generate the
|
||||
// SQL used to create and migrate our table (unless we're a computed entity)
|
||||
_columnFields = new ColumnExp[_allFields.length];
|
||||
_columnFields = new ColumnExp<?>[_allFields.length];
|
||||
ColumnDefinition[] declarations = new ColumnDefinition[_allFields.length];
|
||||
int jj = 0;
|
||||
for (ColumnExp field : _allFields) {
|
||||
for (ColumnExp<?> field : _allFields) {
|
||||
FieldMarshaller<?> fm = _fields.get(field.name);
|
||||
// include all persistent non-computed fields
|
||||
ColumnDefinition colDef = fm.getColumnDefinition();
|
||||
@@ -778,7 +778,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
Set<String> preMigrateColumns = Sets.newHashSet(metaData.tableColumns);
|
||||
|
||||
// add any missing columns
|
||||
for (ColumnExp field : _columnFields) {
|
||||
for (ColumnExp<?> field : _columnFields) {
|
||||
final FieldMarshaller<?> fmarsh = _fields.get(field.name);
|
||||
if (metaData.tableColumns.remove(fmarsh.getColumnName())) {
|
||||
continue;
|
||||
@@ -969,10 +969,10 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
protected CreateIndexClause buildIndex (String name, boolean unique, Object config)
|
||||
{
|
||||
List<Tuple<SQLExpression, Order>> definition = Lists.newArrayList();
|
||||
if (config instanceof ColumnExp) {
|
||||
definition.add(new Tuple<SQLExpression, Order>((ColumnExp)config, Order.ASC));
|
||||
} else if (config instanceof ColumnExp[]) {
|
||||
for (ColumnExp column : (ColumnExp[])config) {
|
||||
if (config instanceof ColumnExp<?>) {
|
||||
definition.add(new Tuple<SQLExpression, Order>((ColumnExp<?>)config, Order.ASC));
|
||||
} else if (config instanceof ColumnExp<?>[]) {
|
||||
for (ColumnExp<?> column : (ColumnExp<?>[])config) {
|
||||
definition.add(new Tuple<SQLExpression, Order>(column, Order.ASC));
|
||||
}
|
||||
} else if (config instanceof SQLExpression) {
|
||||
@@ -994,7 +994,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
|
||||
// translate an array of field names to an array of column names
|
||||
protected String[] fieldsToColumns (ColumnExp[] fields)
|
||||
protected String[] fieldsToColumns (ColumnExp<?>[] fields)
|
||||
{
|
||||
String[] columns = new String[fields.length];
|
||||
for (int ii = 0; ii < columns.length; ii ++) {
|
||||
@@ -1015,7 +1015,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
TableMetaData meta, PersistenceContext ctx, SQLBuilder builder)
|
||||
throws DatabaseException
|
||||
{
|
||||
for (ColumnExp field : _columnFields) {
|
||||
for (ColumnExp<?> field : _columnFields) {
|
||||
FieldMarshaller<?> fmarsh = _fields.get(field.name);
|
||||
meta.tableColumns.remove(fmarsh.getColumnName());
|
||||
}
|
||||
@@ -1135,10 +1135,10 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
protected List<FieldMarshaller<?>> _pkColumns;
|
||||
|
||||
/** The persisent fields of our object, in definition order. */
|
||||
protected ColumnExp[] _allFields;
|
||||
protected ColumnExp<?>[] _allFields;
|
||||
|
||||
/** The fields of our object with directly corresponding table columns. */
|
||||
protected ColumnExp[] _columnFields;
|
||||
protected ColumnExp<?>[] _columnFields;
|
||||
|
||||
/** The indexes defined for this record. */
|
||||
protected List<CreateIndexClause> _indexes = Lists.newArrayList();
|
||||
|
||||
@@ -35,8 +35,8 @@ public class DepotMigrationHistoryRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<DepotMigrationHistoryRecord> _R = DepotMigrationHistoryRecord.class;
|
||||
public static final ColumnExp IDENT = colexp(_R, "ident");
|
||||
public static final ColumnExp WHEN_COMPLETED = colexp(_R, "whenCompleted");
|
||||
public static final ColumnExp<String> IDENT = colexp(_R, "ident");
|
||||
public static final ColumnExp<Timestamp> WHEN_COMPLETED = colexp(_R, "whenCompleted");
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** Our schema version. Probably not likely to change. */
|
||||
|
||||
@@ -41,7 +41,7 @@ public class DepotUtil
|
||||
* Returns an array containing the names of the primary key fields for the supplied persistent
|
||||
* class. The values are introspected and cached for the lifetime of the VM.
|
||||
*/
|
||||
public static ColumnExp[] getKeyFields (Class<? extends PersistentRecord> pClass)
|
||||
public static ColumnExp<?>[] getKeyFields (Class<? extends PersistentRecord> pClass)
|
||||
{
|
||||
return _keyFields.get(pClass);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class DepotUtil
|
||||
* subclasses. Calling this method after the key fields have already been registered will
|
||||
* have no effect.
|
||||
*/
|
||||
public static void registerKeyFields (ColumnExp... fields)
|
||||
public static void registerKeyFields (ColumnExp<?>... fields)
|
||||
{
|
||||
// TODO: Checks? For example: Validate all exps from same class?
|
||||
// Make a defensive copy of the array? Hide this method from public consumption?
|
||||
@@ -69,21 +69,21 @@ public class DepotUtil
|
||||
|
||||
/** A (never expiring) cache of primary key field names for all persistent classes (of which
|
||||
* there are merely dozens, so we don't need to worry about expiring). */
|
||||
protected static ConcurrentMap<Class<? extends PersistentRecord>, ColumnExp[]> _keyFields =
|
||||
protected static ConcurrentMap<Class<? extends PersistentRecord>, ColumnExp<?>[]> _keyFields =
|
||||
new MapMaker()
|
||||
// newly generated PersistentRecord classes will register their key fields via
|
||||
// registerKeyFields, which will return an ordering determined at genrecord time.
|
||||
// We fall back to computing the fields at runtime for older PersistentRecord classes.
|
||||
.makeComputingMap(new Function<Class<? extends PersistentRecord>, ColumnExp[]>() {
|
||||
public ColumnExp[] apply (Class<? extends PersistentRecord> pClass) {
|
||||
List<ColumnExp> kflist = Lists.newArrayList();
|
||||
.makeComputingMap(new Function<Class<? extends PersistentRecord>, ColumnExp<?>[]>() {
|
||||
public ColumnExp<?>[] apply (Class<? extends PersistentRecord> pClass) {
|
||||
List<ColumnExp<?>> kflist = Lists.newArrayList();
|
||||
for (Field field : pClass.getFields()) {
|
||||
// look for @Id fields
|
||||
if (field.getAnnotation(Id.class) != null) {
|
||||
kflist.add(new ColumnExp(pClass, field.getName()));
|
||||
kflist.add(new ColumnExp<Object>(pClass, field.getName()));
|
||||
}
|
||||
}
|
||||
return kflist.toArray(new ColumnExp[kflist.size()]);
|
||||
return kflist.toArray(new ColumnExp<?>[kflist.size()]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public class ExpressionEvaluator
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object visit (ColumnExp columnExp)
|
||||
public Object visit (ColumnExp<?> columnExp)
|
||||
{
|
||||
Class<? extends PersistentRecord> pClass = columnExp.getPersistentClass();
|
||||
if (pClass != _pClass) {
|
||||
@@ -186,7 +186,7 @@ public class ExpressionEvaluator
|
||||
Field field = pClass.getField(columnExp.name);
|
||||
if (field == null) {
|
||||
log.warning("Couldn't locate field on class", "field", columnExp.name,
|
||||
"class", pClass);
|
||||
"class", pClass);
|
||||
return new NoValue("Internal Error");
|
||||
}
|
||||
return field.get(_pRec);
|
||||
@@ -241,7 +241,7 @@ public class ExpressionEvaluator
|
||||
return new NoValue("Column lookup on unknown persistent class: " + pClass);
|
||||
}
|
||||
|
||||
ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
ColumnExp<?>[] keyFields = DepotUtil.getKeyFields(pClass);
|
||||
Comparable<?>[] values = key.getValues();
|
||||
|
||||
for (int ii = 0; ii < keyFields.length; ii ++) {
|
||||
|
||||
@@ -35,7 +35,6 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.depot.CacheAdapter.CacheCategory;
|
||||
import com.samskivert.depot.DatabaseException;
|
||||
import com.samskivert.depot.DepotRepository.CacheStrategy;
|
||||
@@ -48,7 +47,9 @@ import com.samskivert.depot.Stats;
|
||||
import com.samskivert.depot.clause.FieldOverride;
|
||||
import com.samskivert.depot.clause.QueryClause;
|
||||
import com.samskivert.depot.clause.SelectClause;
|
||||
import com.samskivert.depot.expression.ColumnExp;
|
||||
import com.samskivert.depot.impl.operator.In;
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
|
||||
import static com.samskivert.depot.Log.log;
|
||||
|
||||
@@ -56,13 +57,28 @@ import static com.samskivert.depot.Log.log;
|
||||
* 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> extends Query<List<T>>
|
||||
public abstract class FindAllQuery<T extends PersistentRecord,R>
|
||||
extends Query<List<R>>
|
||||
{
|
||||
/**
|
||||
* A base class for queries that fetch a full record at a time.
|
||||
*/
|
||||
public static abstract class FullRecordQuery<T extends PersistentRecord>
|
||||
extends FindAllQuery<T, T>
|
||||
{
|
||||
protected FullRecordQuery (PersistenceContext ctx, Class<T> type) {
|
||||
super(type, ctx.getMarshaller(type), new CloningCloner<T>());
|
||||
_dmarsh = ctx.getMarshaller(type);
|
||||
}
|
||||
|
||||
protected DepotMarshaller<T> _dmarsh;
|
||||
}
|
||||
|
||||
/**
|
||||
* The two-pass collection query implementation. See {@link DepotRepository#findAll} for
|
||||
* details.
|
||||
*/
|
||||
public static class WithCache<T extends PersistentRecord> extends FindAllQuery<T>
|
||||
public static class WithCache<T extends PersistentRecord> extends FullRecordQuery<T>
|
||||
{
|
||||
public WithCache (PersistenceContext ctx, Class<T> type,
|
||||
Iterable<? extends QueryClause> clauses, CacheStrategy strategy)
|
||||
@@ -70,7 +86,7 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
{
|
||||
super(ctx, type);
|
||||
|
||||
if (_marsh.getComputed() != null) {
|
||||
if (_dmarsh.getComputed() != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"This algorithm doesn't work on @Computed records.");
|
||||
}
|
||||
@@ -81,10 +97,10 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
}
|
||||
}
|
||||
|
||||
_select = new SelectClause(_type, _marsh.getPrimaryKeyFields(), clauses);
|
||||
_select = new SelectClause(_type, _dmarsh.getPrimaryKeyFields(), clauses);
|
||||
switch(strategy) {
|
||||
case SHORT_KEYS: case LONG_KEYS:
|
||||
_qkey = new SimpleCacheKey(_marsh.getTableName() + "Keys", _select.toString());
|
||||
_qkey = new SimpleCacheKey(_dmarsh.getTableName() + "Keys", _select.toString());
|
||||
_category = (strategy == CacheStrategy.SHORT_KEYS) ?
|
||||
CacheCategory.SHORT_KEYSET : CacheCategory.LONG_KEYSET;
|
||||
break;
|
||||
@@ -130,12 +146,12 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
stmtString = stmt.toString(); // for debugging
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
keys.add(_marsh.makePrimaryKey(rs));
|
||||
keys.add(_dmarsh.makePrimaryKey(rs));
|
||||
}
|
||||
_keys = KeySet.newKeySet(_type, keys);
|
||||
_uncachedQueries++;
|
||||
if (PersistenceContext.CACHE_DEBUG) {
|
||||
log.info("Loaded " + _marsh.getTableName() + " keys", "query", _select,
|
||||
log.info("Loaded " + _dmarsh.getTableName() + " keys", "query", _select,
|
||||
"keys", keysToString(_keys), "cached", (_qkey != null));
|
||||
}
|
||||
if (_qkey != null) {
|
||||
@@ -162,7 +178,7 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
* The two-pass collection query implementation. See {@link DepotRepository#findAll} for
|
||||
* details.
|
||||
*/
|
||||
public static class WithKeys<T extends PersistentRecord> extends FindAllQuery<T>
|
||||
public static class WithKeys<T extends PersistentRecord> extends FullRecordQuery<T>
|
||||
{
|
||||
public WithKeys (PersistenceContext ctx, Iterable<Key<T>> keys)
|
||||
throws DatabaseException
|
||||
@@ -197,7 +213,7 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
* The single-pass collection query implementation. See {@link DepotRepository#findAll} for
|
||||
* details.
|
||||
*/
|
||||
public static class Explicitly<T extends PersistentRecord> extends FindAllQuery<T>
|
||||
public static class Explicitly<T extends PersistentRecord> extends FullRecordQuery<T>
|
||||
{
|
||||
public Explicitly (PersistenceContext ctx, Class<T> type,
|
||||
Iterable<? extends QueryClause> clauses, boolean cachedContents)
|
||||
@@ -205,10 +221,10 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
{
|
||||
super(ctx, type);
|
||||
|
||||
_select = new SelectClause(type, _marsh.getFieldNames(), clauses);
|
||||
_select = new SelectClause(type, _dmarsh.getFieldNames(), clauses);
|
||||
|
||||
if (cachedContents) {
|
||||
_qkey = new SimpleCacheKey(_marsh.getTableName() + "Contents", _select.toString());
|
||||
_qkey = new SimpleCacheKey(_dmarsh.getTableName() + "Contents", _select.toString());
|
||||
} else {
|
||||
_qkey = null;
|
||||
}
|
||||
@@ -233,11 +249,11 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
builder.newQuery(_select);
|
||||
ResultSet rs = builder.prepare(conn).executeQuery();
|
||||
while (rs.next()) {
|
||||
result.add(_marsh.createObject(rs));
|
||||
result.add(_dmarsh.createObject(rs));
|
||||
}
|
||||
_explicitQueries++;
|
||||
if (PersistenceContext.CACHE_DEBUG) {
|
||||
log.info("Loaded " + _marsh.getTableName(), "query", _select, "rows",
|
||||
log.info("Loaded " + _dmarsh.getTableName(), "query", _select, "rows",
|
||||
result.size(), "cacheKey", _qkey);
|
||||
}
|
||||
if (_qkey != null) {
|
||||
@@ -251,6 +267,73 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
protected SimpleCacheKey _qkey;
|
||||
}
|
||||
|
||||
public static class ForColumns<T extends PersistentRecord,R>
|
||||
extends FindAllQuery<T,R>
|
||||
{
|
||||
public ForColumns (PersistenceContext ctx, ColumnSet<T,R> cset,
|
||||
Iterable<? extends QueryClause> clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
super(cset.ptype, new ColumnSetQueryMarshaller<T,R>(ctx, cset),
|
||||
new NonCloningCloner<R>());
|
||||
_select = new SelectClause(cset.ptype, cset.columns, clauses);
|
||||
}
|
||||
|
||||
@Override // from Query
|
||||
public List<R> getCachedResult (PersistenceContext ctx)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// from Query
|
||||
public List<R> invoke (PersistenceContext ctx, Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select));
|
||||
builder.newQuery(_select);
|
||||
ResultSet rs = builder.prepare(conn).executeQuery();
|
||||
List<R> result = Lists.newArrayList();
|
||||
while (rs.next()) {
|
||||
result.add(_marsh.createObject(rs));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected SelectClause _select;
|
||||
}
|
||||
|
||||
protected static class ColumnSetQueryMarshaller<T extends PersistentRecord,R>
|
||||
implements QueryMarshaller<T,R>
|
||||
{
|
||||
public ColumnSetQueryMarshaller (PersistenceContext ctx, ColumnSet<T,R> cset) {
|
||||
_cset = cset;
|
||||
_cmarsh = ctx.getMarshaller(cset.ptype);
|
||||
}
|
||||
|
||||
public String getTableName () {
|
||||
return _cmarsh.getTableName();
|
||||
}
|
||||
|
||||
public ColumnExp<?>[] getFieldNames () {
|
||||
return _cset.columns;
|
||||
}
|
||||
|
||||
public Key<T> getPrimaryKey (Object object) {
|
||||
return _cmarsh.getPrimaryKey(object);
|
||||
}
|
||||
|
||||
public R createObject (ResultSet rs) throws SQLException {
|
||||
Object[] data = new Object[_cset.columns.length];
|
||||
for (int ii = 0; ii < data.length; ii++) {
|
||||
data[ii] = _cmarsh.getFieldMarshaller(_cset.columns[ii].name).getFromSet(rs);
|
||||
}
|
||||
return _cset.createObject(data);
|
||||
}
|
||||
|
||||
protected ColumnSet<T,R> _cset;
|
||||
protected DepotMarshaller<T> _cmarsh;
|
||||
}
|
||||
|
||||
// from Query
|
||||
public void updateStats (Stats stats)
|
||||
{
|
||||
@@ -258,21 +341,22 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
_cachedRecords, _uncachedRecords);
|
||||
}
|
||||
|
||||
protected FindAllQuery (PersistenceContext ctx, Class<T> type)
|
||||
protected FindAllQuery (Class<T> type, QueryMarshaller<T,R> marsh, Cloner<R> cloner)
|
||||
throws DatabaseException
|
||||
{
|
||||
_type = type;
|
||||
_marsh = ctx.getMarshaller(type);
|
||||
_marsh = marsh;
|
||||
_cloner = cloner;
|
||||
}
|
||||
|
||||
protected Set<Key<T>> loadFromCache (PersistenceContext ctx, Iterable<Key<T>> allKeys,
|
||||
Map<Key<T>, T> entities)
|
||||
Map<Key<T>, R> entities)
|
||||
{
|
||||
Set<Key<T>> fetchKeys = Sets.newHashSet();
|
||||
for (Key<T> key : allKeys) {
|
||||
T value = ctx.<T>cacheLookup(new KeyCacheKey(key));
|
||||
R value = ctx.<R>cacheLookup(new KeyCacheKey(key));
|
||||
if (value != null) {
|
||||
@SuppressWarnings("unchecked") T newValue = (T) value.clone();
|
||||
R newValue = _cloner.clone(value);
|
||||
entities.put(key, newValue);
|
||||
continue;
|
||||
}
|
||||
@@ -286,13 +370,13 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
return fetchKeys;
|
||||
}
|
||||
|
||||
protected List<T> resolve (Iterable<Key<T>> allKeys, Map<Key<T>, T> entities)
|
||||
protected List<R> resolve (Iterable<Key<T>> allKeys, Map<Key<T>, R> entities)
|
||||
{
|
||||
List<T> result = (allKeys instanceof Collection<?>)
|
||||
? Lists.<T>newArrayListWithCapacity(((Collection<?>)allKeys).size())
|
||||
: Lists.<T>newArrayList();
|
||||
List<R> result = (allKeys instanceof Collection<?>)
|
||||
? Lists.<R>newArrayListWithCapacity(((Collection<?>)allKeys).size())
|
||||
: Lists.<R>newArrayList();
|
||||
for (Key<T> key : allKeys) {
|
||||
T value = entities.get(key);
|
||||
R value = entities.get(key);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
@@ -300,9 +384,9 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
return result;
|
||||
}
|
||||
|
||||
protected List<T> loadAndResolve (PersistenceContext ctx, Connection conn,
|
||||
Iterable<Key<T>> allKeys, Set<Key<T>> fetchKeys,
|
||||
Map<Key<T>, T> entities, String origStmt)
|
||||
protected List<R> loadAndResolve (PersistenceContext ctx, Connection conn,
|
||||
Iterable<Key<T>> allKeys, Set<Key<T>> fetchKeys,
|
||||
Map<Key<T>, R> entities, String origStmt)
|
||||
throws SQLException
|
||||
{
|
||||
if (PersistenceContext.CACHE_DEBUG && fetchKeys.size() > 0) {
|
||||
@@ -330,7 +414,7 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
}
|
||||
|
||||
protected void loadRecords (PersistenceContext ctx, Connection conn, Set<Key<T>> keys,
|
||||
Map<Key<T>, T> entities, String origStmt)
|
||||
Map<Key<T>, R> entities, String origStmt)
|
||||
throws SQLException
|
||||
{
|
||||
SelectClause select = new SelectClause(
|
||||
@@ -341,12 +425,12 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
ResultSet rs = builder.prepare(conn).executeQuery();
|
||||
int cnt = 0, dups = 0;
|
||||
while (rs.next()) {
|
||||
T obj = _marsh.createObject(rs);
|
||||
R obj = _marsh.createObject(rs);
|
||||
Key<T> key = _marsh.getPrimaryKey(obj);
|
||||
if (entities.put(key, obj) != null) {
|
||||
dups++;
|
||||
}
|
||||
ctx.cacheStore(CacheCategory.RECORD, new KeyCacheKey(key), obj.clone());
|
||||
ctx.cacheStore(CacheCategory.RECORD, new KeyCacheKey(key), _cloner.clone(obj));
|
||||
got.add(key);
|
||||
cnt++;
|
||||
}
|
||||
@@ -378,8 +462,27 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
|
||||
return builder.append(")").toString();
|
||||
}
|
||||
|
||||
// we have to factor out cloning data for cache storage so that we can support storing
|
||||
// immutable data (like Integer and String) which need not and cannot be cloned, versus mutable
|
||||
// data (like PersistentRecords and Tuples) which must be cloned
|
||||
protected static interface Cloner<C> {
|
||||
C clone (C object);
|
||||
}
|
||||
protected static class CloningCloner<C extends QueryResult> implements Cloner<C> {
|
||||
public C clone (C object) {
|
||||
@SuppressWarnings("unchecked") C clone = (C) object.clone();
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
protected static class NonCloningCloner<C> implements Cloner<C> {
|
||||
public C clone (C object) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
protected Class<T> _type;
|
||||
protected DepotMarshaller<T> _marsh;
|
||||
protected QueryMarshaller<T,R> _marsh;
|
||||
protected Cloner<R> _cloner;
|
||||
protected int _cachedQueries, _uncachedQueries, _explicitQueries;
|
||||
protected int _cachedRecords, _uncachedRecords;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public interface FragmentVisitor<T>
|
||||
public T visit (In in);
|
||||
public T visit (FullText.Match match);
|
||||
public T visit (FullText.Rank match);
|
||||
public T visit (ColumnExp columnExp);
|
||||
public T visit (ColumnExp<?> columnExp);
|
||||
public T visit (Not not);
|
||||
public T visit (GroupBy groupBy);
|
||||
public T visit (ForUpdate forUpdate);
|
||||
|
||||
@@ -76,7 +76,7 @@ public class HSQLBuilder
|
||||
for (String ftsWord : ftsWords) {
|
||||
// build comparisons between each word and column
|
||||
bits.add(new Like(new Lower(
|
||||
new ColumnExp(pClass, field)), "%"+ftsWord+"%", true));
|
||||
new ColumnExp<Object>(pClass, field)), "%"+ftsWord+"%", true));
|
||||
}
|
||||
}
|
||||
// then just OR them all together and we have our query
|
||||
|
||||
@@ -160,7 +160,7 @@ public class MySQLBuilder
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
new ColumnExp(pClass, fields[ii]).accept(this);
|
||||
new ColumnExp<Object>(pClass, fields[ii]).accept(this);
|
||||
}
|
||||
_builder.append(") against (");
|
||||
bindValue(fullText.getQuery());
|
||||
|
||||
@@ -138,7 +138,7 @@ public class PostgreSQLBuilder
|
||||
// see if we will be inserting any columns whatsoever
|
||||
Class<? extends PersistentRecord> pClass = insertClause.getPersistentClass();
|
||||
Set<String> idFields = insertClause.getIdentityFields();
|
||||
for (ColumnExp field : _types.getMarshaller(pClass).getColumnFieldNames()) {
|
||||
for (ColumnExp<?> field : _types.getMarshaller(pClass).getColumnFieldNames()) {
|
||||
if (!idFields.contains(field.name)) {
|
||||
// we found a field we're inserting, so call super and finish
|
||||
super.appendInsertColumns(insertClause);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Depot library - a Java relational persistence library
|
||||
// Copyright (C) 2006-2010 Michael Bayne and 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.depot.impl;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.depot.Key;
|
||||
import com.samskivert.depot.PersistentRecord;
|
||||
import com.samskivert.depot.expression.ColumnExp;
|
||||
|
||||
/**
|
||||
* Marshalls the results for a query.
|
||||
*/
|
||||
public interface QueryMarshaller<T extends PersistentRecord,R>
|
||||
{
|
||||
/**
|
||||
* Returns the name of the table in which persistent instances of our class are stored. By
|
||||
* default this is the classname of the persistent object without the package.
|
||||
*/
|
||||
String getTableName ();
|
||||
|
||||
/**
|
||||
* Returns the field names being selected for this query.
|
||||
*/
|
||||
ColumnExp<?>[] getFieldNames ();
|
||||
|
||||
/**
|
||||
* Extracts the primary key from the supplied object.
|
||||
*/
|
||||
Key<T> getPrimaryKey (Object object);
|
||||
|
||||
/**
|
||||
* Creates an instance of the query result from the supplied result set.
|
||||
*/
|
||||
R createObject (ResultSet rs) throws SQLException;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Depot library - a Java relational persistence library
|
||||
// Copyright (C) 2006-2010 Michael Bayne and 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.depot.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A base class for all query results (persistent records, or subsets of persistent records in the
|
||||
* form of tuples of varying arity).
|
||||
*/
|
||||
public abstract class QueryResult
|
||||
implements Serializable, Cloneable
|
||||
{
|
||||
@Override
|
||||
public QueryResult clone ()
|
||||
{
|
||||
try {
|
||||
return (QueryResult) super.clone();
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
throw new AssertionError(cnse); // this should never happen since we are Cloneable
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class UpdateClause
|
||||
implements QueryClause
|
||||
{
|
||||
public UpdateClause (Class<? extends PersistentRecord> pClass, WhereClause where,
|
||||
ColumnExp[] fields, PersistentRecord pojo)
|
||||
ColumnExp<?>[] fields, PersistentRecord pojo)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_where = where;
|
||||
@@ -47,7 +47,7 @@ public class UpdateClause
|
||||
}
|
||||
|
||||
public UpdateClause (Class<? extends PersistentRecord> pClass, WhereClause where,
|
||||
ColumnExp[] fields, SQLExpression[] values)
|
||||
ColumnExp<?>[] fields, SQLExpression[] values)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_fields = fields;
|
||||
@@ -61,7 +61,7 @@ public class UpdateClause
|
||||
return _where;
|
||||
}
|
||||
|
||||
public ColumnExp[] getFields ()
|
||||
public ColumnExp<?>[] getFields ()
|
||||
{
|
||||
return _fields;
|
||||
}
|
||||
@@ -108,7 +108,7 @@ public class UpdateClause
|
||||
protected WhereClause _where;
|
||||
|
||||
/** The persistent fields to update. */
|
||||
protected ColumnExp[] _fields;
|
||||
protected ColumnExp<?>[] _fields;
|
||||
|
||||
/** The field values, or null. */
|
||||
protected SQLExpression[] _values;
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.apache.tools.ant.types.FileSet;
|
||||
import org.apache.tools.ant.types.Reference;
|
||||
import org.apache.tools.ant.util.ClasspathUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -276,6 +277,7 @@ public class GenRecordTask extends Task
|
||||
|
||||
// create our substitution mappings
|
||||
Map<String, String> fsubs = Maps.newHashMap(subs);
|
||||
fsubs.put("type", getTypeName(f.getType()));
|
||||
fsubs.put("field", fname);
|
||||
fsubs.put("capfield", StringUtil.unStudlyName(fname).toUpperCase());
|
||||
|
||||
@@ -458,6 +460,17 @@ public class GenRecordTask extends Task
|
||||
return name;
|
||||
}
|
||||
|
||||
protected static String getTypeName (Class<?> clazz)
|
||||
{
|
||||
Class<?> nclass = clazz.isPrimitive() ? BOXES.get(clazz) : clazz;
|
||||
Class<?> eclass = nclass.getEnclosingClass();
|
||||
if (eclass != null) {
|
||||
return getTypeName(eclass) + "." + nclass.getSimpleName();
|
||||
} else {
|
||||
return nclass.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of filesets that contain tile images. */
|
||||
protected List<FileSet> _filesets = Lists.newArrayList();
|
||||
|
||||
@@ -490,4 +503,15 @@ public class GenRecordTask extends Task
|
||||
/** A regular expression for matching the class or interface declaration. */
|
||||
protected static final Pattern NAME_PATTERN = Pattern.compile(
|
||||
"^\\s*public\\s+(?:abstract\\s+)?(interface|class)\\s+(\\w+)(\\W|$)");
|
||||
|
||||
protected static final Map<Class<?>, Class<?>> BOXES =
|
||||
ImmutableMap.<Class<?>,Class<?>>builder().
|
||||
put(Boolean.TYPE, Boolean.class).
|
||||
put(Byte.TYPE, Byte.class).
|
||||
put(Short.TYPE, Short.class).
|
||||
put(Character.TYPE, Character.class).
|
||||
put(Integer.TYPE, Integer.class).
|
||||
put(Long.TYPE, Long.class).
|
||||
put(Float.TYPE, Float.class).
|
||||
put(Double.TYPE, Double.class).build();
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
public static final ColumnExp @capfield@ = colexp(_R, "@field@");
|
||||
public static final ColumnExp<@type@> @capfield@ = colexp(_R, "@field@");
|
||||
|
||||
@@ -36,42 +36,42 @@ public class AllTypesRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<AllTypesRecord> _R = AllTypesRecord.class;
|
||||
public static final ColumnExp RECORD_ID = colexp(_R, "recordId");
|
||||
public static final ColumnExp BOOLEAN_VALUE = colexp(_R, "booleanValue");
|
||||
public static final ColumnExp BYTE_VALUE = colexp(_R, "byteValue");
|
||||
public static final ColumnExp SHORT_VALUE = colexp(_R, "shortValue");
|
||||
public static final ColumnExp INT_VALUE = colexp(_R, "intValue");
|
||||
public static final ColumnExp LONG_VALUE = colexp(_R, "longValue");
|
||||
public static final ColumnExp FLOAT_VALUE = colexp(_R, "floatValue");
|
||||
public static final ColumnExp DOUBLE_VALUE = colexp(_R, "doubleValue");
|
||||
public static final ColumnExp BOXED_BOOLEAN = colexp(_R, "boxedBoolean");
|
||||
public static final ColumnExp BOXED_BYTE = colexp(_R, "boxedByte");
|
||||
public static final ColumnExp BOXED_SHORT = colexp(_R, "boxedShort");
|
||||
public static final ColumnExp BOXED_INT = colexp(_R, "boxedInt");
|
||||
public static final ColumnExp BOXED_LONG = colexp(_R, "boxedLong");
|
||||
public static final ColumnExp BOXED_FLOAT = colexp(_R, "boxedFloat");
|
||||
public static final ColumnExp BOXED_DOUBLE = colexp(_R, "boxedDouble");
|
||||
public static final ColumnExp BYTE_ARRAY = colexp(_R, "byteArray");
|
||||
public static final ColumnExp INT_ARRAY = colexp(_R, "intArray");
|
||||
public static final ColumnExp STRING = colexp(_R, "string");
|
||||
public static final ColumnExp DATE = colexp(_R, "date");
|
||||
public static final ColumnExp TIME = colexp(_R, "time");
|
||||
public static final ColumnExp TIMESTAMP = colexp(_R, "timestamp");
|
||||
public static final ColumnExp TEST_ENUM = colexp(_R, "testEnum");
|
||||
public static final ColumnExp NULL_BOXED_BOOLEAN = colexp(_R, "nullBoxedBoolean");
|
||||
public static final ColumnExp NULL_BOXED_BYTE = colexp(_R, "nullBoxedByte");
|
||||
public static final ColumnExp NULL_BOXED_SHORT = colexp(_R, "nullBoxedShort");
|
||||
public static final ColumnExp NULL_BOXED_INT = colexp(_R, "nullBoxedInt");
|
||||
public static final ColumnExp NULL_BOXED_LONG = colexp(_R, "nullBoxedLong");
|
||||
public static final ColumnExp NULL_BOXED_FLOAT = colexp(_R, "nullBoxedFloat");
|
||||
public static final ColumnExp NULL_BOXED_DOUBLE = colexp(_R, "nullBoxedDouble");
|
||||
public static final ColumnExp NULL_BYTE_ARRAY = colexp(_R, "nullByteArray");
|
||||
public static final ColumnExp NULL_INT_ARRAY = colexp(_R, "nullIntArray");
|
||||
public static final ColumnExp NULL_STRING = colexp(_R, "nullString");
|
||||
public static final ColumnExp NULL_DATE = colexp(_R, "nullDate");
|
||||
public static final ColumnExp NULL_TIME = colexp(_R, "nullTime");
|
||||
public static final ColumnExp NULL_TIMESTAMP = colexp(_R, "nullTimestamp");
|
||||
public static final ColumnExp NULL_TEST_ENUM = colexp(_R, "nullTestEnum");
|
||||
public static final ColumnExp<Integer> RECORD_ID = colexp(_R, "recordId");
|
||||
public static final ColumnExp<Boolean> BOOLEAN_VALUE = colexp(_R, "booleanValue");
|
||||
public static final ColumnExp<Byte> BYTE_VALUE = colexp(_R, "byteValue");
|
||||
public static final ColumnExp<Short> SHORT_VALUE = colexp(_R, "shortValue");
|
||||
public static final ColumnExp<Integer> INT_VALUE = colexp(_R, "intValue");
|
||||
public static final ColumnExp<Long> LONG_VALUE = colexp(_R, "longValue");
|
||||
public static final ColumnExp<Float> FLOAT_VALUE = colexp(_R, "floatValue");
|
||||
public static final ColumnExp<Double> DOUBLE_VALUE = colexp(_R, "doubleValue");
|
||||
public static final ColumnExp<Boolean> BOXED_BOOLEAN = colexp(_R, "boxedBoolean");
|
||||
public static final ColumnExp<Byte> BOXED_BYTE = colexp(_R, "boxedByte");
|
||||
public static final ColumnExp<Short> BOXED_SHORT = colexp(_R, "boxedShort");
|
||||
public static final ColumnExp<Integer> BOXED_INT = colexp(_R, "boxedInt");
|
||||
public static final ColumnExp<Long> BOXED_LONG = colexp(_R, "boxedLong");
|
||||
public static final ColumnExp<Float> BOXED_FLOAT = colexp(_R, "boxedFloat");
|
||||
public static final ColumnExp<Double> BOXED_DOUBLE = colexp(_R, "boxedDouble");
|
||||
public static final ColumnExp<byte[]> BYTE_ARRAY = colexp(_R, "byteArray");
|
||||
public static final ColumnExp<int[]> INT_ARRAY = colexp(_R, "intArray");
|
||||
public static final ColumnExp<String> STRING = colexp(_R, "string");
|
||||
public static final ColumnExp<Date> DATE = colexp(_R, "date");
|
||||
public static final ColumnExp<Time> TIME = colexp(_R, "time");
|
||||
public static final ColumnExp<Timestamp> TIMESTAMP = colexp(_R, "timestamp");
|
||||
public static final ColumnExp<TestEnum> TEST_ENUM = colexp(_R, "testEnum");
|
||||
public static final ColumnExp<Boolean> NULL_BOXED_BOOLEAN = colexp(_R, "nullBoxedBoolean");
|
||||
public static final ColumnExp<Byte> NULL_BOXED_BYTE = colexp(_R, "nullBoxedByte");
|
||||
public static final ColumnExp<Short> NULL_BOXED_SHORT = colexp(_R, "nullBoxedShort");
|
||||
public static final ColumnExp<Integer> NULL_BOXED_INT = colexp(_R, "nullBoxedInt");
|
||||
public static final ColumnExp<Long> NULL_BOXED_LONG = colexp(_R, "nullBoxedLong");
|
||||
public static final ColumnExp<Float> NULL_BOXED_FLOAT = colexp(_R, "nullBoxedFloat");
|
||||
public static final ColumnExp<Double> NULL_BOXED_DOUBLE = colexp(_R, "nullBoxedDouble");
|
||||
public static final ColumnExp<byte[]> NULL_BYTE_ARRAY = colexp(_R, "nullByteArray");
|
||||
public static final ColumnExp<int[]> NULL_INT_ARRAY = colexp(_R, "nullIntArray");
|
||||
public static final ColumnExp<String> NULL_STRING = colexp(_R, "nullString");
|
||||
public static final ColumnExp<Date> NULL_DATE = colexp(_R, "nullDate");
|
||||
public static final ColumnExp<Time> NULL_TIME = colexp(_R, "nullTime");
|
||||
public static final ColumnExp<Timestamp> NULL_TIMESTAMP = colexp(_R, "nullTimestamp");
|
||||
public static final ColumnExp<TestEnum> NULL_TEST_ENUM = colexp(_R, "nullTestEnum");
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
public static final int SCHEMA_VERSION = 1;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
package com.samskivert.depot;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -63,9 +63,9 @@ public class CrudTest extends TestBase
|
||||
String name = "Bob";
|
||||
int age = 25;
|
||||
int[] numbers = { 1, 2, 3, 4, 5 };
|
||||
_repo.updatePartial(TestRecord.getKey(in.recordId),
|
||||
ImmutableMap.of(TestRecord.NAME, name, TestRecord.AGE, age,
|
||||
TestRecord.NUMBERS, numbers));
|
||||
_repo.updatePartial(TestRecord.getKey(in.recordId), ImmutableMap.of(
|
||||
TestRecord.NAME, name, TestRecord.AGE, age,
|
||||
TestRecord.NUMBERS, numbers));
|
||||
TestRecord up = _repo.loadNoCache(in.recordId);
|
||||
assertEquals(name, up.name);
|
||||
assertEquals(age, up.age);
|
||||
|
||||
@@ -30,8 +30,8 @@ public class EnumKeyRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<EnumKeyRecord> _R = EnumKeyRecord.class;
|
||||
public static final ColumnExp TYPE = colexp(_R, "type");
|
||||
public static final ColumnExp NAME = colexp(_R, "name");
|
||||
public static final ColumnExp<Type> TYPE = colexp(_R, "type");
|
||||
public static final ColumnExp<String> NAME = colexp(_R, "name");
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
public static final int SCHEMA_VERSION = 1;
|
||||
|
||||
@@ -28,8 +28,8 @@ public class GeneratedValueRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<GeneratedValueRecord> _R = GeneratedValueRecord.class;
|
||||
public static final ColumnExp RECORD_ID = colexp(_R, "recordId");
|
||||
public static final ColumnExp VALUE = colexp(_R, "value");
|
||||
public static final ColumnExp<Integer> RECORD_ID = colexp(_R, "recordId");
|
||||
public static final ColumnExp<Integer> VALUE = colexp(_R, "value");
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
public static final int SCHEMA_VERSION = 1;
|
||||
|
||||
@@ -30,9 +30,9 @@ public class MonkeyRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<MonkeyRecord> _R = MonkeyRecord.class;
|
||||
public static final ColumnExp SPECIES = colexp(_R, "species");
|
||||
public static final ColumnExp MONKEY_ID = colexp(_R, "monkeyId");
|
||||
public static final ColumnExp NAME = colexp(_R, "name");
|
||||
public static final ColumnExp<Integer> SPECIES = colexp(_R, "species");
|
||||
public static final ColumnExp<Integer> MONKEY_ID = colexp(_R, "monkeyId");
|
||||
public static final ColumnExp<String> NAME = colexp(_R, "name");
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** This monkey's species. This is part of our key so that we have a composite key. */
|
||||
|
||||
@@ -38,13 +38,13 @@ public class TestRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<TestRecord> _R = TestRecord.class;
|
||||
public static final ColumnExp RECORD_ID = colexp(_R, "recordId");
|
||||
public static final ColumnExp NAME = colexp(_R, "name");
|
||||
public static final ColumnExp AGE = colexp(_R, "age");
|
||||
public static final ColumnExp HOME_TOWN = colexp(_R, "homeTown");
|
||||
public static final ColumnExp CREATED = colexp(_R, "created");
|
||||
public static final ColumnExp LAST_MODIFIED = colexp(_R, "lastModified");
|
||||
public static final ColumnExp NUMBERS = colexp(_R, "numbers");
|
||||
public static final ColumnExp<Integer> RECORD_ID = colexp(_R, "recordId");
|
||||
public static final ColumnExp<String> NAME = colexp(_R, "name");
|
||||
public static final ColumnExp<Integer> AGE = colexp(_R, "age");
|
||||
public static final ColumnExp<String> HOME_TOWN = colexp(_R, "homeTown");
|
||||
public static final ColumnExp<Date> CREATED = colexp(_R, "created");
|
||||
public static final ColumnExp<Timestamp> LAST_MODIFIED = colexp(_R, "lastModified");
|
||||
public static final ColumnExp<int[]> NUMBERS = colexp(_R, "numbers");
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
public static final int SCHEMA_VERSION = 3;
|
||||
|
||||
Reference in New Issue
Block a user