From 84622e56c763a489b237244c774bc84176d3974e Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Fri, 5 Sep 2008 19:39:08 +0000 Subject: [PATCH] Formalized the two-phase "get primary keys that match an arbitrary query, then load or modify the rows that match those keys" pattern so that we can almost always just magically do the right thing with regard to the cache. Added a version of deleteAll() that makes use of this. Also nixed a bunch of checked exception tomfoolery which was almost entirely unnecessary and now with DatabaseException is completely unnecessary. A few things remain to be done: - PrimaryKeySet tries to be "smart" if its passed 0 keys and use LiteralExp("false") but that causes things to freak out because Depot then doesn't know what primary class it's dealing with. I'm probably going to make All and None expressions that match all and none of the records in a table respectively. - deleteAll() doesn't currently split its keys into chunks small enough to be digestible by the database if we match more than 32,768 rows. I'll see if I can't abstract out that code from FindAllQuery so that we can easily use it everywhere. It would be cool to handle that a a lower level and allow the WhereClause to say that it needs to be run in phases, but that would probably complicate the crap out of the low-level code. - I need to create PrimaryKey to go between Key and PrimaryKeySet so that we can avoid duplicating the primary key columns thousands of times in a large PrimaryKeySet. --- .../samskivert/jdbc/depot/BindVisitor.java | 112 +++++++------ .../samskivert/jdbc/depot/BuildVisitor.java | 53 ++---- .../jdbc/depot/DepotMarshaller.java | 4 +- .../jdbc/depot/DepotRepository.java | 45 +++++- .../jdbc/depot/FieldMarshaller.java | 38 ++--- .../samskivert/jdbc/depot/FindAllQuery.java | 35 +--- src/java/com/samskivert/jdbc/depot/Key.java | 79 +++++---- .../com/samskivert/jdbc/depot/KeyUtil.java | 59 +++++++ .../com/samskivert/jdbc/depot/MultiKey.java | 9 +- .../samskivert/jdbc/depot/MySQLBuilder.java | 17 +- .../jdbc/depot/PostgreSQLBuilder.java | 17 +- .../samskivert/jdbc/depot/PrimaryKeySet.java | 153 ++++++++++++++++++ .../com/samskivert/jdbc/depot/SQLBuilder.java | 17 +- .../samskivert/jdbc/depot/WhereClause.java | 6 + .../jdbc/depot/clause/DeleteClause.java | 2 +- .../jdbc/depot/clause/FieldDefinition.java | 1 - .../jdbc/depot/clause/ForUpdate.java | 2 +- .../jdbc/depot/clause/FromOverride.java | 1 - .../samskivert/jdbc/depot/clause/GroupBy.java | 2 +- .../jdbc/depot/clause/InsertClause.java | 2 +- .../samskivert/jdbc/depot/clause/Join.java | 2 +- .../samskivert/jdbc/depot/clause/Limit.java | 2 +- .../samskivert/jdbc/depot/clause/OrderBy.java | 2 +- .../jdbc/depot/clause/SelectClause.java | 2 +- .../jdbc/depot/clause/UpdateClause.java | 2 +- .../samskivert/jdbc/depot/clause/Where.java | 5 +- .../jdbc/depot/expression/ColumnExp.java | 2 +- .../jdbc/depot/expression/EpochSeconds.java | 1 - .../depot/expression/ExpressionVisitor.java | 82 ++++------ .../jdbc/depot/expression/FunctionExp.java | 1 - .../jdbc/depot/expression/LiteralExp.java | 2 +- .../jdbc/depot/expression/SQLExpression.java | 3 +- .../jdbc/depot/expression/ValueExp.java | 2 +- .../jdbc/depot/operator/Conditionals.java | 11 +- .../samskivert/jdbc/depot/operator/Logic.java | 2 +- .../jdbc/depot/operator/SQLOperator.java | 4 +- 36 files changed, 472 insertions(+), 307 deletions(-) create mode 100644 src/java/com/samskivert/jdbc/depot/KeyUtil.java create mode 100644 src/java/com/samskivert/jdbc/depot/PrimaryKeySet.java diff --git a/src/java/com/samskivert/jdbc/depot/BindVisitor.java b/src/java/com/samskivert/jdbc/depot/BindVisitor.java index 563adf9..aa4b689 100644 --- a/src/java/com/samskivert/jdbc/depot/BindVisitor.java +++ b/src/java/com/samskivert/jdbc/depot/BindVisitor.java @@ -46,13 +46,15 @@ import com.samskivert.jdbc.depot.expression.LiteralExp; import com.samskivert.jdbc.depot.expression.SQLExpression; import com.samskivert.jdbc.depot.expression.ValueExp; import com.samskivert.jdbc.depot.operator.Conditionals.Exists; +import com.samskivert.jdbc.depot.operator.Conditionals.FullTextMatch; import com.samskivert.jdbc.depot.operator.Conditionals.In; import com.samskivert.jdbc.depot.operator.Conditionals.IsNull; -import com.samskivert.jdbc.depot.operator.Conditionals.FullTextMatch; import com.samskivert.jdbc.depot.operator.Logic.Not; import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator; import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator; +import com.samskivert.util.StringUtil; + /** * Implements the base functionality of the argument-binding pass of {@link SQLBuilder}. Dialectal * subclasses of this should be created and returned from {@link SQLBuilder#getBindVisitor()}. @@ -62,17 +64,16 @@ import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator; public class BindVisitor implements ExpressionVisitor { public void visit (FromOverride override) - throws Exception { + // nothing needed } public void visit (FieldDefinition fieldOverride) - throws Exception { + // nothing needed } public void visit (WhereCondition whereCondition) - throws Exception { for (Comparable value : whereCondition.getValues()) { if (value != null) { @@ -81,14 +82,7 @@ public class BindVisitor implements ExpressionVisitor } } - public void visit (Key key) - throws Exception - { - key.condition.accept(this); - } - public void visit (MultiKey key) - throws Exception { for (Map.Entry> entry : key.getSingleFieldsMap().entrySet()) { if (entry.getValue() != null) { @@ -102,34 +96,31 @@ public class BindVisitor implements ExpressionVisitor } public void visit (FunctionExp functionExp) - throws Exception { visit(functionExp.getArguments()); } public void visit (EpochSeconds epochSeconds) - throws Exception { epochSeconds.getArgument().accept(this); } public void visit (MultiOperator multiOperator) - throws Exception { visit(multiOperator.getConditions()); } - public void visit (BinaryOperator binaryOperator) throws Exception + public void visit (BinaryOperator binaryOperator) { binaryOperator.getLeftHandSide().accept(this); binaryOperator.getRightHandSide().accept(this); } - public void visit (IsNull isNull) throws Exception + public void visit (IsNull isNull) { } - public void visit (In in) throws Exception + public void visit (In in) { Comparable[] values = in.getValues(); for (int ii = 0; ii < values.length; ii++) { @@ -137,70 +128,74 @@ public class BindVisitor implements ExpressionVisitor } } - public void visit (FullTextMatch match) throws Exception + public void visit (FullTextMatch match) { // we never get here } - public void visit (ColumnExp columnExp) throws Exception + public void visit (ColumnExp columnExp) { // no arguments } - public void visit (Not not) throws Exception + public void visit (Not not) { not.getCondition().accept(this); } - public void visit (GroupBy groupBy) throws Exception + public void visit (GroupBy groupBy) { visit(groupBy.getValues()); } - public void visit (ForUpdate forUpdate) throws Exception + public void visit (ForUpdate forUpdate) { // do nothing } - public void visit (OrderBy orderBy) throws Exception + public void visit (OrderBy orderBy) { visit(orderBy.getValues()); } - public void visit (Where where) throws Exception + public void visit (WhereClause where) { - where.getCondition().accept(this); + where.getWhereExpression().accept(this); } - public void visit (Join join) throws Exception + public void visit (Join join) { join.getJoinCondition().accept(this); } - public void visit (Limit limit) throws Exception + public void visit (Limit limit) { - _stmt.setInt(_argIdx++, limit.getCount()); - _stmt.setInt(_argIdx++, limit.getOffset()); + try { + _stmt.setInt(_argIdx++, limit.getCount()); + _stmt.setInt(_argIdx++, limit.getOffset()); + } catch (SQLException sqe) { + throw new DatabaseException("Failed to configure statement with limit clause " + + "[count=" + limit.getCount() + + ", offset=" + limit.getOffset() + "]", sqe); + } } - public void visit (LiteralExp literalExp) throws Exception + public void visit (LiteralExp literalExp) { // do nothing } - public void visit (ValueExp valueExp) throws Exception + public void visit (ValueExp valueExp) { writeValueToStatement(valueExp.getValue()); } public void visit (Exists exists) - throws Exception { exists.getSubClause().accept(this); } public void visit (SelectClause selectClause) - throws Exception { for (Join clause : selectClause.getJoinClauses()) { clause.accept(this); @@ -222,16 +217,21 @@ public class BindVisitor implements ExpressionVisitor } } - public void visit (UpdateClause updateClause) throws Exception + public void visit (UpdateClause updateClause) { DepotMarshaller marsh = _types.getMarshaller(updateClause.getPersistentClass()); // bind the update arguments - String[] fields = updateClause.getFields(); Object pojo = updateClause.getPojo(); if (pojo != null) { - for (int ii = 0; ii < fields.length; ii ++) { - marsh.getFieldMarshaller(fields[ii]).readFromObject(pojo, _stmt, _argIdx++); + for (String field : updateClause.getFields()) { + try { + marsh.getFieldMarshaller(field).getAndWriteToStatement(_stmt, _argIdx++, pojo); + } catch (Exception e) { + throw new DatabaseException( + "Failed to read field from persistent record and write it to prepared " + + "statement [field=" + field + "]", e); + } } } else { visit(updateClause.getValues()); @@ -239,24 +239,29 @@ public class BindVisitor implements ExpressionVisitor updateClause.getWhereClause().accept(this); } - public void visit (DeleteClause deleteClause) throws Exception - { - deleteClause.getWhereClause().accept(this); - } - - public void visit (InsertClause insertClause) throws Exception + public void visit (InsertClause insertClause) { DepotMarshaller marsh = _types.getMarshaller(insertClause.getPersistentClass()); - Object pojo = insertClause.getPojo(); Set idFields = insertClause.getIdentityFields(); for (String field : marsh.getColumnFieldNames()) { if (!idFields.contains(field)) { - marsh.getFieldMarshaller(field).readFromObject(pojo, _stmt, _argIdx ++); + try { + marsh.getFieldMarshaller(field).getAndWriteToStatement(_stmt, _argIdx++, pojo); + } catch (Exception e) { + throw new DatabaseException( + "Failed to read field from persistent record and write it to prepared " + + "statement [field=" + field + "]", e); + } } } } + public void visit (DeleteClause deleteClause) + { + deleteClause.getWhereClause().accept(this); + } + protected BindVisitor (DepotTypes types, PreparedStatement stmt) { _types = types; @@ -265,7 +270,6 @@ public class BindVisitor implements ExpressionVisitor } protected void visit (SQLExpression[] expressions) - throws Exception { for (int ii = 0; ii < expressions.length; ii ++) { expressions[ii].accept(this); @@ -274,13 +278,17 @@ public class BindVisitor implements ExpressionVisitor // write the value to the next argument slot in the prepared statement protected void writeValueToStatement (Object value) - throws SQLException { - // setObject handles almost all conversions internally, but enums require special care - if (value instanceof ByteEnum) { - _stmt.setByte(_argIdx++, ((ByteEnum)value).toByte()); - } else { - _stmt.setObject(_argIdx++, value); + try { + // setObject handles almost all conversions internally, but enums require special care + if (value instanceof ByteEnum) { + _stmt.setByte(_argIdx++, ((ByteEnum)value).toByte()); + } else { + _stmt.setObject(_argIdx++, value); + } + } catch (SQLException sqe) { + throw new DatabaseException("Failed to write value to statement [idx=" + (_argIdx-1) + + ", value=" + StringUtil.safeToString(value) + "]", sqe); } } diff --git a/src/java/com/samskivert/jdbc/depot/BuildVisitor.java b/src/java/com/samskivert/jdbc/depot/BuildVisitor.java index 10ce038..ad7ded6 100644 --- a/src/java/com/samskivert/jdbc/depot/BuildVisitor.java +++ b/src/java/com/samskivert/jdbc/depot/BuildVisitor.java @@ -26,7 +26,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.samskivert.jdbc.depot.Key.WhereCondition; import com.samskivert.jdbc.depot.annotation.Computed; import com.samskivert.jdbc.depot.clause.DeleteClause; import com.samskivert.jdbc.depot.clause.FieldDefinition; @@ -48,9 +47,9 @@ import com.samskivert.jdbc.depot.expression.LiteralExp; import com.samskivert.jdbc.depot.expression.SQLExpression; import com.samskivert.jdbc.depot.expression.ValueExp; import com.samskivert.jdbc.depot.operator.Conditionals.Exists; +import com.samskivert.jdbc.depot.operator.Conditionals.FullTextMatch; import com.samskivert.jdbc.depot.operator.Conditionals.In; import com.samskivert.jdbc.depot.operator.Conditionals.IsNull; -import com.samskivert.jdbc.depot.operator.Conditionals.FullTextMatch; import com.samskivert.jdbc.depot.operator.Logic.Not; import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator; import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator; @@ -69,7 +68,6 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (FromOverride override) - throws Exception { _builder.append(" from " ); List> from = override.getFromClasses(); @@ -84,7 +82,6 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (FieldDefinition definition) - throws Exception { definition.getDefinition().accept(this); if (_enableAliasing) { @@ -93,11 +90,10 @@ public abstract class BuildVisitor implements ExpressionVisitor } } - public void visit (WhereCondition whereCondition) - throws Exception + public void visit (Key.WhereCondition whereCondition) { Class pClass = whereCondition.getPersistentClass(); - String[] keyFields = Key.getKeyFields(pClass); + String[] keyFields = KeyUtil.getKeyFields(pClass); List> values = whereCondition.getValues(); for (int ii = 0; ii < keyFields.length; ii ++) { if (ii > 0) { @@ -113,15 +109,13 @@ public abstract class BuildVisitor implements ExpressionVisitor } } - public void visit (Key key) - throws Exception + public void visit (WhereClause where) { _builder.append(" where "); - key.condition.accept(this); + where.getWhereExpression().accept(this); } public void visit (MultiKey key) - throws Exception { _builder.append(" where "); boolean first = true; @@ -156,7 +150,6 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (FunctionExp functionExp) - throws Exception { _builder.append(functionExp.getFunction()); _builder.append("("); @@ -171,7 +164,6 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (MultiOperator multiOperator) - throws Exception { SQLExpression[] conditions = multiOperator.getConditions(); for (int ii = 0; ii < conditions.length; ii++) { @@ -185,7 +177,6 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (BinaryOperator binaryOperator) - throws Exception { _builder.append('('); binaryOperator.getLeftHandSide().accept(this); @@ -195,14 +186,12 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (IsNull isNull) - throws Exception { isNull.getColumn().accept(this); _builder.append(" is null"); } public void visit (In in) - throws Exception { in.getColumn().accept(this); _builder.append(" in ("); @@ -216,17 +205,14 @@ public abstract class BuildVisitor implements ExpressionVisitor _builder.append(")"); } - public abstract void visit (FullTextMatch match) - throws Exception; + public abstract void visit (FullTextMatch match); public void visit (ColumnExp columnExp) - throws Exception { appendRhsColumn(columnExp.getPersistentClass(), columnExp.getField()); } public void visit (Not not) - throws Exception { _builder.append(" not ("); not.getCondition().accept(this); @@ -234,7 +220,6 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (GroupBy groupBy) - throws Exception { _builder.append(" group by "); @@ -248,13 +233,11 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (ForUpdate forUpdate) - throws Exception { _builder.append(" for update "); } public void visit (OrderBy orderBy) - throws Exception { _builder.append(" order by "); @@ -269,15 +252,7 @@ public abstract class BuildVisitor implements ExpressionVisitor } } - public void visit (Where where) - throws Exception - { - _builder.append(" where "); - where.getCondition().accept(this); - } - public void visit (Join join) - throws Exception { switch (join.getType()) { case INNER: @@ -298,32 +273,27 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (Limit limit) - throws Exception { _builder.append(" limit ? offset ? "); } public void visit (LiteralExp literalExp) - throws Exception { _builder.append(literalExp.getText()); } public void visit (ValueExp valueExp) - throws Exception { _builder.append("?"); } public void visit (Exists exists) - throws Exception { _builder.append("exists "); exists.getSubClause().accept(this); } public void visit (SelectClause selectClause) - throws Exception { Class pClass = selectClause.getPersistentClass(); boolean isInner = _innerClause; @@ -381,7 +351,8 @@ public abstract class BuildVisitor implements ExpressionVisitor } else if (_types.getTableName(pClass) != null) { tClass = pClass; } else { - throw new SQLException("Query on @Computed entity with no FromOverrideClause."); + throw new IllegalStateException( + "Query on @Computed entity with no FromOverrideClause."); } _builder.append(" from "); appendTableName(tClass); @@ -417,10 +388,10 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (UpdateClause updateClause) - throws Exception { if (updateClause.getWhereClause() == null) { - throw new SQLException("I dare not currently perform UPDATE without a WHERE clause."); + throw new IllegalArgumentException( + "I dare not currently perform UPDATE without a WHERE clause."); } Class pClass = updateClause.getPersistentClass(); _innerClause = true; @@ -451,7 +422,6 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (DeleteClause deleteClause) - throws Exception { _builder.append("delete from "); appendTableName(deleteClause.getPersistentClass()); @@ -462,7 +432,6 @@ public abstract class BuildVisitor implements ExpressionVisitor } public void visit (InsertClause insertClause) - throws Exception { Class pClass = insertClause.getPersistentClass(); DepotMarshaller marsh = _types.getMarshaller(pClass); @@ -512,7 +481,6 @@ public abstract class BuildVisitor implements ExpressionVisitor // 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 type, String field) - throws Exception { DepotMarshaller dm = _types.getMarshaller(type); if (dm == null) { @@ -527,7 +495,6 @@ public abstract class BuildVisitor implements ExpressionVisitor // 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 type, String field) - throws Exception { DepotMarshaller dm = _types.getMarshaller(type); if (dm == null) { diff --git a/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java b/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java index 716b999..c61a41e 100644 --- a/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java +++ b/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java @@ -45,6 +45,8 @@ import com.samskivert.jdbc.depot.annotation.Index; import com.samskivert.jdbc.depot.annotation.TableGenerator; import com.samskivert.jdbc.depot.annotation.Transient; import com.samskivert.jdbc.depot.annotation.UniqueConstraint; +import com.samskivert.jdbc.depot.operator.Conditionals; +import com.samskivert.jdbc.depot.operator.Logic; import com.samskivert.jdbc.ColumnDefinition; import com.samskivert.jdbc.DatabaseLiaison; @@ -458,7 +460,7 @@ public class DepotMarshaller } throw new SQLException("ResultSet missing field: " + fm.getField().getName()); } - fm.writeToObject(rs, po); + fm.getAndWriteToObject(rs, po); } return po; diff --git a/src/java/com/samskivert/jdbc/depot/DepotRepository.java b/src/java/com/samskivert/jdbc/depot/DepotRepository.java index ff54738..adf3932 100644 --- a/src/java/com/samskivert/jdbc/depot/DepotRepository.java +++ b/src/java/com/samskivert/jdbc/depot/DepotRepository.java @@ -22,10 +22,12 @@ package com.samskivert.jdbc.depot; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -41,6 +43,7 @@ import com.samskivert.jdbc.depot.clause.DeleteClause; import com.samskivert.jdbc.depot.clause.FieldOverride; import com.samskivert.jdbc.depot.clause.InsertClause; import com.samskivert.jdbc.depot.clause.QueryClause; +import com.samskivert.jdbc.depot.clause.SelectClause; import com.samskivert.jdbc.depot.clause.UpdateClause; import com.samskivert.jdbc.depot.expression.SQLExpression; import com.samskivert.jdbc.depot.expression.ValueExp; @@ -399,7 +402,7 @@ public abstract class DepotRepository protected int updatePartial (Key key, Object... fieldsValues) throws DatabaseException { - return updatePartial(key.condition.getPersistentClass(), key, key, fieldsValues); + return updatePartial(key.getPersistentClass(), key, key, fieldsValues); } /** @@ -699,21 +702,55 @@ public abstract class DepotRepository return deleteAll(type, primaryKey, primaryKey); } + /** + * Deletes all persistent objects from the database that match the supplied where clause. + * + * @return the number of rows deleted by this action. + */ + protected int deleteAll (Class type, final WhereClause where) + throws DatabaseException + { + // first look up the primary keys for all the rows that match our where clause + final Set> keys = new HashSet>(); + final DepotMarshaller marsh = _ctx.getMarshaller(type); + final SQLBuilder fbuilder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, where)); + fbuilder.newQuery(new SelectClause(type, marsh.getPrimaryKeyFields(), where)); + _ctx.invoke(new Modifier(null) { + @Override + public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { + PreparedStatement stmt = fbuilder.prepare(conn); + try { + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + keys.add(marsh.makePrimaryKey(rs)); + } + return 0; + } finally { + JDBCUtil.close(stmt); + } + } + }); + + // now delete using that primary key set + PrimaryKeySet pwhere = new PrimaryKeySet(type, keys); + return deleteAll(type, pwhere, pwhere); + } + /** * Deletes all persistent objects from the database that match the supplied key. * * @return the number of rows deleted by this action. */ protected int deleteAll ( - Class type, final WhereClause key, CacheInvalidator invalidator) + Class type, final WhereClause where, CacheInvalidator invalidator) throws DatabaseException { if (invalidator instanceof ValidatingCacheInvalidator) { ((ValidatingCacheInvalidator)invalidator).validateFlushType(type); // sanity check } - key.validateQueryType(type); // and another + where.validateQueryType(type); // and another - DeleteClause delete = new DeleteClause(type, key); + DeleteClause delete = new DeleteClause(type, where); final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, delete)); builder.newQuery(delete); diff --git a/src/java/com/samskivert/jdbc/depot/FieldMarshaller.java b/src/java/com/samskivert/jdbc/depot/FieldMarshaller.java index 3d7b672..9ec2edf 100644 --- a/src/java/com/samskivert/jdbc/depot/FieldMarshaller.java +++ b/src/java/com/samskivert/jdbc/depot/FieldMarshaller.java @@ -161,10 +161,10 @@ public abstract class FieldMarshaller } /** - * Reads and returns this field from the result set. + * Reads this field from the given persistent object. */ - public abstract T getFromSet (ResultSet rs) - throws SQLException; + public abstract T getFromObject (Object po) + throws IllegalArgumentException, IllegalAccessException; /** * Sets the specified column of the given prepared statement to the given value. @@ -173,10 +173,20 @@ public abstract class FieldMarshaller throws SQLException; /** - * Reads this field from the given persistent object. + * Reads the value of our field from the supplied persistent object and sets that value into + * the specified column of the supplied prepared statement. */ - public abstract T getFromObject (Object po) - throws IllegalArgumentException, IllegalAccessException; + public void getAndWriteToStatement (PreparedStatement ps, int column, Object po) + throws SQLException, IllegalAccessException + { + writeToStatement(ps, column, getFromObject(po)); + } + + /** + * Reads and returns this field from the result set. + */ + public abstract T getFromSet (ResultSet rs) + throws SQLException; /** * Writes the given value to the given persistent value. @@ -185,20 +195,10 @@ public abstract class FieldMarshaller throws IllegalArgumentException, IllegalAccessException; /** - * Reads the value of our field from the persistent object and sets that - * value into the specified column of the supplied prepared statement. + * Reads the specified column from the supplied result set and writes it to the appropriate + * field of the persistent object. */ - public void readFromObject (Object po, PreparedStatement ps, int column) - throws SQLException, IllegalAccessException - { - writeToStatement(ps, column, getFromObject(po)); - } - - /** - * Reads the specified column from the supplied result set and writes it to - * the appropriate field of the persistent object. - */ - public void writeToObject (ResultSet rset, Object po) + public void getAndWriteToObject (ResultSet rset, Object po) throws SQLException, IllegalAccessException { writeToObject(po, getFromSet(rset)); diff --git a/src/java/com/samskivert/jdbc/depot/FindAllQuery.java b/src/java/com/samskivert/jdbc/depot/FindAllQuery.java index 6ee84cb..fa45f57 100644 --- a/src/java/com/samskivert/jdbc/depot/FindAllQuery.java +++ b/src/java/com/samskivert/jdbc/depot/FindAllQuery.java @@ -116,12 +116,12 @@ public abstract class FindAllQuery } // if we're fetching a huge number of records, we have to do it in multiple queries - if (fetchKeys.size() > MAX_IN_KEYS) { + if (fetchKeys.size() > In.MAX_KEYS) { int keyCount = fetchKeys.size(); do { Set> keys = new HashSet>(); Iterator> iter = fetchKeys.iterator(); - for (int ii = 0; ii < Math.min(keyCount, MAX_IN_KEYS); ii++) { + for (int ii = 0; ii < Math.min(keyCount, In.MAX_KEYS); ii++) { keys.add(iter.next()); iter.remove(); } @@ -146,33 +146,9 @@ public abstract class FindAllQuery protected void loadRecords (Connection conn, Set> keys, Map, T> entities) throws SQLException { - SQLExpression condition; - - if (_marsh.getPrimaryKeyFields().length == 1) { - // Single-column keys result in the compact IN(keyVal1, keyVal2, ...) - Comparable[] keyFieldValues = new Comparable[keys.size()]; - int ii = 0; - for (Key key : keys) { - keyFieldValues[ii ++] = key.condition.getValues().get(0); - } - condition = new In(_type, _marsh.getPrimaryKeyFields()[0], keyFieldValues); - - } else { - // Multi-column keys result in OR'd AND's, of unknown efficiency (TODO check). - SQLExpression[] keyArray = new SQLExpression[keys.size()]; - int ii = 0; - for (Key key : keys) { - keyArray[ii ++] = key.condition; - } - condition = new Or(keyArray); - } - - Where keyWhere = new Where(condition); - // finally build the new query - _builder.newQuery(new SelectClause(_type, _marsh.getFieldNames(), keyWhere)); + _builder.newQuery(new SelectClause(_type, _marsh.getFieldNames(), + new PrimaryKeySet(_type, keys))); PreparedStatement stmt = _builder.prepare(conn); - - // and execute it try { ResultSet rs = stmt.executeQuery(); int cnt = 0, dups = 0; @@ -275,7 +251,4 @@ public abstract class FindAllQuery protected SQLBuilder _builder; protected DepotMarshaller _marsh; protected Class _type; - - /** The maximum number of keys allowed in an IN() clause. */ - protected static final int MAX_IN_KEYS = Short.MAX_VALUE; } diff --git a/src/java/com/samskivert/jdbc/depot/Key.java b/src/java/com/samskivert/jdbc/depot/Key.java index 0b748a7..3793efc 100644 --- a/src/java/com/samskivert/jdbc/depot/Key.java +++ b/src/java/com/samskivert/jdbc/depot/Key.java @@ -24,7 +24,6 @@ import java.io.Serializable; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.HashMap; import java.util.Map; @@ -55,7 +54,6 @@ public class Key extends WhereClause // from SQLExpression public void accept (ExpressionVisitor builder) - throws Exception { builder.visit(this); } @@ -96,9 +94,6 @@ public class Key extends WhereClause protected ArrayList> _values; // List is not Serializable } - /** The expression that identifies our row. */ - public final WhereCondition condition; - /** * Constructs a new single-column {@code Key} with the given value. */ @@ -141,7 +136,7 @@ public class Key extends WhereClause } // look up the cached primary key fields for this object - String[] keyFields = getKeyFields(pClass); + String[] keyFields = KeyUtil.getKeyFields(pClass); // now extract the values in field order and ensure none are extra or missing ArrayList> newValues = new ArrayList>(); @@ -165,18 +160,39 @@ public class Key extends WhereClause "Non-key columns given: " + StringUtil.join(map.keySet().toArray(), ", ")); } - condition = new WhereCondition(pClass, newValues); + _condition = new WhereCondition(pClass, newValues); + } + + /** + * Returns the persistent class for which we represent a key. + */ + public Class getPersistentClass () + { + return _condition.getPersistentClass(); + } + + /** + * Returns the values bound to this key. + */ + public ArrayList> getValues () + { + return _condition.getValues(); + } + + // from WhereClause + public SQLExpression getWhereExpression () + { + return _condition; } // from SQLExpression public void addClasses (Collection> classSet) { - classSet.add(condition.getPersistentClass()); + classSet.add(_condition.getPersistentClass()); } // from SQLExpression public void accept (ExpressionVisitor builder) - throws Exception { builder.visit(this); } @@ -184,23 +200,23 @@ public class Key extends WhereClause // from CacheKey public String getCacheId () { - return condition.getPersistentClass().getName(); + return _condition.getPersistentClass().getName(); } // from CacheKey public Serializable getCacheKey () { - return condition.getValues(); + return _condition.getValues(); } // from ValidatingCacheInvalidator public void validateFlushType (Class pClass) { - if (!pClass.equals(condition.getPersistentClass())) { + if (!pClass.equals(_condition.getPersistentClass())) { throw new IllegalArgumentException( "Class mismatch between persistent record and cache invalidator " + "[record=" + pClass.getSimpleName() + - ", invtype=" + condition.getPersistentClass().getSimpleName() + "]."); + ", invtype=" + _condition.getPersistentClass().getSimpleName() + "]."); } } @@ -214,7 +230,7 @@ public class Key extends WhereClause public void validateQueryType (Class pClass) { super.validateQueryType(pClass); - validateTypesMatch(pClass, condition.getPersistentClass()); + validateTypesMatch(pClass, _condition.getPersistentClass()); } @Override @@ -226,52 +242,31 @@ public class Key extends WhereClause if (obj == null || getClass() != obj.getClass()) { return false; } - return condition.equals(((Key) obj).condition); + return _condition.equals(((Key) obj)._condition); } @Override public int hashCode () { - return condition.hashCode(); + return _condition.hashCode(); } @Override public String toString () { - StringBuilder builder = new StringBuilder(condition.getPersistentClass().getSimpleName()); + StringBuilder builder = new StringBuilder(_condition.getPersistentClass().getSimpleName()); builder.append("("); - String[] keyFields = getKeyFields(condition.getPersistentClass()); + String[] keyFields = KeyUtil.getKeyFields(_condition.getPersistentClass()); for (int ii = 0; ii < keyFields.length; ii ++) { if (ii > 0) { builder.append(", "); } - builder.append(keyFields[ii]).append("=").append(condition.getValues().get(ii)); + builder.append(keyFields[ii]).append("=").append(_condition.getValues().get(ii)); } builder.append(")"); return builder.toString(); } - /** - * 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. - */ - protected static String[] getKeyFields (Class pClass) - { - String[] fields = _keyFields.get(pClass); - if (fields == null) { - List kflist = new ArrayList(); - for (Field field : pClass.getFields()) { - // look for @Id fields - if (field.getAnnotation(Id.class) != null) { - kflist.add(field.getName()); - } - } - _keyFields.put(pClass, fields = kflist.toArray(new String[kflist.size()])); - } - return fields; - } - - /** 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 HashMap,String[]> _keyFields = new HashMap,String[]>(); + /** The expression that identifies our row. */ + protected final WhereCondition _condition; } diff --git a/src/java/com/samskivert/jdbc/depot/KeyUtil.java b/src/java/com/samskivert/jdbc/depot/KeyUtil.java new file mode 100644 index 0000000..69aea6e --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/KeyUtil.java @@ -0,0 +1,59 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001-2008 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.samskivert.jdbc.depot.annotation.Id; + +/** + * Simple utility methods used by {@link Key} and {@link PrimaryKeySet}. + */ +public class KeyUtil +{ + /** + * 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 String[] getKeyFields (Class pClass) + { + String[] fields = _keyFields.get(pClass); + if (fields == null) { + List kflist = new ArrayList(); + for (Field field : pClass.getFields()) { + // look for @Id fields + if (field.getAnnotation(Id.class) != null) { + kflist.add(field.getName()); + } + } + _keyFields.put(pClass, fields = kflist.toArray(new String[kflist.size()])); + } + return fields; + } + + /** 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 Map,String[]> _keyFields = new HashMap,String[]>(); +} diff --git a/src/java/com/samskivert/jdbc/depot/MultiKey.java b/src/java/com/samskivert/jdbc/depot/MultiKey.java index 3e796e0..3b1d74f 100644 --- a/src/java/com/samskivert/jdbc/depot/MultiKey.java +++ b/src/java/com/samskivert/jdbc/depot/MultiKey.java @@ -26,6 +26,7 @@ import java.util.Map; import com.samskivert.jdbc.depot.clause.Where; import com.samskivert.jdbc.depot.expression.ExpressionVisitor; +import com.samskivert.jdbc.depot.expression.SQLExpression; /** * A special form of {@link Where} clause that specifies an explicit range of database rows. It @@ -101,8 +102,14 @@ public class MultiKey extends WhereClause return _mValues; } + // from WhereClause + public SQLExpression getWhereExpression () + { + throw new RuntimeException("Not used"); + } + // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/MySQLBuilder.java b/src/java/com/samskivert/jdbc/depot/MySQLBuilder.java index befa693..16937a2 100644 --- a/src/java/com/samskivert/jdbc/depot/MySQLBuilder.java +++ b/src/java/com/samskivert/jdbc/depot/MySQLBuilder.java @@ -57,7 +57,6 @@ public class MySQLBuilder public class MSBuildVisitor extends BuildVisitor { @Override public void visit (FullTextMatch match) - throws Exception { _builder.append("match("); Class pClass = match.getPersistentRecord(); @@ -73,7 +72,6 @@ public class MySQLBuilder } @Override public void visit (DeleteClause deleteClause) - throws Exception { _builder.append("delete from "); appendTableName(deleteClause.getPersistentClass()); @@ -90,7 +88,6 @@ public class MySQLBuilder } public void visit (EpochSeconds epochSeconds) - throws Exception { _builder.append("unix_timestamp("); epochSeconds.getArgument().accept(this); @@ -120,15 +117,17 @@ public class MySQLBuilder public class MSBindVisitor extends BindVisitor { - protected MSBindVisitor (DepotTypes types, PreparedStatement stmt) - { + protected MSBindVisitor (DepotTypes types, PreparedStatement stmt) { super(types, stmt); } - @Override public void visit (FullTextMatch match) - throws Exception - { - _stmt.setString(_argIdx ++, match.getQuery()); + @Override public void visit (FullTextMatch match) { + try { + _stmt.setString(_argIdx++, match.getQuery()); + } catch (SQLException sqe) { + throw new DatabaseException("Failed to configure full-text match column " + + "[idx=" + (_argIdx-1) + ", match=" + match + "]", sqe); + } } } diff --git a/src/java/com/samskivert/jdbc/depot/PostgreSQLBuilder.java b/src/java/com/samskivert/jdbc/depot/PostgreSQLBuilder.java index d2cb407..1d103a0 100644 --- a/src/java/com/samskivert/jdbc/depot/PostgreSQLBuilder.java +++ b/src/java/com/samskivert/jdbc/depot/PostgreSQLBuilder.java @@ -59,14 +59,12 @@ public class PostgreSQLBuilder public class PGBuildVisitor extends BuildVisitor { @Override public void visit (FullTextMatch match) - throws Exception { appendIdentifier("ftsCol_" + match.getName()); _builder.append(" @@ TO_TSQUERY('default', ?)"); } public void visit (EpochSeconds epochSeconds) - throws Exception { _builder.append("date_part('epoch', "); epochSeconds.getArgument().accept(this); @@ -86,25 +84,26 @@ public class PostgreSQLBuilder public class PGBindVisitor extends BindVisitor { - @Override public void visit (FullTextMatch match) - throws Exception - { + @Override public void visit (FullTextMatch match) { // The tsearch2 engine takes queries on the form // (foo&bar)|goop // so in this first simple implementation, we just take the user query, chop it into // words by space/punctuation and 'or' those together like so: // 'ho! who goes there?' -> 'ho|who|goes|there' - String[] searchTerms = match.getQuery().toLowerCase().split("\\W+"); if (searchTerms.length > 0 && searchTerms[0].length() == 0) { searchTerms = ArrayUtil.splice(searchTerms, 0, 1); } String query = StringUtil.join(searchTerms, "|"); - _stmt.setString(_argIdx ++, query); + try { + _stmt.setString(_argIdx++, query); + } catch (SQLException sqe) { + throw new DatabaseException("Failed to configure full-text match column " + + "[idx=" + (_argIdx-1) + ", query=" + query + "]", sqe); + } } - protected PGBindVisitor (DepotTypes types, PreparedStatement stmt) - { + protected PGBindVisitor (DepotTypes types, PreparedStatement stmt) { super(types, stmt); } } diff --git a/src/java/com/samskivert/jdbc/depot/PrimaryKeySet.java b/src/java/com/samskivert/jdbc/depot/PrimaryKeySet.java new file mode 100644 index 0000000..607c1da --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/PrimaryKeySet.java @@ -0,0 +1,153 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001-2008 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot; + +import java.util.Collection; +import java.util.Set; + +import com.samskivert.jdbc.depot.expression.ExpressionVisitor; +import com.samskivert.jdbc.depot.expression.LiteralExp; +import com.samskivert.jdbc.depot.expression.SQLExpression; +import com.samskivert.jdbc.depot.operator.Conditionals; +import com.samskivert.jdbc.depot.operator.Logic; + +/** + * Contains a set of primary keys that match a set of persistent records. This is used internally + * in Depot when decomposing queries into two parts: first a query for the primary keys that + * identify the records that match a free-form query and then another query that operates on the + * previously identified keys. The keys obtained in the first query are used to create a + * PrimaryKeySet and modifications and deletons using this set will automatically flush the + * appropriate records from the cache. + */ +public class PrimaryKeySet extends WhereClause + implements SQLExpression, ValidatingCacheInvalidator +{ + /** + * Creates a set from the supplied primary keys. + */ + public PrimaryKeySet (Class pClass, Set> keys) + { + _pClass = pClass; + _keys = keys; + + String[] keyFields = KeyUtil.getKeyFields(pClass); + if (keys.size() == 0) { + _condition = new LiteralExp("false"); + + } else if (keyFields.length == 1) { + if (keys.size() > Conditionals.In.MAX_KEYS) { + throw new IllegalArgumentException("Cannot create where clause for more than " + + Conditionals.In.MAX_KEYS + " at a time."); + } + + // Single-column keys result in the compact IN(keyVal1, keyVal2, ...) + + Comparable[] keyFieldValues = new Comparable[keys.size()]; + int ii = 0; + for (Key key : keys) { + keyFieldValues[ii++] = key.getValues().get(0); + } + _condition = new Conditionals.In(pClass, keyFields[0], keyFieldValues); + + } else { + // TODO: is there a maximum size of an or query? 32768? + + // Multi-column keys result in OR'd AND's, of unknown efficiency (TODO check). + SQLExpression[] keyArray = new SQLExpression[keys.size()]; + int ii = 0; + for (Key key : keys) { + keyArray[ii ++] = key.getWhereExpression(); + } + _condition = new Logic.Or(keyArray); + } + } + + // from WhereClause + public SQLExpression getWhereExpression () + { + return _condition; + } + + // from SQLExpression + public void addClasses (Collection> classSet) + { + classSet.add(_pClass); + } + + // from SQLExpression + public void accept (ExpressionVisitor builder) + { + builder.visit(this); + } + + // from ValidatingCacheInvalidator + public void validateFlushType (Class pClass) + { + if (!pClass.equals(_pClass)) { + throw new IllegalArgumentException( + "Class mismatch between persistent record and cache invalidator " + + "[record=" + pClass.getSimpleName() + ", invtype=" + _pClass.getSimpleName() + "]."); + } + } + + // from CacheInvalidator + public void invalidate (PersistenceContext ctx) + { + for (Key key : _keys) { + ctx.cacheInvalidate(key); + } + } + + @Override // from WhereClause + public void validateQueryType (Class pClass) + { + super.validateQueryType(pClass); + validateTypesMatch(pClass, _pClass); + } + + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + return _condition.equals(((Key) obj)._condition); + } + + @Override + public int hashCode () + { + return _condition.hashCode(); + } + + @Override + public String toString () + { + return _keys.toString(); + } + + protected Class _pClass; + protected Set> _keys; + protected SQLExpression _condition; +} diff --git a/src/java/com/samskivert/jdbc/depot/SQLBuilder.java b/src/java/com/samskivert/jdbc/depot/SQLBuilder.java index 9e20fe8..4973a90 100644 --- a/src/java/com/samskivert/jdbc/depot/SQLBuilder.java +++ b/src/java/com/samskivert/jdbc/depot/SQLBuilder.java @@ -58,12 +58,7 @@ public abstract class SQLBuilder { _clause = clause; _buildVisitor = getBuildVisitor(); - - try { - _clause.accept(_buildVisitor); - } catch (Exception e) { - throw new RuntimeException("Failed to build SQL", e); - } + _clause.accept(_buildVisitor); } /** @@ -80,16 +75,10 @@ public abstract class SQLBuilder if (_buildVisitor == null) { throw new IllegalArgumentException("Cannot prepare query until it's been built."); } + PreparedStatement stmt = conn.prepareStatement(_buildVisitor.getQuery()); _bindVisitor = getBindVisitor(stmt); - - try { - _clause.accept(_bindVisitor); - } catch (SQLException se) { - throw se; - } catch (Exception e) { - throw new RuntimeException("Failed to find SQL parameters", e); - } + _clause.accept(_bindVisitor); if (PersistenceContext.DEBUG) { log.info("SQL: " + stmt.toString()); diff --git a/src/java/com/samskivert/jdbc/depot/WhereClause.java b/src/java/com/samskivert/jdbc/depot/WhereClause.java index ac03734..db6d8eb 100644 --- a/src/java/com/samskivert/jdbc/depot/WhereClause.java +++ b/src/java/com/samskivert/jdbc/depot/WhereClause.java @@ -21,12 +21,18 @@ package com.samskivert.jdbc.depot; import com.samskivert.jdbc.depot.clause.QueryClause; +import com.samskivert.jdbc.depot.expression.SQLExpression; /** * Currently only exists as a type without any functionality of its own. */ public abstract class WhereClause extends QueryClause { + /** + * Returns the condition associated with this where clause. + */ + public abstract SQLExpression getWhereExpression (); + /** * Validates that the supplied persistent record type is the type matched by this where clause. * Not all clauses will be able to perform this validation, but those that can, should do so to diff --git a/src/java/com/samskivert/jdbc/depot/clause/DeleteClause.java b/src/java/com/samskivert/jdbc/depot/clause/DeleteClause.java index c00167b..a35df28 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/DeleteClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/DeleteClause.java @@ -54,7 +54,7 @@ public class DeleteClause extends QueryClause } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/FieldDefinition.java b/src/java/com/samskivert/jdbc/depot/clause/FieldDefinition.java index b837a82..a5b7f6f 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/FieldDefinition.java +++ b/src/java/com/samskivert/jdbc/depot/clause/FieldDefinition.java @@ -76,7 +76,6 @@ public class FieldDefinition extends QueryClause // from SQLExpression public void accept (ExpressionVisitor visitor) - throws Exception { visitor.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/ForUpdate.java b/src/java/com/samskivert/jdbc/depot/clause/ForUpdate.java index 8ca4917..2adb39e 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/ForUpdate.java +++ b/src/java/com/samskivert/jdbc/depot/clause/ForUpdate.java @@ -31,7 +31,7 @@ import com.samskivert.jdbc.depot.expression.ExpressionVisitor; public class ForUpdate extends QueryClause { // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/FromOverride.java b/src/java/com/samskivert/jdbc/depot/clause/FromOverride.java index a447339..22160d6 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/FromOverride.java +++ b/src/java/com/samskivert/jdbc/depot/clause/FromOverride.java @@ -62,7 +62,6 @@ public class FromOverride extends QueryClause // from SQLExpression public void accept (ExpressionVisitor builder) - throws Exception { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/GroupBy.java b/src/java/com/samskivert/jdbc/depot/clause/GroupBy.java index 7583e40..497da3b 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/GroupBy.java +++ b/src/java/com/samskivert/jdbc/depot/clause/GroupBy.java @@ -42,7 +42,7 @@ public class GroupBy extends QueryClause } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/InsertClause.java b/src/java/com/samskivert/jdbc/depot/clause/InsertClause.java index 039d07e..5024b01 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/InsertClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/InsertClause.java @@ -62,7 +62,7 @@ public class InsertClause extends QueryClause } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/Join.java b/src/java/com/samskivert/jdbc/depot/clause/Join.java index 80edd07..4261658 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/Join.java +++ b/src/java/com/samskivert/jdbc/depot/clause/Join.java @@ -80,7 +80,7 @@ public class Join extends QueryClause } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/Limit.java b/src/java/com/samskivert/jdbc/depot/clause/Limit.java index 6cc135f..375558a 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/Limit.java +++ b/src/java/com/samskivert/jdbc/depot/clause/Limit.java @@ -47,7 +47,7 @@ public class Limit extends QueryClause } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/OrderBy.java b/src/java/com/samskivert/jdbc/depot/clause/OrderBy.java index 66d92c6..0459c12 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/OrderBy.java +++ b/src/java/com/samskivert/jdbc/depot/clause/OrderBy.java @@ -76,7 +76,7 @@ public class OrderBy extends QueryClause } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/SelectClause.java b/src/java/com/samskivert/jdbc/depot/clause/SelectClause.java index b081d9f..8dbc79d 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/SelectClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/SelectClause.java @@ -195,7 +195,7 @@ public class SelectClause extends QueryClause } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/UpdateClause.java b/src/java/com/samskivert/jdbc/depot/clause/UpdateClause.java index ba89baa..8d4766a 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/UpdateClause.java +++ b/src/java/com/samskivert/jdbc/depot/clause/UpdateClause.java @@ -92,7 +92,7 @@ public class UpdateClause extends QueryClause } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/clause/Where.java b/src/java/com/samskivert/jdbc/depot/clause/Where.java index 19d7588..2392d7c 100644 --- a/src/java/com/samskivert/jdbc/depot/clause/Where.java +++ b/src/java/com/samskivert/jdbc/depot/clause/Where.java @@ -67,13 +67,14 @@ public class Where extends WhereClause _condition = condition; } - public SQLExpression getCondition () + // from WhereClause + public SQLExpression getWhereExpression () { return _condition; } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/expression/ColumnExp.java b/src/java/com/samskivert/jdbc/depot/expression/ColumnExp.java index 68946b7..d10f8fa 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/ColumnExp.java +++ b/src/java/com/samskivert/jdbc/depot/expression/ColumnExp.java @@ -38,7 +38,7 @@ public class ColumnExp } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/expression/EpochSeconds.java b/src/java/com/samskivert/jdbc/depot/expression/EpochSeconds.java index e9d164b..94118df 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/EpochSeconds.java +++ b/src/java/com/samskivert/jdbc/depot/expression/EpochSeconds.java @@ -39,7 +39,6 @@ public class EpochSeconds implements SQLExpression // from SQLExpression public void accept (ExpressionVisitor builder) - throws Exception { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/expression/ExpressionVisitor.java b/src/java/com/samskivert/jdbc/depot/expression/ExpressionVisitor.java index 6b7740e..46d3a0b 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/ExpressionVisitor.java +++ b/src/java/com/samskivert/jdbc/depot/expression/ExpressionVisitor.java @@ -20,10 +20,10 @@ package com.samskivert.jdbc.depot.expression; -import com.samskivert.jdbc.depot.Key.WhereCondition; import com.samskivert.jdbc.depot.Key; import com.samskivert.jdbc.depot.MultiKey; import com.samskivert.jdbc.depot.PersistentRecord; +import com.samskivert.jdbc.depot.WhereClause; import com.samskivert.jdbc.depot.clause.DeleteClause; import com.samskivert.jdbc.depot.clause.FieldDefinition; @@ -51,58 +51,30 @@ import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator; */ public interface ExpressionVisitor { - public void visit (FieldDefinition fieldOverride) - throws Exception; - public void visit (WhereCondition whereCondition) - throws Exception; - public void visit (Key key) - throws Exception; - public void visit (MultiKey key) - throws Exception; - public void visit (FunctionExp functionExp) - throws Exception; - public void visit (EpochSeconds epochSeconds) - throws Exception; - public void visit (FromOverride fromOverride) - throws Exception; - public void visit (MultiOperator multiOperator) - throws Exception; - public void visit (BinaryOperator binaryOperator) - throws Exception; - public void visit (IsNull isNull) - throws Exception; - public void visit (In in) - throws Exception; - public void visit (FullTextMatch match) - throws Exception; - public void visit (ColumnExp columnExp) - throws Exception; - public void visit (Not not) - throws Exception; - public void visit (GroupBy groupBy) - throws Exception; - public void visit (ForUpdate forUpdate) - throws Exception; - public void visit (OrderBy orderBy) - throws Exception; - public void visit (Where where) - throws Exception; - public void visit (Join join) - throws Exception; - public void visit (Limit limit) - throws Exception; - public void visit (LiteralExp literalExp) - throws Exception; - public void visit (ValueExp valueExp) - throws Exception; - public void visit (Exists exists) - throws Exception; - public void visit (SelectClause selectClause) - throws Exception; - public void visit (UpdateClause updateClause) - throws Exception; - public void visit (DeleteClause deleteClause) - throws Exception; - public void visit (InsertClause insertClause) - throws Exception; + public void visit (FieldDefinition fieldOverride); + public void visit (Key.WhereCondition whereCondition); + public void visit (MultiKey key); + public void visit (FunctionExp functionExp); + public void visit (EpochSeconds epochSeconds); + public void visit (FromOverride fromOverride); + public void visit (MultiOperator multiOperator); + public void visit (BinaryOperator binaryOperator); + public void visit (IsNull isNull); + public void visit (In in); + public void visit (FullTextMatch match); + public void visit (ColumnExp columnExp); + public void visit (Not not); + public void visit (GroupBy groupBy); + public void visit (ForUpdate forUpdate); + public void visit (OrderBy orderBy); + public void visit (WhereClause where); + public void visit (Join join); + public void visit (Limit limit); + public void visit (LiteralExp literalExp); + public void visit (ValueExp valueExp); + public void visit (Exists exists); + public void visit (SelectClause selectClause); + public void visit (UpdateClause updateClause); + public void visit (DeleteClause deleteClause); + public void visit (InsertClause insertClause); } diff --git a/src/java/com/samskivert/jdbc/depot/expression/FunctionExp.java b/src/java/com/samskivert/jdbc/depot/expression/FunctionExp.java index c2834d3..f17864c 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/FunctionExp.java +++ b/src/java/com/samskivert/jdbc/depot/expression/FunctionExp.java @@ -40,7 +40,6 @@ public class FunctionExp implements SQLExpression // from SQLExpression public void accept (ExpressionVisitor builder) - throws Exception { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/expression/LiteralExp.java b/src/java/com/samskivert/jdbc/depot/expression/LiteralExp.java index f15bd70..2092f40 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/LiteralExp.java +++ b/src/java/com/samskivert/jdbc/depot/expression/LiteralExp.java @@ -37,7 +37,7 @@ public class LiteralExp } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/expression/SQLExpression.java b/src/java/com/samskivert/jdbc/depot/expression/SQLExpression.java index 0b24ffb..d70ad22 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/SQLExpression.java +++ b/src/java/com/samskivert/jdbc/depot/expression/SQLExpression.java @@ -36,8 +36,7 @@ public interface SQLExpression * * @see SQLBuilder */ - public void accept (ExpressionVisitor builder) - throws Exception; + public void accept (ExpressionVisitor builder); /** * Adds all persistent classes that are brought into the SQL context by this clause: FROM diff --git a/src/java/com/samskivert/jdbc/depot/expression/ValueExp.java b/src/java/com/samskivert/jdbc/depot/expression/ValueExp.java index b306377..080c7b4 100644 --- a/src/java/com/samskivert/jdbc/depot/expression/ValueExp.java +++ b/src/java/com/samskivert/jdbc/depot/expression/ValueExp.java @@ -36,7 +36,7 @@ public class ValueExp } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/operator/Conditionals.java b/src/java/com/samskivert/jdbc/depot/operator/Conditionals.java index e1915cc..1e083bc 100644 --- a/src/java/com/samskivert/jdbc/depot/operator/Conditionals.java +++ b/src/java/com/samskivert/jdbc/depot/operator/Conditionals.java @@ -59,7 +59,7 @@ public abstract class Conditionals } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } @@ -190,6 +190,9 @@ public abstract class Conditionals public static class In implements SQLOperator { + /** The maximum number of keys allowed in an IN() clause. */ + public static final int MAX_KEYS = Short.MAX_VALUE; + public In (Class pClass, String pColumn, Comparable... values) { this(new ColumnExp(pClass, pColumn), values); @@ -226,7 +229,7 @@ public abstract class Conditionals } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } @@ -268,7 +271,7 @@ public abstract class Conditionals _clause = clause; } - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } @@ -316,7 +319,7 @@ public abstract class Conditionals } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/operator/Logic.java b/src/java/com/samskivert/jdbc/depot/operator/Logic.java index 53d0450..ee08788 100644 --- a/src/java/com/samskivert/jdbc/depot/operator/Logic.java +++ b/src/java/com/samskivert/jdbc/depot/operator/Logic.java @@ -91,7 +91,7 @@ public abstract class Logic } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } diff --git a/src/java/com/samskivert/jdbc/depot/operator/SQLOperator.java b/src/java/com/samskivert/jdbc/depot/operator/SQLOperator.java index 5c4ee15..86022bf 100644 --- a/src/java/com/samskivert/jdbc/depot/operator/SQLOperator.java +++ b/src/java/com/samskivert/jdbc/depot/operator/SQLOperator.java @@ -46,7 +46,7 @@ public interface SQLOperator extends SQLExpression } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); } @@ -89,7 +89,7 @@ public interface SQLOperator extends SQLExpression } // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception + public void accept (ExpressionVisitor builder) { builder.visit(this); }