El Giganto Depot database dialects support from Zell. PostgresSQL here we
come. Automatic schema migration is currently disabled and remains to be dialectified.
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2006-2007 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;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.jdbc.depot.Key.WhereCondition;
|
||||
import com.samskivert.jdbc.depot.clause.DeleteClause;
|
||||
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.InsertClause;
|
||||
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.SelectClause;
|
||||
import com.samskivert.jdbc.depot.clause.UpdateClause;
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.FunctionExp;
|
||||
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.In;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.IsNull;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.Match;
|
||||
import com.samskivert.jdbc.depot.operator.Logic.Not;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator;
|
||||
|
||||
/**
|
||||
* 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()}.
|
||||
*
|
||||
* This class is intimately paired with {#link BuildVisitor}.
|
||||
*/
|
||||
public class BindVisitor implements ExpressionVisitor
|
||||
{
|
||||
public void visit (FromOverride override)
|
||||
throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void visit (FieldOverride fieldOverride)
|
||||
throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void visit (WhereCondition<? extends PersistentRecord> whereCondition)
|
||||
throws Exception
|
||||
{
|
||||
Comparable[] values = whereCondition.getValues();
|
||||
for (int ii = 0; ii < values.length; ii ++) {
|
||||
if (values[ii] != null) {
|
||||
_stmt.setObject(_argIdx ++, values[ii]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (Key key)
|
||||
throws Exception
|
||||
{
|
||||
key.condition.accept(this);
|
||||
}
|
||||
|
||||
public void visit (MultiKey<? extends PersistentRecord> key)
|
||||
throws Exception
|
||||
{
|
||||
for (Map.Entry entry : key.getSingleFieldsMap().entrySet()) {
|
||||
if (entry.getValue() != null) {
|
||||
_stmt.setObject(_argIdx ++, entry.getValue());
|
||||
}
|
||||
}
|
||||
Comparable[] values = key.getMultiValues();
|
||||
for (int ii = 0; ii < values.length; ii++) {
|
||||
_stmt.setObject(_argIdx ++, values[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (FunctionExp functionExp)
|
||||
throws Exception
|
||||
{
|
||||
visit(functionExp.getArguments());
|
||||
}
|
||||
|
||||
public void visit (MultiOperator multiOperator)
|
||||
throws Exception
|
||||
{
|
||||
visit(multiOperator.getConditions());
|
||||
}
|
||||
|
||||
public void visit (BinaryOperator binaryOperator) throws Exception
|
||||
{
|
||||
binaryOperator.getLeftHandSide().accept(this);
|
||||
binaryOperator.getRightHandSide().accept(this);
|
||||
}
|
||||
|
||||
public void visit (IsNull isNull) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void visit (In in) throws Exception
|
||||
{
|
||||
Comparable[] values = in.getValues();
|
||||
for (int ii = 0; ii < values.length; ii++) {
|
||||
_stmt.setObject(_argIdx ++, values[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (Match match) throws Exception
|
||||
{
|
||||
// we never get here
|
||||
}
|
||||
|
||||
public void visit (ColumnExp columnExp) throws Exception
|
||||
{
|
||||
// no arguments
|
||||
}
|
||||
|
||||
public void visit (Not not) throws Exception
|
||||
{
|
||||
not.getCondition().accept(this);
|
||||
}
|
||||
|
||||
public void visit (GroupBy groupBy) throws Exception
|
||||
{
|
||||
visit(groupBy.getValues());
|
||||
}
|
||||
|
||||
public void visit (ForUpdate forUpdate) throws Exception
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public void visit (OrderBy orderBy) throws Exception
|
||||
{
|
||||
visit(orderBy.getValues());
|
||||
}
|
||||
|
||||
public void visit (Where where) throws Exception
|
||||
{
|
||||
where.getCondition().accept(this);
|
||||
}
|
||||
|
||||
public void visit (Join join) throws Exception
|
||||
{
|
||||
join.getJoinCondition().accept(this);
|
||||
}
|
||||
|
||||
public void visit (Limit limit) throws Exception
|
||||
{
|
||||
_stmt.setObject(_argIdx++, limit.getCount());
|
||||
_stmt.setObject(_argIdx++, limit.getOffset());
|
||||
}
|
||||
|
||||
public void visit (LiteralExp literalExp) throws Exception
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public void visit (ValueExp valueExp) throws Exception
|
||||
{
|
||||
// workaround for postgres bug, fixed in next release:
|
||||
// http://archives.postgresql.org/pgsql-jdbc/2007-06/msg00080.php
|
||||
if (valueExp.getValue() instanceof Byte) {
|
||||
_stmt.setByte(_argIdx ++, (Byte) valueExp.getValue());
|
||||
} else {
|
||||
_stmt.setObject(_argIdx ++, valueExp.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (SelectClause<? extends PersistentRecord> selectClause)
|
||||
throws Exception
|
||||
{
|
||||
for (Join clause : selectClause.getJoinClauses()) {
|
||||
clause.accept(this);
|
||||
}
|
||||
if (selectClause.getWhereClause() != null) {
|
||||
selectClause.getWhereClause().accept(this);
|
||||
}
|
||||
if (selectClause.getGroupBy() != null) {
|
||||
selectClause.getGroupBy().accept(this);
|
||||
}
|
||||
if (selectClause.getOrderBy() != null) {
|
||||
selectClause.getOrderBy().accept(this);
|
||||
}
|
||||
if (selectClause.getLimit() != null) {
|
||||
selectClause.getLimit().accept(this);
|
||||
}
|
||||
if (selectClause.getForUpdate() != null) {
|
||||
selectClause.getForUpdate().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (UpdateClause<? extends PersistentRecord> updateClause) throws Exception
|
||||
{
|
||||
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++);
|
||||
}
|
||||
} else {
|
||||
visit(updateClause.getValues());
|
||||
}
|
||||
updateClause.getWhereClause().accept(this);
|
||||
}
|
||||
|
||||
public void visit (DeleteClause<? extends PersistentRecord> deleteClause) throws Exception
|
||||
{
|
||||
deleteClause.getWhereClause().accept(this);
|
||||
}
|
||||
|
||||
public void visit (InsertClause<? extends PersistentRecord> insertClause) throws Exception
|
||||
{
|
||||
DepotMarshaller marsh = _types.getMarshaller(insertClause.getPersistentClass());
|
||||
|
||||
Object pojo = insertClause.getPojo();
|
||||
String ixField = insertClause.getIndexField();
|
||||
for (String field : marsh.getColumnFieldNames()) {
|
||||
if (ixField == null || !ixField.equals(field)) {
|
||||
marsh.getFieldMarshaller(field).readFromObject(pojo, _stmt, _argIdx ++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected BindVisitor (DepotTypes types, PreparedStatement stmt)
|
||||
{
|
||||
_types = types;
|
||||
_stmt = stmt;
|
||||
_argIdx = 1;
|
||||
}
|
||||
|
||||
protected void visit (SQLExpression[] expressions)
|
||||
throws Exception
|
||||
{
|
||||
for (int ii = 0; ii < expressions.length; ii ++) {
|
||||
expressions[ii].accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected DepotTypes _types;
|
||||
protected PreparedStatement _stmt;
|
||||
protected int _argIdx;
|
||||
}
|
||||
@@ -0,0 +1,516 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2006-2007 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;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.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.InsertClause;
|
||||
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.SelectClause;
|
||||
import com.samskivert.jdbc.depot.clause.UpdateClause;
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.FunctionExp;
|
||||
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.In;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.IsNull;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.Match;
|
||||
import com.samskivert.jdbc.depot.operator.Logic.Not;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator;
|
||||
|
||||
/**
|
||||
* Implements the base functionality of the SQL-building pass of {@link SQLBuilder}. Dialectal
|
||||
* subclasses of this should be created and returned from {@link SQLBuilder#getBuildVisitor()}.
|
||||
*
|
||||
* This class is intimately paired with {#link BindVisitor}.
|
||||
*/
|
||||
public abstract class BuildVisitor implements ExpressionVisitor
|
||||
{
|
||||
public String getQuery ()
|
||||
{
|
||||
return _builder.toString();
|
||||
}
|
||||
|
||||
public void visit (FromOverride override)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append(" from " );
|
||||
List<Class<? extends PersistentRecord>> from = override.getFromClasses();
|
||||
for (int ii = 0; ii < from.size(); ii++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
appendTableName(from.get(ii));
|
||||
_builder.append(" as ");
|
||||
appendTableAbbreviation(from.get(ii));
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (FieldOverride fieldOverride)
|
||||
throws Exception
|
||||
{
|
||||
fieldOverride.getOverride().accept(this);
|
||||
_builder.append(" as ");
|
||||
appendField(fieldOverride.getField());
|
||||
}
|
||||
|
||||
public void visit (WhereCondition<? extends PersistentRecord> whereCondition)
|
||||
throws Exception
|
||||
{
|
||||
String[] keyFields = Key.getKeyFields(whereCondition.getPersistentClass());
|
||||
Comparable[] values = whereCondition.getValues();
|
||||
for (int ii = 0; ii < keyFields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(" and ");
|
||||
}
|
||||
appendColumn(whereCondition.getPersistentClass(), keyFields[ii]);
|
||||
_builder.append(values[ii] == null ? " is null " : " = ? ");
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (Key key)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append(" where ");
|
||||
key.condition.accept(this);
|
||||
}
|
||||
|
||||
public void visit (MultiKey<? extends PersistentRecord> key)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append(" where ");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, Comparable> entry : key.getSingleFieldsMap().entrySet()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
_builder.append(" and ");
|
||||
}
|
||||
appendColumn(key.getPersistentClass(), entry.getKey());
|
||||
_builder.append(entry.getValue() == null ? " is null " : " = ? ");
|
||||
}
|
||||
if (!first) {
|
||||
_builder.append(" and ");
|
||||
}
|
||||
appendColumn(key.getPersistentClass(), key.getMultiField());
|
||||
_builder.append(" in (");
|
||||
|
||||
Comparable[] values = key.getMultiValues();
|
||||
for (int ii = 0; ii < values.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
_builder.append("?");
|
||||
}
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
public void visit (FunctionExp functionExp)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append(functionExp.getFunction());
|
||||
_builder.append("(");
|
||||
SQLExpression[] arguments = functionExp.getArguments();
|
||||
for (int ii = 0; ii < arguments.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
arguments[ii].accept(this);
|
||||
}
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
public void visit (MultiOperator multiOperator)
|
||||
throws Exception
|
||||
{
|
||||
SQLExpression[] conditions = multiOperator.getConditions();
|
||||
for (int ii = 0; ii < conditions.length; ii++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(" ").append(multiOperator.operator()).append(" ");
|
||||
}
|
||||
_builder.append("(");
|
||||
conditions[ii].accept(this);
|
||||
_builder.append(")");
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (BinaryOperator binaryOperator)
|
||||
throws Exception
|
||||
{
|
||||
binaryOperator.getLeftHandSide().accept(this);
|
||||
_builder.append(binaryOperator.operator());
|
||||
binaryOperator.getRightHandSide().accept(this);
|
||||
}
|
||||
|
||||
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 (");
|
||||
Comparable[] values = in.getValues();
|
||||
for (int ii = 0; ii < values.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
_builder.append("?");
|
||||
}
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
public void visit (Match match)
|
||||
throws Exception
|
||||
{
|
||||
throw new IllegalArgumentException("Match() is MySQL-specific.");
|
||||
}
|
||||
|
||||
public void visit (ColumnExp columnExp)
|
||||
throws Exception
|
||||
{
|
||||
appendTableAbbreviation(columnExp.getPersistentClass());
|
||||
_builder.append(".");
|
||||
appendColumn(columnExp.getPersistentClass(), columnExp.getField());
|
||||
}
|
||||
|
||||
public void visit (Not not)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append(" not (");
|
||||
not.getCondition().accept(this);
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
public void visit (GroupBy groupBy)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append(" group by ");
|
||||
|
||||
SQLExpression[] values = groupBy.getValues();
|
||||
for (int ii = 0; ii < values.length; ii++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
values[ii].accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (ForUpdate forUpdate)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append(" for update ");
|
||||
}
|
||||
|
||||
public void visit (OrderBy orderBy)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append(" order by ");
|
||||
|
||||
SQLExpression[] values = orderBy.getValues();
|
||||
OrderBy.Order[] orders = orderBy.getOrders();
|
||||
for (int ii = 0; ii < values.length; ii++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
values[ii].accept(this);
|
||||
_builder.append(" ").append(orders[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
_builder.append(" inner join " );
|
||||
break;
|
||||
case LEFT_OUTER:
|
||||
_builder.append(" left outer join " );
|
||||
break;
|
||||
case RIGHT_OUTER:
|
||||
_builder.append(" right outer join " );
|
||||
break;
|
||||
}
|
||||
appendTableName(join.getJoinClass());
|
||||
_builder.append(" as ");
|
||||
appendTableAbbreviation(join.getJoinClass());
|
||||
_builder.append(" on ");
|
||||
join.getJoinCondition().accept(this);
|
||||
}
|
||||
|
||||
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 (SelectClause<? extends PersistentRecord> selectClause)
|
||||
throws Exception
|
||||
{
|
||||
Class<? extends PersistentRecord> pClass = selectClause.getPersistentClass();
|
||||
boolean isInner = _innerClause;
|
||||
_innerClause = true;
|
||||
|
||||
if (isInner) {
|
||||
_builder.append("(");
|
||||
}
|
||||
_builder.append("select ");
|
||||
|
||||
Computed entityComputed = pClass.getAnnotation(Computed.class);
|
||||
|
||||
// iterate over the fields we're filling in and figure out whence each one comes
|
||||
boolean skip = true;
|
||||
for (String field : selectClause.getFields()) {
|
||||
if (!skip) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
skip = false;
|
||||
|
||||
// first, see if there's a field override
|
||||
FieldOverride override = selectClause.lookupOverride(field);
|
||||
if (override != null) {
|
||||
override.accept(this);
|
||||
continue;
|
||||
}
|
||||
|
||||
// figure out the class we're selecting from unless we're otherwise overriden:
|
||||
// for a concrete record, simply use the corresponding table; for a computed one,
|
||||
// default to the shadowed concrete record, or null if there isn't one
|
||||
|
||||
Class<? extends PersistentRecord> tableClass;
|
||||
if (entityComputed == null) {
|
||||
tableClass = pClass;
|
||||
} else if (!PersistentRecord.class.equals(entityComputed.shadowOf())) {
|
||||
tableClass = entityComputed.shadowOf();
|
||||
} else {
|
||||
tableClass = null;
|
||||
}
|
||||
|
||||
// handle the field-level @Computed annotation, if there is one
|
||||
FieldMarshaller fm = _types.getMarshaller(pClass).getFieldMarshaller(field);
|
||||
if (fm == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"could not find marshaller for field: " + field);
|
||||
}
|
||||
Computed fieldComputed = fm.getComputed();
|
||||
if (fieldComputed != null) {
|
||||
// check if the computed field has a literal SQL definition
|
||||
if (fieldComputed.fieldDefinition().length() > 0) {
|
||||
_builder.append(fieldComputed.fieldDefinition()).append(" as ");
|
||||
appendField(field);
|
||||
continue;
|
||||
}
|
||||
|
||||
// or if we can simply ignore the field
|
||||
if (!fieldComputed.required()) {
|
||||
skip = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// else see if there's an overriding shadowOf definition
|
||||
if (fieldComputed.shadowOf() != null) {
|
||||
tableClass = fieldComputed.shadowOf();
|
||||
}
|
||||
}
|
||||
|
||||
// if we get this far we hopefully have a table to select from
|
||||
if (tableClass != null) {
|
||||
appendTableAbbreviation(tableClass);
|
||||
_builder.append(".");
|
||||
appendColumn(tableClass, field);
|
||||
continue;
|
||||
}
|
||||
|
||||
// else owie
|
||||
throw new IllegalArgumentException(
|
||||
"Persistent field has no definition [class=" +
|
||||
selectClause.getPersistentClass() + ", field=" + field + "]");
|
||||
}
|
||||
|
||||
if (selectClause.getFromOverride() != null) {
|
||||
selectClause.getFromOverride().accept(this);
|
||||
|
||||
} else if (_types.getTableName(pClass) != null) {
|
||||
_builder.append(" from ");
|
||||
appendTableName(pClass);
|
||||
_builder.append(" as ");
|
||||
appendTableAbbreviation(pClass);
|
||||
|
||||
} else {
|
||||
throw new SQLException("Query on @Computed entity with no FromOverrideClause.");
|
||||
}
|
||||
|
||||
for (Join clause : selectClause.getJoinClauses()) {
|
||||
clause.accept(this);
|
||||
}
|
||||
if (selectClause.getWhereClause() != null) {
|
||||
selectClause.getWhereClause().accept(this);
|
||||
}
|
||||
if (selectClause.getGroupBy() != null) {
|
||||
selectClause.getGroupBy().accept(this);
|
||||
}
|
||||
if (selectClause.getOrderBy() != null) {
|
||||
selectClause.getOrderBy().accept(this);
|
||||
}
|
||||
if (selectClause.getLimit() != null) {
|
||||
selectClause.getLimit().accept(this);
|
||||
}
|
||||
if (selectClause.getForUpdate() != null) {
|
||||
selectClause.getForUpdate().accept(this);
|
||||
}
|
||||
if (isInner) {
|
||||
_builder.append(")");
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (UpdateClause<? extends PersistentRecord> updateClause)
|
||||
throws Exception
|
||||
{
|
||||
Class<? extends PersistentRecord> pClass = updateClause.getPersistentClass();
|
||||
_innerClause = true;
|
||||
|
||||
_builder.append("update ");
|
||||
appendTableName(pClass);
|
||||
_builder.append(" as ");
|
||||
appendTableAbbreviation(pClass);
|
||||
_builder.append(" set ");
|
||||
|
||||
String[] fields = updateClause.getFields();
|
||||
Object pojo = updateClause.getPojo();
|
||||
SQLExpression[] values = updateClause.getValues();
|
||||
for (int ii = 0; ii < fields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
appendColumn(pClass, fields[ii]);
|
||||
|
||||
_builder.append(" = ");
|
||||
if (pojo != null) {
|
||||
_builder.append("?");
|
||||
} else {
|
||||
values[ii].accept(this);
|
||||
}
|
||||
}
|
||||
updateClause.getWhereClause().accept(this);
|
||||
}
|
||||
|
||||
public void visit (DeleteClause<? extends PersistentRecord> deleteClause)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append("delete from ");
|
||||
appendTableName(deleteClause.getPersistentClass());
|
||||
_builder.append(" ");
|
||||
deleteClause.getWhereClause().accept(this);
|
||||
}
|
||||
|
||||
public void visit (InsertClause<? extends PersistentRecord> insertClause)
|
||||
throws Exception
|
||||
{
|
||||
Class<? extends PersistentRecord> pClass = insertClause.getPersistentClass();
|
||||
DepotMarshaller marsh = _types.getMarshaller(pClass);
|
||||
_innerClause = true;
|
||||
|
||||
String[] fields = marsh.getColumnFieldNames();
|
||||
|
||||
_builder.append("insert into ");
|
||||
appendTableName(insertClause.getPersistentClass());
|
||||
_builder.append(" (");
|
||||
for (int ii = 0; ii < fields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
appendColumn(pClass, fields[ii]);
|
||||
}
|
||||
_builder.append(") values(");
|
||||
|
||||
String ixField = insertClause.getIndexField();
|
||||
for (int ii = 0; ii < fields.length; ii++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
if (ixField != null && ixField.equals(fields[ii])) {
|
||||
_builder.append("DEFAULT");
|
||||
} else {
|
||||
_builder.append("?");
|
||||
}
|
||||
}
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
protected abstract void appendTableName (Class<? extends PersistentRecord> type);
|
||||
protected abstract void appendTableAbbreviation (Class<? extends PersistentRecord> type);
|
||||
protected abstract void appendColumn (Class<? extends PersistentRecord> type, String field);
|
||||
protected abstract void appendField (String field);
|
||||
|
||||
protected BuildVisitor (DepotTypes types)
|
||||
{
|
||||
_types = types;
|
||||
_builder = new StringBuilder();
|
||||
_innerClause = false;
|
||||
}
|
||||
|
||||
protected DepotTypes _types;
|
||||
|
||||
/** A StringBuilder to hold the constructed SQL. */
|
||||
protected StringBuilder _builder;
|
||||
|
||||
/** A flag that's set to true for inner SELECT's */
|
||||
protected boolean _innerClause;
|
||||
}
|
||||
@@ -48,11 +48,8 @@ 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.JDBCUtil;
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.ListUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import static com.samskivert.jdbc.depot.Log.log;
|
||||
|
||||
@@ -75,7 +72,6 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
_pclass = pclass;
|
||||
|
||||
Entity entity = pclass.getAnnotation(Entity.class);
|
||||
Table table = null;
|
||||
|
||||
// see if this is a computed entity
|
||||
Computed computed = pclass.getAnnotation(Computed.class);
|
||||
@@ -89,11 +85,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
if (entity.name().length() > 0) {
|
||||
_tableName = entity.name();
|
||||
}
|
||||
_postamble = entity.postamble();
|
||||
}
|
||||
|
||||
// check for a Table annotation, for unique constraints
|
||||
table = pclass.getAnnotation(Table.class);
|
||||
}
|
||||
|
||||
// if the entity defines a new TableGenerator, map that in our static table as those are
|
||||
@@ -127,8 +119,8 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
|
||||
FieldMarshaller fm = FieldMarshaller.createMarshaller(field);
|
||||
_fields.put(fm.getColumnName(), fm);
|
||||
fields.add(fm.getColumnName());
|
||||
_fields.put(field.getName(), fm);
|
||||
fields.add(field.getName());
|
||||
|
||||
// check to see if this is our primary key
|
||||
if (field.getAnnotation(Id.class) != null) {
|
||||
@@ -181,7 +173,8 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
switch(gv.strategy()) {
|
||||
case AUTO:
|
||||
case IDENTITY:
|
||||
_keyGenerator = new IdentityKeyGenerator();
|
||||
_keyGenerator = new IdentityKeyGenerator(
|
||||
gv, getTableName(), keyField.getColumnName());
|
||||
break;
|
||||
|
||||
case TABLE:
|
||||
@@ -191,7 +184,8 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown generator [generator=" + name + "]");
|
||||
}
|
||||
_keyGenerator = new TableKeyGenerator(generator);
|
||||
_keyGenerator = new TableKeyGenerator(
|
||||
generator, gv, getTableName(), keyField.getColumnName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -199,54 +193,6 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
|
||||
// generate our full list of fields/columns for use in queries
|
||||
_allFields = fields.toArray(new String[fields.size()]);
|
||||
|
||||
// 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];
|
||||
int jj = 0;
|
||||
for (int ii = 0; ii < _allFields.length; ii++) {
|
||||
// include all persistent non-computed fields
|
||||
String colDef = _fields.get(_allFields[ii]).getColumnDefinition();
|
||||
if (colDef != null) {
|
||||
_columnFields[jj] = _allFields[ii];
|
||||
_declarations.add(colDef);
|
||||
jj ++;
|
||||
}
|
||||
}
|
||||
_columnFields = ArrayUtil.splice(_columnFields, jj);
|
||||
|
||||
// determine whether we have any index definitions
|
||||
if (entity != null) {
|
||||
for (Index index : entity.indices()) {
|
||||
// TODO: delegate this to a database specific SQL generator
|
||||
_declarations.add(index.type() + " index " + index.name() +
|
||||
" (" + StringUtil.join(index.columns(), ", ") + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// add any unique constraints given
|
||||
if (table != null) {
|
||||
for (UniqueConstraint constraint : table.uniqueConstraints()) {
|
||||
_declarations.add(
|
||||
"UNIQUE (" + StringUtil.join(constraint.columnNames(), ", ") + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// add the primary key, if we have one
|
||||
if (hasPrimaryKey()) {
|
||||
_declarations.add("PRIMARY KEY (" + getJoinedKeyColumns() + ")");
|
||||
}
|
||||
|
||||
// if we did not find a schema version field, complain
|
||||
if (_schemaVersion < 0) {
|
||||
log.warning("Unable to read " + _pclass.getName() + "." + SCHEMA_VERSION_FIELD +
|
||||
". Schema migration disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,6 +212,14 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
return _allFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the persistent fields that correspond to concrete table columns.
|
||||
*/
|
||||
public String[] getColumnFieldNames ()
|
||||
{
|
||||
return _columnFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link FieldMarshaller} for a named field on our persistent class.
|
||||
*/
|
||||
@@ -283,14 +237,23 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the names of the columns that constitute the primary key of our associated
|
||||
* persistent record.
|
||||
* Returns the {@link KeyGenerator} used to generate primary keys for this persistent object,
|
||||
* or null if it does not use a key generator.
|
||||
*/
|
||||
public String[] getPrimaryKeyColumns ()
|
||||
public KeyGenerator getKeyGenerator ()
|
||||
{
|
||||
return _keyGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the names of the columns that constitute the primary key of our associated persistent
|
||||
* record.
|
||||
*/
|
||||
public String[] getPrimaryKeyFields ()
|
||||
{
|
||||
String[] pkcols = new String[_pkColumns.size()];
|
||||
for (int ii = 0; ii < pkcols.length; ii ++) {
|
||||
pkcols[ii] = _pkColumns.get(ii).getColumnName();
|
||||
pkcols[ii] = _pkColumns.get(ii).getField().getName();
|
||||
}
|
||||
return pkcols;
|
||||
}
|
||||
@@ -359,11 +322,11 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
throw new UnsupportedOperationException(
|
||||
getClass().getName() + " does not define a primary key");
|
||||
}
|
||||
String[] columns = new String[_pkColumns.size()];
|
||||
String[] fields = new String[_pkColumns.size()];
|
||||
for (int ii = 0; ii < _pkColumns.size(); ii++) {
|
||||
columns[ii] = _pkColumns.get(ii).getColumnName();
|
||||
fields[ii] = _pkColumns.get(ii).getField().getName();
|
||||
}
|
||||
return new Key<T>(_pclass, columns, values);
|
||||
return new Key<T>(_pclass, fields, values);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,7 +379,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
// then create and populate the persistent object
|
||||
T po = _pclass.newInstance();
|
||||
for (FieldMarshaller fm : _fields.values()) {
|
||||
if (!fields.contains(fm.getField().getName())) {
|
||||
if (!fields.contains(fm.getColumnName())) {
|
||||
// this field was not in the result set, make sure that's OK
|
||||
if (fm.getComputed() != null && !fm.getComputed().required()) {
|
||||
continue;
|
||||
@@ -438,46 +401,6 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a statement that will insert the supplied persistent object into the database.
|
||||
*/
|
||||
public PreparedStatement createInsert (Connection conn, Object po)
|
||||
throws SQLException
|
||||
{
|
||||
requireNotComputed("insert rows into");
|
||||
|
||||
try {
|
||||
StringBuilder insert = new StringBuilder();
|
||||
insert.append("insert into ").append(getTableName());
|
||||
insert.append(" (").append(StringUtil.join(_columnFields, ","));
|
||||
insert.append(")").append(" values(");
|
||||
for (int ii = 0; ii < _columnFields.length; ii++) {
|
||||
if (ii > 0) {
|
||||
insert.append(", ");
|
||||
}
|
||||
insert.append("?");
|
||||
}
|
||||
insert.append(")");
|
||||
|
||||
// TODO: handle primary key, nullable fields specially?
|
||||
PreparedStatement pstmt = conn.prepareStatement(insert.toString());
|
||||
int idx = 0;
|
||||
for (String field : _columnFields) {
|
||||
_fields.get(field).readFromObject(po, pstmt, ++idx);
|
||||
}
|
||||
return pstmt;
|
||||
|
||||
} catch (SQLException sqe) {
|
||||
// pass this on through
|
||||
throw sqe;
|
||||
|
||||
} catch (Exception e) {
|
||||
String errmsg = "Failed to marshall persistent object [pclass=" +
|
||||
_pclass.getName() + "]";
|
||||
throw (SQLException)new SQLException(errmsg).initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in the primary key just assigned to the supplied persistence object by the execution
|
||||
* of the results of {@link #createInsert}.
|
||||
@@ -485,7 +408,8 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
* @return the newly assigned primary key or null if the object does not use primary keys or
|
||||
* this is not the right time to assign the key.
|
||||
*/
|
||||
public Key assignPrimaryKey (Connection conn, Object po, boolean postFactum)
|
||||
public Key assignPrimaryKey (
|
||||
Connection conn, DatabaseLiaison liaison, Object po, boolean postFactum)
|
||||
throws SQLException
|
||||
{
|
||||
// if we have no primary key or no generator, then we're done
|
||||
@@ -499,7 +423,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
|
||||
try {
|
||||
int nextValue = _keyGenerator.nextGeneratedValue(conn);
|
||||
int nextValue = _keyGenerator.nextGeneratedValue(conn, liaison);
|
||||
_pkColumns.get(0).getField().set(po, nextValue);
|
||||
return makePrimaryKey(nextValue);
|
||||
} catch (Exception e) {
|
||||
@@ -508,131 +432,6 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a statement that will update the supplied persistent object using the supplied key.
|
||||
*/
|
||||
public PreparedStatement createUpdate (Connection conn, Object po, Where key)
|
||||
throws SQLException
|
||||
{
|
||||
return createUpdate(conn, po, key, _columnFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a statement that will update the supplied persistent object
|
||||
* using the supplied key.
|
||||
*/
|
||||
public PreparedStatement createUpdate (
|
||||
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;
|
||||
for (String field : modifiedFields) {
|
||||
if (idx++ > 0) {
|
||||
update.append(", ");
|
||||
}
|
||||
update.append(field).append(" = ?");
|
||||
}
|
||||
key.appendClause(null, update);
|
||||
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement(update.toString());
|
||||
idx = 0;
|
||||
// bind the update arguments
|
||||
for (String field : modifiedFields) {
|
||||
_fields.get(field).readFromObject(po, pstmt, ++idx);
|
||||
}
|
||||
// now bind the key arguments
|
||||
key.bindClauseArguments(pstmt, ++idx);
|
||||
return pstmt;
|
||||
|
||||
} catch (SQLException sqe) {
|
||||
// pass this on through
|
||||
throw sqe;
|
||||
|
||||
} catch (Exception e) {
|
||||
String errmsg = "Failed to marshall persistent object " +
|
||||
"[pclass=" + _pclass.getName() + "]";
|
||||
throw (SQLException)new SQLException(errmsg).initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a statement that will update the specified set of fields for all persistent objects
|
||||
* that match the supplied key.
|
||||
*/
|
||||
public PreparedStatement createPartialUpdate (
|
||||
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;
|
||||
for (String field : modifiedFields) {
|
||||
if (idx++ > 0) {
|
||||
update.append(", ");
|
||||
}
|
||||
update.append(field).append(" = ?");
|
||||
}
|
||||
key.appendClause(null, update);
|
||||
|
||||
PreparedStatement pstmt = conn.prepareStatement(update.toString());
|
||||
idx = 0;
|
||||
// bind the update arguments
|
||||
for (Object value : modifiedValues) {
|
||||
// TODO: use the field marshaller?
|
||||
pstmt.setObject(++idx, value);
|
||||
}
|
||||
// now bind the key arguments
|
||||
key.bindClauseArguments(pstmt, ++idx);
|
||||
return pstmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a statement that will delete all rows matching the supplied 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());
|
||||
key.bindClauseArguments(pstmt, 1);
|
||||
return pstmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a statement that will update the specified set of fields, using the supplied literal
|
||||
* SQL values, for all persistent objects that match the supplied key.
|
||||
*/
|
||||
public PreparedStatement createLiteralUpdate (
|
||||
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++) {
|
||||
if (ii > 0) {
|
||||
update.append(", ");
|
||||
}
|
||||
update.append(modifiedFields[ii]).append(" = ");
|
||||
update.append(modifiedValues[ii]);
|
||||
}
|
||||
key.appendClause(null, update);
|
||||
|
||||
PreparedStatement pstmt = conn.prepareStatement(update.toString());
|
||||
key.bindClauseArguments(pstmt, 1);
|
||||
return pstmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called by the persistence context to register a migration for the entity managed by
|
||||
@@ -652,47 +451,122 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
protected void init (PersistenceContext ctx)
|
||||
throws PersistenceException
|
||||
{
|
||||
SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.TRIVIAL);
|
||||
|
||||
if (_initialized) { // sanity check
|
||||
throw new IllegalStateException(
|
||||
"Cannot re-initialize marshaller [type=" + _pclass + "].");
|
||||
}
|
||||
_initialized = true;
|
||||
|
||||
// perform the context-sensitive initialization of the field marshallers
|
||||
for (FieldMarshaller fm : _fields.values()) {
|
||||
fm.init(builder);
|
||||
}
|
||||
|
||||
// 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];
|
||||
String[] declarations = new String[_allFields.length];
|
||||
int jj = 0;
|
||||
for (int ii = 0; ii < _allFields.length; ii++) {
|
||||
@SuppressWarnings("unchecked") FieldMarshaller<T> fm = _fields.get(_allFields[ii]);
|
||||
// include all persistent non-computed fields
|
||||
String colDef = fm.getColumnDefinition();
|
||||
if (colDef != null) {
|
||||
_columnFields[jj] = _allFields[ii];
|
||||
declarations[jj] = colDef;
|
||||
jj ++;
|
||||
}
|
||||
}
|
||||
_columnFields = ArrayUtil.splice(_columnFields, jj);
|
||||
declarations = ArrayUtil.splice(declarations, jj);
|
||||
|
||||
// if we have no table (i.e. we're a computed entity), we have nothing to create
|
||||
if (getTableName() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add any additional unique constraints
|
||||
String[][] uniqueConstraintColumns = null;
|
||||
Table table = _pclass.getAnnotation(Table.class);
|
||||
if (table != null) {
|
||||
UniqueConstraint[] uCons = table.uniqueConstraints();
|
||||
uniqueConstraintColumns = new String[uCons.length][];
|
||||
for (int kk = 0; kk < uCons.length; kk ++) {
|
||||
String[] columns = uCons[kk].columnNames();
|
||||
for (int ii = 0; ii < columns.length; ii ++) {
|
||||
FieldMarshaller fm = getFieldMarshaller(columns[ii]);
|
||||
if (fm == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown field in @UniqueConstraint: " + columns[ii]);
|
||||
}
|
||||
columns[ii] = fm.getColumnName();
|
||||
}
|
||||
uniqueConstraintColumns[kk] = columns;
|
||||
}
|
||||
}
|
||||
|
||||
// if we did not find a schema version field, complain
|
||||
if (_schemaVersion < 0) {
|
||||
log.warning("Unable to read " + _pclass.getName() + "." + SCHEMA_VERSION_FIELD +
|
||||
". Schema migration disabled.");
|
||||
}
|
||||
|
||||
// check to see if our schema version table exists, create it if not
|
||||
ctx.invoke(new Modifier() {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
JDBCUtil.createTableIfMissing(
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
liaison.createTableIfMissing(
|
||||
conn, SCHEMA_VERSION_TABLE,
|
||||
new String[] { "persistentClass VARCHAR(255) NOT NULL",
|
||||
"version INTEGER NOT NULL" }, "");
|
||||
new String[] { "persistentClass", "version" },
|
||||
new String[] { "VARCHAR(255) NOT NULL", "INTEGER NOT NULL" },
|
||||
null,
|
||||
new String[] { "persistentClass" });
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
// now create the table for our persistent class if it does not exist
|
||||
final String[] fDeclarations = declarations;
|
||||
final String[][] fUniqueConstraintColumns = uniqueConstraintColumns;
|
||||
ctx.invoke(new Modifier() {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
if (!JDBCUtil.tableExists(conn, getTableName())) {
|
||||
log.info("Creating table " + getTableName() + " (" + _declarations + ") " +
|
||||
_postamble);
|
||||
String[] definition = _declarations.toArray(new String[_declarations.size()]);
|
||||
JDBCUtil.createTableIfMissing(conn, getTableName(), definition, _postamble);
|
||||
updateVersion(conn, _schemaVersion);
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
String[] columns = new String[_columnFields.length];
|
||||
for (int ii = 0; ii < columns.length; ii ++) {
|
||||
columns[ii] = _fields.get(_columnFields[ii]).getColumnName();
|
||||
}
|
||||
String[] primaryKeyColumns = null;
|
||||
if (_pkColumns != null) {
|
||||
primaryKeyColumns = new String[_pkColumns.size()];
|
||||
for (int ii = 0; ii < primaryKeyColumns.length; ii ++) {
|
||||
primaryKeyColumns[ii] = _pkColumns.get(ii).getColumnName();
|
||||
}
|
||||
}
|
||||
liaison.createTableIfMissing(conn, getTableName(), columns, fDeclarations,
|
||||
fUniqueConstraintColumns, primaryKeyColumns);
|
||||
updateVersion(conn, liaison, _schemaVersion);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
// ensure that all indices are created
|
||||
final Entity entity = _pclass.getAnnotation(Entity.class);
|
||||
if (entity != null && entity.indices().length > 0) {
|
||||
ctx.invoke(new Modifier() {
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
for (Index idx : entity.indices()) {
|
||||
liaison.addIndexToTable(conn, getTableName(), idx.columns(), idx.name());
|
||||
}
|
||||
return entity.indices().length;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// if we have a key generator, initialize that too
|
||||
if (_keyGenerator != null) {
|
||||
ctx.invoke(new Modifier() {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
_keyGenerator.init(conn);
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
_keyGenerator.init(conn, liaison);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
@@ -706,9 +580,12 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
|
||||
// make sure the versions match
|
||||
int currentVersion = ctx.invoke(new Modifier() {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
String query = "select version from " + SCHEMA_VERSION_TABLE +
|
||||
" where persistentClass = '" + getTableName() + "'";
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
String query =
|
||||
" select " + liaison.columnSQL("version") +
|
||||
" from " + liaison.tableSQL(SCHEMA_VERSION_TABLE) +
|
||||
" where " + liaison.columnSQL("persistentClass") +
|
||||
" = '" + getTableName() + "'";
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery(query);
|
||||
@@ -723,6 +600,12 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
return;
|
||||
}
|
||||
|
||||
if (true) {
|
||||
log.warning("Automatic schema migration is temporarily disabled.");
|
||||
verifySchemasMatch(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise try to migrate the schema
|
||||
log.info("Migrating " + getTableName() + " from " +
|
||||
currentVersion + " to " + _schemaVersion + "...");
|
||||
@@ -736,35 +619,29 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
}
|
||||
|
||||
// enumerate all of the columns now that we've run our pre-migrations
|
||||
Set<String> columns = new HashSet<String>();
|
||||
Map<String, Set<String>> indexColumns = new HashMap<String, Set<String>>();
|
||||
loadMetaData(ctx, columns, indexColumns);
|
||||
// enumerate all columns and indexes now that we've run our pre-migrations
|
||||
TableMetaData metaData = loadMetaData(ctx);
|
||||
|
||||
// this is a little silly, but we need a copy for name disambiguation later
|
||||
Set<String> indicesCopy = new HashSet<String>(indexColumns.keySet());
|
||||
Set<String> indicesCopy = new HashSet<String>(metaData.indexColumns.keySet());
|
||||
|
||||
// add any missing columns
|
||||
for (String fname : _columnFields) {
|
||||
FieldMarshaller fmarsh = _fields.get(fname);
|
||||
if (columns.remove(fmarsh.getColumnName())) {
|
||||
@SuppressWarnings("unchecked") final FieldMarshaller<T> fmarsh = _fields.get(fname);
|
||||
if (metaData.tableColumns.remove(fmarsh.getColumnName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise add the column
|
||||
String coldef = fmarsh.getColumnDefinition();
|
||||
String query = "alter table " + getTableName() + " add column " + coldef;
|
||||
|
||||
// try to add it to the appropriate spot
|
||||
int fidx = ListUtil.indexOf(_allFields, fmarsh.getColumnName());
|
||||
if (fidx == 0) {
|
||||
query += " first";
|
||||
} else {
|
||||
query += " after " + _allFields[fidx-1];
|
||||
final String coldef = fmarsh.getColumnDefinition();
|
||||
log.info("Adding column to " + getTableName() + ": " +
|
||||
fmarsh.getColumnName() + " " + coldef);
|
||||
ctx.invoke(new Modifier.Simple() {
|
||||
protected String createQuery (DatabaseLiaison liaison) {
|
||||
return "alter table " + liaison.tableSQL(getTableName()) +
|
||||
" add column " + liaison.columnSQL(fmarsh.getColumnName()) + " " + coldef;
|
||||
}
|
||||
|
||||
log.info("Adding column to " + getTableName() + ": " + coldef);
|
||||
ctx.invoke(new Modifier.Simple(query));
|
||||
});
|
||||
|
||||
// if the column is a TIMESTAMP or DATETIME column, we need to run a special query to
|
||||
// update all existing rows to the current time because MySQL annoyingly assigns
|
||||
@@ -773,75 +650,84 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
// cannot accept CURRENT_TIME or NOW() defaults at all.
|
||||
if (coldef.toLowerCase().indexOf(" timestamp") != -1 ||
|
||||
coldef.toLowerCase().indexOf(" datetime") != -1) {
|
||||
query = "update " + getTableName() + " set " + fmarsh.getColumnName() + " = NOW()";
|
||||
log.info("Assigning current time to column: " + query);
|
||||
ctx.invoke(new Modifier.Simple(query));
|
||||
log.info("Assigning current time to " + fmarsh.getColumnName() + ".");
|
||||
ctx.invoke(new Modifier.Simple() {
|
||||
protected String createQuery (DatabaseLiaison liaison) {
|
||||
// TODO: is NOW() standard SQL?
|
||||
return "update " + liaison.tableSQL(getTableName()) +
|
||||
" set " + liaison.columnSQL(fmarsh.getColumnName()) + " = NOW()";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// add or remove the primary key as needed
|
||||
if (hasPrimaryKey() && !indexColumns.containsKey("PRIMARY")) {
|
||||
String pkdef = "primary key (" + getJoinedKeyColumns() + ")";
|
||||
log.info("Adding primary key to " + getTableName() + ": " + pkdef);
|
||||
ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " add " + pkdef));
|
||||
// TODO: This needs more work to be database-independent, I think. Assuming the index name
|
||||
// is going to be PRIMARY seems like a MySQL-ism, and we'll do the rest of the index work
|
||||
// when we do that.
|
||||
|
||||
} else if (!hasPrimaryKey() && indexColumns.containsKey("PRIMARY")) {
|
||||
indexColumns.remove("PRIMARY");
|
||||
log.info("Dropping primary from " + getTableName());
|
||||
ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " drop primary key"));
|
||||
}
|
||||
|
||||
// add any missing indices
|
||||
Entity entity = _pclass.getAnnotation(Entity.class);
|
||||
for (Index index : (entity == null ? new Index[0] : entity.indices())) {
|
||||
if (indexColumns.containsKey(index.name())) {
|
||||
indexColumns.remove(index.name());
|
||||
continue;
|
||||
}
|
||||
String indexdef = "create " + index.type() + " index " + index.name() +
|
||||
" on " + getTableName() + " (" + StringUtil.join(index.columns(), ", ") + ")";
|
||||
log.info("Adding index: " + indexdef);
|
||||
ctx.invoke(new Modifier.Simple(indexdef));
|
||||
}
|
||||
|
||||
// to get the @Table(uniqueIndices...) indices, we use our clever set of column name sets
|
||||
Set<Set<String>> uniqueIndices = new HashSet<Set<String>>(indexColumns.values());
|
||||
Table table;
|
||||
if (getTableName() != null && (table = _pclass.getAnnotation(Table.class)) != null) {
|
||||
Set<String> colSet = new HashSet<String>();
|
||||
for (UniqueConstraint constraint : table.uniqueConstraints()) {
|
||||
// for each given UniqueConstraint, build a new column set
|
||||
colSet.clear();
|
||||
for (String column : constraint.columnNames()) {
|
||||
colSet.add(column);
|
||||
}
|
||||
// and check if the table contained this set
|
||||
if (uniqueIndices.contains(colSet)) {
|
||||
continue; // good, carry on
|
||||
}
|
||||
|
||||
// else build the index; we'll use mysql's convention of naming it after a column,
|
||||
// with possible _N disambiguation; luckily we made a copy of the index names!
|
||||
String indexName = colSet.iterator().next();
|
||||
if (indicesCopy.contains(indexName)) {
|
||||
int num = 1;
|
||||
indexName += "_";
|
||||
while (indicesCopy.contains(indexName + num)) {
|
||||
num ++;
|
||||
}
|
||||
indexName += num;
|
||||
}
|
||||
|
||||
String[] columnArr = colSet.toArray(new String[colSet.size()]);
|
||||
String indexdef = "create unique index " + indexName + " on " +
|
||||
getTableName() + " (" + StringUtil.join(columnArr, ", ") + ")";
|
||||
log.info("Adding unique index: " + indexdef);
|
||||
ctx.invoke(new Modifier.Simple(indexdef));
|
||||
}
|
||||
}
|
||||
// // add or remove the primary key as needed
|
||||
// if (hasPrimaryKey() && !indexColumns.containsKey("PRIMARY")) {
|
||||
// String pkdef = "primary key (" + getJoinedKeyColumns() + ")";
|
||||
// log.info("Adding primary key to " + getTableName() + ": " + pkdef);
|
||||
// ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " add " + pkdef));
|
||||
//
|
||||
// } else if (!hasPrimaryKey() && indexColumns.containsKey("PRIMARY")) {
|
||||
// indexColumns.remove("PRIMARY");
|
||||
// log.info("Dropping primary from " + getTableName());
|
||||
// ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " drop primary key"));
|
||||
// }
|
||||
//
|
||||
// // add any missing indices
|
||||
// for (Index index : (entity == null ? new Index[0] : entity.indices())) {
|
||||
// if (indexColumns.containsKey(index.name())) {
|
||||
// indexColumns.remove(index.name());
|
||||
// continue;
|
||||
// }
|
||||
// String indexdef =
|
||||
// "create " + index.type() + " index " + _liaison.indexSQL(index.name()) +
|
||||
// " on " + _liaison.tableSQL(getTableName()) + " (" + StringUtil.join(index.columns(), ", ") + ")";
|
||||
// log.info("Adding index: " + indexdef);
|
||||
// ctx.invoke(new Modifier.Simple(indexdef));
|
||||
// }
|
||||
//
|
||||
// // to get the @Table(uniqueIndices...) indices, we use our clever set of column name sets
|
||||
// Set<Set<String>> uniqueIndices = new HashSet<Set<String>>(indexColumns.values());
|
||||
// Table table;
|
||||
// if (getTableName() != null && (table = _pclass.getAnnotation(Table.class)) != null) {
|
||||
// Set<String> colSet = new HashSet<String>();
|
||||
// for (UniqueConstraint constraint : table.uniqueConstraints()) {
|
||||
// // for each given UniqueConstraint, build a new column set
|
||||
// colSet.clear();
|
||||
// for (String column : constraint.columnNames()) {
|
||||
// colSet.add(column);
|
||||
// }
|
||||
// // and check if the table contained this set
|
||||
// if (uniqueIndices.contains(colSet)) {
|
||||
// continue; // good, carry on
|
||||
// }
|
||||
//
|
||||
// // else build the index; we'll use mysql's convention of naming it after a column,
|
||||
// // with possible _N disambiguation; luckily we made a copy of the index names!
|
||||
// String indexName = colSet.iterator().next();
|
||||
// if (indicesCopy.contains(indexName)) {
|
||||
// int num = 1;
|
||||
// indexName += "_";
|
||||
// while (indicesCopy.contains(indexName + num)) {
|
||||
// num ++;
|
||||
// }
|
||||
// indexName += num;
|
||||
// }
|
||||
//
|
||||
// String[] columnArr = colSet.toArray(new String[colSet.size()]);
|
||||
// String indexdef = "create unique index " + indexName + " on " +
|
||||
// getTableName() + " (" + StringUtil.join(columnArr, ", ") + ")";
|
||||
// log.info("Adding unique index: " + indexdef);
|
||||
// ctx.invoke(new Modifier.Simple(indexdef));
|
||||
// }
|
||||
// }
|
||||
|
||||
// we do not auto-remove columns but rather require that EntityMigration.Drop records be
|
||||
// registered by hand to avoid accidentally causin the loss of data
|
||||
// registered by hand to avoid accidentally causing the loss of data
|
||||
|
||||
// we don't auto-remove indices either because we'd have to sort out the potentially
|
||||
// complex origins of an index (which might be because of a @Unique column or maybe the
|
||||
@@ -858,29 +744,39 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
|
||||
// record our new version in the database
|
||||
ctx.invoke(new Modifier() {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
updateVersion(conn, _schemaVersion);
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
updateVersion(conn, liaison, _schemaVersion);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up the metadata for our database table: the names of our columns and indices.
|
||||
*/
|
||||
protected void loadMetaData (PersistenceContext ctx, final Set<String> columns,
|
||||
final Map<String, Set<String>> indexColumns)
|
||||
protected TableMetaData loadMetaData (PersistenceContext ctx)
|
||||
throws PersistenceException
|
||||
{
|
||||
ctx.invoke(new Modifier() {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
DatabaseMetaData meta = conn.getMetaData();
|
||||
ResultSet rs = meta.getColumns(null, null, getTableName(), "%");
|
||||
while (rs.next()) {
|
||||
columns.add(rs.getString("COLUMN_NAME"));
|
||||
return ctx.invoke(new Query.TrivialQuery<TableMetaData>() {
|
||||
public TableMetaData invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException {
|
||||
return new TableMetaData(conn.getMetaData());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected class TableMetaData
|
||||
{
|
||||
public Set<String> tableColumns = new HashSet<String>();
|
||||
public Map<String, Set<String>> indexColumns = new HashMap<String, Set<String>>();
|
||||
public String pkName;
|
||||
public Set<String> pkColumns = new HashSet<String>();
|
||||
|
||||
public TableMetaData (DatabaseMetaData meta)
|
||||
throws SQLException
|
||||
{
|
||||
ResultSet rs = meta.getColumns(null, null, getTableName(), "%");
|
||||
while (rs.next()) {
|
||||
tableColumns.add(rs.getString("COLUMN_NAME"));
|
||||
}
|
||||
|
||||
if (indexColumns != null) {
|
||||
rs = meta.getIndexInfo(null, null, getTableName(), false, false);
|
||||
while (rs.next()) {
|
||||
String indexName = rs.getString("INDEX_NAME");
|
||||
@@ -900,11 +796,13 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
set.add(rs.getString("COLUMN_NAME"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
rs = meta.getPrimaryKeys(null, null, getTableName());
|
||||
while (rs.next()) {
|
||||
pkName = rs.getString("PK_NAME");
|
||||
pkColumns.add(rs.getString("COLUMN_NAME"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -913,32 +811,29 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
protected void verifySchemasMatch (PersistenceContext ctx)
|
||||
throws PersistenceException
|
||||
{
|
||||
Set<String> columns = new HashSet<String>();
|
||||
loadMetaData(ctx, columns, null);
|
||||
TableMetaData meta = loadMetaData(ctx);
|
||||
for (String fname : _columnFields) {
|
||||
FieldMarshaller fmarsh = _fields.get(fname);
|
||||
columns.remove(fmarsh.getColumnName());
|
||||
meta.tableColumns.remove(fmarsh.getColumnName());
|
||||
}
|
||||
for (String column : columns) {
|
||||
for (String column : meta.tableColumns) {
|
||||
log.warning(getTableName() + " contains stale column '" + column + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
protected String getJoinedKeyColumns ()
|
||||
{
|
||||
return StringUtil.join(getPrimaryKeyColumns(), ", ");
|
||||
}
|
||||
|
||||
protected void updateVersion (Connection conn, int version)
|
||||
protected void updateVersion (Connection conn, DatabaseLiaison liaison, int version)
|
||||
throws SQLException
|
||||
{
|
||||
String update = "update " + SCHEMA_VERSION_TABLE +
|
||||
" set version = " + version + " where persistentClass = '" + getTableName() + "'";
|
||||
String insert = "insert into " + SCHEMA_VERSION_TABLE +
|
||||
" values('" + getTableName() + "', " + version + ")";
|
||||
String update =
|
||||
"update " + liaison.tableSQL(SCHEMA_VERSION_TABLE) +
|
||||
" set " + liaison.columnSQL("version") + " = " + version +
|
||||
" where " + liaison.columnSQL("persistentClass") + " = '" + getTableName() + "'";
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
if (stmt.executeUpdate(update) == 0) {
|
||||
String insert =
|
||||
"insert into " + liaison.tableSQL(SCHEMA_VERSION_TABLE) +
|
||||
" values('" + getTableName() + "', " + version + ")";
|
||||
stmt.executeUpdate(insert);
|
||||
}
|
||||
} finally {
|
||||
@@ -946,15 +841,6 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
}
|
||||
}
|
||||
|
||||
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<T> _pclass;
|
||||
|
||||
@@ -962,10 +848,10 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
protected String _tableName;
|
||||
|
||||
/** A field marshaller for each persistent field in our object. */
|
||||
protected HashMap<String, FieldMarshaller> _fields = new HashMap<String, FieldMarshaller>();
|
||||
protected Map<String, FieldMarshaller> _fields = new HashMap<String, FieldMarshaller>();
|
||||
|
||||
/** The field marshallers for our persistent object's primary key columns
|
||||
* or null if it did not define a primary key. */
|
||||
/** The field marshallers for our persistent object's primary key columns or null if it did not
|
||||
* define a primary key. */
|
||||
protected ArrayList<FieldMarshaller> _pkColumns;
|
||||
|
||||
/** The generator to use for auto-generating primary key values, or null. */
|
||||
@@ -977,16 +863,9 @@ public class DepotMarshaller<T extends PersistentRecord>
|
||||
/** The fields of our object with directly corresponding table columns. */
|
||||
protected String[] _columnFields;
|
||||
|
||||
/** The version of our persistent object schema as specified in the class
|
||||
* definition. */
|
||||
/** The version of our persistent object schema as specified in the class definition. */
|
||||
protected int _schemaVersion = -1;
|
||||
|
||||
/** Used when creating and migrating our table schema. */
|
||||
protected ArrayList<String> _declarations = new ArrayList<String>();
|
||||
|
||||
/** Used when creating and migrating our table schema. */
|
||||
protected String _postamble = "";
|
||||
|
||||
/** Indicates that we have been initialized (created or migrated our tables). */
|
||||
protected boolean _initialized;
|
||||
|
||||
|
||||
@@ -29,15 +29,22 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
|
||||
import com.samskivert.jdbc.ConnectionProvider;
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
|
||||
import com.samskivert.jdbc.depot.Modifier.*;
|
||||
import com.samskivert.jdbc.depot.clause.DeleteClause;
|
||||
import com.samskivert.jdbc.depot.clause.FieldOverride;
|
||||
import com.samskivert.jdbc.depot.clause.FromOverride;
|
||||
import com.samskivert.jdbc.depot.clause.InsertClause;
|
||||
import com.samskivert.jdbc.depot.clause.Join;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.jdbc.depot.clause.UpdateClause;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.expression.ValueExp;
|
||||
|
||||
/**
|
||||
* Provides a base for classes that provide access to persistent objects. Also defines the
|
||||
@@ -163,18 +170,36 @@ public class DepotRepository
|
||||
protected <T extends PersistentRecord> int insert (T record)
|
||||
throws PersistenceException
|
||||
{
|
||||
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
|
||||
final Key key = marsh.getPrimaryKey(record, false);
|
||||
@SuppressWarnings("unchecked") final Class<T> pClass = (Class<T>) record.getClass();
|
||||
final DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||
Key<T> key = marsh.getPrimaryKey(record, false);
|
||||
|
||||
DepotTypes types = DepotTypes.getDepotTypes(_ctx);
|
||||
types.addClass(_ctx, pClass);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(types);
|
||||
|
||||
// key will be null if record was supplied without a primary key
|
||||
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
// update our modifier's key so that it can cache our results
|
||||
updateKey(marsh.assignPrimaryKey(conn, _result, false));
|
||||
PreparedStatement stmt = marsh.createInsert(conn, _result);
|
||||
if (_key == null) {
|
||||
updateKey(marsh.assignPrimaryKey(conn, liaison, _result, false));
|
||||
}
|
||||
|
||||
String ixField = null;
|
||||
if (_key == null && marsh.hasPrimaryKey() && marsh.getKeyGenerator() != null &&
|
||||
marsh.getKeyGenerator() instanceof IdentityKeyGenerator) {
|
||||
ixField = ((IdentityKeyGenerator) marsh.getKeyGenerator()).getColumn();
|
||||
}
|
||||
builder.newQuery(new InsertClause<T>(pClass, _result, ixField));
|
||||
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
int mods = stmt.executeUpdate();
|
||||
// check again in case we have a post-factum key generator
|
||||
updateKey(marsh.assignPrimaryKey(conn, _result, true));
|
||||
if (_key == null) {
|
||||
updateKey(marsh.assignPrimaryKey(conn, liaison, _result, true));
|
||||
}
|
||||
return mods;
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
@@ -192,14 +217,22 @@ public class DepotRepository
|
||||
protected <T extends PersistentRecord> int update (T record)
|
||||
throws PersistenceException
|
||||
{
|
||||
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
|
||||
final Key key = marsh.getPrimaryKey(record);
|
||||
@SuppressWarnings("unchecked") Class<T> pClass = (Class<T>) record.getClass();
|
||||
requireNotComputed(pClass, "update");
|
||||
|
||||
DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||
Key key = marsh.getPrimaryKey(record);
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("Can't update record with null primary key.");
|
||||
}
|
||||
|
||||
UpdateClause<T> update = new UpdateClause<T>(pClass, key, marsh._columnFields, record);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, update));
|
||||
builder.newQuery(update);
|
||||
|
||||
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
PreparedStatement stmt = marsh.createUpdate(conn, _result, key);
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} finally {
|
||||
@@ -220,14 +253,26 @@ public class DepotRepository
|
||||
protected <T extends PersistentRecord> int update (T record, final String... modifiedFields)
|
||||
throws PersistenceException
|
||||
{
|
||||
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
|
||||
final Key key = marsh.getPrimaryKey(record);
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<T> pClass = (Class<T>) record.getClass();
|
||||
|
||||
requireNotComputed(pClass, "updatePartial");
|
||||
|
||||
DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||
Key key = marsh.getPrimaryKey(record);
|
||||
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("Can't update record with null primary key.");
|
||||
}
|
||||
|
||||
UpdateClause<T> update = new UpdateClause<T>(pClass, key, modifiedFields, record);
|
||||
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, update));
|
||||
builder.newQuery(update);
|
||||
|
||||
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
PreparedStatement stmt = marsh.createUpdate(conn, _result, key, modifiedFields);
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
// clear out _result so that we don't rewrite this partial record to the cache
|
||||
_result = null;
|
||||
try {
|
||||
@@ -336,21 +381,23 @@ public class DepotRepository
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updatePartial (
|
||||
Class<T> type, final Where key, CacheInvalidator invalidator, Object... fieldsValues)
|
||||
Class<T> type, final WhereClause key, CacheInvalidator invalidator, Object... fieldsValues)
|
||||
throws PersistenceException
|
||||
{
|
||||
// separate the arguments into keys and values
|
||||
final String[] fields = new String[fieldsValues.length/2];
|
||||
final Object[] values = new Object[fields.length];
|
||||
final SQLExpression[] values = new SQLExpression[fields.length];
|
||||
for (int ii = 0, idx = 0; ii < fields.length; ii++) {
|
||||
fields[ii] = (String)fieldsValues[idx++];
|
||||
values[ii] = fieldsValues[idx++];
|
||||
values[ii] = new ValueExp(fieldsValues[idx++]);
|
||||
}
|
||||
UpdateClause<T> update = new UpdateClause<T>(type, key, fields, values);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, update));
|
||||
builder.newQuery(update);
|
||||
|
||||
final DepotMarshaller marsh = _ctx.getMarshaller(type);
|
||||
return _ctx.invoke(new Modifier(invalidator) {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
PreparedStatement stmt = marsh.createPartialUpdate(conn, key, fields, values);
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} finally {
|
||||
@@ -378,11 +425,11 @@ public class DepotRepository
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updateLiteral (
|
||||
Class<T> type, Comparable primaryKey, String... fieldsValues)
|
||||
Class<T> type, Comparable primaryKey, Map<String, SQLExpression> fieldsToValues)
|
||||
throws PersistenceException
|
||||
{
|
||||
Key<T> key = _ctx.getMarshaller(type).makePrimaryKey(primaryKey);
|
||||
return updateLiteral(type, key, key, fieldsValues);
|
||||
return updateLiteral(type, key, key, fieldsToValues);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,11 +451,11 @@ public class DepotRepository
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updateLiteral (
|
||||
Class<T> type, String ix1, Comparable val1, String ix2, Comparable val2,
|
||||
String... fieldsValues)
|
||||
Map<String, SQLExpression> fieldsToValues)
|
||||
throws PersistenceException
|
||||
{
|
||||
Key<T> key = new Key<T>(type, ix1, val1, ix2, val2);
|
||||
return updateLiteral(type, key, key, fieldsValues);
|
||||
return updateLiteral(type, key, key, fieldsToValues);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,11 +477,11 @@ public class DepotRepository
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updateLiteral (
|
||||
Class<T> type, String ix1, Comparable val1, String ix2, Comparable val2,
|
||||
String ix3, Comparable val3, String... fieldsValues)
|
||||
String ix3, Comparable val3, Map<String, SQLExpression> fieldsToValues)
|
||||
throws PersistenceException
|
||||
{
|
||||
Key<T> key = new Key<T>(type, ix1, val1, ix2, val2, ix3, val3);
|
||||
return updateLiteral(type, key, key, fieldsValues);
|
||||
return updateLiteral(type, key, key, fieldsToValues);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -455,21 +502,29 @@ public class DepotRepository
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updateLiteral (
|
||||
Class<T> type, final Where key, CacheInvalidator invalidator, String... fieldsValues)
|
||||
Class<T> type, final WhereClause key, CacheInvalidator invalidator,
|
||||
Map<String, SQLExpression> fieldsToValues)
|
||||
throws PersistenceException
|
||||
{
|
||||
requireNotComputed(type, "updateLiteral");
|
||||
|
||||
// separate the arguments into keys and values
|
||||
final String[] fields = new String[fieldsValues.length/2];
|
||||
final String[] values = new String[fields.length];
|
||||
for (int ii = 0, idx = 0; ii < fields.length; ii++) {
|
||||
fields[ii] = fieldsValues[idx++];
|
||||
values[ii] = fieldsValues[idx++];
|
||||
final String[] fields = new String[fieldsToValues.size()];
|
||||
final SQLExpression[] values = new SQLExpression[fields.length];
|
||||
int ii = 0;
|
||||
for (Map.Entry<String, SQLExpression> entry : fieldsToValues.entrySet()) {
|
||||
fields[ii] = entry.getKey();
|
||||
values[ii] = entry.getValue();
|
||||
ii ++;
|
||||
}
|
||||
|
||||
final DepotMarshaller marsh = _ctx.getMarshaller(type);
|
||||
UpdateClause<T> update = new UpdateClause<T>(type, key, fields, values);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, update));
|
||||
builder.newQuery(update);
|
||||
|
||||
return _ctx.invoke(new Modifier(invalidator) {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
PreparedStatement stmt = marsh.createLiteralUpdate(conn, key, fields, values);
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} finally {
|
||||
@@ -489,17 +544,25 @@ public class DepotRepository
|
||||
protected <T extends PersistentRecord> boolean store (T record)
|
||||
throws PersistenceException
|
||||
{
|
||||
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
|
||||
final Key key = marsh.hasPrimaryKey() ? marsh.getPrimaryKey(record) : null;
|
||||
@SuppressWarnings("unchecked") final Class<T> pClass = (Class<T>) record.getClass();
|
||||
requireNotComputed(pClass, "store");
|
||||
|
||||
final DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||
Key<T> key = marsh.hasPrimaryKey() ? marsh.getPrimaryKey(record) : null;
|
||||
final UpdateClause<T> update =
|
||||
new UpdateClause<T>(pClass, key, marsh._columnFields, record);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, update));
|
||||
builder.newQuery(update);
|
||||
|
||||
final boolean[] created = new boolean[1];
|
||||
_ctx.invoke(new CachingModifier<T>(record, key, key) {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
// if our primary key isn't null, update rather than insert the record
|
||||
// before been persisted and insert
|
||||
if (key != null) {
|
||||
stmt = marsh.createUpdate(conn, _result, key);
|
||||
// if our primary key isn't null, update rather than insert the record before
|
||||
// been persisted and insert
|
||||
if (_key != null) {
|
||||
stmt = builder.prepare(conn);
|
||||
int mods = stmt.executeUpdate();
|
||||
if (mods > 0) {
|
||||
return mods;
|
||||
@@ -509,10 +572,22 @@ public class DepotRepository
|
||||
|
||||
// if the update modified zero rows or the primary key was obviously unset, do
|
||||
// an insertion
|
||||
updateKey(marsh.assignPrimaryKey(conn, _result, false));
|
||||
stmt = marsh.createInsert(conn, _result);
|
||||
if (_key == null) {
|
||||
updateKey(marsh.assignPrimaryKey(conn, liaison, _result, false));
|
||||
}
|
||||
|
||||
String ixField = null;
|
||||
if (_key == null && marsh.hasPrimaryKey() && marsh.getKeyGenerator() != null &&
|
||||
marsh.getKeyGenerator() instanceof IdentityKeyGenerator) {
|
||||
ixField = ((IdentityKeyGenerator) marsh.getKeyGenerator()).getColumn();
|
||||
}
|
||||
builder.newQuery(new InsertClause<T>(pClass, _result, ixField));
|
||||
|
||||
stmt = builder.prepare(conn);
|
||||
int mods = stmt.executeUpdate();
|
||||
updateKey(marsh.assignPrimaryKey(conn, _result, true));
|
||||
if (_key == null) {
|
||||
updateKey(marsh.assignPrimaryKey(conn, liaison, _result, true));
|
||||
}
|
||||
created[0] = true;
|
||||
return mods;
|
||||
|
||||
@@ -571,13 +646,16 @@ public class DepotRepository
|
||||
* @return the number of rows deleted by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int deleteAll (
|
||||
Class<T> type, final Where key, CacheInvalidator invalidator)
|
||||
Class<T> type, final WhereClause key, CacheInvalidator invalidator)
|
||||
throws PersistenceException
|
||||
{
|
||||
final DepotMarshaller marsh = _ctx.getMarshaller(type);
|
||||
DeleteClause<T> delete = new DeleteClause<T>(type, key);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, delete));
|
||||
builder.newQuery(delete);
|
||||
|
||||
return _ctx.invoke(new Modifier(invalidator) {
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
PreparedStatement stmt = marsh.createDelete(conn, key);
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} finally {
|
||||
@@ -587,25 +665,18 @@ public class DepotRepository
|
||||
});
|
||||
}
|
||||
|
||||
protected static abstract class CollectionQuery<T extends Collection> implements Query<T>
|
||||
{
|
||||
public CollectionQuery (CacheKey key)
|
||||
// make sure the given type corresponds to a concrete class
|
||||
protected void requireNotComputed (Class<? extends PersistentRecord> type, String action)
|
||||
throws PersistenceException
|
||||
{
|
||||
_key = key;
|
||||
DepotMarshaller marsh = _ctx.getMarshaller(type);
|
||||
if (marsh == null) {
|
||||
throw new PersistenceException("Unknown persistent type [class=" + type + "]");
|
||||
}
|
||||
|
||||
public CacheKey getCacheKey ()
|
||||
{
|
||||
return _key;
|
||||
if (marsh.getTableName() == null) {
|
||||
throw new PersistenceException(
|
||||
"Can't " + action + " computed entities [class=" + type + "]");
|
||||
}
|
||||
|
||||
public void updateCache (PersistenceContext ctx, T result)
|
||||
{
|
||||
ctx.cacheStore(_key, result);
|
||||
}
|
||||
|
||||
protected CacheKey _key;
|
||||
}
|
||||
|
||||
protected PersistenceContext _ctx;
|
||||
|
||||
@@ -20,52 +20,60 @@
|
||||
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Primarily an implementation of {@link QueryBuilderContext}, this class holds the persistent
|
||||
* classes used in the context of a single query and maps them to {@link DepotMarshaller}
|
||||
* instances as well as table names and table abbreviations.
|
||||
* Maintains a record of the persistent classes brought into the context of the associated SQL,
|
||||
* i.e. any class associated with a concrete table that would appear in FROM or JOIN clauses or as
|
||||
* the target of an UPDATE or an INSERT or any other place where a table abbreviation could be
|
||||
* constructed.
|
||||
*
|
||||
* The main motivation for breaking this functionality out into its own class is to encapsulate
|
||||
* the operation that throws {@link PersistenceException} as separate from the operations that
|
||||
* throw {@link SQLException}.
|
||||
* The main motivation for breaking this functionality out into its own class is to encapsulate the
|
||||
* operation that throws {@link PersistenceException} as separate from the operations that throw
|
||||
* {@link SQLException}. Once this class has been constructed, it may be used to create {@link
|
||||
* SQLBuilder} instances without any {@link PersistenceException} worries.
|
||||
*/
|
||||
public class DepotTypes<T>
|
||||
implements QueryBuilderContext<T>
|
||||
public class DepotTypes
|
||||
{
|
||||
public DepotTypes (PersistenceContext ctx,
|
||||
Class<? extends PersistentRecord> main,
|
||||
Set<Class<? extends PersistentRecord>> others)
|
||||
/** A trivial instance that is accessible in places where we want the dialectal benefits of the
|
||||
* SQLBuilder without really requiring per-persistent-class context. */
|
||||
public static DepotTypes TRIVIAL = new DepotTypes();
|
||||
|
||||
/**
|
||||
* Conveniently constructs a {@link DepotTypes} object given {@link QueryClause} objects, which
|
||||
* are interrogated for their class definition sets through {@link SQLExpression#addClasses}.
|
||||
*/
|
||||
public static <T extends PersistentRecord> DepotTypes getDepotTypes (
|
||||
PersistenceContext ctx, QueryClause... clauses)
|
||||
throws PersistenceException
|
||||
{
|
||||
_mainType = main;
|
||||
_classMap = new HashMap<Class, DepotMarshaller>();
|
||||
|
||||
for (Class<? extends PersistentRecord> c : others) {
|
||||
_classMap.put(c, ctx.getMarshaller(c));
|
||||
Set<Class<? extends PersistentRecord>> classSet =
|
||||
new HashSet<Class<? extends PersistentRecord>>();
|
||||
for (QueryClause clause : clauses) {
|
||||
if (clause != null) {
|
||||
clause.addClasses(classSet);
|
||||
}
|
||||
_classList = new ArrayList<Class<? extends PersistentRecord>>(others);
|
||||
}
|
||||
return new DepotTypes(ctx, classSet);
|
||||
}
|
||||
|
||||
public Class<? extends PersistentRecord> getMainType ()
|
||||
{
|
||||
return _mainType;
|
||||
}
|
||||
|
||||
// from ConstructedQuery
|
||||
public String getTableName (Class<? extends PersistentRecord> cl)
|
||||
{
|
||||
return _classMap.get(cl).getTableName();
|
||||
}
|
||||
|
||||
// from ConstructedQuery
|
||||
public String getTableAbbreviation (Class<? extends PersistentRecord> cl)
|
||||
{
|
||||
int ix = _classList.indexOf(cl);
|
||||
@@ -75,32 +83,42 @@ public class DepotTypes<T>
|
||||
return "T" + (ix+1);
|
||||
}
|
||||
|
||||
public String getColumnName (Class<? extends PersistentRecord> cl, String field)
|
||||
{
|
||||
return getMarshaller(cl).getFieldMarshaller(field).getColumnName();
|
||||
}
|
||||
|
||||
public DepotMarshaller getMarshaller (Class cl)
|
||||
{
|
||||
return _classMap.get(cl);
|
||||
}
|
||||
|
||||
public String getMainTableName ()
|
||||
public void addClass (PersistenceContext ctx, Class <? extends PersistentRecord> type)
|
||||
throws PersistenceException
|
||||
{
|
||||
return getTableName(_mainType);
|
||||
if (_classMap.containsKey(type)) {
|
||||
return;
|
||||
}
|
||||
_classList.add(type);
|
||||
_classMap.put(type, ctx.getMarshaller(type));
|
||||
}
|
||||
|
||||
public DepotMarshaller getMainMarshaller ()
|
||||
protected DepotTypes (PersistenceContext ctx, Set<Class<? extends PersistentRecord>> others)
|
||||
throws PersistenceException
|
||||
{
|
||||
return getMarshaller(_mainType);
|
||||
for (Class<? extends PersistentRecord> c : others) {
|
||||
addClass(ctx, c);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMainTableAbbreviation ()
|
||||
protected DepotTypes ()
|
||||
{
|
||||
return getTableAbbreviation(_mainType);
|
||||
}
|
||||
|
||||
/** The persistent class to instantiate for the results. */
|
||||
protected Class<? extends PersistentRecord> _mainType;
|
||||
|
||||
/** A list of referenced classes, used to generate table abbreviations. */
|
||||
protected List<Class<? extends PersistentRecord>> _classList;
|
||||
protected List<Class<? extends PersistentRecord>> _classList =
|
||||
new ArrayList<Class<? extends PersistentRecord>>();
|
||||
|
||||
/** Classes mapped to marshallers, used for table names and field lists. */
|
||||
protected Map<Class, DepotMarshaller> _classMap;
|
||||
protected Map<Class, DepotMarshaller> _classMap = new HashMap<Class, DepotMarshaller>();
|
||||
}
|
||||
|
||||
@@ -22,10 +22,11 @@ package com.samskivert.jdbc.depot;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
import com.samskivert.jdbc.LiaisonRegistry;
|
||||
|
||||
import static com.samskivert.jdbc.depot.Log.log;
|
||||
|
||||
@@ -47,21 +48,15 @@ public abstract class EntityMigration extends Modifier
|
||||
_columnName = columnName;
|
||||
}
|
||||
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
if (!JDBCUtil.tableContainsColumn(conn, _tableName, _columnName)) {
|
||||
// we'll accept this inconsistency
|
||||
log.warning(_tableName + "." + _columnName + " already dropped.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
log.info("Dropping '" + _columnName + "' from " + _tableName);
|
||||
return stmt.executeUpdate(
|
||||
"alter table " + _tableName + " drop column " + _columnName);
|
||||
} finally {
|
||||
stmt.close();
|
||||
}
|
||||
return liaison.dropColumn(conn, _tableName, _columnName) ? 1 : 0;
|
||||
}
|
||||
|
||||
protected String _columnName;
|
||||
@@ -78,7 +73,7 @@ public abstract class EntityMigration extends Modifier
|
||||
_newColumnName = newColumnName;
|
||||
}
|
||||
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
if (!JDBCUtil.tableContainsColumn(conn, _tableName, _oldColumnName)) {
|
||||
if (JDBCUtil.tableContainsColumn(conn, _tableName, _newColumnName)) {
|
||||
// we'll accept this inconsistency
|
||||
@@ -97,60 +92,46 @@ public abstract class EntityMigration extends Modifier
|
||||
_tableName + " already contains '" + _newColumnName + "'");
|
||||
}
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
log.info("Changing '" + _oldColumnName + "' to '" + _newColumnDef + "' in " +
|
||||
log.info("Renaming '" + _oldColumnName + "' to '" + _newColumnName + "' in: " +
|
||||
_tableName);
|
||||
return stmt.executeUpdate("alter table " + _tableName + " change column " +
|
||||
_oldColumnName + " " + _newColumnDef);
|
||||
} finally {
|
||||
stmt.close();
|
||||
}
|
||||
return liaison.renameColumn(conn, _tableName, _oldColumnName, _newColumnName) ? 1 : 0;
|
||||
}
|
||||
|
||||
public boolean runBeforeDefault () {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void init (String tableName, HashMap<String,FieldMarshaller> marshallers) {
|
||||
super.init(tableName, marshallers);
|
||||
_newColumnDef = marshallers.get(_newColumnName).getColumnDefinition();
|
||||
}
|
||||
|
||||
protected String _oldColumnName, _newColumnName, _newColumnDef;
|
||||
protected String _oldColumnName, _newColumnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient migration for changing the type of an existing column.
|
||||
* A convenient migration for changing the type of an existing field. NOTE: This object is
|
||||
* instantiated with the name of a persistent field, not the name of a database column. These
|
||||
* can be very different things for classes that use @Column annotations.
|
||||
*/
|
||||
public static class Retype extends EntityMigration
|
||||
{
|
||||
public Retype (int targetVersion, String columnName) {
|
||||
public Retype (int targetVersion, String fieldName) {
|
||||
super(targetVersion);
|
||||
_columnName = columnName;
|
||||
_fieldName = fieldName;
|
||||
}
|
||||
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
log.info("Updating type of '" + _columnName + "' in " + _tableName);
|
||||
return stmt.executeUpdate("alter table " + _tableName + " change column " +
|
||||
_columnName + " " + _newColumnDef);
|
||||
} finally {
|
||||
stmt.close();
|
||||
}
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
log.info("Updating type of '" + _fieldName + "' in " + _tableName);
|
||||
return liaison.changeColumn(conn, _tableName, _fieldName, _newColumnDef) ? 1 : 0;
|
||||
}
|
||||
|
||||
public boolean runBeforeDefault () {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void init (String tableName, HashMap<String,FieldMarshaller> marshallers) {
|
||||
protected void init (String tableName, Map<String,FieldMarshaller> marshallers) {
|
||||
super.init(tableName, marshallers);
|
||||
_newColumnDef = marshallers.get(_columnName).getColumnDefinition();
|
||||
_columnName = marshallers.get(_fieldName).getColumnName();
|
||||
_newColumnDef = marshallers.get(_fieldName).getColumnDefinition();
|
||||
}
|
||||
|
||||
protected String _columnName, _newColumnDef;
|
||||
protected String _fieldName, _columnName, _newColumnDef;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,7 +165,7 @@ public abstract class EntityMigration extends Modifier
|
||||
* migration has been determined to be runnable so one cannot rely on this method having been
|
||||
* called in {@link #shouldRunMigration}.
|
||||
*/
|
||||
protected void init (String tableName, HashMap<String,FieldMarshaller> marshallers)
|
||||
protected void init (String tableName, Map<String,FieldMarshaller> marshallers)
|
||||
{
|
||||
_tableName = tableName;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.sql.SQLException;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.jdbc.depot.annotation.Column;
|
||||
import com.samskivert.jdbc.depot.annotation.Computed;
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
@@ -106,10 +107,20 @@ public abstract class FieldMarshaller<T>
|
||||
"Cannot marshall field of type '" + ftype.getName() + "'.");
|
||||
}
|
||||
|
||||
marshaller.init(field);
|
||||
marshaller.create(field);
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this field marshaller with a SQL builder which it uses to construct its column
|
||||
* definition according to the appropriate database dialect.
|
||||
*/
|
||||
public void init (SQLBuilder builder)
|
||||
throws PersistenceException
|
||||
{
|
||||
_columnDefinition = builder.buildColumnDefinition(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Field} handled by this marshaller.
|
||||
*/
|
||||
@@ -194,98 +205,30 @@ public abstract class FieldMarshaller<T>
|
||||
writeToObject(po, getFromSet(rset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type used in the SQL column definition for this field.
|
||||
*/
|
||||
public abstract String getColumnType ();
|
||||
|
||||
protected void init (Field field)
|
||||
protected void create (Field field)
|
||||
{
|
||||
_field = field;
|
||||
_columnName = field.getName();
|
||||
|
||||
// read our column metadata from the annotation (if it exists); annoyingly we can't create
|
||||
// a Column instance to read the defaults so we have to duplicate them here
|
||||
int length = 255;
|
||||
boolean nullable = false;
|
||||
boolean unique = false;
|
||||
String defval = "";
|
||||
Column column = _field.getAnnotation(Column.class);
|
||||
if (column != null) {
|
||||
nullable = column.nullable();
|
||||
unique = column.unique();
|
||||
length = column.length();
|
||||
defval = column.defaultValue();
|
||||
if (!StringUtil.isBlank(column.name())) {
|
||||
_columnName = column.name();
|
||||
}
|
||||
}
|
||||
|
||||
_computed = field.getAnnotation(Computed.class);
|
||||
// if this field is @Computed, it has no SQL definition
|
||||
if (_computed != null) {
|
||||
_columnDefinition = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// create our SQL column definition
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (column != null && !StringUtil.isBlank(column.columnDefinition())) {
|
||||
builder.append(column.columnDefinition());
|
||||
|
||||
} else {
|
||||
builder.append(getColumnName());
|
||||
String type = getColumnType();
|
||||
builder.append(" ").append(type);
|
||||
|
||||
// if this is a VARCHAR field, add the length
|
||||
if (type.equals("VARCHAR") || type.equals("VARBINARY")) {
|
||||
builder.append("(").append(length).append(")");
|
||||
}
|
||||
|
||||
// TODO: handle precision and scale
|
||||
|
||||
// handle nullability and uniqueness
|
||||
if (!nullable) {
|
||||
builder.append(" NOT NULL");
|
||||
}
|
||||
if (unique) {
|
||||
builder.append(" UNIQUE");
|
||||
}
|
||||
|
||||
// append the default value if one was specified
|
||||
if (defval.length() > 0) {
|
||||
builder.append(" DEFAULT ").append(defval);
|
||||
}
|
||||
}
|
||||
|
||||
// handle primary keyness
|
||||
if (field.getAnnotation(Id.class) != null) {
|
||||
// figure out how we're going to generate our primary key values
|
||||
_generatedValue = field.getAnnotation(GeneratedValue.class);
|
||||
if (_generatedValue != null) {
|
||||
switch (_generatedValue.strategy()) {
|
||||
case AUTO:
|
||||
case IDENTITY:
|
||||
builder.append(" AUTO_INCREMENT");
|
||||
break;
|
||||
case SEQUENCE: // TODO
|
||||
throw new IllegalArgumentException(
|
||||
"SEQUENCE key generation strategy not yet supported.");
|
||||
case TABLE:
|
||||
// nothing to do here, it'll be handled later
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_columnDefinition = builder.toString();
|
||||
}
|
||||
|
||||
protected static class BooleanMarshaller extends FieldMarshaller<Boolean> {
|
||||
public String getColumnType () {
|
||||
return "TINYINT";
|
||||
}
|
||||
public Boolean getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getBoolean(po);
|
||||
@@ -305,9 +248,6 @@ public abstract class FieldMarshaller<T>
|
||||
}
|
||||
|
||||
protected static class ByteMarshaller extends FieldMarshaller<Byte> {
|
||||
public String getColumnType () {
|
||||
return "TINYINT";
|
||||
}
|
||||
public Byte getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getByte(po);
|
||||
@@ -327,9 +267,6 @@ public abstract class FieldMarshaller<T>
|
||||
}
|
||||
|
||||
protected static class ShortMarshaller extends FieldMarshaller<Short> {
|
||||
public String getColumnType () {
|
||||
return "SMALLINT";
|
||||
}
|
||||
public Short getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getShort(po);
|
||||
@@ -349,9 +286,6 @@ public abstract class FieldMarshaller<T>
|
||||
}
|
||||
|
||||
protected static class IntMarshaller extends FieldMarshaller<Integer> {
|
||||
public String getColumnType () {
|
||||
return "INTEGER";
|
||||
}
|
||||
public Integer getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getInt(po);
|
||||
@@ -371,9 +305,6 @@ public abstract class FieldMarshaller<T>
|
||||
}
|
||||
|
||||
protected static class LongMarshaller extends FieldMarshaller<Long> {
|
||||
public String getColumnType () {
|
||||
return "BIGINT";
|
||||
}
|
||||
public Long getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getLong(po);
|
||||
@@ -393,9 +324,6 @@ public abstract class FieldMarshaller<T>
|
||||
}
|
||||
|
||||
protected static class FloatMarshaller extends FieldMarshaller<Float> {
|
||||
public String getColumnType () {
|
||||
return "FLOAT";
|
||||
}
|
||||
public Float getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getFloat(po);
|
||||
@@ -415,9 +343,6 @@ public abstract class FieldMarshaller<T>
|
||||
}
|
||||
|
||||
protected static class DoubleMarshaller extends FieldMarshaller<Double> {
|
||||
public String getColumnType () {
|
||||
return "DOUBLE";
|
||||
}
|
||||
public Double getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getDouble(po);
|
||||
@@ -437,37 +362,6 @@ public abstract class FieldMarshaller<T>
|
||||
}
|
||||
|
||||
protected static class ObjectMarshaller extends FieldMarshaller<Object> {
|
||||
public String getColumnType () {
|
||||
Class<?> ftype = _field.getType();
|
||||
if (ftype.equals(Byte.class)) {
|
||||
return "TINYINT";
|
||||
} else if (ftype.equals(Short.class)) {
|
||||
return "SMALLINT";
|
||||
} else if (ftype.equals(Integer.class)) {
|
||||
return "INTEGER";
|
||||
} else if (ftype.equals(Long.class)) {
|
||||
return "BIGINT";
|
||||
} else if (ftype.equals(Float.class)) {
|
||||
return "FLOAT";
|
||||
} else if (ftype.equals(Double.class)) {
|
||||
return "DOUBLE";
|
||||
} else if (ftype.equals(String.class)) {
|
||||
return "VARCHAR";
|
||||
} else if (ftype.equals(Date.class)) {
|
||||
return "DATE";
|
||||
} else if (ftype.equals(Time.class)) {
|
||||
return "DATETIME";
|
||||
} else if (ftype.equals(Timestamp.class)) {
|
||||
return "TIMESTAMP";
|
||||
} else if (ftype.equals(Blob.class)) {
|
||||
return "BLOB";
|
||||
} else if (ftype.equals(Clob.class)) {
|
||||
return "CLOB";
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Don't know how to create SQL for " + ftype + ".");
|
||||
}
|
||||
}
|
||||
public Object getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.get(po);
|
||||
@@ -503,9 +397,6 @@ public abstract class FieldMarshaller<T>
|
||||
throws SQLException {
|
||||
ps.setBytes(column, value);
|
||||
}
|
||||
public String getColumnType () {
|
||||
return "VARBINARY";
|
||||
}
|
||||
}
|
||||
|
||||
protected static class IntArrayMarshaller extends FieldMarshaller<int[]> {
|
||||
@@ -525,9 +416,6 @@ public abstract class FieldMarshaller<T>
|
||||
throws SQLException {
|
||||
ps.setObject(column, value);
|
||||
}
|
||||
public String getColumnType () {
|
||||
return "BLOB";
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ByteEnumMarshaller extends FieldMarshaller<ByteEnum> {
|
||||
@@ -545,10 +433,6 @@ public abstract class FieldMarshaller<T>
|
||||
}
|
||||
}
|
||||
|
||||
public String getColumnType () {
|
||||
return "TINYINT";
|
||||
}
|
||||
|
||||
public ByteEnum getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return (ByteEnum) _field.get(po);
|
||||
|
||||
@@ -33,14 +33,18 @@ import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.Element;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
import com.samskivert.jdbc.depot.operator.Logic.*;
|
||||
|
||||
import com.samskivert.jdbc.depot.clause.FieldOverride;
|
||||
import com.samskivert.jdbc.depot.clause.Join;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.clause.SelectClause;
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.jdbc.depot.operator.Logic.*;
|
||||
|
||||
/**
|
||||
* This class implements the functionality required by {@link DepotRepository#findAll): fetch
|
||||
@@ -58,18 +62,20 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
||||
throws PersistenceException
|
||||
{
|
||||
super(ctx, type);
|
||||
_types = SQLQueryBuilder.getDepotTypes(ctx, type, clauses);
|
||||
DepotTypes types = DepotTypes.getDepotTypes(ctx, clauses);
|
||||
types.addClass(ctx, type);
|
||||
_builder = _ctx.getSQLBuilder(types);
|
||||
_clauses = clauses;
|
||||
}
|
||||
|
||||
public List<T> invoke (Connection conn) throws SQLException {
|
||||
SQLQueryBuilder<T> keyFieldQuery = new SQLQueryBuilder<T>(
|
||||
_ctx, _types, _marsh.getPrimaryKeyColumns(), _clauses);
|
||||
PreparedStatement stmt = keyFieldQuery.prepare(conn);
|
||||
|
||||
public List<T> invoke (Connection conn, DatabaseLiaison liaison) throws SQLException
|
||||
{
|
||||
Map<Key<T>, T> entities = new HashMap<Key<T>, T>();
|
||||
List<Key<T>> allKeys = new ArrayList<Key<T>>();
|
||||
List<Key<T>> fetchKeys = new ArrayList<Key<T>>();
|
||||
|
||||
_builder.newQuery(new SelectClause<T>(_type, _marsh.getPrimaryKeyFields(), _clauses));
|
||||
PreparedStatement stmt = _builder.prepare(conn);
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
@@ -97,26 +103,28 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
||||
|
||||
if (fetchKeys.size() > 0) {
|
||||
int jj = 0;
|
||||
QueryClause[] newClauses = new QueryClause[_clauses.length + 1];
|
||||
// a select subset of query clauses are preserved for the entity query
|
||||
QueryClause[] newClauses = new QueryClause[_clauses.length + 1];
|
||||
for (int ii = 0; ii < _clauses.length; ii ++) {
|
||||
if (_clauses[ii] instanceof Join || _clauses[ii] instanceof FieldOverride) {
|
||||
newClauses[jj ++] = _clauses[ii];
|
||||
}
|
||||
}
|
||||
SQLExpression[] keyArray = fetchKeys.toArray(new SQLExpression[0]);
|
||||
|
||||
SQLExpression[] keyArray = new SQLExpression[fetchKeys.size()];
|
||||
for (int ii = 0; ii < keyArray.length; ii ++) {
|
||||
keyArray[ii] = fetchKeys.get(ii).condition;
|
||||
}
|
||||
|
||||
// add our special key-matching where clause
|
||||
newClauses[jj ++] = new Where(new Or(keyArray));
|
||||
newClauses = ArrayUtil.splice(newClauses, jj);
|
||||
|
||||
// build the new query
|
||||
SQLQueryBuilder<T> entityQuery = new SQLQueryBuilder<T>(
|
||||
_ctx, _types, _marsh.getFieldNames(), newClauses);
|
||||
_builder.newQuery(new SelectClause<T>(_type, _marsh.getFieldNames(), newClauses));
|
||||
stmt = _builder.prepare(conn);
|
||||
|
||||
// and execute it
|
||||
stmt = entityQuery.prepare(conn);
|
||||
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
for (Key<T> key : fetchKeys) {
|
||||
@@ -138,7 +146,6 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
||||
return result;
|
||||
}
|
||||
|
||||
protected DepotTypes<T> _types;
|
||||
protected QueryClause[] _clauses;
|
||||
}
|
||||
|
||||
@@ -151,33 +158,32 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
||||
throws PersistenceException
|
||||
{
|
||||
super(ctx, type);
|
||||
DepotTypes<List<T>> types = SQLQueryBuilder.getDepotTypes(ctx, type, clauses);
|
||||
_builder = new SQLQueryBuilder<List<T>>(ctx, types, _marsh.getFieldNames(), clauses);
|
||||
SelectClause<T> select = new SelectClause<T>(type, _marsh.getFieldNames(), clauses);
|
||||
_builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, select));
|
||||
_builder.newQuery(select);
|
||||
}
|
||||
|
||||
public List<T> invoke (Connection conn) throws SQLException {
|
||||
PreparedStatement stmt = _builder.prepare(conn);
|
||||
|
||||
public List<T> invoke (Connection conn, DatabaseLiaison liaison) throws SQLException
|
||||
{
|
||||
List<T> result = new ArrayList<T>();
|
||||
PreparedStatement stmt = _builder.prepare(conn);
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
result.add(_marsh.createObject(rs));
|
||||
}
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected SQLQueryBuilder<List<T>> _builder;
|
||||
}
|
||||
|
||||
public FindAllQuery (PersistenceContext ctx, Class<T> type)
|
||||
throws PersistenceException
|
||||
{
|
||||
_ctx = ctx;
|
||||
_type = type;
|
||||
_marsh = _ctx.getMarshaller(type);
|
||||
}
|
||||
|
||||
@@ -216,5 +222,7 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
||||
}
|
||||
|
||||
protected PersistenceContext _ctx;
|
||||
protected SQLBuilder _builder;
|
||||
protected DepotMarshaller<T> _marsh;
|
||||
protected Class<T> _type;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,11 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.clause.SelectClause;
|
||||
|
||||
/**
|
||||
* The implementation of {@link DepotRepository#find) functionality.
|
||||
@@ -39,21 +42,23 @@ public class FindOneQuery<T extends PersistentRecord>
|
||||
throws PersistenceException
|
||||
{
|
||||
_marsh = ctx.getMarshaller(type);
|
||||
DepotTypes<T> types = SQLQueryBuilder.getDepotTypes(ctx, type, clauses);
|
||||
_builder = new SQLQueryBuilder<T>(ctx, types, _marsh.getFieldNames(), clauses);
|
||||
_select = new SelectClause<T>(type, _marsh.getFieldNames(), clauses);
|
||||
_builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select));
|
||||
_builder.newQuery(_select);
|
||||
}
|
||||
|
||||
// from Query
|
||||
public CacheKey getCacheKey ()
|
||||
{
|
||||
if (_builder.where != null && _builder.where instanceof CacheKey) {
|
||||
return (CacheKey) _builder.where;
|
||||
WhereClause where = _select.getWhereClause();
|
||||
if (where != null && where instanceof CacheKey) {
|
||||
return (CacheKey) where;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// from Query
|
||||
public T invoke (Connection conn) throws SQLException {
|
||||
public T invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = _builder.prepare(conn);
|
||||
try {
|
||||
T result = null;
|
||||
@@ -96,5 +101,6 @@ public class FindOneQuery<T extends PersistentRecord>
|
||||
}
|
||||
|
||||
protected DepotMarshaller<T> _marsh;
|
||||
protected SQLQueryBuilder<T> _builder;
|
||||
protected SelectClause<T> _select;
|
||||
protected SQLBuilder _builder;
|
||||
}
|
||||
|
||||
@@ -21,17 +21,22 @@
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.LiaisonRegistry;
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
|
||||
/**
|
||||
* Generates primary keys using an identity column.
|
||||
*/
|
||||
public class IdentityKeyGenerator implements KeyGenerator
|
||||
public class IdentityKeyGenerator extends KeyGenerator
|
||||
{
|
||||
public IdentityKeyGenerator (GeneratedValue gv, String table, String column)
|
||||
{
|
||||
super(gv, table, column);
|
||||
}
|
||||
|
||||
// from interface KeyGenerator
|
||||
public boolean isPostFactum ()
|
||||
{
|
||||
@@ -39,27 +44,16 @@ public class IdentityKeyGenerator implements KeyGenerator
|
||||
}
|
||||
|
||||
// from interface KeyGenerator
|
||||
public void init (Connection conn)
|
||||
public void init (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
// nothing to do here
|
||||
liaison.initializeGenerator(conn, _table, _column, _initialValue, _allocationSize);
|
||||
}
|
||||
|
||||
// from interface KeyGenerator
|
||||
public int nextGeneratedValue (Connection conn)
|
||||
public int nextGeneratedValue (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
// load up the last inserted ID mysql style
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery("select LAST_INSERT_ID()");
|
||||
if (rs.next()) {
|
||||
return rs.getInt(1);
|
||||
}
|
||||
throw new SQLException("Failed to generate next ID");
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
return liaison.lastInsertedId(conn, _table, _column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ package com.samskivert.jdbc.depot;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -31,20 +29,77 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.jdbc.depot.annotation.Id;
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* A special form of {@link Where} clause that uniquely specifies a single database row and
|
||||
* A special form of {@link WhereClause} that uniquely specifies a single database row and
|
||||
* thus also a single persistent object. Because it implements both {@link CacheKey} and
|
||||
* {@link CacheInvalidator} it also uniquely indexes into the cache and knows how to invalidate
|
||||
* itself upon modification. This class is created by many {@link DepotMarshaller} methods as
|
||||
* a convenience, and may also be instantiated explicitly.
|
||||
*/
|
||||
public class Key<T extends PersistentRecord> extends Where
|
||||
public class Key<T extends PersistentRecord> extends WhereClause
|
||||
implements SQLExpression, CacheKey, CacheInvalidator, Serializable
|
||||
{
|
||||
/** An expression that contains our key columns and values. */
|
||||
public static class WhereCondition<U extends PersistentRecord> implements SQLExpression
|
||||
{
|
||||
public WhereCondition (Class<U> pClass, Comparable[] values)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_values = values;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
}
|
||||
|
||||
public Class<U> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public Comparable[] getValues ()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj)
|
||||
{
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
WhereCondition other = (WhereCondition) obj;
|
||||
return _pClass == other._pClass && Arrays.equals(_values, other._values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
return _pClass.hashCode() ^ Arrays.hashCode(_values);
|
||||
}
|
||||
|
||||
protected Class<U> _pClass;
|
||||
protected Comparable[] _values;
|
||||
}
|
||||
|
||||
/** The expression that identifies our row. */
|
||||
public final WhereCondition<T> condition;
|
||||
|
||||
/**
|
||||
* Constructs a new single-column {@code Key} with the given value.
|
||||
*/
|
||||
@@ -76,10 +131,6 @@ public class Key<T extends PersistentRecord> extends Where
|
||||
*/
|
||||
public Key (Class<T> pClass, String[] fields, Comparable[] values)
|
||||
{
|
||||
// TODO: make Where an interface so we don't have to do this ugly super call
|
||||
super(null);
|
||||
_pClass = pClass;
|
||||
|
||||
if (fields.length != values.length) {
|
||||
throw new IllegalArgumentException("Field and Value arrays must be of equal length.");
|
||||
}
|
||||
@@ -94,69 +145,41 @@ public class Key<T extends PersistentRecord> extends Where
|
||||
String[] keyFields = getKeyFields(pClass);
|
||||
|
||||
// now extract the values in field order and ensure none are extra or missing
|
||||
_values = new Comparable[fields.length];
|
||||
Comparable[] newValues = new Comparable[fields.length];
|
||||
for (int ii = 0; ii < keyFields.length; ii++) {
|
||||
_values[ii] = map.remove(keyFields[ii]);
|
||||
newValues[ii] = map.remove(keyFields[ii]);
|
||||
// make sure we were provided with a value for this primary key field
|
||||
if (_values[ii] == null) {
|
||||
if (newValues[ii] == null) {
|
||||
throw new IllegalArgumentException("Missing value for key field: " + keyFields[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
// finally make sure we were not given any fields that are not in fact primary key fields
|
||||
if (map.size() > 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Non-key columns given: " + StringUtil.join(map.keySet().toArray(), ", "));
|
||||
}
|
||||
|
||||
condition = new WhereCondition<T>(pClass, newValues);
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_pClass);
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
{
|
||||
builder.append(" where ");
|
||||
appendExpression(query, builder);
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
{
|
||||
for (int ii = 0; ii < _values.length; ii ++) {
|
||||
if (_values[ii] != null) {
|
||||
pstmt.setObject(argIdx ++, _values[ii]);
|
||||
}
|
||||
}
|
||||
return argIdx;
|
||||
classSet.add(condition.getPersistentClass());
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
public void accept (ExpressionVisitor builder)
|
||||
throws Exception
|
||||
{
|
||||
String[] keyFields = getKeyFields(_pClass);
|
||||
for (int ii = 0; ii < keyFields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
builder.append(" and ");
|
||||
}
|
||||
builder.append(keyFields[ii]).append(_values[ii] == null ? " is null " : " = ? ");
|
||||
}
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
{
|
||||
return bindClauseArguments(pstmt, argIdx);
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from CacheKey
|
||||
public String getCacheId ()
|
||||
{
|
||||
return _pClass.getName();
|
||||
return condition.getPersistentClass().getName();
|
||||
}
|
||||
|
||||
// from CacheKey
|
||||
@@ -171,12 +194,6 @@ public class Key<T extends PersistentRecord> extends Where
|
||||
ctx.cacheInvalidate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
return _pClass.hashCode() ^ Arrays.hashCode(_values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj)
|
||||
{
|
||||
@@ -186,17 +203,24 @@ public class Key<T extends PersistentRecord> extends Where
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Key other = (Key) obj;
|
||||
return _pClass == other._pClass && Arrays.equals(_values, other._values);
|
||||
return condition.equals(((Key) obj).condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
return condition.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder("[Key pClass=" + _pClass.getName());
|
||||
String[] keyFields = getKeyFields(_pClass);
|
||||
StringBuilder builder =
|
||||
new StringBuilder("[Key pClass=").append(condition.getPersistentClass().getName());
|
||||
String[] keyFields = getKeyFields(condition.getPersistentClass());
|
||||
for (int ii = 0; ii < keyFields.length; ii ++) {
|
||||
builder.append(", ").append(keyFields[ii]).append("=").append(_values[ii]);
|
||||
builder.append(", ").append(keyFields[ii]).append("=").
|
||||
append(condition.getValues()[ii]);
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
@@ -222,9 +246,6 @@ public class Key<T extends PersistentRecord> extends Where
|
||||
return fields;
|
||||
}
|
||||
|
||||
protected Class<T> _pClass;
|
||||
protected Comparable[] _values;
|
||||
|
||||
/** 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<Class<?>,String[]> _keyFields = new HashMap<Class<?>,String[]>();
|
||||
|
||||
@@ -23,18 +23,58 @@ package com.samskivert.jdbc.depot;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
|
||||
/**
|
||||
* Defines the interface to our primary key generators.
|
||||
*/
|
||||
public interface KeyGenerator
|
||||
public abstract class KeyGenerator
|
||||
{
|
||||
/** If true, this key generator will be run after the insert statement, if false, it will be
|
||||
* run before. */
|
||||
public boolean isPostFactum ();
|
||||
public KeyGenerator (GeneratedValue gv, String table, String column)
|
||||
{
|
||||
_allocationSize = gv.allocationSize();
|
||||
_initialValue = gv.initialValue();
|
||||
_table = table;
|
||||
_column = column;
|
||||
}
|
||||
|
||||
/** Prepares the generator for operation. */
|
||||
public void init (Connection conn) throws SQLException;
|
||||
/**
|
||||
* If true, this key generator will be run after the insert statement, if false, it will be run
|
||||
* before.
|
||||
*/
|
||||
public abstract boolean isPostFactum ();
|
||||
|
||||
/** Fetch/generate the next primary key value. */
|
||||
public int nextGeneratedValue (Connection conn) throws SQLException;
|
||||
/**
|
||||
* Prepares the generator for operation.
|
||||
*/
|
||||
public abstract void init (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Fetch/generate the next primary key value.
|
||||
*/
|
||||
public abstract int nextGeneratedValue (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Returns the name of the table for which we're generating a key value.
|
||||
*/
|
||||
public String getTable ()
|
||||
{
|
||||
return _table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the column for which we're generating a key value.
|
||||
*/
|
||||
public String getColumn ()
|
||||
{
|
||||
return _column;
|
||||
}
|
||||
|
||||
protected int _initialValue;
|
||||
protected int _allocationSize;
|
||||
protected String _table;
|
||||
protected String _column;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
|
||||
/**
|
||||
* Encapsulates a modification of persistent objects.
|
||||
*/
|
||||
@@ -33,23 +35,22 @@ public abstract class Modifier
|
||||
* A simple modifier that executes a single SQL statement. No cache flushing is done as a
|
||||
* result of this operation.
|
||||
*/
|
||||
public static class Simple extends Modifier
|
||||
public abstract static class Simple extends Modifier
|
||||
{
|
||||
public Simple (String query) {
|
||||
public Simple () {
|
||||
super(null);
|
||||
_query = query;
|
||||
}
|
||||
|
||||
public int invoke (Connection conn) throws SQLException {
|
||||
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
return stmt.executeUpdate(_query);
|
||||
return stmt.executeUpdate(createQuery(liaison));
|
||||
} finally {
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected String _query;
|
||||
protected abstract String createQuery (DatabaseLiaison liaison);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,10 +99,10 @@ public abstract class Modifier
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriden to perform the actual database modifications represented by this object;
|
||||
* should return the number of modified rows.
|
||||
* Overriden to perform the actual database modifications represented by this object; should
|
||||
* return the number of modified rows.
|
||||
*/
|
||||
public abstract int invoke (Connection conn) throws SQLException;
|
||||
public abstract int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException;
|
||||
|
||||
/**
|
||||
* Constructs a {@link Modifier} without a cache invalidator.
|
||||
|
||||
@@ -20,21 +20,19 @@
|
||||
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* A special form of {@link Where} clause that specifies an explicit range of database rows. It
|
||||
* does not implement {@link CacheKey} but it does implement {@link CacheInvalidator} which means
|
||||
* it can be sent into e.g. {@link DepotRepository#deleteAll) and have it clean up after itself.
|
||||
*/
|
||||
public class MultiKey<T extends PersistentRecord> extends Where
|
||||
public class MultiKey<T extends PersistentRecord> extends WhereClause
|
||||
implements CacheInvalidator
|
||||
{
|
||||
/**
|
||||
@@ -69,8 +67,6 @@ public class MultiKey<T extends PersistentRecord> extends Where
|
||||
public MultiKey (Class<T> pClass, String[] sFields, Comparable[] sValues,
|
||||
String mField, Comparable[] mValues)
|
||||
{
|
||||
// TODO
|
||||
super(null);
|
||||
if (sFields.length != sValues.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Key field and values arrays must be of equal length.");
|
||||
@@ -84,52 +80,16 @@ public class MultiKey<T extends PersistentRecord> extends Where
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from QueryClause
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_pClass);
|
||||
}
|
||||
|
||||
@Override // from Where
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
{
|
||||
builder.append(" where ");
|
||||
boolean first = true;
|
||||
for (Map.Entry entry : _map.entrySet()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
builder.append(" and ");
|
||||
}
|
||||
builder.append(entry.getKey());
|
||||
builder.append(entry.getValue() == null ? " is null " : " = ? ");
|
||||
}
|
||||
if (!first) {
|
||||
builder.append(" and ");
|
||||
}
|
||||
builder.append(_mField).append(" in (");
|
||||
for (int ii = 0; ii < _mValues.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append("?");
|
||||
}
|
||||
builder.append(")");
|
||||
}
|
||||
|
||||
@Override // from Where
|
||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
{
|
||||
for (Map.Entry entry : _map.entrySet()) {
|
||||
if (entry.getValue() != null) {
|
||||
pstmt.setObject(argIdx ++, entry.getValue());
|
||||
}
|
||||
}
|
||||
for (int ii = 0; ii < _mValues.length; ii++) {
|
||||
pstmt.setObject(argIdx ++, _mValues[ii]);
|
||||
}
|
||||
return argIdx;
|
||||
// nothing to add
|
||||
}
|
||||
|
||||
// from CacheInvalidator
|
||||
@@ -142,8 +102,28 @@ public class MultiKey<T extends PersistentRecord> extends Where
|
||||
}
|
||||
}
|
||||
|
||||
protected Class<T> _pClass;
|
||||
protected HashMap<String, Comparable> _map;
|
||||
public Class<T> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public Map<String, Comparable> getSingleFieldsMap ()
|
||||
{
|
||||
return _map;
|
||||
}
|
||||
|
||||
public String getMultiField ()
|
||||
{
|
||||
return _mField;
|
||||
}
|
||||
|
||||
public Comparable[] getMultiValues ()
|
||||
{
|
||||
return _mValues;
|
||||
}
|
||||
|
||||
protected String _mField;
|
||||
protected Comparable[] _mValues;
|
||||
protected Class<T> _pClass;
|
||||
protected HashMap<String, Comparable> _map;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
//
|
||||
// $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;
|
||||
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Date;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.BooleanMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ByteArrayMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ByteEnumMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ByteMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.DoubleMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.FloatMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.IntArrayMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.IntMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.LongMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ObjectMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ShortMarshaller;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.Match;
|
||||
|
||||
public class MySQLBuilder
|
||||
extends SQLBuilder
|
||||
{
|
||||
public class MSBuildVisitor extends BuildVisitor
|
||||
{
|
||||
protected MSBuildVisitor (DepotTypes types)
|
||||
{
|
||||
super(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit (Match match)
|
||||
throws Exception
|
||||
{
|
||||
_builder.append("match(");
|
||||
ColumnExp[] columns = match.getColumns();
|
||||
for (int ii = 0; ii < columns.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
columns[ii].accept(this);
|
||||
}
|
||||
_builder.append(") against (?");
|
||||
switch (match.getMode()) {
|
||||
case BOOLEAN:
|
||||
_builder.append(" in boolean mode");
|
||||
break;
|
||||
case NATURAL_LANGUAGE:
|
||||
_builder.append(" in natural language mode");
|
||||
break;
|
||||
}
|
||||
if (match.isQueryExpansion()) {
|
||||
_builder.append(" with query expansion");
|
||||
}
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
protected void appendTableName (Class<? extends PersistentRecord> type)
|
||||
{
|
||||
_builder.append(_types.getTableName(type));
|
||||
}
|
||||
|
||||
protected void appendTableAbbreviation (Class<? extends PersistentRecord> type)
|
||||
{
|
||||
_builder.append(_types.getTableAbbreviation(type));
|
||||
}
|
||||
|
||||
protected void appendColumn (Class<? extends PersistentRecord> type, String field)
|
||||
{
|
||||
_builder.append(_types.getColumnName(type, field));
|
||||
}
|
||||
|
||||
protected void appendField (String field)
|
||||
{
|
||||
_builder.append(field);
|
||||
}
|
||||
}
|
||||
|
||||
public class MSBindVisitor extends BindVisitor
|
||||
{
|
||||
protected MSBindVisitor (DepotTypes types, PreparedStatement stmt)
|
||||
{
|
||||
super(types, stmt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit (Match match)
|
||||
throws Exception
|
||||
{
|
||||
_stmt.setString(_argIdx ++, match.getQuery());
|
||||
}
|
||||
}
|
||||
|
||||
public MySQLBuilder (DepotTypes types)
|
||||
{
|
||||
super(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BuildVisitor getBuildVisitor ()
|
||||
{
|
||||
return new MSBuildVisitor(_types);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BindVisitor getBindVisitor (PreparedStatement stmt)
|
||||
{
|
||||
return new MSBindVisitor(_types, stmt);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> String getColumnType (FieldMarshaller fm)
|
||||
{
|
||||
if (fm instanceof ByteMarshaller) {
|
||||
return "TINYINT";
|
||||
} else if (fm instanceof ShortMarshaller) {
|
||||
return "SMALLINT";
|
||||
} else if (fm instanceof IntMarshaller) {
|
||||
return "INTEGER";
|
||||
} else if (fm instanceof LongMarshaller) {
|
||||
return "BIGINT";
|
||||
} else if (fm instanceof FloatMarshaller) {
|
||||
return "FLOAT";
|
||||
} else if (fm instanceof DoubleMarshaller) {
|
||||
return "DOUBLE";
|
||||
} else if (fm instanceof ObjectMarshaller) {
|
||||
Class<?> ftype = fm.getField().getType();
|
||||
if (ftype.equals(Byte.class)) {
|
||||
return "SMALLINT";
|
||||
} else if (ftype.equals(Short.class)) {
|
||||
return "SMALLINT";
|
||||
} else if (ftype.equals(Integer.class)) {
|
||||
return "INTEGER";
|
||||
} else if (ftype.equals(Long.class)) {
|
||||
return "BIGINT";
|
||||
} else if (ftype.equals(Float.class)) {
|
||||
return "FLOAT";
|
||||
} else if (ftype.equals(Double.class)) {
|
||||
return "DOUBLE";
|
||||
} else if (ftype.equals(String.class)) {
|
||||
return "VARCHAR";
|
||||
} else if (ftype.equals(Date.class)) {
|
||||
return "DATE";
|
||||
} else if (ftype.equals(Time.class)) {
|
||||
return "DATETIME";
|
||||
} else if (ftype.equals(Timestamp.class)) {
|
||||
return "DATETIME";
|
||||
} else if (ftype.equals(Blob.class)) {
|
||||
return "BYTEA";
|
||||
} else if (ftype.equals(Clob.class)) {
|
||||
return "TEXT";
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Don't know how to create SQL for " + ftype + ".");
|
||||
}
|
||||
} else if (fm instanceof ByteArrayMarshaller) {
|
||||
return "VARBINARY";
|
||||
} else if (fm instanceof IntArrayMarshaller) {
|
||||
return "BLOB";
|
||||
} else if (fm instanceof ByteEnumMarshaller) {
|
||||
return "TINYINT";
|
||||
} else if (fm instanceof BooleanMarshaller) {
|
||||
return "TINYINT";
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown field marshaller type: " + fm.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,11 @@ import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.samskivert.jdbc.ConnectionProvider;
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.DuplicateKeyException;
|
||||
|
||||
import com.samskivert.jdbc.LiaisonRegistry;
|
||||
import com.samskivert.jdbc.MySQLLiaison;
|
||||
import com.samskivert.jdbc.PostgreSQLLiaison;
|
||||
|
||||
/**
|
||||
* Defines a scope in which global annotations are shared.
|
||||
@@ -124,6 +127,23 @@ public class PersistenceContext
|
||||
_ident = ident;
|
||||
_conprov = conprov;
|
||||
_cachemgr = CacheManager.getInstance();
|
||||
_liaison = LiaisonRegistry.getLiaison(conprov.getURL(ident));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new {@link SQLBuilder} for the appropriate dialect.
|
||||
*
|
||||
* TODO: At some point perhaps use a more elegant way of discerning our dialect.
|
||||
*/
|
||||
public SQLBuilder getSQLBuilder (DepotTypes types)
|
||||
{
|
||||
if (_liaison instanceof PostgreSQLLiaison) {
|
||||
return new PostgreSQLBuilder(types);
|
||||
}
|
||||
if (_liaison instanceof MySQLLiaison) {
|
||||
return new MySQLBuilder(types);
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown liaison type: " + _liaison.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,25 +419,21 @@ public class PersistenceContext
|
||||
Connection conn = _conprov.getConnection(_ident, isReadOnlyQuery);
|
||||
try {
|
||||
if (isReadOnlyQuery) {
|
||||
// if this becomes more complex than this single statement,
|
||||
// then this should turn into a method call that contains
|
||||
// the complexity
|
||||
return query.invoke(conn);
|
||||
// if this becomes more complex than this single statement, then this should turn
|
||||
// into a method call that contains the complexity
|
||||
return query.invoke(conn, _liaison);
|
||||
|
||||
} else {
|
||||
// if this becomes more complex than this single statement,
|
||||
// then this should turn into a method call that contains
|
||||
// the complexity
|
||||
return modifier.invoke(conn);
|
||||
// if this becomes more complex than this single statement, then this should turn
|
||||
// into a method call that contains the complexity
|
||||
return modifier.invoke(conn, _liaison);
|
||||
}
|
||||
|
||||
} catch (SQLException sqe) {
|
||||
if (!isReadOnlyQuery) {
|
||||
// convert this exception to a DuplicateKeyException if
|
||||
// appropriate
|
||||
String msg = sqe.getMessage();
|
||||
if (msg != null && msg.indexOf("Duplicate entry") != -1) {
|
||||
throw new DuplicateKeyException(msg);
|
||||
// convert this exception to a DuplicateKeyException if appropriate
|
||||
if (_liaison.isDuplicateRowException(sqe)) {
|
||||
throw new DuplicateKeyException(sqe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +441,7 @@ public class PersistenceContext
|
||||
_conprov.connectionFailed(_ident, isReadOnlyQuery, conn, sqe);
|
||||
conn = null;
|
||||
|
||||
if (retryOnTransientFailure && isTransientException(sqe)) {
|
||||
if (retryOnTransientFailure && _liaison.isTransientException(sqe)) {
|
||||
// the MySQL JDBC driver has the annoying habit of including
|
||||
// the embedded exception stack trace in the message of their
|
||||
// outer exception; if I want a fucking stack trace, I'll call
|
||||
@@ -448,21 +464,10 @@ public class PersistenceContext
|
||||
return invoke(query, modifier, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the specified exception is a transient failure that can be retried.
|
||||
*/
|
||||
protected boolean isTransientException (SQLException sqe)
|
||||
{
|
||||
// TODO: this is MySQL specific. This was snarfed from MySQLLiaison.
|
||||
String msg = sqe.getMessage();
|
||||
return (msg != null && (msg.indexOf("Lost connection") != -1 ||
|
||||
msg.indexOf("link failure") != -1 ||
|
||||
msg.indexOf("Broken pipe") != -1));
|
||||
}
|
||||
|
||||
protected String _ident;
|
||||
protected ConnectionProvider _conprov;
|
||||
protected CacheManager _cachemgr;
|
||||
protected DatabaseLiaison _liaison;
|
||||
|
||||
protected Map<String, Set<CacheListener<?>>> _listenerSets =
|
||||
new HashMap<String, Set<CacheListener<?>>>();
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// $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;
|
||||
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Date;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.BooleanMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ByteArrayMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ByteEnumMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ByteMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.DoubleMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.FloatMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.IntArrayMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.IntMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.LongMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ObjectMarshaller;
|
||||
import com.samskivert.jdbc.depot.FieldMarshaller.ShortMarshaller;
|
||||
|
||||
public class PostgreSQLBuilder
|
||||
extends SQLBuilder
|
||||
{
|
||||
public class PGBuildVisitor extends BuildVisitor
|
||||
{
|
||||
protected PGBuildVisitor (DepotTypes types)
|
||||
{
|
||||
super(types);
|
||||
}
|
||||
|
||||
protected void appendTableName (Class<? extends PersistentRecord> type)
|
||||
{
|
||||
_builder.append("\"").append(_types.getTableName(type)).append("\"");
|
||||
}
|
||||
|
||||
protected void appendTableAbbreviation (Class<? extends PersistentRecord> type)
|
||||
{
|
||||
_builder.append("\"").append(_types.getTableAbbreviation(type)).append("\"");
|
||||
}
|
||||
|
||||
protected void appendColumn (Class<? extends PersistentRecord> type, String field)
|
||||
{
|
||||
_builder.append("\"").append(_types.getColumnName(type, field)).append("\"");
|
||||
}
|
||||
|
||||
protected void appendField (String field)
|
||||
{
|
||||
_builder.append("\"").append(field).append("\"");
|
||||
}
|
||||
}
|
||||
|
||||
public class PGBindVisitor extends BindVisitor
|
||||
{
|
||||
protected PGBindVisitor (DepotTypes types, PreparedStatement stmt)
|
||||
{
|
||||
super(types, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
public PostgreSQLBuilder (DepotTypes types)
|
||||
{
|
||||
super(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BuildVisitor getBuildVisitor ()
|
||||
{
|
||||
return new PGBuildVisitor(_types);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BindVisitor getBindVisitor (PreparedStatement stmt)
|
||||
{
|
||||
return new PGBindVisitor(_types, stmt);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> String getColumnType (FieldMarshaller fm)
|
||||
{
|
||||
if (fm instanceof ByteMarshaller) {
|
||||
return "SMALLINT";
|
||||
} else if (fm instanceof ShortMarshaller) {
|
||||
return "SMALLINT";
|
||||
} else if (fm instanceof IntMarshaller) {
|
||||
return "INTEGER";
|
||||
} else if (fm instanceof LongMarshaller) {
|
||||
return "BIGINT";
|
||||
} else if (fm instanceof FloatMarshaller) {
|
||||
return "FLOAT";
|
||||
} else if (fm instanceof DoubleMarshaller) {
|
||||
return "DOUBLE";
|
||||
} else if (fm instanceof ObjectMarshaller) {
|
||||
Class<?> ftype = fm.getField().getType();
|
||||
if (ftype.equals(Byte.class)) {
|
||||
return "SMALLINT";
|
||||
} else if (ftype.equals(Short.class)) {
|
||||
return "SMALLINT";
|
||||
} else if (ftype.equals(Integer.class)) {
|
||||
return "INTEGER";
|
||||
} else if (ftype.equals(Long.class)) {
|
||||
return "BIGINT";
|
||||
} else if (ftype.equals(Float.class)) {
|
||||
return "FLOAT";
|
||||
} else if (ftype.equals(Double.class)) {
|
||||
return "DOUBLE";
|
||||
} else if (ftype.equals(String.class)) {
|
||||
return "VARCHAR";
|
||||
} else if (ftype.equals(Date.class)) {
|
||||
return "DATE";
|
||||
} else if (ftype.equals(Time.class)) {
|
||||
return "TIME";
|
||||
} else if (ftype.equals(Timestamp.class)) {
|
||||
return "TIMESTAMP";
|
||||
} else if (ftype.equals(Blob.class)) {
|
||||
return "BYTEA";
|
||||
} else if (ftype.equals(Clob.class)) {
|
||||
return "TEXT";
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Don't know how to create SQL for " + ftype + ".");
|
||||
}
|
||||
} else if (fm instanceof ByteArrayMarshaller) {
|
||||
return "BYTEA";
|
||||
} else if (fm instanceof IntArrayMarshaller) {
|
||||
return "BYTEA";
|
||||
} else if (fm instanceof ByteEnumMarshaller) {
|
||||
return "SMALLINT";
|
||||
} else if (fm instanceof BooleanMarshaller) {
|
||||
return "BOOLEAN";
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown field marshaller type: " + fm.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,30 @@ package com.samskivert.jdbc.depot;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
|
||||
/**
|
||||
* The base of all read-only queries.
|
||||
*/
|
||||
public interface Query<T>
|
||||
{
|
||||
/** A simple base class for non-complex queries. */
|
||||
public abstract class TrivialQuery<T> implements Query<T>
|
||||
{
|
||||
public abstract T invoke (Connection conn, DatabaseLiaison liaison) throws SQLException;
|
||||
|
||||
public CacheKey getCacheKey () {
|
||||
return null;
|
||||
}
|
||||
|
||||
public T transformCacheHit (CacheKey key, T value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void updateCache (PersistenceContext ctx, T result) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Any query may elect to utilize the built-in cache by returning a non-null {@link CacheKey}
|
||||
* in this method. This is done automatically by the {@link DepotRepository} when looking up
|
||||
@@ -43,7 +62,7 @@ public interface Query<T>
|
||||
/**
|
||||
* Performs the actual JDBC operations associated with this query.
|
||||
*/
|
||||
public T invoke (Connection conn)
|
||||
public T invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2006-2007 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;
|
||||
|
||||
/**
|
||||
* This type is used during the construction of SQL queries to map persistent classes
|
||||
* to table names and table abbreviations. Its primary implementation is {@link DepotTypes}.
|
||||
*/
|
||||
public interface QueryBuilderContext<T>
|
||||
{
|
||||
/**
|
||||
* Maps a class referenced by this query to its associated table.
|
||||
*
|
||||
* This method is called by individual clauses.
|
||||
*/
|
||||
public String getTableName (Class<? extends PersistentRecord> cl);
|
||||
|
||||
/**
|
||||
* Maps a class referenced by this query to the abbreviation used for its associated table. The
|
||||
* abbreviation is meaningful only within the scope of this particular query.
|
||||
*
|
||||
* This method is called by individual clauses.
|
||||
*/
|
||||
public String getTableAbbreviation (Class<? extends PersistentRecord> cl);
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2006-2007 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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.depot.annotation.Column;
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
|
||||
/**
|
||||
* At the heart of Depot's SQL generation, this object constructs two {@link ExpressionVisitor}
|
||||
* objects and executes them, one after another; the first one constructs SQL as it recurses, the
|
||||
* other binds arguments in the {@link PreparedStatement}. This class must be subclassed by the
|
||||
* database dialects we wish to support.
|
||||
*/
|
||||
public abstract class SQLBuilder
|
||||
{
|
||||
public SQLBuilder (DepotTypes types)
|
||||
{
|
||||
_types = types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an entirely new SQL query relative to our configured {@link DepotTypes} data.
|
||||
* This method may be called multiple times, each time beginning a new query, and should be
|
||||
* followed up by a call to {@link #prepare(Connection)} which creates, configures and
|
||||
* returns the actual {@link PreparedStatement} to execute.
|
||||
*/
|
||||
public void newQuery (QueryClause clause)
|
||||
{
|
||||
_clause = clause;
|
||||
_buildVisitor = getBuildVisitor();
|
||||
|
||||
try {
|
||||
_clause.accept(_buildVisitor);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to build SQL", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After {@link #newQuery(QueryClause)} has been executed, this method is run to recurse
|
||||
* through the {@link QueryClause} structure, setting the {@link PreparedStatement} arguments
|
||||
* that were defined in the generated SQL.
|
||||
*
|
||||
* This method throws {@link SQLException} and is thus meant to be called from within
|
||||
* {@link Query#invoke(Connection)} and {@link Modifier#invoke(Connection)}.
|
||||
*/
|
||||
public PreparedStatement prepare (Connection conn)
|
||||
throws SQLException
|
||||
{
|
||||
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);
|
||||
}
|
||||
return stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the SQL needed to construct a database column for field represented by the given
|
||||
* {@link FieldMarshaller}.
|
||||
*
|
||||
* TODO: This method should be split into several parts that are more easily overridden on a
|
||||
* case-by-case basis in the dialectal subclasses.
|
||||
*/
|
||||
public <T> String buildColumnDefinition (FieldMarshaller<T> fm)
|
||||
{
|
||||
// if this field is @Computed, it has no SQL definition
|
||||
if (fm.getComputed() != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Field field = fm.getField();
|
||||
|
||||
// read our column metadata from the annotation (if it exists); annoyingly we can't create
|
||||
// a Column instance to read the defaults so we have to duplicate them here
|
||||
int length = 255;
|
||||
boolean nullable = false;
|
||||
boolean unique = false;
|
||||
String type = "";
|
||||
String defval = "";
|
||||
Column column = field.getAnnotation(Column.class);
|
||||
if (column != null) {
|
||||
nullable = column.nullable();
|
||||
unique = column.unique();
|
||||
length = column.length();
|
||||
type = column.type();
|
||||
defval = column.defaultValue();
|
||||
}
|
||||
|
||||
// create our SQL column definition
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean typeDone = false;
|
||||
|
||||
// handle primary keyness
|
||||
GeneratedValue genValue = fm.getGeneratedValue();
|
||||
if (genValue != null) {
|
||||
switch (genValue.strategy()) {
|
||||
case AUTO:
|
||||
case IDENTITY:
|
||||
builder.append(" SERIAL UNIQUE");
|
||||
typeDone = true;
|
||||
break;
|
||||
case SEQUENCE: // TODO
|
||||
throw new IllegalArgumentException(
|
||||
"SEQUENCE key generation strategy not yet supported.");
|
||||
case TABLE:
|
||||
// nothing to do here, it'll be handled later
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!typeDone) {
|
||||
if (type.length() == 0) {
|
||||
type = getColumnType(fm);
|
||||
}
|
||||
builder.append(" ").append(type);
|
||||
|
||||
// if this is a VARCHAR field, add the length
|
||||
if (type.equals("VARCHAR") || type.equals("VARBINARY")) {
|
||||
builder.append("(").append(length).append(")");
|
||||
}
|
||||
|
||||
// TODO: handle precision and scale
|
||||
|
||||
// handle nullability and uniqueness
|
||||
if (!nullable) {
|
||||
builder.append(" NOT NULL");
|
||||
}
|
||||
if (unique) {
|
||||
builder.append(" UNIQUE");
|
||||
}
|
||||
|
||||
// append the default value if one was specified
|
||||
if (defval.length() > 0) {
|
||||
builder.append(" DEFAULT ").append(defval);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden by subclasses to create a dialect-specific {@link BuildVisitor}.
|
||||
*/
|
||||
protected abstract BuildVisitor getBuildVisitor ();
|
||||
|
||||
/**
|
||||
* Overridden by subclasses to create a dialect-specific {@link BindVisitor}.
|
||||
*/
|
||||
protected abstract BindVisitor getBindVisitor (PreparedStatement stmt);
|
||||
|
||||
/**
|
||||
* Overridden by subclasses to figure the dialect-specific SQL type of the given field.
|
||||
*/
|
||||
protected abstract <T> String getColumnType (FieldMarshaller fm);
|
||||
|
||||
/** The class that maps persistent classes to marshallers. */
|
||||
protected DepotTypes _types;
|
||||
|
||||
protected QueryClause _clause;
|
||||
protected BuildVisitor _buildVisitor;
|
||||
protected BindVisitor _bindVisitor;
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2006-2007 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;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.jdbc.depot.annotation.Computed;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Builds actual SQL given a main persistent type and some {@link QueryClause} objects.
|
||||
*/
|
||||
public class SQLQueryBuilder<T>
|
||||
{
|
||||
/** The from override clause, if any. */
|
||||
public FromOverride fromOverride;
|
||||
|
||||
/** The where clause. */
|
||||
public Where where;
|
||||
|
||||
/** A list of join clauses, each potentially referencing a new class. */
|
||||
public List<Join> joinClauses = new ArrayList<Join>();
|
||||
|
||||
/** The order by clause, if any. */
|
||||
public OrderBy orderBy;
|
||||
|
||||
/** The group by clause, if any. */
|
||||
public GroupBy groupBy;
|
||||
|
||||
/** The limit clause, if any. */
|
||||
public Limit limit;
|
||||
|
||||
/** The For Update clause, if any. */
|
||||
public ForUpdate forUpdate;
|
||||
|
||||
/**
|
||||
* Constructs a {@link DepotTypes} object for your convenience given similar arguments used to
|
||||
* construct a {@link SQLQueryBuilder}. This method mainly offloads the task of interrogating
|
||||
* the various clauses for what persistent classes they introduce into the query.
|
||||
*/
|
||||
public static <T> DepotTypes<T> getDepotTypes (
|
||||
PersistenceContext ctx, Class<? extends PersistentRecord> type, QueryClause... clauses)
|
||||
throws PersistenceException
|
||||
{
|
||||
Set<Class<? extends PersistentRecord>> classSet =
|
||||
new HashSet<Class<? extends PersistentRecord>>();
|
||||
if (type != null) {
|
||||
classSet.add(type);
|
||||
}
|
||||
|
||||
for (QueryClause clause : clauses) {
|
||||
if (clause != null) {
|
||||
clause.addClasses(classSet);
|
||||
}
|
||||
}
|
||||
|
||||
return new DepotTypes<T>(ctx, type, classSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Query object to generate one or more instances of the specified persistent
|
||||
* 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.
|
||||
*/
|
||||
public SQLQueryBuilder (PersistenceContext ctx, DepotTypes<T> types, String[] fields,
|
||||
QueryClause... clauses)
|
||||
{
|
||||
_builder = new StringBuilder("select ");
|
||||
_types = types;
|
||||
|
||||
// iterate over the clauses and sort them into the different types we understand
|
||||
for (QueryClause clause : clauses) {
|
||||
if (clause == null) {
|
||||
continue;
|
||||
}
|
||||
if (clause instanceof Where) {
|
||||
if (where != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple Where clauses.");
|
||||
}
|
||||
where = (Where) clause;
|
||||
|
||||
} else if (clause instanceof FromOverride) {
|
||||
if (fromOverride != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple FromOverride clauses.");
|
||||
}
|
||||
fromOverride = (FromOverride) clause;
|
||||
|
||||
} else if (clause instanceof Join) {
|
||||
joinClauses.add((Join) clause);
|
||||
|
||||
} 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 = (OrderBy) clause;
|
||||
|
||||
} else if (clause instanceof GroupBy) {
|
||||
if (groupBy != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple GroupBy clauses.");
|
||||
}
|
||||
groupBy = (GroupBy) clause;
|
||||
|
||||
} else if (clause instanceof Limit) {
|
||||
if (limit != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple Limit clauses.");
|
||||
}
|
||||
limit = (Limit) clause;
|
||||
|
||||
} else if (clause instanceof ForUpdate) {
|
||||
if (forUpdate != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple For Update clauses.");
|
||||
}
|
||||
forUpdate = (ForUpdate) clause;
|
||||
}
|
||||
}
|
||||
|
||||
Computed entityComputed = _types.getMainType().getAnnotation(Computed.class);
|
||||
|
||||
// iterate over the fields we're filling in and figure out whence each one comes
|
||||
boolean skip = true;
|
||||
for (String field : fields) {
|
||||
if (!skip) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
skip = false;
|
||||
|
||||
// first, see if there's a field override
|
||||
FieldOverride clause1 = _disMap.get(field);
|
||||
if (clause1 != null) {
|
||||
clause1.appendClause(_types, _builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
// figure out the class we're selecting from unless we're otherwise overriden:
|
||||
// for a concrete record, simply use the corresponding table; for a computed one,
|
||||
// default to the shadowed concrete record, or null if there isn't one
|
||||
Class<? extends PersistentRecord> tableClass =
|
||||
entityComputed == null ? _types.getMainType() : entityComputed.shadowOf();
|
||||
|
||||
// handle the field-level @Computed annotation, if there is one
|
||||
Computed fieldComputed =
|
||||
_types.getMainMarshaller().getFieldMarshaller(field).getComputed();
|
||||
if (fieldComputed != null) {
|
||||
// check if the computed field has a literal SQL definition
|
||||
if (fieldComputed.fieldDefinition().length() > 0) {
|
||||
_builder.append(fieldComputed.fieldDefinition()).append(" as ").append(field);
|
||||
continue;
|
||||
}
|
||||
|
||||
// or if we can simply ignore the field
|
||||
if (!fieldComputed.required()) {
|
||||
skip = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// else see if there's an overriding shadowOf definition
|
||||
if (fieldComputed.shadowOf() != null) {
|
||||
tableClass = fieldComputed.shadowOf();
|
||||
}
|
||||
}
|
||||
|
||||
// if we get this far we hopefully have a table to select from
|
||||
if (tableClass != null) {
|
||||
String tableName = _types.getTableAbbreviation(tableClass);
|
||||
_builder.append(tableName).append(".").append(field);
|
||||
continue;
|
||||
}
|
||||
|
||||
// else owie
|
||||
throw new IllegalArgumentException(
|
||||
"Persistent field has no definition [class=" + _types.getMainType() +
|
||||
", field=" + field + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public PreparedStatement prepare (Connection conn)
|
||||
throws SQLException
|
||||
{
|
||||
if (fromOverride != null) {
|
||||
fromOverride.appendClause(_types, _builder);
|
||||
|
||||
} else if (_types.getMainTableName() != null) {
|
||||
_builder.append(" from ").append(_types.getMainTableName()).
|
||||
append(" as ").append(_types.getMainTableAbbreviation());
|
||||
|
||||
} else {
|
||||
throw new SQLException("Query on @Computed entity with no FromOverrideClause.");
|
||||
}
|
||||
|
||||
for (Join clause : joinClauses) {
|
||||
clause.appendClause(_types, _builder);
|
||||
}
|
||||
if (where != null) {
|
||||
where.appendClause(_types, _builder);
|
||||
}
|
||||
if (groupBy != null) {
|
||||
groupBy.appendClause(_types, _builder);
|
||||
}
|
||||
if (orderBy != null) {
|
||||
orderBy.appendClause(_types, _builder);
|
||||
}
|
||||
if (limit != null) {
|
||||
limit.appendClause(_types, _builder);
|
||||
}
|
||||
if (forUpdate != null) {
|
||||
forUpdate.appendClause(_types, _builder);
|
||||
}
|
||||
|
||||
PreparedStatement pstmt = conn.prepareStatement(_builder.toString());
|
||||
int argIdx = 1;
|
||||
for (Join clause : joinClauses) {
|
||||
argIdx = clause.bindClauseArguments(pstmt, argIdx);
|
||||
}
|
||||
if (where != null) {
|
||||
argIdx = where.bindClauseArguments(pstmt, argIdx);
|
||||
}
|
||||
if (groupBy != null) {
|
||||
argIdx = groupBy.bindClauseArguments(pstmt, argIdx);
|
||||
}
|
||||
if (orderBy != null) {
|
||||
argIdx = orderBy.bindClauseArguments(pstmt, argIdx);
|
||||
}
|
||||
if (limit != null) {
|
||||
argIdx = limit.bindClauseArguments(pstmt, argIdx);
|
||||
}
|
||||
if (forUpdate != null) {
|
||||
argIdx = forUpdate.bindClauseArguments(pstmt, argIdx);
|
||||
}
|
||||
|
||||
return pstmt;
|
||||
}
|
||||
|
||||
/** A StringBuilder to hold the constructed SQL. */
|
||||
protected StringBuilder _builder;
|
||||
|
||||
/** The class that maps persistent classes to marshallers. */
|
||||
protected DepotTypes<T> _types;
|
||||
|
||||
/** Persistent class fields mapped to field override clauses. */
|
||||
protected Map<String, FieldOverride> _disMap = new HashMap<String, FieldOverride>();
|
||||
}
|
||||
@@ -25,23 +25,25 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
import com.samskivert.jdbc.depot.annotation.TableGenerator;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
import com.samskivert.jdbc.LiaisonRegistry;
|
||||
|
||||
/**
|
||||
* Generates primary keys using an external table
|
||||
* Generates primary keys using an external table .
|
||||
*/
|
||||
public class TableKeyGenerator implements KeyGenerator
|
||||
public class TableKeyGenerator extends KeyGenerator
|
||||
{
|
||||
public TableKeyGenerator (TableGenerator annotation)
|
||||
public TableKeyGenerator (TableGenerator tg, GeneratedValue gv, String table, String column)
|
||||
{
|
||||
_table = defStr(annotation.table(), "IdSequences");
|
||||
_pkColumnName = defStr(annotation.pkColumnName(), "sequence");
|
||||
_pkColumnValue = defStr(annotation.pkColumnValue(), "default");
|
||||
_valueColumnName = defStr(annotation.valueColumnName(), "value");
|
||||
_allocationSize = annotation.allocationSize();
|
||||
_initialValue = annotation.initialValue();
|
||||
super(gv, table, column);
|
||||
_valueTable = defStr(tg.table(), "IdSequences");
|
||||
_pkColumnName = defStr(tg.pkColumnName(), "sequence");
|
||||
_pkColumnValue = defStr(tg.pkColumnValue(), "default");
|
||||
_valueColumnName = defStr(tg.valueColumnName(), "value");
|
||||
}
|
||||
|
||||
// from interface KeyGenerator
|
||||
@@ -51,20 +53,23 @@ public class TableKeyGenerator implements KeyGenerator
|
||||
}
|
||||
|
||||
// from interface KeyGenerator
|
||||
public void init (Connection conn)
|
||||
public void init (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
// make sure our table exists
|
||||
JDBCUtil.createTableIfMissing(conn, _table, new String[] {
|
||||
_pkColumnName + " VARCHAR(255) PRIMARY KEY",
|
||||
_valueColumnName + " INTEGER NOT NULL"
|
||||
}, "");
|
||||
liaison.createTableIfMissing(
|
||||
conn, _valueTable,
|
||||
new String[] { _pkColumnName, _valueColumnName },
|
||||
new String[] { "VARCHAR(255)", "INTEGER NOT NULL" },
|
||||
null,
|
||||
new String[] { _pkColumnName });
|
||||
|
||||
// and also that there's a row in it for us
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
stmt = conn.prepareStatement(
|
||||
"SELECT * FROM " + _table + " WHERE " + _pkColumnName + " = ?");
|
||||
" SELECT * FROM " + liaison.tableSQL(_valueTable) +
|
||||
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ?");
|
||||
stmt.setString(1, _pkColumnValue);
|
||||
if (stmt.executeQuery().next()) {
|
||||
return;
|
||||
@@ -72,8 +77,10 @@ public class TableKeyGenerator implements KeyGenerator
|
||||
|
||||
JDBCUtil.close(stmt);
|
||||
stmt = null;
|
||||
stmt = conn.prepareStatement("INSERT INTO " + _table + " SET " +
|
||||
_pkColumnName + " = ?, " + _valueColumnName + " = ?");
|
||||
stmt = conn.prepareStatement(
|
||||
" INSERT INTO " + liaison.tableSQL(_valueTable) + " (" +
|
||||
liaison.columnSQL(_pkColumnName) + ", " + liaison.columnSQL(_valueColumnName) +
|
||||
") VALUES (?, ?)");
|
||||
stmt.setString(1, _pkColumnValue);
|
||||
stmt.setInt(2, _initialValue);
|
||||
stmt.executeUpdate();
|
||||
@@ -84,27 +91,32 @@ public class TableKeyGenerator implements KeyGenerator
|
||||
}
|
||||
|
||||
// from interface KeyGenerator
|
||||
public int nextGeneratedValue (Connection conn)
|
||||
public int nextGeneratedValue (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
String query = "SELECT " + _valueColumnName + " FROM " + _table +
|
||||
" WHERE " + _pkColumnName + "= ? FOR UPDATE";
|
||||
// TODO: Make this lockless!
|
||||
String query =
|
||||
" SELECT " + liaison.columnSQL(_valueColumnName) +
|
||||
" FROM " + liaison.tableSQL(_valueTable) +
|
||||
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ? FOR UPDATE";
|
||||
stmt = conn.prepareStatement(query);
|
||||
stmt.setString(1, _pkColumnValue);
|
||||
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
if (!rs.next()) {
|
||||
throw new SQLException("Failed to find next primary key value [table=" + _table +
|
||||
", column=" + _valueColumnName +
|
||||
throw new SQLException("Failed to find next primary key value " +
|
||||
"[table=" + _valueTable + ", column=" + _valueColumnName +
|
||||
", where=" + _pkColumnName + "=" + _pkColumnValue + "]");
|
||||
}
|
||||
int val = rs.getInt(1);
|
||||
JDBCUtil.close(stmt);
|
||||
|
||||
stmt = conn.prepareStatement("UPDATE " + _table + " SET " + _valueColumnName + " = ? " +
|
||||
"WHERE " + _pkColumnName + " = ?");
|
||||
stmt = conn.prepareStatement(
|
||||
" UPDATE " + liaison.tableSQL(_valueTable) +
|
||||
" SET " + liaison.columnSQL(_valueColumnName) + " = ? " +
|
||||
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ?");
|
||||
stmt.setInt(1, val + _allocationSize);
|
||||
stmt.setString(2, _pkColumnValue);
|
||||
stmt.executeUpdate();
|
||||
@@ -126,10 +138,8 @@ public class TableKeyGenerator implements KeyGenerator
|
||||
return value;
|
||||
}
|
||||
|
||||
protected String _table;
|
||||
protected String _valueTable;
|
||||
protected String _pkColumnName;
|
||||
protected String _pkColumnValue;
|
||||
protected String _valueColumnName;
|
||||
protected int _initialValue;
|
||||
protected int _allocationSize;
|
||||
}
|
||||
|
||||
+10
@@ -18,3 +18,13 @@
|
||||
// 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 com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
|
||||
/**
|
||||
* Currently only exists as a type without any functionality of its own.
|
||||
*/
|
||||
public abstract class WhereClause extends QueryClause
|
||||
{
|
||||
}
|
||||
@@ -53,10 +53,9 @@ public @interface Column
|
||||
boolean nullable () default false;
|
||||
|
||||
/**
|
||||
* The SQL fragment that is used when generating the DDL for the column. Defaults to the
|
||||
* generated SQL to create a column of the inferred type.
|
||||
* The SQL type that is used when generating the DDL for the column.
|
||||
*/
|
||||
String columnDefinition () default "";
|
||||
String type() default "";
|
||||
|
||||
/**
|
||||
* The column length. (Applies to String and byte[] columns.)
|
||||
|
||||
@@ -25,13 +25,11 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* Marks a field as computed, meaning it is ignored for schema purposes and it does not directly
|
||||
* correspond to a column in a table, thus its value must be overridden in the {@link
|
||||
* QueryBuilderContext}.
|
||||
* correspond to a column in a table.
|
||||
*/
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.TYPE })
|
||||
|
||||
@@ -37,8 +37,4 @@ public @interface Entity
|
||||
|
||||
/** Defines indices to add to this entity's table. */
|
||||
Index[] indices () default {};
|
||||
|
||||
/** Additional SQL to be added when creating this entity's table for the first time. You can
|
||||
* specify a MySQL table storage type for example. */
|
||||
String postamble () default "";
|
||||
}
|
||||
|
||||
@@ -40,4 +40,18 @@ public @interface GeneratedValue
|
||||
|
||||
/** Identifies the strategy to be used to generate this value. */
|
||||
GenerationType strategy () default GenerationType.AUTO;
|
||||
|
||||
/**
|
||||
* The initial value to be used when allocating id numbers from the generator. The default
|
||||
* initial value is 1. <em>Note:</em> this default differs from the value used by the EJB3
|
||||
* persistence framework.
|
||||
*/
|
||||
int initialValue () default 1;
|
||||
|
||||
/**
|
||||
* The amount to increment by when allocating id numbers from the generator. The default
|
||||
* allocation size is 1. <em>Note:</em> this default differs from the value used by the EJB3
|
||||
* persistence framework.
|
||||
*/
|
||||
int allocationSize () default 1;
|
||||
}
|
||||
|
||||
@@ -63,18 +63,4 @@ public @interface TableGenerator
|
||||
* the primary key column of the generator table
|
||||
*/
|
||||
String pkColumnValue () default "";
|
||||
|
||||
/**
|
||||
* The initial value to be used when allocating id numbers from the generator. The default
|
||||
* initial value is 1. <em>Note:</em> this default differs from the value used by the EJB3
|
||||
* persistence framework.
|
||||
*/
|
||||
int initialValue () default 1;
|
||||
|
||||
/**
|
||||
* The amount to increment by when allocating id numbers from the generator. The default
|
||||
* allocation size is 1. <em>Note:</em> this default differs from the value used by the EJB3
|
||||
* persistence framework.
|
||||
*/
|
||||
int allocationSize () default 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// $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.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.WhereClause;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Builds actual SQL given a main persistent type and some {@link QueryClause} objects.
|
||||
*/
|
||||
public class DeleteClause<T extends PersistentRecord> extends QueryClause
|
||||
{
|
||||
public DeleteClause (Class<? extends PersistentRecord> pClass, WhereClause where)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_where = where;
|
||||
}
|
||||
|
||||
public Class<? extends PersistentRecord> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public WhereClause getWhereClause ()
|
||||
{
|
||||
return _where;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_pClass);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
/** The type of persistent record on which we operate. */
|
||||
protected Class<? extends PersistentRecord> _pClass;
|
||||
|
||||
/** The where clause. */
|
||||
protected WhereClause _where;
|
||||
}
|
||||
@@ -20,14 +20,12 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.clause;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.LiteralExp;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
@@ -68,11 +66,22 @@ public class FieldOverride extends QueryClause
|
||||
return _field;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
public SQLExpression getOverride ()
|
||||
{
|
||||
_override.appendExpression(query, builder);
|
||||
builder.append(" as ").append(_field);
|
||||
return _override;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_override.addClasses(classSet);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor visitor)
|
||||
throws Exception
|
||||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
/** The name of the field on the persistent object to override. */
|
||||
@@ -80,4 +89,5 @@ public class FieldOverride extends QueryClause
|
||||
|
||||
/** The overriding expression. */
|
||||
protected SQLExpression _override;
|
||||
|
||||
}
|
||||
|
||||
@@ -20,21 +20,24 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.clause;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Represents a FOR UPDATE clause.
|
||||
*/
|
||||
public class ForUpdate extends QueryClause
|
||||
{
|
||||
// from QueryClause
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
builder.append(" for update ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,13 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.clause;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Completely overrides the FROM clause, if it exists.
|
||||
@@ -54,27 +53,25 @@ public class FromOverride extends QueryClause
|
||||
_fromClasses.addAll(fromClasses);
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
public List<Class<? extends PersistentRecord>> getFromClasses ()
|
||||
{
|
||||
classSet.addAll(_fromClasses);
|
||||
return _fromClasses;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
builder.append(" from " );
|
||||
for (int ii = 0; ii < _fromClasses.size(); ii++) {
|
||||
if (ii > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append(query.getTableName(_fromClasses.get(ii))).
|
||||
append(" as ").append(query.getTableAbbreviation(_fromClasses.get(ii)));
|
||||
classSet.addAll(getFromClasses());
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
/** The classes of the tables we're selecting from. */
|
||||
protected ArrayList<Class<? extends PersistentRecord>> _fromClasses =
|
||||
protected List<Class<? extends PersistentRecord>> _fromClasses =
|
||||
new ArrayList<Class<? extends PersistentRecord>>();
|
||||
|
||||
}
|
||||
|
||||
@@ -20,12 +20,10 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.clause;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
@@ -38,28 +36,24 @@ public class GroupBy extends QueryClause
|
||||
_values = values;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
public SQLExpression[] getValues ()
|
||||
{
|
||||
builder.append(" group by ");
|
||||
for (int ii = 0; ii < _values.length; ii++) {
|
||||
if (ii > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
_values[ii].appendExpression(query, builder);
|
||||
}
|
||||
return _values;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
for (int ii = 0; ii < _values.length; ii++) {
|
||||
argIdx = _values[ii].bindExpressionArguments(pstmt, argIdx);
|
||||
builder.visit(this);
|
||||
}
|
||||
return argIdx;
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
// I can't imagine a GROUP BY clause bringing in new tables... ?
|
||||
}
|
||||
|
||||
/** The expressions that are generated for the clause. */
|
||||
protected SQLExpression[] _values;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// $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.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Builds actual SQL given a main persistent type and some {@link QueryClause} objects.
|
||||
*/
|
||||
public class InsertClause<T extends PersistentRecord> extends QueryClause
|
||||
{
|
||||
public InsertClause (Class<? extends PersistentRecord> pClass, Object pojo, String ixField)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_pojo = pojo;
|
||||
_ixField = ixField;
|
||||
}
|
||||
|
||||
public Class<? extends PersistentRecord> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public Object getPojo ()
|
||||
{
|
||||
return _pojo;
|
||||
}
|
||||
|
||||
public String getIndexField ()
|
||||
{
|
||||
return _ixField;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_pClass);
|
||||
// If we add SQLExpression[] values INSERT, remember to recurse into them here.
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
protected Class<? extends PersistentRecord> _pClass;
|
||||
|
||||
/** The object from which to fetch values, or null. */
|
||||
protected Object _pojo;
|
||||
|
||||
protected String _ixField;
|
||||
}
|
||||
@@ -20,16 +20,14 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.clause;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.*;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.Equals;
|
||||
|
||||
/**
|
||||
* Represents a JOIN.
|
||||
@@ -50,7 +48,7 @@ public class Join extends QueryClause
|
||||
public Join (ColumnExp primary, ColumnExp join)
|
||||
throws PersistenceException
|
||||
{
|
||||
_joinClass = join.pClass;
|
||||
_joinClass = join.getPersistentClass();
|
||||
_joinCondition = new Equals(primary, join);
|
||||
}
|
||||
|
||||
@@ -60,12 +58,6 @@ public class Join extends QueryClause
|
||||
_joinCondition = joinCondition;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_joinClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the type of join to be performed.
|
||||
*/
|
||||
@@ -75,30 +67,31 @@ public class Join extends QueryClause
|
||||
return this;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
public Type getType ()
|
||||
{
|
||||
switch (_type) {
|
||||
case INNER:
|
||||
builder.append(" inner join " );
|
||||
break;
|
||||
case LEFT_OUTER:
|
||||
builder.append(" left outer join " );
|
||||
break;
|
||||
case RIGHT_OUTER:
|
||||
builder.append(" right outer join " );
|
||||
break;
|
||||
}
|
||||
builder.append(query.getTableName(_joinClass)).append(" as ");
|
||||
builder.append(query.getTableAbbreviation(_joinClass)).append(" on ");
|
||||
_joinCondition.appendExpression(query, builder);
|
||||
return _type;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public Class<? extends PersistentRecord> getJoinClass ()
|
||||
{
|
||||
return _joinCondition.bindExpressionArguments(pstmt, argIdx);
|
||||
return _joinClass;
|
||||
}
|
||||
|
||||
public SQLExpression getJoinCondition ()
|
||||
{
|
||||
return _joinCondition;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_joinClass);
|
||||
}
|
||||
|
||||
/** Indicates the type of join to be performed. */
|
||||
|
||||
@@ -20,12 +20,10 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.clause;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Represents a LIMIT/OFFSET clause, for pagination.
|
||||
@@ -38,19 +36,25 @@ public class Limit extends QueryClause
|
||||
_count = count;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
public int getOffset ()
|
||||
{
|
||||
builder.append(" limit ? offset ? ");
|
||||
return _offset;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public int getCount ()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
pstmt.setObject(argIdx++, _count);
|
||||
pstmt.setObject(argIdx++, _offset);
|
||||
return argIdx;
|
||||
}
|
||||
|
||||
/** The first row of the result set to return. */
|
||||
|
||||
@@ -20,13 +20,10 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.clause;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.LiteralExp;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
@@ -46,22 +43,6 @@ public class OrderBy extends QueryClause
|
||||
return ascending(new LiteralExp("rand()"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an ascending order by clause on the supplied column.
|
||||
*/
|
||||
public static OrderBy ascending (String column)
|
||||
{
|
||||
return ascending(new ColumnExp(null, column));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a descending order by clause on the supplied column.
|
||||
*/
|
||||
public static OrderBy descending (String column)
|
||||
{
|
||||
return descending(new ColumnExp(null, column));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an ascending order by clause on the supplied expression.
|
||||
*/
|
||||
@@ -84,27 +65,28 @@ public class OrderBy extends QueryClause
|
||||
_orders = orders;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
public SQLExpression[] getValues ()
|
||||
{
|
||||
builder.append(" order by ");
|
||||
for (int ii = 0; ii < _values.length; ii++) {
|
||||
if (ii > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
_values[ii].appendExpression(query, builder);
|
||||
builder.append(" ").append(_orders[ii]);
|
||||
}
|
||||
return _values;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public Order[] getOrders ()
|
||||
{
|
||||
for (int ii = 0; ii < _values.length; ii++) {
|
||||
argIdx = _values[ii].bindExpressionArguments(pstmt, argIdx);
|
||||
return _orders;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
for (SQLExpression expression : _values) {
|
||||
expression.addClasses(classSet);
|
||||
}
|
||||
return argIdx;
|
||||
}
|
||||
|
||||
/** The expressions that are generated for the clause. */
|
||||
@@ -112,4 +94,5 @@ public class OrderBy extends QueryClause
|
||||
|
||||
/** Whether the ordering is to be ascending or descending. */
|
||||
protected Order[] _orders;
|
||||
|
||||
}
|
||||
|
||||
@@ -20,39 +20,11 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.clause;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Represents a piece or modifier of an SQL query.
|
||||
*/
|
||||
public abstract class QueryClause
|
||||
public abstract class QueryClause implements SQLExpression
|
||||
{
|
||||
/**
|
||||
* Adds all persistent classes referenced by this clause, if any, to the supplied set.
|
||||
*/
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs 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 abstract void appendClause (QueryBuilderContext<?> query, StringBuilder builder);
|
||||
|
||||
/**
|
||||
* Binds any objects that were referenced in the generated SQL. For each ? that appears in the
|
||||
* SQL, precisely one parameter must be claimed and bound in this method, and argIdx
|
||||
* incremented and returned. The default implementation binds nothing.
|
||||
*/
|
||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
{
|
||||
return argIdx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
//
|
||||
// $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.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.WhereClause;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Builds actual SQL given a main persistent type and some {@link QueryClause} objects.
|
||||
*/
|
||||
public class SelectClause<T extends PersistentRecord> extends QueryClause
|
||||
{
|
||||
/**
|
||||
* Creates a new Query object to generate one or more instances of the specified persistent
|
||||
* 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.
|
||||
*/
|
||||
public SelectClause (Class<T> pClass, String[] fields, QueryClause... clauses)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_fields = fields;
|
||||
|
||||
// iterate over the clauses and sort them into the different types we understand
|
||||
for (QueryClause clause : clauses) {
|
||||
if (clause == null) {
|
||||
continue;
|
||||
}
|
||||
if (clause instanceof WhereClause) {
|
||||
if (_where != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple Where clauses.");
|
||||
}
|
||||
_where = (WhereClause) clause;
|
||||
|
||||
} else if (clause instanceof FromOverride) {
|
||||
if (_fromOverride != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple FromOverride clauses.");
|
||||
}
|
||||
_fromOverride = (FromOverride) clause;
|
||||
|
||||
} else if (clause instanceof Join) {
|
||||
_joinClauses.add((Join) clause);
|
||||
|
||||
} 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 = (OrderBy) clause;
|
||||
|
||||
} else if (clause instanceof GroupBy) {
|
||||
if (_groupBy != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple GroupBy clauses.");
|
||||
}
|
||||
_groupBy = (GroupBy) clause;
|
||||
|
||||
} else if (clause instanceof Limit) {
|
||||
if (_limit != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple Limit clauses.");
|
||||
}
|
||||
_limit = (Limit) clause;
|
||||
|
||||
} else if (clause instanceof ForUpdate) {
|
||||
if (_forUpdate != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Query can't contain multiple For Update clauses.");
|
||||
}
|
||||
_forUpdate = (ForUpdate) clause;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FieldOverride lookupOverride (String field)
|
||||
{
|
||||
return _disMap.get(field);
|
||||
}
|
||||
|
||||
public Collection<FieldOverride> getFieldOverrides ()
|
||||
{
|
||||
return _disMap.values();
|
||||
}
|
||||
|
||||
public Class<T> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public String[] getFields ()
|
||||
{
|
||||
return _fields;
|
||||
}
|
||||
|
||||
public FromOverride getFromOverride ()
|
||||
{
|
||||
return _fromOverride;
|
||||
}
|
||||
|
||||
public WhereClause getWhereClause ()
|
||||
{
|
||||
return _where;
|
||||
}
|
||||
|
||||
public List<Join> getJoinClauses ()
|
||||
{
|
||||
return _joinClauses;
|
||||
}
|
||||
|
||||
public OrderBy getOrderBy ()
|
||||
{
|
||||
return _orderBy;
|
||||
}
|
||||
|
||||
public GroupBy getGroupBy ()
|
||||
{
|
||||
return _groupBy;
|
||||
}
|
||||
|
||||
public Limit getLimit ()
|
||||
{
|
||||
return _limit;
|
||||
}
|
||||
|
||||
public ForUpdate getForUpdate ()
|
||||
{
|
||||
return _forUpdate;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_pClass);
|
||||
if (_fromOverride != null) {
|
||||
_fromOverride.addClasses(classSet);
|
||||
}
|
||||
if (_where != null) {
|
||||
_where.addClasses(classSet);
|
||||
}
|
||||
for (Join join : _joinClauses) {
|
||||
join.addClasses(classSet);
|
||||
}
|
||||
for (FieldOverride override : _disMap.values()) {
|
||||
override.addClasses(classSet);
|
||||
}
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
/** Persistent class fields mapped to field override clauses. */
|
||||
protected Map<String, FieldOverride> _disMap = new HashMap<String, FieldOverride>();
|
||||
|
||||
/** The persistent class this select defines. */
|
||||
protected Class<T> _pClass;
|
||||
|
||||
/** The persistent fields to select. */
|
||||
protected String[] _fields;
|
||||
|
||||
/** The from override clause, if any. */
|
||||
protected FromOverride _fromOverride;
|
||||
|
||||
/** The where clause. */
|
||||
protected WhereClause _where;
|
||||
|
||||
/** A list of join clauses, each potentially referencing a new class. */
|
||||
protected List<Join> _joinClauses = new ArrayList<Join>();
|
||||
|
||||
/** The order by clause, if any. */
|
||||
protected OrderBy _orderBy;
|
||||
|
||||
/** The group by clause, if any. */
|
||||
protected GroupBy _groupBy;
|
||||
|
||||
/** The limit clause, if any. */
|
||||
protected Limit _limit;
|
||||
|
||||
/** The For Update clause, if any. */
|
||||
protected ForUpdate _forUpdate;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// $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.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.WhereClause;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Builds actual SQL given a main persistent type and some {@link QueryClause} objects.
|
||||
*/
|
||||
public class UpdateClause<T extends PersistentRecord> extends QueryClause
|
||||
{
|
||||
public UpdateClause (Class<? extends PersistentRecord> pClass, WhereClause where,
|
||||
String[] fields, T pojo)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_where = where;
|
||||
_fields = fields;
|
||||
_values = null;
|
||||
_pojo = pojo;
|
||||
}
|
||||
|
||||
public UpdateClause (Class<? extends PersistentRecord> pClass, WhereClause where,
|
||||
String[] fields, SQLExpression[] values)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_fields = fields;
|
||||
_where = where;
|
||||
_values = values;
|
||||
_pojo = null;
|
||||
}
|
||||
|
||||
public WhereClause getWhereClause ()
|
||||
{
|
||||
return _where;
|
||||
}
|
||||
|
||||
public String[] getFields ()
|
||||
{
|
||||
return _fields;
|
||||
}
|
||||
|
||||
public SQLExpression[] getValues ()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
public Object getPojo ()
|
||||
{
|
||||
return _pojo;
|
||||
}
|
||||
|
||||
public Class<? extends PersistentRecord> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_pClass);
|
||||
if (_where != null) {
|
||||
_where.addClasses(classSet);
|
||||
}
|
||||
if (_values != null) {
|
||||
for (int ii = 0; ii < _values.length; ii ++) {
|
||||
_values[ii].addClasses(classSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
/** The class we're updating. */
|
||||
protected Class<? extends PersistentRecord> _pClass;
|
||||
|
||||
/** The where clause. */
|
||||
protected WhereClause _where;
|
||||
|
||||
/** The persistent fields to update. */
|
||||
protected String[] _fields;
|
||||
|
||||
/** The field values, or null. */
|
||||
protected SQLExpression[] _values;
|
||||
|
||||
/** The object from which to fetch values, or null. */
|
||||
protected Object _pojo;
|
||||
}
|
||||
@@ -20,14 +20,12 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.clause;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.WhereClause;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.expression.ValueExp;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.Equals;
|
||||
@@ -38,13 +36,8 @@ import com.samskivert.jdbc.depot.operator.Logic.And;
|
||||
* Represents a where clause: the condition can be any comparison operator or logical combination
|
||||
* thereof.
|
||||
*/
|
||||
public class Where extends QueryClause
|
||||
public class Where extends WhereClause
|
||||
{
|
||||
public Where (String index, Comparable value)
|
||||
{
|
||||
this(new ColumnExp(index), value);
|
||||
}
|
||||
|
||||
public Where (ColumnExp column, Comparable value)
|
||||
{
|
||||
this(new ColumnExp[] { column }, new Comparable[] { value });
|
||||
@@ -64,18 +57,6 @@ public class Where extends QueryClause
|
||||
new Comparable[] { value1, value2, value3 });
|
||||
}
|
||||
|
||||
public Where (String index1, Comparable value1, String index2, Comparable value2)
|
||||
{
|
||||
this(new ColumnExp(index1), value1, new ColumnExp(index2), value2);
|
||||
}
|
||||
|
||||
public Where (String index1, Comparable value1, String index2, Comparable value2,
|
||||
String index3, Comparable value3)
|
||||
{
|
||||
this(new ColumnExp(index1), value1, new ColumnExp(index2), value2,
|
||||
new ColumnExp(index3), value3);
|
||||
}
|
||||
|
||||
public Where (ColumnExp[] columns, Comparable[] values)
|
||||
{
|
||||
this(toCondition(columns, values));
|
||||
@@ -86,18 +67,21 @@ public class Where extends QueryClause
|
||||
_condition = condition;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
public SQLExpression getCondition ()
|
||||
{
|
||||
builder.append(" where ");
|
||||
_condition.appendExpression(query, builder);
|
||||
return _condition;
|
||||
}
|
||||
|
||||
// from QueryClause
|
||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
return _condition.bindExpressionArguments(pstmt, argIdx);
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_condition.addClasses(classSet);
|
||||
}
|
||||
|
||||
protected static SQLExpression toCondition (ColumnExp[] columns, Comparable[] values)
|
||||
|
||||
@@ -20,53 +20,48 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.expression;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* 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 that unambiguously identifies a field of a class, e.g. GameRecord.itemId.
|
||||
*/
|
||||
public class ColumnExp
|
||||
implements SQLExpression
|
||||
{
|
||||
/** The table that hosts the column we reference, or null. */
|
||||
final public Class<? extends PersistentRecord> pClass;
|
||||
|
||||
/** The name of the column we reference. */
|
||||
final public String pColumn;
|
||||
|
||||
public ColumnExp (String column)
|
||||
{
|
||||
this(null, column);
|
||||
}
|
||||
|
||||
public ColumnExp (Class<? extends PersistentRecord> c, String column)
|
||||
public ColumnExp (Class<? extends PersistentRecord> pClass, String field)
|
||||
{
|
||||
super();
|
||||
pClass = c;
|
||||
this.pColumn = column;
|
||||
_pClass = pClass;
|
||||
_pField = field;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext<?> query, StringBuilder builder)
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
if (pClass == null || query == null) {
|
||||
builder.append(pColumn);
|
||||
} else {
|
||||
String tRef = query.getTableAbbreviation(pClass);
|
||||
builder.append(tRef).append(".").append(pColumn);
|
||||
}
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
return argIdx;
|
||||
classSet.add(_pClass);
|
||||
}
|
||||
|
||||
public Class<? extends PersistentRecord> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public String getField ()
|
||||
{
|
||||
return _pField;
|
||||
}
|
||||
|
||||
/** The table that hosts the column we reference, or null. */
|
||||
protected final Class<? extends PersistentRecord> _pClass;
|
||||
|
||||
/** The name of the column we reference. */
|
||||
protected final String _pField;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// $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.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.clause.DeleteClause;
|
||||
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.InsertClause;
|
||||
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.SelectClause;
|
||||
import com.samskivert.jdbc.depot.clause.UpdateClause;
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.In;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.IsNull;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.Match;
|
||||
import com.samskivert.jdbc.depot.operator.Logic.Not;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator;
|
||||
|
||||
/**
|
||||
* Enumerates visitation methods for every possible SQL expression type.
|
||||
*/
|
||||
public interface ExpressionVisitor
|
||||
{
|
||||
public void visit (FieldOverride fieldOverride)
|
||||
throws Exception;
|
||||
public void visit (WhereCondition<? extends PersistentRecord> whereCondition)
|
||||
throws Exception;
|
||||
public void visit (Key key)
|
||||
throws Exception;
|
||||
public void visit (MultiKey<? extends PersistentRecord> key)
|
||||
throws Exception;
|
||||
public void visit (FunctionExp functionExp)
|
||||
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 (Match 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 (SelectClause<? extends PersistentRecord> selectClause)
|
||||
throws Exception;
|
||||
public void visit (UpdateClause<? extends PersistentRecord> updateClause)
|
||||
throws Exception;
|
||||
public void visit (DeleteClause<? extends PersistentRecord> deleteClause)
|
||||
throws Exception;
|
||||
public void visit (InsertClause<? extends PersistentRecord> insertClause)
|
||||
throws Exception;
|
||||
}
|
||||
@@ -20,16 +20,14 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.expression;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* An expression for a function, e.g. FLOOR(blah).
|
||||
*/
|
||||
public class FunctionExp
|
||||
implements SQLExpression
|
||||
public class FunctionExp implements SQLExpression
|
||||
{
|
||||
/**
|
||||
* Create a new FunctionExp with the given function and arguments.
|
||||
@@ -41,27 +39,28 @@ public class FunctionExp
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
||||
public void accept (ExpressionVisitor builder)
|
||||
throws Exception
|
||||
{
|
||||
builder.append(_function);
|
||||
builder.append("(");
|
||||
for (int ii = 0; ii < _arguments.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
_arguments[ii].appendExpression(query, builder);
|
||||
}
|
||||
builder.append(")");
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
for (int ii = 0; ii < _arguments.length; ii ++) {
|
||||
argIdx = _arguments[ii].bindExpressionArguments(pstmt, argIdx);
|
||||
_arguments[ii].addClasses(classSet);
|
||||
}
|
||||
return argIdx;
|
||||
}
|
||||
|
||||
public String getFunction ()
|
||||
{
|
||||
return _function;
|
||||
}
|
||||
|
||||
public SQLExpression[] getArguments ()
|
||||
{
|
||||
return _arguments;
|
||||
}
|
||||
|
||||
/** The literal name of this function, e.g. FLOOR */
|
||||
|
||||
@@ -20,10 +20,9 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.expression;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* An expression for things we don't support natively, e.g. COUNT(*).
|
||||
@@ -38,16 +37,19 @@ public class LiteralExp
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.append(_text);
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
return argIdx;
|
||||
}
|
||||
|
||||
public String getText ()
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
|
||||
/** The literal text of this expression, e.g. COUNT(*) */
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.expression;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.SQLBuilder;
|
||||
|
||||
/**
|
||||
* Represents an SQL expression, e.g. column name, function, or constant.
|
||||
@@ -31,16 +31,19 @@ import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
public interface SQLExpression
|
||||
{
|
||||
/**
|
||||
* Construct the SQL form of this expression. The implementor is invited to call methods on the
|
||||
* Query object to e.g. resolve the current table abbreviations associated with classes.
|
||||
* Most uses of this class have been implemented with a visitor pattern. Create your own
|
||||
* {@link ExpressionVisitor} and call this method with it.
|
||||
*
|
||||
* @see SQLBuilder
|
||||
*/
|
||||
public void appendExpression (QueryBuilderContext<?> query, StringBuilder builder);
|
||||
public void accept (ExpressionVisitor builder)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Bind any objects that were referenced in the generated SQL. For each ? that appears in the
|
||||
* SQL, precisely one parameter must be claimed and bound in this method, and argIdx
|
||||
* incremented and returned.
|
||||
* Adds all persistent classes that are brought into the SQL context by this clause: FROM
|
||||
* clauses, JOINs, UPDATEs, anything that could create a new table abbreviation. This method
|
||||
* should recurse into any subordinate state that may in turn bring in new classes so that
|
||||
* sub-queries work correctly.
|
||||
*/
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException;
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet);
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.expression;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* A Java value that is bound as a parameter to the query, e.g. 1 or 'abc'.
|
||||
@@ -31,25 +30,27 @@ import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
public class ValueExp
|
||||
implements SQLExpression
|
||||
{
|
||||
public ValueExp (Comparable _value)
|
||||
public ValueExp (Object _value)
|
||||
{
|
||||
this._value = _value;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.append("?");
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
pstmt.setObject(argIdx ++, _value);
|
||||
return argIdx;
|
||||
}
|
||||
|
||||
public Object getValue ()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
/** The value to be bound to the SQL parameters. */
|
||||
protected Comparable _value;
|
||||
protected Object _value;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.operator;
|
||||
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
||||
|
||||
@@ -34,11 +33,6 @@ public abstract class Arithmetic
|
||||
/** The SQL '+' operator. */
|
||||
public static class Add extends BinaryOperator
|
||||
{
|
||||
public Add (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public Add (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -50,7 +44,7 @@ public abstract class Arithmetic
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "+";
|
||||
}
|
||||
@@ -59,11 +53,6 @@ public abstract class Arithmetic
|
||||
/** The SQL '-' operator. */
|
||||
public static class Sub extends BinaryOperator
|
||||
{
|
||||
public Sub (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public Sub (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -75,7 +64,7 @@ public abstract class Arithmetic
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "-";
|
||||
}
|
||||
@@ -84,11 +73,6 @@ public abstract class Arithmetic
|
||||
/** The SQL '*' operator. */
|
||||
public static class Mul extends BinaryOperator
|
||||
{
|
||||
public Mul (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public Mul (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -100,7 +84,7 @@ public abstract class Arithmetic
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "*";
|
||||
}
|
||||
@@ -109,11 +93,6 @@ public abstract class Arithmetic
|
||||
/** The SQL '/' operator. */
|
||||
public static class Div extends BinaryOperator
|
||||
{
|
||||
public Div (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public Div (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -125,7 +104,7 @@ public abstract class Arithmetic
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "/";
|
||||
}
|
||||
@@ -134,11 +113,6 @@ public abstract class Arithmetic
|
||||
/** The SQL '&' operator. */
|
||||
public static class BitAnd extends BinaryOperator
|
||||
{
|
||||
public BitAnd (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public BitAnd (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -150,7 +124,7 @@ public abstract class Arithmetic
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "&";
|
||||
}
|
||||
@@ -159,11 +133,6 @@ public abstract class Arithmetic
|
||||
/** The SQL '|' operator. */
|
||||
public static class BitOr extends BinaryOperator
|
||||
{
|
||||
public BitOr (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public BitOr (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -175,7 +144,7 @@ public abstract class Arithmetic
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "|";
|
||||
}
|
||||
|
||||
@@ -20,15 +20,12 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.operator;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
||||
|
||||
/**
|
||||
* A convenient container for implementations of conditional operators. Classes that value brevity
|
||||
@@ -56,31 +53,28 @@ public abstract class Conditionals
|
||||
_column = column;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
||||
public ColumnExp getColumn()
|
||||
{
|
||||
_column.appendExpression(query, builder);
|
||||
builder.append(" is null");
|
||||
return _column;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
return argIdx;
|
||||
}
|
||||
|
||||
protected ColumnExp _column;
|
||||
}
|
||||
|
||||
/** The SQL '=' operator. */
|
||||
public static class Equals extends BinaryOperator
|
||||
public static class Equals extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public Equals (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public Equals (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -92,20 +86,15 @@ public abstract class Conditionals
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "=";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '<' operator. */
|
||||
public static class LessThan extends BinaryOperator
|
||||
public static class LessThan extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public LessThan (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public LessThan (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -117,20 +106,15 @@ public abstract class Conditionals
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "<";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '<=' operator. */
|
||||
public static class LessThanEquals extends BinaryOperator
|
||||
public static class LessThanEquals extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public LessThanEquals (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public LessThanEquals (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -142,20 +126,15 @@ public abstract class Conditionals
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "<=";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '>' operator. */
|
||||
public static class GreaterThan extends BinaryOperator
|
||||
public static class GreaterThan extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public GreaterThan (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public GreaterThan (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -167,20 +146,15 @@ public abstract class Conditionals
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return ">";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '>=' operator. */
|
||||
public static class GreaterThanEquals extends BinaryOperator
|
||||
public static class GreaterThanEquals extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public GreaterThanEquals (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public GreaterThanEquals (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -192,7 +166,7 @@ public abstract class Conditionals
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return ">=";
|
||||
}
|
||||
@@ -202,16 +176,6 @@ public abstract class Conditionals
|
||||
public static class In
|
||||
implements SQLOperator
|
||||
{
|
||||
public In (String pColumn, Comparable... values)
|
||||
{
|
||||
this(new ColumnExp(null, pColumn), values);
|
||||
}
|
||||
|
||||
public In (String pColumn, Collection<? extends Comparable> values)
|
||||
{
|
||||
this(new ColumnExp(null, pColumn), values.toArray(new Comparable[values.size()]));
|
||||
}
|
||||
|
||||
public In (Class<? extends PersistentRecord> pClass, String pColumn, Comparable... values)
|
||||
{
|
||||
this(new ColumnExp(pClass, pColumn), values);
|
||||
@@ -232,107 +196,40 @@ public abstract class Conditionals
|
||||
_values = values;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
||||
public In (ColumnExp pColumn, Collection<? extends Comparable> values)
|
||||
{
|
||||
_column.appendExpression(query, builder);
|
||||
builder.append(" in (");
|
||||
for (int ii = 0; ii < _values.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
builder.append(", ");
|
||||
this(pColumn, values.toArray(new Comparable[values.size()]));
|
||||
}
|
||||
builder.append("?");
|
||||
|
||||
public ColumnExp getColumn ()
|
||||
{
|
||||
return _column;
|
||||
}
|
||||
builder.append(")");
|
||||
|
||||
public Comparable[] getValues ()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
for (int ii = 0; ii < _values.length; ii++) {
|
||||
pstmt.setObject(argIdx ++, _values[ii]);
|
||||
builder.visit(this);
|
||||
}
|
||||
return argIdx;
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_column.addClasses(classSet);
|
||||
}
|
||||
|
||||
protected ColumnExp _column;
|
||||
protected Comparable[] _values;
|
||||
}
|
||||
|
||||
/** The MySQL 'match (...) against (...)' operator. */
|
||||
public static class Match
|
||||
implements SQLOperator
|
||||
{
|
||||
public enum Mode { DEFAULT, BOOLEAN, NATURAL_LANGUAGE };
|
||||
|
||||
public Match (String query, Mode mode, boolean queryExpansion, String... pColumns)
|
||||
{
|
||||
_query = query;
|
||||
_mode = mode;
|
||||
_queryExpansion = queryExpansion;
|
||||
_columns = new ColumnExp[pColumns.length];
|
||||
for (int ii = 0; ii < pColumns.length; ii++) {
|
||||
_columns[ii] = new ColumnExp(null, pColumns[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
public Match (String query, Mode mode, boolean queryExpansion, ColumnExp... columns)
|
||||
{
|
||||
_query = query;
|
||||
_queryExpansion = queryExpansion;
|
||||
_mode = mode;
|
||||
_columns = columns;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
||||
{
|
||||
builder.append("match(");
|
||||
int idx = 0;
|
||||
for (ColumnExp column : _columns) {
|
||||
if (idx++ > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
column.appendExpression(query, builder);
|
||||
}
|
||||
builder.append(") against (?");
|
||||
switch (_mode) {
|
||||
case BOOLEAN:
|
||||
builder.append(" in boolean mode");
|
||||
break;
|
||||
case NATURAL_LANGUAGE:
|
||||
builder.append(" in natural language mode");
|
||||
break;
|
||||
}
|
||||
if (_queryExpansion) {
|
||||
builder.append(" with query expansion");
|
||||
}
|
||||
builder.append(")");
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
{
|
||||
pstmt.setString(argIdx++, _query);
|
||||
return argIdx;
|
||||
}
|
||||
|
||||
protected String _query;
|
||||
protected Mode _mode;
|
||||
protected boolean _queryExpansion;
|
||||
protected ColumnExp[] _columns;
|
||||
}
|
||||
|
||||
/** The SQL ' like ' operator. */
|
||||
public static class Like extends BinaryOperator
|
||||
public static class Like extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public Like (String pColumn, Comparable value)
|
||||
{
|
||||
super(new ColumnExp(pColumn), value);
|
||||
}
|
||||
|
||||
public Like (SQLExpression column, Comparable value)
|
||||
{
|
||||
super(column, value);
|
||||
@@ -344,9 +241,64 @@ public abstract class Conditionals
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return " like ";
|
||||
}
|
||||
}
|
||||
|
||||
/** The MySQL 'match (...) against (...)' operator. */
|
||||
@Deprecated
|
||||
public static class Match
|
||||
implements SQLOperator
|
||||
{
|
||||
public enum Mode { DEFAULT, BOOLEAN, NATURAL_LANGUAGE };
|
||||
|
||||
public Match (String query, Mode mode, boolean queryExpansion, ColumnExp... columns)
|
||||
{
|
||||
_query = query;
|
||||
_queryExpansion = queryExpansion;
|
||||
_mode = mode;
|
||||
_columns = columns;
|
||||
}
|
||||
|
||||
public String getQuery ()
|
||||
{
|
||||
return _query;
|
||||
}
|
||||
|
||||
public Mode getMode ()
|
||||
{
|
||||
return _mode;
|
||||
}
|
||||
|
||||
public boolean isQueryExpansion ()
|
||||
{
|
||||
return _queryExpansion;
|
||||
}
|
||||
|
||||
public ColumnExp[] getColumns ()
|
||||
{
|
||||
return _columns;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
for (ColumnExp column : _columns) {
|
||||
column.addClasses(classSet);
|
||||
}
|
||||
}
|
||||
|
||||
protected String _query;
|
||||
protected Mode _mode;
|
||||
protected boolean _queryExpansion;
|
||||
protected ColumnExp[] _columns;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.operator;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator;
|
||||
|
||||
/**
|
||||
* A convenient container for implementations of logical operators. Classes that value brevity
|
||||
@@ -37,7 +36,7 @@ public abstract class Logic
|
||||
/**
|
||||
* Represents a condition that is false iff all its subconditions are false.
|
||||
*/
|
||||
public static class Or extends MultiOperator
|
||||
public static class Or extends SQLOperator.MultiOperator
|
||||
{
|
||||
public Or (SQLExpression... conditions)
|
||||
{
|
||||
@@ -45,7 +44,7 @@ public abstract class Logic
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "or";
|
||||
}
|
||||
@@ -54,7 +53,7 @@ public abstract class Logic
|
||||
/**
|
||||
* Represents a condition that is true iff all its subconditions are true.
|
||||
*/
|
||||
public static class And extends MultiOperator
|
||||
public static class And extends SQLOperator.MultiOperator
|
||||
{
|
||||
public And (SQLExpression... conditions)
|
||||
{
|
||||
@@ -62,7 +61,7 @@ public abstract class Logic
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String operator()
|
||||
public String operator()
|
||||
{
|
||||
return "and";
|
||||
}
|
||||
@@ -76,23 +75,24 @@ public abstract class Logic
|
||||
{
|
||||
public Not (SQLExpression condition)
|
||||
{
|
||||
super();
|
||||
_condition = condition;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
||||
public SQLExpression getCondition ()
|
||||
{
|
||||
builder.append(" not (");
|
||||
_condition.appendExpression(query, builder);
|
||||
builder.append(")");
|
||||
return _condition;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
return _condition.bindExpressionArguments(pstmt, argIdx);
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_condition.addClasses(classSet);
|
||||
}
|
||||
|
||||
protected SQLExpression _condition;
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
|
||||
package com.samskivert.jdbc.depot.operator;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.expression.ValueExp;
|
||||
|
||||
@@ -42,37 +42,32 @@ public interface SQLOperator extends SQLExpression
|
||||
{
|
||||
public MultiOperator (SQLExpression ... conditions)
|
||||
{
|
||||
super();
|
||||
_conditions = conditions;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
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(")");
|
||||
}
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
for (int ii = 0; ii < _conditions.length; ii++) {
|
||||
argIdx = _conditions[ii].bindExpressionArguments(pstmt, argIdx);
|
||||
for (int ii = 0; ii < _conditions.length; ii ++) {
|
||||
_conditions[ii].addClasses(classSet);
|
||||
}
|
||||
return argIdx;
|
||||
}
|
||||
|
||||
public SQLExpression[] getConditions ()
|
||||
{
|
||||
return _conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text infix to be used to join expressions together.
|
||||
*/
|
||||
protected abstract String operator ();
|
||||
public abstract String operator ();
|
||||
|
||||
protected SQLExpression[] _conditions;
|
||||
}
|
||||
@@ -94,26 +89,32 @@ public interface SQLOperator extends SQLExpression
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
||||
public void accept (ExpressionVisitor builder) throws Exception
|
||||
{
|
||||
_lhs.appendExpression(query, builder);
|
||||
builder.append(operator());
|
||||
_rhs.appendExpression(query, builder);
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
||||
throws SQLException
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
argIdx = _lhs.bindExpressionArguments(pstmt, argIdx);
|
||||
argIdx = _rhs.bindExpressionArguments(pstmt, argIdx);
|
||||
return argIdx;
|
||||
_lhs.addClasses(classSet);
|
||||
_rhs.addClasses(classSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the operator.
|
||||
*/
|
||||
protected abstract String operator();
|
||||
public abstract String operator();
|
||||
|
||||
public SQLExpression getLeftHandSide ()
|
||||
{
|
||||
return _lhs;
|
||||
}
|
||||
|
||||
public SQLExpression getRightHandSide ()
|
||||
{
|
||||
return _rhs;
|
||||
}
|
||||
|
||||
protected SQLExpression _lhs;
|
||||
protected SQLExpression _rhs;
|
||||
|
||||
Reference in New Issue
Block a user