Faster Key construction via the generated getKey() factory.
A little bit back Mike added a fast-path for Key construction for keys with one Comparable. Keys with multiple values would still validate the order every time. This was necessary even when using the generated factory because Class' getFields() does not guarantee any order. In practice, the Sun JVM seems to return them in declared order, but apparently other JVMs may not. The arguments to getKey() are determined when 'genrecord' is run, but the field order used during runtime is computed at runtime. This change generates code that stashes the key fields in an order deteremined at the time 'genrecord' is run and that is guaranteed to match the argument order to each PersistentRecord subclass' getKey(). Thus, getKey() can now directly construct a key with minimal overhead. The field order is still computed at runtime as a fallback for PersistentRecord classes that haven't yet been updated. The ordering of getFields() is still being relied upon at 'genrecord' time, but Mike may introduce a patch that will guarantee that ordering matches declaration order. In the meantime, it's no worse than before and if somehow someone does run 'genrecord' with an alternate JVM they should immediately notice the discrepency in their diffs. The Key constructor utilized is still public because it is used by DepotUtil. A nice change would be to make it package-private.
This commit is contained in:
@@ -135,6 +135,7 @@ public class Key<T extends PersistentRecord> extends WhereClause
|
|||||||
* Used to create a key when you know you have the canonical values array. Don't call this
|
* Used to create a key when you know you have the canonical values array. Don't call this
|
||||||
* unless you know what you're doing!
|
* unless you know what you're doing!
|
||||||
*/
|
*/
|
||||||
|
// TODO: This should perhaps be made package-private, but DepotMarshaller uses it
|
||||||
public Key (Class<T> pClass, Comparable<?>[] values)
|
public Key (Class<T> pClass, Comparable<?>[] values)
|
||||||
{
|
{
|
||||||
_pClass = pClass;
|
_pClass = pClass;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ package com.samskivert.depot;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import com.samskivert.depot.expression.ColumnExp;
|
import com.samskivert.depot.expression.ColumnExp;
|
||||||
|
import com.samskivert.depot.impl.DepotUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base class for all persistent records used in Depot. Persistent records must be cloneable
|
* The base class for all persistent records used in Depot. Persistent records must be cloneable
|
||||||
@@ -41,7 +42,7 @@ public class PersistentRecord
|
|||||||
try {
|
try {
|
||||||
return (PersistentRecord) super.clone();
|
return (PersistentRecord) super.clone();
|
||||||
} catch (CloneNotSupportedException cnse) {
|
} catch (CloneNotSupportedException cnse) {
|
||||||
throw new RuntimeException(cnse); // this should never happen
|
throw new AssertionError(cnse); // this should never happen since we are Cloneable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,4 +54,22 @@ public class PersistentRecord
|
|||||||
{
|
{
|
||||||
return new ColumnExp(clazz, fieldName);
|
return new ColumnExp(clazz, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Key for this class. Called by autogenerated code, and placed here because
|
||||||
|
* we hope to make this form of the Key constructor protected.
|
||||||
|
*/
|
||||||
|
protected static <R extends PersistentRecord> Key<R> newKey (
|
||||||
|
Class<R> pClass, Comparable... values)
|
||||||
|
{
|
||||||
|
return new Key<R>(pClass, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the key fields for a subclass. Called by autogenerated code.
|
||||||
|
*/
|
||||||
|
protected static void registerKeyFields (ColumnExp... fields)
|
||||||
|
{
|
||||||
|
DepotUtil.registerKeyFields(fields);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,10 +56,10 @@ public class DepotMigrationHistoryRecord extends PersistentRecord
|
|||||||
*/
|
*/
|
||||||
public static Key<DepotMigrationHistoryRecord> getKey (String ident)
|
public static Key<DepotMigrationHistoryRecord> getKey (String ident)
|
||||||
{
|
{
|
||||||
return new Key<DepotMigrationHistoryRecord>(
|
return newKey(_R, ident);
|
||||||
DepotMigrationHistoryRecord.class,
|
|
||||||
new ColumnExp[] { IDENT },
|
|
||||||
new Comparable[] { ident });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Register the key fields in an order matching the getKey() factory. */
|
||||||
|
static { registerKeyFields(IDENT); }
|
||||||
// AUTO-GENERATED: METHODS END
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,18 +46,34 @@ public class DepotUtil
|
|||||||
return _keyFields.get(pClass);
|
return _keyFields.get(pClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the key fields to their PersistentRecord class. It should never be necessary
|
||||||
|
* to do this manually, as it is done via a static initializer in generated PersistentRecord
|
||||||
|
* subclasses. Calling this method after the key fields have already been registered will
|
||||||
|
* have no effect.
|
||||||
|
*/
|
||||||
|
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?
|
||||||
|
_keyFields.putIfAbsent(fields[0].getPersistentClass(), fields);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the supplied class minus its package.
|
* Returns the name of the supplied class minus its package.
|
||||||
*/
|
*/
|
||||||
public static String justClassName (Class<?> clazz)
|
public static String justClassName (Class<?> clazz)
|
||||||
{
|
{
|
||||||
return clazz.getName().substring(clazz.getName().lastIndexOf(".")+1);
|
return clazz.getName().substring(clazz.getName().lastIndexOf(".") + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A (never expiring) cache of primary key field names for all persistent classes (of which
|
/** 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). */
|
* 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()
|
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[]>() {
|
.makeComputingMap(new Function<Class<? extends PersistentRecord>, ColumnExp[]>() {
|
||||||
public ColumnExp[] apply (Class<? extends PersistentRecord> pClass) {
|
public ColumnExp[] apply (Class<? extends PersistentRecord> pClass) {
|
||||||
List<ColumnExp> kflist = Lists.newArrayList();
|
List<ColumnExp> kflist = Lists.newArrayList();
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ public class MonkeyRecord extends PersistentRecord
|
|||||||
*/
|
*/
|
||||||
public static Key<MonkeyRecord> getKey (int species, int monkeyId)
|
public static Key<MonkeyRecord> getKey (int species, int monkeyId)
|
||||||
{
|
{
|
||||||
return new Key<MonkeyRecord>(
|
return newKey(_R, species, monkeyId);
|
||||||
MonkeyRecord.class,
|
|
||||||
new ColumnExp[] { SPECIES, MONKEY_ID },
|
|
||||||
new Comparable[] { species, monkeyId });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Register the key fields in an order matching the getKey() factory. */
|
||||||
|
static { registerKeyFields(SPECIES, MONKEY_ID); }
|
||||||
// AUTO-GENERATED: METHODS END
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,10 +80,10 @@ public class TestRecord extends PersistentRecord
|
|||||||
*/
|
*/
|
||||||
public static Key<TestRecord> getKey (int recordId)
|
public static Key<TestRecord> getKey (int recordId)
|
||||||
{
|
{
|
||||||
return new Key<TestRecord>(
|
return newKey(_R, recordId);
|
||||||
TestRecord.class,
|
|
||||||
new ColumnExp[] { RECORD_ID },
|
|
||||||
new Comparable[] { recordId });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Register the key fields in an order matching the getKey() factory. */
|
||||||
|
static { registerKeyFields(RECORD_ID); }
|
||||||
// AUTO-GENERATED: METHODS END
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
public static Key<@record@> getKey (@argList@)
|
public static Key<@record@> getKey (@argList@)
|
||||||
{
|
{
|
||||||
return new Key<@record@>(
|
return newKey(_R, @argNameList@);
|
||||||
@record@.class,
|
|
||||||
new ColumnExp[] { @fieldNameList@ },
|
|
||||||
new Comparable[] { @argNameList@ });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Register the key fields in an order matching the getKey() factory. */
|
||||||
|
static { registerKeyFields(@fieldNameList@); }
|
||||||
|
|||||||
Reference in New Issue
Block a user