diff --git a/src/java/com/samskivert/jdbc/depot/Computed.java b/src/java/com/samskivert/jdbc/depot/Computed.java index 409fe70..dfa51ee 100644 --- a/src/java/com/samskivert/jdbc/depot/Computed.java +++ b/src/java/com/samskivert/jdbc/depot/Computed.java @@ -30,7 +30,7 @@ import java.lang.annotation.Target; * correspond to a column in a table, and thus its value must be overridden in the {@link Query}. */ @Retention(value=RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) +@Target({ ElementType.FIELD, ElementType.TYPE }) public @interface Computed { /** If this value is false, the field is not populated by Depot at all. */ diff --git a/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java b/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java index 9b610fe..4d3009d 100644 --- a/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java +++ b/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java @@ -27,12 +27,14 @@ import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Set; import java.util.logging.Level; import javax.persistence.GeneratedValue; @@ -41,7 +43,8 @@ import javax.persistence.TableGenerator; import javax.persistence.Transient; import com.samskivert.jdbc.JDBCUtil; -import com.samskivert.jdbc.depot.expression.ColumnExpression; +import com.samskivert.jdbc.depot.clause.Where; +import com.samskivert.jdbc.depot.expression.ColumnExp; import com.samskivert.util.ArrayUtil; import com.samskivert.util.ListUtil; import com.samskivert.util.StringUtil; @@ -66,9 +69,13 @@ public class DepotMarshaller { _pclass = pclass; - // determine our table name - _tableName = _pclass.getName(); - _tableName = _tableName.substring(_tableName.lastIndexOf(".")+1); + // see if this is a computed entity + Computed computed = pclass.getAnnotation(Computed.class); + if (computed == null) { + // if not, this class has a corresponding SQL table + _tableName = _pclass.getName(); + _tableName = _tableName.substring(_tableName.lastIndexOf(".")+1); + } // if the entity defines a new TableGenerator, map that in our static table as those are // shared across all entities @@ -172,8 +179,13 @@ public class DepotMarshaller // generate our full list of fields/columns for use in queries _allFields = fields.toArray(new String[fields.size()]); - // figure out the list of fields that correspond to actual table columns and create the SQL - // used to create and migrate our table + // if we're a computed entity, stop here + if (_tableName == null) { + return; + } + + // 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 String[_allFields.length]; _columnDefinitions = new String[_allFields.length]; int jj = 0; @@ -261,10 +273,10 @@ public class DepotMarshaller "Argument count (" + values.length + ") must match primary key size (" + _pkColumns.size() + ")"); } - ColumnExpression[] columns = new ColumnExpression[_pkColumns.size()]; + ColumnExp[] columns = new ColumnExp[_pkColumns.size()]; for (int ii = 0; ii < _pkColumns.size(); ii++) { FieldMarshaller field = _pkColumns.get(ii); - columns[ii] = new ColumnExpression(_pclass, field.getColumnName()); + columns[ii] = new ColumnExp(_pclass, field.getColumnName()); } return new Key(columns, values); } @@ -277,6 +289,11 @@ public class DepotMarshaller public void init (Connection conn) throws SQLException { + // if we have no table (i.e. we're a computed entity), we have nothing to create + if (getTableName() == null) { + return; + } + // check to see if our schema version table exists, create it if not JDBCUtil.createTableIfMissing(conn, SCHEMA_VERSION_TABLE, new String[] { "persistentClass VARCHAR(255) NOT NULL", @@ -358,10 +375,22 @@ public class DepotMarshaller throws SQLException { try { - T po = (T)_pclass.newInstance(); + // first, build a set of the fields that we actually received + Set fields = new HashSet(); + ResultSetMetaData metadata = rs.getMetaData(); + for (int ii = 1; ii <= metadata.getColumnCount(); ii ++) { + fields.add(metadata.getColumnName(ii)); + } + + // then create and populate the persistent object + T po = _pclass.newInstance(); for (FieldMarshaller fm : _fields.values()) { - if (fm.getComputed() != null && !fm.getComputed().required()) { - continue; + if (!fields.contains(fm.getField().getName())) { + // this field was not in the result set, make sure that's OK + if (fm.getComputed() != null && !fm.getComputed().required()) { + continue; + } + throw new SQLException("ResultSet missing field: " + fm.getField().getName()); } fm.getValue(rs, po); } @@ -384,6 +413,8 @@ public class DepotMarshaller public PreparedStatement createInsert (Connection conn, Object po) throws SQLException { + requireNotComputed("insert rows into"); + try { StringBuilder insert = new StringBuilder(); insert.append("insert into ").append(getTableName()); @@ -449,7 +480,7 @@ public class DepotMarshaller /** * Creates a statement that will update the supplied persistent object using the supplied key. */ - public PreparedStatement createUpdate (Connection conn, Object po, Key key) + public PreparedStatement createUpdate (Connection conn, Object po, Where key) throws SQLException { return createUpdate(conn, po, key, _columnFields); @@ -460,9 +491,11 @@ public class DepotMarshaller * using the supplied key. */ public PreparedStatement createUpdate ( - Connection conn, Object po, Key key, String[] modifiedFields) + Connection conn, Object po, Where key, String[] modifiedFields) throws SQLException { + requireNotComputed("update rows in"); + StringBuilder update = new StringBuilder(); update.append("update ").append(getTableName()).append(" set "); int idx = 0; @@ -501,9 +534,11 @@ public class DepotMarshaller * that match the supplied key. */ public PreparedStatement createPartialUpdate ( - Connection conn, Key key, String[] modifiedFields, Object[] modifiedValues) + Connection conn, Where key, String[] modifiedFields, Object[] modifiedValues) throws SQLException { + requireNotComputed("update rows in"); + StringBuilder update = new StringBuilder(); update.append("update ").append(getTableName()).append(" set "); int idx = 0; @@ -530,9 +565,11 @@ public class DepotMarshaller /** * Creates a statement that will delete all rows matching the supplied key. */ - public PreparedStatement createDelete (Connection conn, Key key) + public PreparedStatement createDelete (Connection conn, Where key) throws SQLException { + requireNotComputed("delete rows from"); + StringBuilder query = new StringBuilder("delete from " + getTableName()); key.appendClause(null, query); PreparedStatement pstmt = conn.prepareStatement(query.toString()); @@ -545,9 +582,11 @@ public class DepotMarshaller * SQL values, for all persistent objects that match the supplied key. */ public PreparedStatement createLiteralUpdate ( - Connection conn, Key key, String[] modifiedFields, Object[] modifiedValues) + Connection conn, Where key, String[] modifiedFields, Object[] modifiedValues) throws SQLException { + requireNotComputed("update rows in"); + StringBuilder update = new StringBuilder(); update.append("update ").append(getTableName()).append(" set "); for (int ii = 0; ii < modifiedFields.length; ii++) { @@ -595,6 +634,15 @@ public class DepotMarshaller } } + protected void requireNotComputed (String action) + throws SQLException + { + if (getTableName() == null) { + throw new IllegalArgumentException( + "Can't " + action + " computed entities [class=" + _pclass + "]"); + } + } + /** The persistent object class that we manage. */ protected Class _pclass; diff --git a/src/java/com/samskivert/jdbc/depot/DepotRepository.java b/src/java/com/samskivert/jdbc/depot/DepotRepository.java index 528210f..65e5a1a 100644 --- a/src/java/com/samskivert/jdbc/depot/DepotRepository.java +++ b/src/java/com/samskivert/jdbc/depot/DepotRepository.java @@ -31,6 +31,8 @@ import java.util.Collection; import com.samskivert.io.PersistenceException; import com.samskivert.jdbc.ConnectionProvider; import com.samskivert.jdbc.depot.clause.QueryClause; +import com.samskivert.jdbc.depot.clause.Where; +import com.samskivert.util.ArrayUtil; /** * Provides a base for classes that provide access to persistent objects. Also defines the @@ -61,17 +63,18 @@ public class DepotRepository protected T load (Class type, Comparable primaryKey, QueryClause... clauses) throws PersistenceException { - return load(type, _ctx.getMarshaller(type).makePrimaryKey(primaryKey), clauses); + clauses = ArrayUtil.append(clauses, _ctx.getMarshaller(type).makePrimaryKey(primaryKey)); + return load(type, clauses); } /** * Loads the first persistent object that matches the supplied key. */ - protected T load (Class type, Key key, QueryClause... clauses) + protected T load (Class type, QueryClause... clauses) throws PersistenceException { final DepotMarshaller marsh = _ctx.getMarshaller(type); - return _ctx.invoke(new Query(_ctx, type, key, clauses) { + return _ctx.invoke(new Query(_ctx, type, clauses) { public T invoke (Connection conn) throws SQLException { PreparedStatement stmt = createQuery(conn); try { @@ -91,24 +94,15 @@ public class DepotRepository }); } - /** - * Loads all persistent objects of the specified type. - */ - protected > Collection findAll (Class type) - throws PersistenceException - { - return findAll(type, null); - } - /** * Loads all persistent objects that match the specified key. */ protected > Collection findAll ( - Class type, Key key, QueryClause... clauses) + Class type, QueryClause... clauses) throws PersistenceException { final DepotMarshaller marsh = _ctx.getMarshaller(type); - return _ctx.invoke(new Query>(_ctx, type, key, clauses) { + return _ctx.invoke(new Query>(_ctx, type, clauses) { public ArrayList invoke (Connection conn) throws SQLException { PreparedStatement stmt = createQuery(conn); try { @@ -130,8 +124,7 @@ public class DepotRepository * Inserts the supplied persistent object into the database, assigning its primary key (if it * has one) in the process. * - * @return the number of rows modified by this action, this should always - * be one. + * @return the number of rows modified by this action, this should always be one. */ protected int insert (final Object record) throws PersistenceException @@ -182,7 +175,7 @@ public class DepotRepository * * @return the number of rows modified by this action. */ - protected int update (final Object record, final String ... modifiedFields) + protected int update (final Object record, final String... modifiedFields) throws PersistenceException { final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass()); @@ -208,7 +201,7 @@ public class DepotRepository * * @return the number of rows modified by this action. */ - protected int updatePartial (Class type, Comparable primaryKey, Object ... fieldsValues) + protected int updatePartial (Class type, Comparable primaryKey, Object... fieldsValues) throws PersistenceException { return updatePartial( @@ -225,7 +218,7 @@ public class DepotRepository * * @return the number of rows modified by this action. */ - protected int updatePartial (Class type, Key key, Object ... fieldsValues) + protected int updatePartial (Class type, Where key, Object... fieldsValues) throws PersistenceException { // separate the arguments into keys and values @@ -266,7 +259,7 @@ public class DepotRepository * * @return the number of rows modified by this action. */ - protected int updateLiteral (Class type, Comparable primaryKey, String ... fieldsValues) + protected int updateLiteral (Class type, Comparable primaryKey, String... fieldsValues) throws PersistenceException { return updateLiteral( @@ -290,7 +283,7 @@ public class DepotRepository * * @return the number of rows modified by this action. */ - protected int updateLiteral (Class type, Key key, String ... fieldsValues) + protected int updateLiteral (Class type, Where key, String... fieldsValues) throws PersistenceException { // separate the arguments into keys and values @@ -387,7 +380,7 @@ public class DepotRepository * * @return the number of rows deleted by this action. */ - protected int deleteAll (Class type, Key key) + protected int deleteAll (Class type, Where key) throws PersistenceException { final DepotMarshaller marsh = _ctx.getMarshaller(type); diff --git a/src/java/com/samskivert/jdbc/depot/Key.java b/src/java/com/samskivert/jdbc/depot/Key.java index a5dda5f..0fe72da 100644 --- a/src/java/com/samskivert/jdbc/depot/Key.java +++ b/src/java/com/samskivert/jdbc/depot/Key.java @@ -20,91 +20,68 @@ package com.samskivert.jdbc.depot; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.Arrays; -import java.util.List; - -import com.samskivert.jdbc.depot.clause.QueryClause; -import com.samskivert.jdbc.depot.expression.ColumnExpression; - +import com.samskivert.jdbc.depot.clause.Where; +import com.samskivert.jdbc.depot.expression.ColumnExp; +import com.samskivert.jdbc.depot.expression.ValueExp; +import com.samskivert.jdbc.depot.operator.Conditionals.Equals; +import com.samskivert.jdbc.depot.operator.Logic.And; +import com.samskivert.jdbc.depot.operator.SQLOperator; /** - * Encapsulates a key used to match persistent objects in a query. - * - * TODO: Is it useful for this to implement QueryClause? Can it be meaningfully put on a peer level - * with the other clauses? + * Encapsulates a key used to match persistent objects in a query. This is a special form of + * where clause that is meant to uniquely identify a specific row for caching purposes. It is + * generated by the update/insert code, as well as instantiated directly by users. */ -public class Key - implements QueryClause +public class Key extends Where { - public ColumnExpression[] columns; - public Comparable[] values; - public Key (String index, Comparable value) { - this(new ColumnExpression(index), value); + this(new ColumnExp(index), value); } - public Key (ColumnExpression column, Comparable value) + public Key (ColumnExp column, Comparable value) { - this(new ColumnExpression[] { column }, new Comparable[] { value }); + this(new ColumnExp[] { column }, new Comparable[] { value }); + } + + public Key (ColumnExp index1, Comparable value1, + ColumnExp index2, Comparable value2) + { + this(new ColumnExp[] { index1, index2 }, + new Comparable[] { value1, value2 }); + } + + public Key (ColumnExp index1, Comparable value1, + ColumnExp index2, Comparable value2, + ColumnExp index3, Comparable value3) + { + this(new ColumnExp[] { index1, index2, index3 }, + new Comparable[] { value1, value2, value3 }); } public Key (String index1, Comparable value1, String index2, Comparable value2) { - this(new ColumnExpression[] { new ColumnExpression(index1), - new ColumnExpression(index2) }, - new Comparable[] { value1, value2 }); + this(new ColumnExp(index1), value1, new ColumnExp(index2), value2); } public Key (String index1, Comparable value1, String index2, Comparable value2, String index3, Comparable value3) { - this(new ColumnExpression[] { new ColumnExpression(index1), - new ColumnExpression(index2), - new ColumnExpression(index3) }, - new Comparable[] { value1, value2, value3 }); + this(new ColumnExp(index1), value1, new ColumnExp(index2), value2, + new ColumnExp(index3), value3); } - public Key (String[] columns, Comparable[] values) + public Key (ColumnExp[] columns, Comparable[] values) { - ColumnExpression[] colExp = new ColumnExpression[columns.length]; + super(toCondition(columns, values)); + } + + protected static SQLOperator toCondition (ColumnExp[] columns, Comparable[] values) + { + SQLOperator[] comparisons = new Equals[columns.length]; for (int ii = 0; ii < columns.length; ii ++) { - colExp[ii] = new ColumnExpression(columns[ii]); + comparisons[ii] = new Equals(columns[ii], new ValueExp(values[ii])); } - this.columns = colExp; - this.values = values; - } - - public Key (ColumnExpression[] columns, Comparable[] values) - { - this.columns = columns; - this.values = values; - } - - public List getClassSet() { - return Arrays.asList(new Class[] { }); - } - - public void appendClause (Query query, StringBuilder builder) - { - builder.append(" where "); - for (int ii = 0; ii < columns.length; ii++) { - if (ii > 0) { - builder.append(" and "); - } - columns[ii].appendExpression(query, builder); - builder.append(" = ?"); - } - } - - public int bindArguments (PreparedStatement stmt, int startIdx) - throws SQLException - { - for (int ii = 0; ii < values.length; ii++) { - stmt.setObject(startIdx++, values[ii]); - } - return startIdx; + return new And(comparisons); } } diff --git a/src/java/com/samskivert/jdbc/depot/Modifier.java b/src/java/com/samskivert/jdbc/depot/Modifier.java index c4e9361..09b68af 100644 --- a/src/java/com/samskivert/jdbc/depot/Modifier.java +++ b/src/java/com/samskivert/jdbc/depot/Modifier.java @@ -23,19 +23,16 @@ package com.samskivert.jdbc.depot; import java.sql.Connection; import java.sql.SQLException; +import com.samskivert.jdbc.depot.clause.Where; + /** * Encapsulates a modification of persistent objects. */ public abstract class Modifier { - public Key getKey () - { - return _key; - } - public abstract int invoke (Connection conn) throws SQLException; - protected Modifier (Key key) + protected Modifier (Where key) { _key = key; } @@ -47,5 +44,5 @@ public abstract class Modifier } } - protected Key _key; + protected Where _key; } diff --git a/src/java/com/samskivert/jdbc/depot/Query.java b/src/java/com/samskivert/jdbc/depot/Query.java index c5bb3a3..608ec83 100644 --- a/src/java/com/samskivert/jdbc/depot/Query.java +++ b/src/java/com/samskivert/jdbc/depot/Query.java @@ -32,13 +32,15 @@ import java.util.Map; import java.util.Set; import com.samskivert.io.PersistenceException; -import com.samskivert.jdbc.depot.clause.FieldOverrideClause; -import com.samskivert.jdbc.depot.clause.ForUpdateClause; -import com.samskivert.jdbc.depot.clause.GroupByClause; -import com.samskivert.jdbc.depot.clause.JoinClause; -import com.samskivert.jdbc.depot.clause.LimitClause; -import com.samskivert.jdbc.depot.clause.OrderByClause; +import com.samskivert.jdbc.depot.clause.FieldOverride; +import com.samskivert.jdbc.depot.clause.ForUpdate; +import com.samskivert.jdbc.depot.clause.FromOverride; +import com.samskivert.jdbc.depot.clause.GroupBy; +import com.samskivert.jdbc.depot.clause.Join; +import com.samskivert.jdbc.depot.clause.Limit; +import com.samskivert.jdbc.depot.clause.OrderBy; import com.samskivert.jdbc.depot.clause.QueryClause; +import com.samskivert.jdbc.depot.clause.Where; /** * Encapsulates a non-modifying query of persistent objects. @@ -68,9 +70,6 @@ public abstract class Query */ public String getTableAbbreviation (Class cl) { - if (cl.equals(_mainType)) { - return "T"; - } int ix = _classList.indexOf(cl); if (ix < 0) { throw new IllegalArgumentException("Unknown persistence class: " + cl); @@ -102,17 +101,23 @@ public abstract class Query } skip = false; - FieldOverrideClause clause = _disMap.get(fields[ii]); + FieldOverride clause = _disMap.get(fields[ii]); if (clause != null) { clause.appendClause(this, query); continue; } - Computed computed = mainMarshaller._fields.get(fields[ii]).getComputed(); + 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; + // make sure the object corresponds to a table, otherwise the whole thing is + // computed + if (mainMarshaller.getTableName() != null) { + // if it's neither overridden nor computed, it's a standard field + query.append(getTableAbbreviation(_mainType)).append(".").append(fields[ii]); + continue; + } + throw new SQLException( + "@Computed entity field without definition [field=" + fields[ii] + "]"); } // check if the computed field has a literal SQL definition @@ -128,13 +133,21 @@ public abstract class Query "@Computed(required) field without definition [field=" + fields[ii] + "]"); } } - query.append(" from " + mainMarshaller.getTableName() + " as T "); - for (JoinClause clause : _joinClauses) { + if (_fromOverride != null) { + _fromOverride.appendClause(this, query); + } else if (mainMarshaller.getTableName() != null) { + query.append(" from ").append(mainMarshaller.getTableName()); + query.append(" as ").append(getTableAbbreviation(_mainType)); + } else { + throw new SQLException("Query on @Computed entity with no FromOverrideClause."); + } + + for (Join clause : _joinClauses) { clause.appendClause(this, query); } - if (_key != null) { - _key.appendClause(this, query); + if (_where != null) { + _where.appendClause(this, query); } if (_groupBy != null) { _groupBy.appendClause(this, query); @@ -151,8 +164,8 @@ public abstract class Query PreparedStatement pstmt = conn.prepareStatement(query.toString()); int argIdx = 1; - if (_key != null) { - argIdx = _key.bindArguments(pstmt, argIdx); + if (_where != null) { + argIdx = _where.bindArguments(pstmt, argIdx); } if (_groupBy != null) { argIdx = _groupBy.bindArguments(pstmt, argIdx); @@ -175,55 +188,66 @@ public abstract class Query * class, as dictated by the key and query clauses. A persistence context is supplied for * instantiation of marshallers, which may trigger table creations and schema migrations. */ - protected Query (PersistenceContext ctx, Class type, Key key, QueryClause... clauses) + protected Query (PersistenceContext ctx, Class type, QueryClause... clauses) throws PersistenceException { - _key = key; _mainType = type; Set classSet = new HashSet(); if (type != null) { classSet.add(type); } - if (_key != null) { - classSet.addAll(_key.getClassSet()); - } for (QueryClause clause : clauses) { - if (clause instanceof JoinClause) { - _joinClauses.add((JoinClause) clause); - classSet.addAll(((JoinClause) clause).getClassSet()); + if (clause instanceof Where) { + if (_where != null) { + throw new IllegalArgumentException( + "Query can't contain multiple Where clauses."); + } + _where = (Where) clause; - } else if (clause instanceof FieldOverrideClause) { - _disMap.put(((FieldOverrideClause) clause).getField(), - ((FieldOverrideClause) clause)); + } else if (clause instanceof FromOverride) { + if (_fromOverride != null) { + throw new IllegalArgumentException( + "Query can't contain multiple FromOverride clauses."); + } + _fromOverride = (FromOverride) clause; + classSet.addAll(_fromOverride.getClassSet()); - } else if (clause instanceof OrderByClause) { + } else if (clause instanceof Join) { + _joinClauses.add((Join) clause); + classSet.addAll(((Join) clause).getClassSet()); + + } else if (clause instanceof FieldOverride) { + _disMap.put(((FieldOverride) clause).getField(), + ((FieldOverride) clause)); + + } else if (clause instanceof OrderBy) { if (_orderBy != null) { throw new IllegalArgumentException( "Query can't contain multiple OrderBy clauses."); } - _orderBy = (OrderByClause) clause; + _orderBy = (OrderBy) clause; - } else if (clause instanceof GroupByClause) { + } else if (clause instanceof GroupBy) { if (_groupBy != null) { throw new IllegalArgumentException( "Query can't contain multiple GroupBy clauses."); } - _groupBy = (GroupByClause) clause; + _groupBy = (GroupBy) clause; - } else if (clause instanceof LimitClause) { + } else if (clause instanceof Limit) { if (_limit != null) { throw new IllegalArgumentException( "Query can't contain multiple Limit clauses."); } - _limit = (LimitClause) clause; + _limit = (Limit) clause; - } else if (clause instanceof ForUpdateClause) { + } else if (clause instanceof ForUpdate) { if (_forUpdate != null) { throw new IllegalArgumentException( "Query can't contain multiple For Update clauses."); } - _forUpdate = (ForUpdateClause) clause; + _forUpdate = (ForUpdate) clause; } } _classMap = new HashMap(); @@ -234,9 +258,6 @@ public abstract class Query _classList = new ArrayList(classSet); } - /** The key to this query, or null. Translated into a WHERE clause. */ - protected Key _key; - /** The persistent class to instantiate for the results. */ protected Class _mainType; @@ -247,20 +268,26 @@ public abstract class Query protected Map _classMap; /** Persistent class fields mapped to field override clauses. */ - protected Map _disMap = new HashMap(); + protected Map _disMap = new HashMap(); + + /** The from override clause, if any. */ + protected FromOverride _fromOverride; + + /** The where clause. */ + protected Where _where; /** A list of join clauses, each potentially referencing a new class. */ - protected List _joinClauses = new ArrayList(); + protected List _joinClauses = new ArrayList(); /** The order by clause, if any. */ - protected OrderByClause _orderBy; + protected OrderBy _orderBy; /** The group by clause, if any. */ - protected GroupByClause _groupBy; + protected GroupBy _groupBy; /** The limit clause, if any. */ - protected LimitClause _limit; - + protected Limit _limit; + /** The For Update clause, if any. */ - protected ForUpdateClause _forUpdate; + protected ForUpdate _forUpdate; } diff --git a/src/java/com/samskivert/jdbc/depot/clause/FieldOverrideClause.java b/src/java/com/samskivert/jdbc/depot/clause/FieldOverride.java similarity index 75% rename from src/java/com/samskivert/jdbc/depot/clause/FieldOverrideClause.java rename to src/java/com/samskivert/jdbc/depot/clause/FieldOverride.java index 02fe459..c2f5e2b 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/FieldOverrideClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/FieldOverride.java @@ -26,28 +26,43 @@ import java.util.Collection; import com.samskivert.io.PersistenceException; import com.samskivert.jdbc.depot.Query; +import com.samskivert.jdbc.depot.expression.ColumnExp; +import com.samskivert.jdbc.depot.expression.LiteralExp; import com.samskivert.jdbc.depot.expression.SQLExpression; /** * Redirects one field of the persistent object we're creating from its default associated column * to a general {@link SQLExpression}. - * + * * Thus the select portion of a query can include a reference to a different column in a different - * table through a {@link ColumnExpression}, or a literal expression such as COUNT(*) through a - * {@link LiteralExpression}. - */ -public class FieldOverrideClause + * table through a {@link ColumnExp}, or a literal expression such as COUNT(*) through a + * {@link LiteralExp}. + */ +public class FieldOverride implements QueryClause { - public FieldOverrideClause (String field, SQLExpression override) + public FieldOverride (String field, String str) + throws PersistenceException + { + this(field, new LiteralExp(str)); + } + + public FieldOverride (String field, Class pClass, String pCol) + throws PersistenceException + { + this(field, new ColumnExp(pClass, pCol)); + } + + public FieldOverride (String field, SQLExpression override) throws PersistenceException { - super(); _field = field; _override = override; } - /** The field we're overriding. The Query object uses this for indexing. */ + /** + * The field we're overriding. The Query object uses this for indexing. + */ public String getField () { return _field; @@ -72,7 +87,7 @@ public class FieldOverrideClause { return argIdx; } - + /** The name of the field on the persistent object to override. */ protected String _field; diff --git a/src/java/com/samskivert/jdbc/depot/clause/ForUpdateClause.java b/src/java/com/samskivert/jdbc/depot/clause/ForUpdate.java similarity index 98% rename from src/java/com/samskivert/jdbc/depot/clause/ForUpdateClause.java rename to src/java/com/samskivert/jdbc/depot/clause/ForUpdate.java index 22f2dc8..a2fe165 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/ForUpdateClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/ForUpdate.java @@ -29,7 +29,7 @@ import com.samskivert.jdbc.depot.Query; /** * Represents a FOR UPDATE clause. */ -public class ForUpdateClause +public class ForUpdate implements QueryClause { // from QueryClause diff --git a/src/java/com/samskivert/jdbc/depot/clause/FromOverride.java b/src/java/com/samskivert/jdbc/depot/clause/FromOverride.java new file mode 100644 index 0000000..e37082c --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/clause/FromOverride.java @@ -0,0 +1,72 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006 Michael Bayne, Pär Winzell +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot.clause; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collection; + +import com.samskivert.io.PersistenceException; +import com.samskivert.jdbc.depot.Query; + +/** + * Completely overrides the FROM clause, if it exists. + */ +public class FromOverride + implements QueryClause +{ + public FromOverride (Class... fromClasses) + throws PersistenceException + { + _fromClasses = fromClasses; + } + + // from QueryClause + public Collection getClassSet () + { + return Arrays.asList(_fromClasses); + } + + // from QueryClause + public void appendClause (Query query, StringBuilder builder) + { + builder.append(" from " ); + for (int ii = 0; ii < _fromClasses.length; ii++) { + if (ii > 0) { + builder.append(", "); + } + builder.append(query.getTableName(_fromClasses[ii])). + append(" as "). + append(query.getTableAbbreviation(_fromClasses[ii])); + } + } + + // from QueryClause + public int bindArguments (PreparedStatement pstmt, int argIdx) + throws SQLException + { + return argIdx; + } + + /** The classes of the tables we're selecting from. */ + protected Class[] _fromClasses; +} diff --git a/src/java/com/samskivert/jdbc/depot/clause/GroupByClause.java b/src/java/com/samskivert/jdbc/depot/clause/GroupBy.java similarity index 95% rename from src/java/com/samskivert/jdbc/depot/clause/GroupByClause.java rename to src/java/com/samskivert/jdbc/depot/clause/GroupBy.java index 194f278..5eb0437 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/GroupByClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/GroupBy.java @@ -30,12 +30,11 @@ import com.samskivert.jdbc.depot.expression.SQLExpression; /** * Represents a GROUP BY clause. */ -public class GroupByClause +public class GroupBy implements QueryClause { - public GroupByClause (SQLExpression... values) + public GroupBy (SQLExpression... values) { - super(); _values = values; } diff --git a/src/java/com/samskivert/jdbc/depot/clause/JoinClause.java b/src/java/com/samskivert/jdbc/depot/clause/Join.java similarity index 61% rename from src/java/com/samskivert/jdbc/depot/clause/JoinClause.java rename to src/java/com/samskivert/jdbc/depot/clause/Join.java index fd46d6a..1183e3e 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/JoinClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/Join.java @@ -27,24 +27,39 @@ import java.util.Collection; import com.samskivert.io.PersistenceException; import com.samskivert.jdbc.depot.Query; +import com.samskivert.jdbc.depot.expression.ColumnExp; +import com.samskivert.jdbc.depot.operator.SQLOperator; +import com.samskivert.jdbc.depot.operator.Conditionals.*; /** * Represents a JOIN -- currently just an INNER one. */ -public class JoinClause +public class Join implements QueryClause { - public JoinClause (String pCol, Class joinClass, String jCol) + public Join (Class pClass, String pCol, Class joinClass, String jCol) throws PersistenceException { - super(); - _primaryColumn = pCol; _joinClass = joinClass; - _joinColumn = jCol; + _joinCondition = new Equals(new ColumnExp(joinClass, jCol), new ColumnExp(pClass, pCol)); + } + + public Join (ColumnExp primary, ColumnExp join) + throws PersistenceException + { + _joinClass = join.pClass; + _joinCondition = new Equals(primary, join); + } + + public Join (Class joinClass, SQLOperator joinCondition) + { + _joinClass = joinClass; + _joinCondition = joinCondition; } // from QueryClause - public Collection getClassSet () { + public Collection getClassSet () + { return Arrays.asList(new Class[] { _joinClass }); } @@ -52,24 +67,21 @@ public class JoinClause public void appendClause (Query query, StringBuilder builder) { builder.append(" inner join " ); - String jAbbrev = query.getTableAbbreviation(_joinClass); - builder.append(query.getTableName(_joinClass) + " as " + jAbbrev + " on T." + - _primaryColumn + " = " + jAbbrev + "." + _joinColumn); + builder.append(query.getTableName(_joinClass)).append(" as "); + builder.append(query.getTableAbbreviation(_joinClass)).append(" on "); + _joinCondition.appendExpression(query, builder); } // from QueryClause public int bindArguments (PreparedStatement pstmt, int argIdx) throws SQLException { - return argIdx; + return _joinCondition.bindArguments(pstmt, argIdx); } - /** The column on which to join. */ - protected String _primaryColumn; - /** The class of the table we're to join against. */ protected Class _joinClass; - /** The column we're joining against. */ - protected String _joinColumn; + /** The condition used to join in the new table. */ + protected SQLOperator _joinCondition; } diff --git a/src/java/com/samskivert/jdbc/depot/clause/LimitClause.java b/src/java/com/samskivert/jdbc/depot/clause/Limit.java similarity index 95% rename from src/java/com/samskivert/jdbc/depot/clause/LimitClause.java rename to src/java/com/samskivert/jdbc/depot/clause/Limit.java index 2faca3c..b54c03f 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/LimitClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/Limit.java @@ -29,12 +29,11 @@ import com.samskivert.jdbc.depot.Query; /** * Represents a LIMIT/OFFSET clause, for pagination. */ -public class LimitClause +public class Limit implements QueryClause { - public LimitClause (int offset, int count) + public Limit (int offset, int count) { - super(); _offset = offset; _count = count; } @@ -59,7 +58,7 @@ public class LimitClause pstmt.setObject(argIdx++, _offset); return argIdx; } - + /** The first row of the result set to return. */ protected int _offset; diff --git a/src/java/com/samskivert/jdbc/depot/clause/OrderByClause.java b/src/java/com/samskivert/jdbc/depot/clause/OrderBy.java similarity index 95% rename from src/java/com/samskivert/jdbc/depot/clause/OrderByClause.java rename to src/java/com/samskivert/jdbc/depot/clause/OrderBy.java index cec109c..6b86484 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/OrderByClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/OrderBy.java @@ -30,15 +30,14 @@ import com.samskivert.jdbc.depot.expression.SQLExpression; /** * Represents an ORDER BY clause. */ -public class OrderByClause +public class OrderBy implements QueryClause { - public OrderByClause (SQLExpression... values) + public OrderBy (SQLExpression... values) { - super(); _values = values; } - + // from QueryClause public Collection getClassSet () { @@ -67,7 +66,7 @@ public class OrderByClause } return argIdx; } - + /** The expressions that are generated for the clause. */ protected SQLExpression[] _values; diff --git a/src/java/com/samskivert/jdbc/depot/clause/QueryClause.java b/src/java/com/samskivert/jdbc/depot/clause/QueryClause.java index 178df73..b944037 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/QueryClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/QueryClause.java @@ -38,8 +38,8 @@ public interface QueryClause public Collection getClassSet (); /** - * Construct the SQL form of this query clause The implementor is invited to call methods on - * the Query object to e.g. resolve the current table abbreviations associated with classes. + * Construct the SQL form of this query clause. The implementor is expected to call methods + * on the Query object to e.g. resolve current table abbreviations associated with classes. */ public void appendClause (Query query, StringBuilder builder); diff --git a/src/java/com/samskivert/jdbc/depot/clause/Where.java b/src/java/com/samskivert/jdbc/depot/clause/Where.java new file mode 100644 index 0000000..93c120b --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/clause/Where.java @@ -0,0 +1,65 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot.clause; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; + +import com.samskivert.jdbc.depot.Query; +import com.samskivert.jdbc.depot.clause.QueryClause; +import com.samskivert.jdbc.depot.operator.SQLOperator; + +/** + * Represents a where clause: the condition can be any comparison operator or logical combination + * thereof. + */ +public class Where + implements QueryClause +{ + public Where (SQLOperator condition) + { + _condition = condition; + } + + // from QueryClause + public List getClassSet() + { + return Arrays.asList(new Class[] { }); + } + + // from QueryClause + public void appendClause (Query query, StringBuilder builder) + { + builder.append(" where "); + _condition.appendExpression(query, builder); + } + + // from QueryClause + public int bindArguments (PreparedStatement pstmt, int argIdx) + throws SQLException + { + return _condition.bindArguments(pstmt, argIdx); + } + + protected SQLOperator _condition; +} diff --git a/src/java/com/samskivert/jdbc/depot/expression/ColumnExpression.java b/src/java/com/samskivert/jdbc/depot/expression/ColumnExp.java similarity index 75% rename from src/java/com/samskivert/jdbc/depot/expression/ColumnExpression.java rename to src/java/com/samskivert/jdbc/depot/expression/ColumnExp.java index d9b727b..19022fa 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/ColumnExpression.java +++ b/src/java/com/samskivert/jdbc/depot/expression/ColumnExp.java @@ -26,32 +26,38 @@ import java.sql.SQLException; import com.samskivert.jdbc.depot.Query; /** - * An expression identifying a column of a class, e.g. GameRecord.itemId. If no class is given, no - * disambiguation occurs in the generated SQL. + * An expression identifying a column of a class, e.g. GameRecord.itemId. If no class is given, + * no disambiguation occurs in the generated SQL. */ -public class ColumnExpression +public class ColumnExp implements SQLExpression { - public ColumnExpression (String column) + /** The table that hosts the column we reference, or null. */ + final public Class pClass; + + /** The name of the column we reference. */ + final public String pColumn; + + public ColumnExp (String column) { this(null, column); } - public ColumnExpression (Class c, String column) + public ColumnExp (Class c, String column) { super(); - _class = c; - _column = column; + pClass = c; + this.pColumn = column; } // from SQLExpression public void appendExpression (Query query, StringBuilder builder) { - if (_class == null || query == null) { - builder.append(_column); + if (pClass == null || query == null) { + builder.append(pColumn); } else { - String tRef = query.getTableAbbreviation(_class); - builder.append(tRef).append(".").append(_column); + String tRef = query.getTableAbbreviation(pClass); + builder.append(tRef).append(".").append(pColumn); } } @@ -62,9 +68,4 @@ public class ColumnExpression return argIdx; } - /** The table that hosts the column we reference, or null. */ - protected Class _class; - - /** The name of the column we reference. */ - protected String _column; } diff --git a/src/java/com/samskivert/jdbc/depot/expression/LiteralExpression.java b/src/java/com/samskivert/jdbc/depot/expression/LiteralExp.java similarity index 95% rename from src/java/com/samskivert/jdbc/depot/expression/LiteralExpression.java rename to src/java/com/samskivert/jdbc/depot/expression/LiteralExp.java index 3a49a14..3befae5 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/LiteralExpression.java +++ b/src/java/com/samskivert/jdbc/depot/expression/LiteralExp.java @@ -28,10 +28,10 @@ import com.samskivert.jdbc.depot.Query; /** * An expression for things we don't support natively, e.g. COUNT(*). */ -public class LiteralExpression +public class LiteralExp implements SQLExpression { - public LiteralExpression (String text) + public LiteralExp (String text) { super(); _text = text; diff --git a/src/java/com/samskivert/jdbc/depot/expression/ValueExpression.java b/src/java/com/samskivert/jdbc/depot/expression/ValueExp.java similarity index 94% rename from src/java/com/samskivert/jdbc/depot/expression/ValueExpression.java rename to src/java/com/samskivert/jdbc/depot/expression/ValueExp.java index 020e760..8f1e4be 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/ValueExpression.java +++ b/src/java/com/samskivert/jdbc/depot/expression/ValueExp.java @@ -28,12 +28,11 @@ import com.samskivert.jdbc.depot.Query; /** * A Java value that is bound as a parameter to the query, e.g. 1 or 'abc'. */ -public class ValueExpression +public class ValueExp implements SQLExpression { - public ValueExpression (Comparable _value) + public ValueExp (Comparable _value) { - super(); this._value = _value; } diff --git a/src/java/com/samskivert/jdbc/depot/operator/Conditionals.java b/src/java/com/samskivert/jdbc/depot/operator/Conditionals.java new file mode 100644 index 0000000..1558464 --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/operator/Conditionals.java @@ -0,0 +1,145 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006 Michael Bayne, Pär Winzell +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot.operator; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import com.samskivert.jdbc.depot.Query; +import com.samskivert.jdbc.depot.expression.ColumnExp; +import com.samskivert.jdbc.depot.expression.SQLExpression; +import com.samskivert.jdbc.depot.expression.ValueExp; + +/** + * A convenient container for implementations of conditional operators. Classes that value brevity + * over disambiguation will import Conditionals.* and construct queries with Equals() and In(); + * classes that feel otherwise will use Conditionals.Equals() and Conditionals.In(). + */ +public abstract class Conditionals +{ + /** The SQL '=' operator. */ + public static class Equals extends BinaryOperator + { + public Equals (SQLExpression column, Comparable value) + { + super(column, value); + } + + public Equals (SQLExpression column, SQLExpression value) + { + super(column, value); + } + + @Override + protected String operator() + { + return "="; + } + } + + /** The SQL 'in (...)' operator. */ + public static class In + implements SQLOperator + { + public In (Class pClass, String pColumn, Comparable... values) + { + this(new ColumnExp(pClass, pColumn), values); + } + + public In (ColumnExp column, Comparable... values) + { + if (values.length == 0) { + throw new IllegalArgumentException("In() condition needs at least one value."); + } + _column = column; + _values = values; + } + + // from SQLExpression + public void appendExpression (Query query, StringBuilder builder) + { + _column.appendExpression(query, builder); + builder.append(" in ("); + for (int ii = 0; ii < _values.length; ii ++) { + if (ii > 0) { + builder.append(", "); + } + builder.append("?"); + } + builder.append(")"); + } + + // from SQLExpression + public int bindArguments (PreparedStatement pstmt, int argIdx) + throws SQLException + { + for (int ii = 0; ii < _values.length; ii++) { + pstmt.setObject(argIdx ++, _values[ii]); + } + return argIdx; + } + + protected ColumnExp _column; + protected Comparable[] _values; + } + + /** + * Does the real work for simple binary operators such as Equals. + */ + protected abstract static class BinaryOperator implements SQLOperator + { + public BinaryOperator (SQLExpression lhs, SQLExpression rhs) + { + _lhs = lhs; + _rhs = rhs; + } + + public BinaryOperator (SQLExpression lhs, Comparable rhs) + { + this(lhs, new ValueExp(rhs)); + } + + // from SQLExpression + public void appendExpression (Query query, StringBuilder builder) + { + _lhs.appendExpression(query, builder); + builder.append(operator()); + _rhs.appendExpression(query, builder); + } + + // from SQLExpression + public int bindArguments (PreparedStatement pstmt, int argIdx) + throws SQLException + { + argIdx = _lhs.bindArguments(pstmt, argIdx); + argIdx = _rhs.bindArguments(pstmt, argIdx); + return argIdx; + } + + /** + * Returns the string representation of the operator. + */ + protected abstract String operator(); + + protected SQLExpression _lhs; + protected SQLExpression _rhs; + } +} diff --git a/src/java/com/samskivert/jdbc/depot/operator/Logic.java b/src/java/com/samskivert/jdbc/depot/operator/Logic.java new file mode 100644 index 0000000..69f689b --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/operator/Logic.java @@ -0,0 +1,142 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006 Michael Bayne, Pär Winzell +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot.operator; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import com.samskivert.jdbc.depot.Query; + +/** + * A convenient container for implementations of logical operators. Classes that value brevity + * over disambiguation will import Logic.* and construct queries with And() and Not(); classes that + * feel otherwise will use Logic.And() and Logic.Not(). + */ +public abstract class Logic +{ + /** + * Represents a condition that is false iff all its subconditions are false. + */ + public static class Or extends MultiOperator + { + public Or (SQLOperator... conditions) + { + super(conditions); + } + + @Override + protected String operator() + { + return "or"; + } + } + + /** + * Represents a condition that is true iff all its subconditions are true. + */ + public static class And extends MultiOperator + { + public And (SQLOperator... conditions) + { + super(conditions); + } + + @Override + protected String operator() + { + return "and"; + } + } + + + /** + * Represents the truth negation of another conditon. + */ + public static class Not + implements SQLOperator + { + public Not (SQLOperator condition) + { + super(); + _condition = condition; + } + + // from SQLExpression + public void appendExpression (Query query, StringBuilder builder) + { + builder.append(" not ("); + _condition.appendExpression(query, builder); + builder.append(")"); + } + + // from SQLExpression + public int bindArguments (PreparedStatement pstmt, int argIdx) + throws SQLException + { + return _condition.bindArguments(pstmt, argIdx); + } + + protected SQLOperator _condition; + } + + /** + * Represents an operator with any number of operands. + */ + protected abstract static class MultiOperator + implements SQLOperator + { + public MultiOperator (SQLOperator... conditions) + { + super(); + _conditions = conditions; + } + + // from SQLExpression + public void appendExpression (Query query, StringBuilder builder) + { + for (int ii = 0; ii < _conditions.length; ii++) { + if (ii > 0) { + builder.append(" ").append(operator()).append(" "); + } + builder.append("("); + _conditions[ii].appendExpression(query, builder); + builder.append(")"); + } + } + + // from SQLExpression + public int bindArguments (PreparedStatement pstmt, int argIdx) + throws SQLException + { + for (int ii = 0; ii < _conditions.length; ii++) { + argIdx = _conditions[ii].bindArguments(pstmt, argIdx); + } + return argIdx; + } + + /** + * Returns the text infix to be used to join expressions together. + */ + protected abstract String operator (); + + protected SQLOperator[] _conditions; + } +} diff --git a/src/java/com/samskivert/jdbc/depot/operator/SQLOperator.java b/src/java/com/samskivert/jdbc/depot/operator/SQLOperator.java new file mode 100644 index 0000000..7cbee57 --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/operator/SQLOperator.java @@ -0,0 +1,32 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006 Michael Bayne, Pär Winzell +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot.operator; + +import com.samskivert.jdbc.depot.expression.SQLExpression; + +/** + * A common interface for operator hierarchies in SQL. The main purpose of breaking this out from + * SQLExpression is to capture the recursive nature of e.g. the logical operators, which work on + * other SQLOperators but not general SQLExpressions. + */ +public interface SQLOperator extends SQLExpression +{ +}