diff --git a/src/java/com/samskivert/jdbc/depot/Computed.java b/src/java/com/samskivert/jdbc/depot/Computed.java index c6fb193..409fe70 100644 --- a/src/java/com/samskivert/jdbc/depot/Computed.java +++ b/src/java/com/samskivert/jdbc/depot/Computed.java @@ -20,12 +20,22 @@ package com.samskivert.jdbc.depot; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** * Marks a field as computed, meaning it's ignored for schema purposes, it does not directly * correspond to a column in a table, and thus its value must be overridden in the {@link Query}. - * - * TODO: Add fieldDefinition="count (*)" or somesuch. */ +@Retention(value=RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) public @interface Computed { + /** If this value is false, the field is not populated by Depot at all. */ + boolean required() default true; + + /** A non-empty value here is taken as literal SQL and used to populate the computed field. */ + String fieldDefinition() default ""; } diff --git a/src/java/com/samskivert/jdbc/depot/FieldMarshaller.java b/src/java/com/samskivert/jdbc/depot/FieldMarshaller.java index 94587ea..c072c91 100644 --- a/src/java/com/samskivert/jdbc/depot/FieldMarshaller.java +++ b/src/java/com/samskivert/jdbc/depot/FieldMarshaller.java @@ -109,6 +109,14 @@ public abstract class FieldMarshaller return _field; } + /** + * Returns the Computed annotation on this field, if any. + */ + public Computed getComputed () + { + return _computed; + } + /** * Returns the GeneratedValue annotation on this field, if any. */ @@ -172,8 +180,9 @@ public abstract class FieldMarshaller } } + _computed = field.getAnnotation(Computed.class); // if this field is @Computed, it has no SQL definition - if (field.getAnnotation(Computed.class) != null) { + if (_computed != null) { _columnDefinition = null; return; } @@ -383,5 +392,6 @@ public abstract class FieldMarshaller protected Field _field; protected String _columnName, _columnDefinition; + protected Computed _computed; protected GeneratedValue _generatedValue; } diff --git a/src/java/com/samskivert/jdbc/depot/Query.java b/src/java/com/samskivert/jdbc/depot/Query.java index 1217493..ec32046 100644 --- a/src/java/com/samskivert/jdbc/depot/Query.java +++ b/src/java/com/samskivert/jdbc/depot/Query.java @@ -94,18 +94,37 @@ public abstract class Query DepotMarshaller mainMarshaller = _classMap.get(_mainType); String[] fields = mainMarshaller._allFields; StringBuilder query = new StringBuilder("select "); + boolean skip = true; for (int ii = 0; ii < fields.length; ii ++) { - if (ii > 0) { + if (!skip) { query.append(", "); } + skip = false; + FieldOverrideClause clause = _disMap.get(fields[ii]); if (clause != null) { clause.appendClause(this, query); - } else if (mainMarshaller._fields.get(fields[ii]).getColumnDefinition() != null) { + continue; + } + + Computed computed = mainMarshaller._fields.get(fields[ii]).getComputed(); + if (computed == null) { + // if it's neither overridden nor computed, it's a standard field query.append("T.").append(fields[ii]); + continue; + } + + // check if the computed field has a literal SQL definition + if (computed.fieldDefinition().length() > 0) { + query.append(computed.fieldDefinition() + " as " + fields[ii]); + + } else if (!computed.required()) { + // or if we can simply ignore the field + skip = true; + } else { throw new SQLException( - "Computed field must be overridden [field=" + fields[ii] + "]"); + "@Computed(required) field without definition [field=" + fields[ii] + "]"); } } query.append(" from " + mainMarshaller.getTableName() + " as T ");