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.Transient;
|
||||||
import com.samskivert.jdbc.depot.annotation.UniqueConstraint;
|
import com.samskivert.jdbc.depot.annotation.UniqueConstraint;
|
||||||
|
|
||||||
import com.samskivert.jdbc.JDBCUtil;
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
import com.samskivert.jdbc.depot.clause.Where;
|
|
||||||
import com.samskivert.util.ArrayUtil;
|
import com.samskivert.util.ArrayUtil;
|
||||||
import com.samskivert.util.ListUtil;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import static com.samskivert.jdbc.depot.Log.log;
|
import static com.samskivert.jdbc.depot.Log.log;
|
||||||
|
|
||||||
@@ -75,7 +72,6 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
_pclass = pclass;
|
_pclass = pclass;
|
||||||
|
|
||||||
Entity entity = pclass.getAnnotation(Entity.class);
|
Entity entity = pclass.getAnnotation(Entity.class);
|
||||||
Table table = null;
|
|
||||||
|
|
||||||
// see if this is a computed entity
|
// see if this is a computed entity
|
||||||
Computed computed = pclass.getAnnotation(Computed.class);
|
Computed computed = pclass.getAnnotation(Computed.class);
|
||||||
@@ -89,11 +85,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
if (entity.name().length() > 0) {
|
if (entity.name().length() > 0) {
|
||||||
_tableName = entity.name();
|
_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
|
// 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);
|
FieldMarshaller fm = FieldMarshaller.createMarshaller(field);
|
||||||
_fields.put(fm.getColumnName(), fm);
|
_fields.put(field.getName(), fm);
|
||||||
fields.add(fm.getColumnName());
|
fields.add(field.getName());
|
||||||
|
|
||||||
// check to see if this is our primary key
|
// check to see if this is our primary key
|
||||||
if (field.getAnnotation(Id.class) != null) {
|
if (field.getAnnotation(Id.class) != null) {
|
||||||
@@ -181,7 +173,8 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
switch(gv.strategy()) {
|
switch(gv.strategy()) {
|
||||||
case AUTO:
|
case AUTO:
|
||||||
case IDENTITY:
|
case IDENTITY:
|
||||||
_keyGenerator = new IdentityKeyGenerator();
|
_keyGenerator = new IdentityKeyGenerator(
|
||||||
|
gv, getTableName(), keyField.getColumnName());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TABLE:
|
case TABLE:
|
||||||
@@ -191,7 +184,8 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Unknown generator [generator=" + name + "]");
|
"Unknown generator [generator=" + name + "]");
|
||||||
}
|
}
|
||||||
_keyGenerator = new TableKeyGenerator(generator);
|
_keyGenerator = new TableKeyGenerator(
|
||||||
|
generator, gv, getTableName(), keyField.getColumnName());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,54 +193,6 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
|
|
||||||
// generate our full list of fields/columns for use in queries
|
// generate our full list of fields/columns for use in queries
|
||||||
_allFields = fields.toArray(new String[fields.size()]);
|
_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;
|
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.
|
* 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
|
* Returns the {@link KeyGenerator} used to generate primary keys for this persistent object,
|
||||||
* persistent record.
|
* 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()];
|
String[] pkcols = new String[_pkColumns.size()];
|
||||||
for (int ii = 0; ii < pkcols.length; ii ++) {
|
for (int ii = 0; ii < pkcols.length; ii ++) {
|
||||||
pkcols[ii] = _pkColumns.get(ii).getColumnName();
|
pkcols[ii] = _pkColumns.get(ii).getField().getName();
|
||||||
}
|
}
|
||||||
return pkcols;
|
return pkcols;
|
||||||
}
|
}
|
||||||
@@ -359,11 +322,11 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
getClass().getName() + " does not define a primary key");
|
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++) {
|
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
|
// then create and populate the persistent object
|
||||||
T po = _pclass.newInstance();
|
T po = _pclass.newInstance();
|
||||||
for (FieldMarshaller fm : _fields.values()) {
|
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
|
// this field was not in the result set, make sure that's OK
|
||||||
if (fm.getComputed() != null && !fm.getComputed().required()) {
|
if (fm.getComputed() != null && !fm.getComputed().required()) {
|
||||||
continue;
|
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
|
* Fills in the primary key just assigned to the supplied persistence object by the execution
|
||||||
* of the results of {@link #createInsert}.
|
* 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
|
* @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.
|
* 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
|
throws SQLException
|
||||||
{
|
{
|
||||||
// if we have no primary key or no generator, then we're done
|
// if we have no primary key or no generator, then we're done
|
||||||
@@ -499,7 +423,7 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int nextValue = _keyGenerator.nextGeneratedValue(conn);
|
int nextValue = _keyGenerator.nextGeneratedValue(conn, liaison);
|
||||||
_pkColumns.get(0).getField().set(po, nextValue);
|
_pkColumns.get(0).getField().set(po, nextValue);
|
||||||
return makePrimaryKey(nextValue);
|
return makePrimaryKey(nextValue);
|
||||||
} catch (Exception e) {
|
} 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
|
* 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)
|
protected void init (PersistenceContext ctx)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
|
SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.TRIVIAL);
|
||||||
|
|
||||||
if (_initialized) { // sanity check
|
if (_initialized) { // sanity check
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Cannot re-initialize marshaller [type=" + _pclass + "].");
|
"Cannot re-initialize marshaller [type=" + _pclass + "].");
|
||||||
}
|
}
|
||||||
_initialized = true;
|
_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 we have no table (i.e. we're a computed entity), we have nothing to create
|
||||||
if (getTableName() == null) {
|
if (getTableName() == null) {
|
||||||
return;
|
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
|
// check to see if our schema version table exists, create it if not
|
||||||
ctx.invoke(new Modifier() {
|
ctx.invoke(new Modifier() {
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
JDBCUtil.createTableIfMissing(
|
liaison.createTableIfMissing(
|
||||||
conn, SCHEMA_VERSION_TABLE,
|
conn, SCHEMA_VERSION_TABLE,
|
||||||
new String[] { "persistentClass VARCHAR(255) NOT NULL",
|
new String[] { "persistentClass", "version" },
|
||||||
"version INTEGER NOT NULL" }, "");
|
new String[] { "VARCHAR(255) NOT NULL", "INTEGER NOT NULL" },
|
||||||
|
null,
|
||||||
|
new String[] { "persistentClass" });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// now create the table for our persistent class if it does not exist
|
// 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() {
|
ctx.invoke(new Modifier() {
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
if (!JDBCUtil.tableExists(conn, getTableName())) {
|
String[] columns = new String[_columnFields.length];
|
||||||
log.info("Creating table " + getTableName() + " (" + _declarations + ") " +
|
for (int ii = 0; ii < columns.length; ii ++) {
|
||||||
_postamble);
|
columns[ii] = _fields.get(_columnFields[ii]).getColumnName();
|
||||||
String[] definition = _declarations.toArray(new String[_declarations.size()]);
|
|
||||||
JDBCUtil.createTableIfMissing(conn, getTableName(), definition, _postamble);
|
|
||||||
updateVersion(conn, _schemaVersion);
|
|
||||||
}
|
}
|
||||||
|
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;
|
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 we have a key generator, initialize that too
|
||||||
if (_keyGenerator != null) {
|
if (_keyGenerator != null) {
|
||||||
ctx.invoke(new Modifier() {
|
ctx.invoke(new Modifier() {
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
_keyGenerator.init(conn);
|
_keyGenerator.init(conn, liaison);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -706,9 +580,12 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
|
|
||||||
// make sure the versions match
|
// make sure the versions match
|
||||||
int currentVersion = ctx.invoke(new Modifier() {
|
int currentVersion = ctx.invoke(new Modifier() {
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
String query = "select version from " + SCHEMA_VERSION_TABLE +
|
String query =
|
||||||
" where persistentClass = '" + getTableName() + "'";
|
" select " + liaison.columnSQL("version") +
|
||||||
|
" from " + liaison.tableSQL(SCHEMA_VERSION_TABLE) +
|
||||||
|
" where " + liaison.columnSQL("persistentClass") +
|
||||||
|
" = '" + getTableName() + "'";
|
||||||
Statement stmt = conn.createStatement();
|
Statement stmt = conn.createStatement();
|
||||||
try {
|
try {
|
||||||
ResultSet rs = stmt.executeQuery(query);
|
ResultSet rs = stmt.executeQuery(query);
|
||||||
@@ -723,6 +600,12 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
log.warning("Automatic schema migration is temporarily disabled.");
|
||||||
|
verifySchemasMatch(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// otherwise try to migrate the schema
|
// otherwise try to migrate the schema
|
||||||
log.info("Migrating " + getTableName() + " from " +
|
log.info("Migrating " + getTableName() + " from " +
|
||||||
currentVersion + " to " + _schemaVersion + "...");
|
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
|
// enumerate all columns and indexes now that we've run our pre-migrations
|
||||||
Set<String> columns = new HashSet<String>();
|
TableMetaData metaData = loadMetaData(ctx);
|
||||||
Map<String, Set<String>> indexColumns = new HashMap<String, Set<String>>();
|
|
||||||
loadMetaData(ctx, columns, indexColumns);
|
|
||||||
|
|
||||||
// this is a little silly, but we need a copy for name disambiguation later
|
// 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
|
// add any missing columns
|
||||||
for (String fname : _columnFields) {
|
for (String fname : _columnFields) {
|
||||||
FieldMarshaller fmarsh = _fields.get(fname);
|
@SuppressWarnings("unchecked") final FieldMarshaller<T> fmarsh = _fields.get(fname);
|
||||||
if (columns.remove(fmarsh.getColumnName())) {
|
if (metaData.tableColumns.remove(fmarsh.getColumnName())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise add the column
|
// otherwise add the column
|
||||||
String coldef = fmarsh.getColumnDefinition();
|
final String coldef = fmarsh.getColumnDefinition();
|
||||||
String query = "alter table " + getTableName() + " add column " + coldef;
|
log.info("Adding column to " + getTableName() + ": " +
|
||||||
|
fmarsh.getColumnName() + " " + coldef);
|
||||||
// try to add it to the appropriate spot
|
ctx.invoke(new Modifier.Simple() {
|
||||||
int fidx = ListUtil.indexOf(_allFields, fmarsh.getColumnName());
|
protected String createQuery (DatabaseLiaison liaison) {
|
||||||
if (fidx == 0) {
|
return "alter table " + liaison.tableSQL(getTableName()) +
|
||||||
query += " first";
|
" add column " + liaison.columnSQL(fmarsh.getColumnName()) + " " + coldef;
|
||||||
} else {
|
|
||||||
query += " after " + _allFields[fidx-1];
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
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
|
// 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
|
// 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.
|
// cannot accept CURRENT_TIME or NOW() defaults at all.
|
||||||
if (coldef.toLowerCase().indexOf(" timestamp") != -1 ||
|
if (coldef.toLowerCase().indexOf(" timestamp") != -1 ||
|
||||||
coldef.toLowerCase().indexOf(" datetime") != -1) {
|
coldef.toLowerCase().indexOf(" datetime") != -1) {
|
||||||
query = "update " + getTableName() + " set " + fmarsh.getColumnName() + " = NOW()";
|
log.info("Assigning current time to " + fmarsh.getColumnName() + ".");
|
||||||
log.info("Assigning current time to column: " + query);
|
ctx.invoke(new Modifier.Simple() {
|
||||||
ctx.invoke(new Modifier.Simple(query));
|
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
|
// TODO: This needs more work to be database-independent, I think. Assuming the index name
|
||||||
if (hasPrimaryKey() && !indexColumns.containsKey("PRIMARY")) {
|
// is going to be PRIMARY seems like a MySQL-ism, and we'll do the rest of the index work
|
||||||
String pkdef = "primary key (" + getJoinedKeyColumns() + ")";
|
// when we do that.
|
||||||
log.info("Adding primary key to " + getTableName() + ": " + pkdef);
|
|
||||||
ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " add " + pkdef));
|
|
||||||
|
|
||||||
} else if (!hasPrimaryKey() && indexColumns.containsKey("PRIMARY")) {
|
// // add or remove the primary key as needed
|
||||||
indexColumns.remove("PRIMARY");
|
// if (hasPrimaryKey() && !indexColumns.containsKey("PRIMARY")) {
|
||||||
log.info("Dropping primary from " + getTableName());
|
// String pkdef = "primary key (" + getJoinedKeyColumns() + ")";
|
||||||
ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " drop primary key"));
|
// log.info("Adding primary key to " + getTableName() + ": " + pkdef);
|
||||||
}
|
// ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " add " + pkdef));
|
||||||
|
//
|
||||||
// add any missing indices
|
// } else if (!hasPrimaryKey() && indexColumns.containsKey("PRIMARY")) {
|
||||||
Entity entity = _pclass.getAnnotation(Entity.class);
|
// indexColumns.remove("PRIMARY");
|
||||||
for (Index index : (entity == null ? new Index[0] : entity.indices())) {
|
// log.info("Dropping primary from " + getTableName());
|
||||||
if (indexColumns.containsKey(index.name())) {
|
// ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " drop primary key"));
|
||||||
indexColumns.remove(index.name());
|
// }
|
||||||
continue;
|
//
|
||||||
}
|
// // add any missing indices
|
||||||
String indexdef = "create " + index.type() + " index " + index.name() +
|
// for (Index index : (entity == null ? new Index[0] : entity.indices())) {
|
||||||
" on " + getTableName() + " (" + StringUtil.join(index.columns(), ", ") + ")";
|
// if (indexColumns.containsKey(index.name())) {
|
||||||
log.info("Adding index: " + indexdef);
|
// indexColumns.remove(index.name());
|
||||||
ctx.invoke(new Modifier.Simple(indexdef));
|
// continue;
|
||||||
}
|
// }
|
||||||
|
// String indexdef =
|
||||||
// to get the @Table(uniqueIndices...) indices, we use our clever set of column name sets
|
// "create " + index.type() + " index " + _liaison.indexSQL(index.name()) +
|
||||||
Set<Set<String>> uniqueIndices = new HashSet<Set<String>>(indexColumns.values());
|
// " on " + _liaison.tableSQL(getTableName()) + " (" + StringUtil.join(index.columns(), ", ") + ")";
|
||||||
Table table;
|
// log.info("Adding index: " + indexdef);
|
||||||
if (getTableName() != null && (table = _pclass.getAnnotation(Table.class)) != null) {
|
// ctx.invoke(new Modifier.Simple(indexdef));
|
||||||
Set<String> colSet = new HashSet<String>();
|
// }
|
||||||
for (UniqueConstraint constraint : table.uniqueConstraints()) {
|
//
|
||||||
// for each given UniqueConstraint, build a new column set
|
// // to get the @Table(uniqueIndices...) indices, we use our clever set of column name sets
|
||||||
colSet.clear();
|
// Set<Set<String>> uniqueIndices = new HashSet<Set<String>>(indexColumns.values());
|
||||||
for (String column : constraint.columnNames()) {
|
// Table table;
|
||||||
colSet.add(column);
|
// if (getTableName() != null && (table = _pclass.getAnnotation(Table.class)) != null) {
|
||||||
}
|
// Set<String> colSet = new HashSet<String>();
|
||||||
// and check if the table contained this set
|
// for (UniqueConstraint constraint : table.uniqueConstraints()) {
|
||||||
if (uniqueIndices.contains(colSet)) {
|
// // for each given UniqueConstraint, build a new column set
|
||||||
continue; // good, carry on
|
// colSet.clear();
|
||||||
}
|
// for (String column : constraint.columnNames()) {
|
||||||
|
// colSet.add(column);
|
||||||
// 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!
|
// // and check if the table contained this set
|
||||||
String indexName = colSet.iterator().next();
|
// if (uniqueIndices.contains(colSet)) {
|
||||||
if (indicesCopy.contains(indexName)) {
|
// continue; // good, carry on
|
||||||
int num = 1;
|
// }
|
||||||
indexName += "_";
|
//
|
||||||
while (indicesCopy.contains(indexName + num)) {
|
// // else build the index; we'll use mysql's convention of naming it after a column,
|
||||||
num ++;
|
// // with possible _N disambiguation; luckily we made a copy of the index names!
|
||||||
}
|
// String indexName = colSet.iterator().next();
|
||||||
indexName += num;
|
// if (indicesCopy.contains(indexName)) {
|
||||||
}
|
// int num = 1;
|
||||||
|
// indexName += "_";
|
||||||
String[] columnArr = colSet.toArray(new String[colSet.size()]);
|
// while (indicesCopy.contains(indexName + num)) {
|
||||||
String indexdef = "create unique index " + indexName + " on " +
|
// num ++;
|
||||||
getTableName() + " (" + StringUtil.join(columnArr, ", ") + ")";
|
// }
|
||||||
log.info("Adding unique index: " + indexdef);
|
// indexName += num;
|
||||||
ctx.invoke(new Modifier.Simple(indexdef));
|
// }
|
||||||
}
|
//
|
||||||
}
|
// 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
|
// 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
|
// 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
|
// 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
|
// record our new version in the database
|
||||||
ctx.invoke(new Modifier() {
|
ctx.invoke(new Modifier() {
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
updateVersion(conn, _schemaVersion);
|
updateVersion(conn, liaison, _schemaVersion);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected TableMetaData loadMetaData (PersistenceContext ctx)
|
||||||
* 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)
|
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
ctx.invoke(new Modifier() {
|
return ctx.invoke(new Query.TrivialQuery<TableMetaData>() {
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public TableMetaData invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
DatabaseMetaData meta = conn.getMetaData();
|
throws SQLException {
|
||||||
ResultSet rs = meta.getColumns(null, null, getTableName(), "%");
|
return new TableMetaData(conn.getMetaData());
|
||||||
while (rs.next()) {
|
}
|
||||||
columns.add(rs.getString("COLUMN_NAME"));
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
rs = meta.getIndexInfo(null, null, getTableName(), false, false);
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
String indexName = rs.getString("INDEX_NAME");
|
String indexName = rs.getString("INDEX_NAME");
|
||||||
@@ -900,11 +796,13 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
set.add(rs.getString("COLUMN_NAME"));
|
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)
|
protected void verifySchemasMatch (PersistenceContext ctx)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
Set<String> columns = new HashSet<String>();
|
TableMetaData meta = loadMetaData(ctx);
|
||||||
loadMetaData(ctx, columns, null);
|
|
||||||
for (String fname : _columnFields) {
|
for (String fname : _columnFields) {
|
||||||
FieldMarshaller fmarsh = _fields.get(fname);
|
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 + "'.");
|
log.warning(getTableName() + " contains stale column '" + column + "'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getJoinedKeyColumns ()
|
protected void updateVersion (Connection conn, DatabaseLiaison liaison, int version)
|
||||||
{
|
|
||||||
return StringUtil.join(getPrimaryKeyColumns(), ", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateVersion (Connection conn, int version)
|
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
String update = "update " + SCHEMA_VERSION_TABLE +
|
String update =
|
||||||
" set version = " + version + " where persistentClass = '" + getTableName() + "'";
|
"update " + liaison.tableSQL(SCHEMA_VERSION_TABLE) +
|
||||||
String insert = "insert into " + SCHEMA_VERSION_TABLE +
|
" set " + liaison.columnSQL("version") + " = " + version +
|
||||||
" values('" + getTableName() + "', " + version + ")";
|
" where " + liaison.columnSQL("persistentClass") + " = '" + getTableName() + "'";
|
||||||
Statement stmt = conn.createStatement();
|
Statement stmt = conn.createStatement();
|
||||||
try {
|
try {
|
||||||
if (stmt.executeUpdate(update) == 0) {
|
if (stmt.executeUpdate(update) == 0) {
|
||||||
|
String insert =
|
||||||
|
"insert into " + liaison.tableSQL(SCHEMA_VERSION_TABLE) +
|
||||||
|
" values('" + getTableName() + "', " + version + ")";
|
||||||
stmt.executeUpdate(insert);
|
stmt.executeUpdate(insert);
|
||||||
}
|
}
|
||||||
} finally {
|
} 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. */
|
/** The persistent object class that we manage. */
|
||||||
protected Class<T> _pclass;
|
protected Class<T> _pclass;
|
||||||
|
|
||||||
@@ -962,10 +848,10 @@ public class DepotMarshaller<T extends PersistentRecord>
|
|||||||
protected String _tableName;
|
protected String _tableName;
|
||||||
|
|
||||||
/** A field marshaller for each persistent field in our object. */
|
/** 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
|
/** The field marshallers for our persistent object's primary key columns or null if it did not
|
||||||
* or null if it did not define a primary key. */
|
* define a primary key. */
|
||||||
protected ArrayList<FieldMarshaller> _pkColumns;
|
protected ArrayList<FieldMarshaller> _pkColumns;
|
||||||
|
|
||||||
/** The generator to use for auto-generating primary key values, or null. */
|
/** 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. */
|
/** The fields of our object with directly corresponding table columns. */
|
||||||
protected String[] _columnFields;
|
protected String[] _columnFields;
|
||||||
|
|
||||||
/** The version of our persistent object schema as specified in the class
|
/** The version of our persistent object schema as specified in the class definition. */
|
||||||
* definition. */
|
|
||||||
protected int _schemaVersion = -1;
|
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). */
|
/** Indicates that we have been initialized (created or migrated our tables). */
|
||||||
protected boolean _initialized;
|
protected boolean _initialized;
|
||||||
|
|
||||||
|
|||||||
@@ -29,15 +29,22 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.util.ArrayUtil;
|
||||||
|
|
||||||
import com.samskivert.jdbc.ConnectionProvider;
|
import com.samskivert.jdbc.ConnectionProvider;
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
import com.samskivert.jdbc.JDBCUtil;
|
import com.samskivert.jdbc.JDBCUtil;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.Modifier.*;
|
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.FieldOverride;
|
||||||
import com.samskivert.jdbc.depot.clause.FromOverride;
|
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.Join;
|
||||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||||
import com.samskivert.jdbc.depot.clause.Where;
|
import com.samskivert.jdbc.depot.clause.UpdateClause;
|
||||||
import com.samskivert.util.ArrayUtil;
|
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
|
* 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)
|
protected <T extends PersistentRecord> int insert (T record)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
|
@SuppressWarnings("unchecked") final Class<T> pClass = (Class<T>) record.getClass();
|
||||||
final Key key = marsh.getPrimaryKey(record, false);
|
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
|
// key will be null if record was supplied without a primary key
|
||||||
return _ctx.invoke(new CachingModifier<T>(record, key, 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
|
// update our modifier's key so that it can cache our results
|
||||||
updateKey(marsh.assignPrimaryKey(conn, _result, false));
|
if (_key == null) {
|
||||||
PreparedStatement stmt = marsh.createInsert(conn, _result);
|
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 {
|
try {
|
||||||
int mods = stmt.executeUpdate();
|
int mods = stmt.executeUpdate();
|
||||||
// check again in case we have a post-factum key generator
|
// 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;
|
return mods;
|
||||||
} finally {
|
} finally {
|
||||||
JDBCUtil.close(stmt);
|
JDBCUtil.close(stmt);
|
||||||
@@ -192,14 +217,22 @@ public class DepotRepository
|
|||||||
protected <T extends PersistentRecord> int update (T record)
|
protected <T extends PersistentRecord> int update (T record)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
|
@SuppressWarnings("unchecked") Class<T> pClass = (Class<T>) record.getClass();
|
||||||
final Key key = marsh.getPrimaryKey(record);
|
requireNotComputed(pClass, "update");
|
||||||
|
|
||||||
|
DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||||
|
Key key = marsh.getPrimaryKey(record);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new IllegalArgumentException("Can't update record with null primary key.");
|
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) {
|
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 {
|
||||||
PreparedStatement stmt = marsh.createUpdate(conn, _result, key);
|
PreparedStatement stmt = builder.prepare(conn);
|
||||||
try {
|
try {
|
||||||
return stmt.executeUpdate();
|
return stmt.executeUpdate();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -220,14 +253,26 @@ public class DepotRepository
|
|||||||
protected <T extends PersistentRecord> int update (T record, final String... modifiedFields)
|
protected <T extends PersistentRecord> int update (T record, final String... modifiedFields)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
|
@SuppressWarnings("unchecked")
|
||||||
final Key key = marsh.getPrimaryKey(record);
|
Class<T> pClass = (Class<T>) record.getClass();
|
||||||
|
|
||||||
|
requireNotComputed(pClass, "updatePartial");
|
||||||
|
|
||||||
|
DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||||
|
Key key = marsh.getPrimaryKey(record);
|
||||||
|
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new IllegalArgumentException("Can't update record with null primary key.");
|
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) {
|
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 {
|
||||||
PreparedStatement stmt = marsh.createUpdate(conn, _result, key, modifiedFields);
|
PreparedStatement stmt = builder.prepare(conn);
|
||||||
// clear out _result so that we don't rewrite this partial record to the cache
|
// clear out _result so that we don't rewrite this partial record to the cache
|
||||||
_result = null;
|
_result = null;
|
||||||
try {
|
try {
|
||||||
@@ -336,21 +381,23 @@ public class DepotRepository
|
|||||||
* @return the number of rows modified by this action.
|
* @return the number of rows modified by this action.
|
||||||
*/
|
*/
|
||||||
protected <T extends PersistentRecord> int updatePartial (
|
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
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
// separate the arguments into keys and values
|
// separate the arguments into keys and values
|
||||||
final String[] fields = new String[fieldsValues.length/2];
|
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++) {
|
for (int ii = 0, idx = 0; ii < fields.length; ii++) {
|
||||||
fields[ii] = (String)fieldsValues[idx++];
|
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) {
|
return _ctx.invoke(new Modifier(invalidator) {
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
PreparedStatement stmt = marsh.createPartialUpdate(conn, key, fields, values);
|
PreparedStatement stmt = builder.prepare(conn);
|
||||||
try {
|
try {
|
||||||
return stmt.executeUpdate();
|
return stmt.executeUpdate();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -378,11 +425,11 @@ public class DepotRepository
|
|||||||
* @return the number of rows modified by this action.
|
* @return the number of rows modified by this action.
|
||||||
*/
|
*/
|
||||||
protected <T extends PersistentRecord> int updateLiteral (
|
protected <T extends PersistentRecord> int updateLiteral (
|
||||||
Class<T> type, Comparable primaryKey, String... fieldsValues)
|
Class<T> type, Comparable primaryKey, Map<String, SQLExpression> fieldsToValues)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
Key<T> key = _ctx.getMarshaller(type).makePrimaryKey(primaryKey);
|
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 (
|
protected <T extends PersistentRecord> int updateLiteral (
|
||||||
Class<T> type, String ix1, Comparable val1, String ix2, Comparable val2,
|
Class<T> type, String ix1, Comparable val1, String ix2, Comparable val2,
|
||||||
String... fieldsValues)
|
Map<String, SQLExpression> fieldsToValues)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
Key<T> key = new Key<T>(type, ix1, val1, ix2, val2);
|
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 (
|
protected <T extends PersistentRecord> int updateLiteral (
|
||||||
Class<T> type, String ix1, Comparable val1, String ix2, Comparable val2,
|
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
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
Key<T> key = new Key<T>(type, ix1, val1, ix2, val2, ix3, val3);
|
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.
|
* @return the number of rows modified by this action.
|
||||||
*/
|
*/
|
||||||
protected <T extends PersistentRecord> int updateLiteral (
|
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
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
|
requireNotComputed(type, "updateLiteral");
|
||||||
|
|
||||||
// separate the arguments into keys and values
|
// separate the arguments into keys and values
|
||||||
final String[] fields = new String[fieldsValues.length/2];
|
final String[] fields = new String[fieldsToValues.size()];
|
||||||
final String[] values = new String[fields.length];
|
final SQLExpression[] values = new SQLExpression[fields.length];
|
||||||
for (int ii = 0, idx = 0; ii < fields.length; ii++) {
|
int ii = 0;
|
||||||
fields[ii] = fieldsValues[idx++];
|
for (Map.Entry<String, SQLExpression> entry : fieldsToValues.entrySet()) {
|
||||||
values[ii] = fieldsValues[idx++];
|
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) {
|
return _ctx.invoke(new Modifier(invalidator) {
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
PreparedStatement stmt = marsh.createLiteralUpdate(conn, key, fields, values);
|
PreparedStatement stmt = builder.prepare(conn);
|
||||||
try {
|
try {
|
||||||
return stmt.executeUpdate();
|
return stmt.executeUpdate();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -489,17 +544,25 @@ public class DepotRepository
|
|||||||
protected <T extends PersistentRecord> boolean store (T record)
|
protected <T extends PersistentRecord> boolean store (T record)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
|
@SuppressWarnings("unchecked") final Class<T> pClass = (Class<T>) record.getClass();
|
||||||
final Key key = marsh.hasPrimaryKey() ? marsh.getPrimaryKey(record) : null;
|
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];
|
final boolean[] created = new boolean[1];
|
||||||
_ctx.invoke(new CachingModifier<T>(record, key, key) {
|
_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;
|
PreparedStatement stmt = null;
|
||||||
try {
|
try {
|
||||||
// if our primary key isn't null, update rather than insert the record
|
// if our primary key isn't null, update rather than insert the record before
|
||||||
// before been persisted and insert
|
// been persisted and insert
|
||||||
if (key != null) {
|
if (_key != null) {
|
||||||
stmt = marsh.createUpdate(conn, _result, key);
|
stmt = builder.prepare(conn);
|
||||||
int mods = stmt.executeUpdate();
|
int mods = stmt.executeUpdate();
|
||||||
if (mods > 0) {
|
if (mods > 0) {
|
||||||
return mods;
|
return mods;
|
||||||
@@ -509,10 +572,22 @@ public class DepotRepository
|
|||||||
|
|
||||||
// if the update modified zero rows or the primary key was obviously unset, do
|
// if the update modified zero rows or the primary key was obviously unset, do
|
||||||
// an insertion
|
// an insertion
|
||||||
updateKey(marsh.assignPrimaryKey(conn, _result, false));
|
if (_key == null) {
|
||||||
stmt = marsh.createInsert(conn, _result);
|
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();
|
int mods = stmt.executeUpdate();
|
||||||
updateKey(marsh.assignPrimaryKey(conn, _result, true));
|
if (_key == null) {
|
||||||
|
updateKey(marsh.assignPrimaryKey(conn, liaison, _result, true));
|
||||||
|
}
|
||||||
created[0] = true;
|
created[0] = true;
|
||||||
return mods;
|
return mods;
|
||||||
|
|
||||||
@@ -571,13 +646,16 @@ public class DepotRepository
|
|||||||
* @return the number of rows deleted by this action.
|
* @return the number of rows deleted by this action.
|
||||||
*/
|
*/
|
||||||
protected <T extends PersistentRecord> int deleteAll (
|
protected <T extends PersistentRecord> int deleteAll (
|
||||||
Class<T> type, final Where key, CacheInvalidator invalidator)
|
Class<T> type, final WhereClause key, CacheInvalidator invalidator)
|
||||||
throws PersistenceException
|
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) {
|
return _ctx.invoke(new Modifier(invalidator) {
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
PreparedStatement stmt = marsh.createDelete(conn, key);
|
PreparedStatement stmt = builder.prepare(conn);
|
||||||
try {
|
try {
|
||||||
return stmt.executeUpdate();
|
return stmt.executeUpdate();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -587,25 +665,18 @@ public class DepotRepository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static abstract class CollectionQuery<T extends Collection> implements Query<T>
|
// make sure the given type corresponds to a concrete class
|
||||||
{
|
protected void requireNotComputed (Class<? extends PersistentRecord> type, String action)
|
||||||
public CollectionQuery (CacheKey key)
|
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
_key = key;
|
DepotMarshaller marsh = _ctx.getMarshaller(type);
|
||||||
|
if (marsh == null) {
|
||||||
|
throw new PersistenceException("Unknown persistent type [class=" + type + "]");
|
||||||
}
|
}
|
||||||
|
if (marsh.getTableName() == null) {
|
||||||
public CacheKey getCacheKey ()
|
throw new PersistenceException(
|
||||||
{
|
"Can't " + action + " computed entities [class=" + type + "]");
|
||||||
return _key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCache (PersistenceContext ctx, T result)
|
|
||||||
{
|
|
||||||
ctx.cacheStore(_key, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CacheKey _key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PersistenceContext _ctx;
|
protected PersistenceContext _ctx;
|
||||||
|
|||||||
@@ -20,52 +20,60 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot;
|
package com.samskivert.jdbc.depot;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
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
|
* Maintains a record of the persistent classes brought into the context of the associated SQL,
|
||||||
* classes used in the context of a single query and maps them to {@link DepotMarshaller}
|
* i.e. any class associated with a concrete table that would appear in FROM or JOIN clauses or as
|
||||||
* instances as well as table names and table abbreviations.
|
* 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 main motivation for breaking this functionality out into its own class is to encapsulate the
|
||||||
* the operation that throws {@link PersistenceException} as separate from the operations that
|
* operation that throws {@link PersistenceException} as separate from the operations that throw
|
||||||
* throw {@link SQLException}.
|
* {@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>
|
public class DepotTypes
|
||||||
implements QueryBuilderContext<T>
|
|
||||||
{
|
{
|
||||||
public DepotTypes (PersistenceContext ctx,
|
/** A trivial instance that is accessible in places where we want the dialectal benefits of the
|
||||||
Class<? extends PersistentRecord> main,
|
* SQLBuilder without really requiring per-persistent-class context. */
|
||||||
Set<Class<? extends PersistentRecord>> others)
|
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
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
_mainType = main;
|
Set<Class<? extends PersistentRecord>> classSet =
|
||||||
_classMap = new HashMap<Class, DepotMarshaller>();
|
new HashSet<Class<? extends PersistentRecord>>();
|
||||||
|
for (QueryClause clause : clauses) {
|
||||||
for (Class<? extends PersistentRecord> c : others) {
|
if (clause != null) {
|
||||||
_classMap.put(c, ctx.getMarshaller(c));
|
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)
|
public String getTableName (Class<? extends PersistentRecord> cl)
|
||||||
{
|
{
|
||||||
return _classMap.get(cl).getTableName();
|
return _classMap.get(cl).getTableName();
|
||||||
}
|
}
|
||||||
|
|
||||||
// from ConstructedQuery
|
|
||||||
public String getTableAbbreviation (Class<? extends PersistentRecord> cl)
|
public String getTableAbbreviation (Class<? extends PersistentRecord> cl)
|
||||||
{
|
{
|
||||||
int ix = _classList.indexOf(cl);
|
int ix = _classList.indexOf(cl);
|
||||||
@@ -75,32 +83,42 @@ public class DepotTypes<T>
|
|||||||
return "T" + (ix+1);
|
return "T" + (ix+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getColumnName (Class<? extends PersistentRecord> cl, String field)
|
||||||
|
{
|
||||||
|
return getMarshaller(cl).getFieldMarshaller(field).getColumnName();
|
||||||
|
}
|
||||||
|
|
||||||
public DepotMarshaller getMarshaller (Class cl)
|
public DepotMarshaller getMarshaller (Class cl)
|
||||||
{
|
{
|
||||||
return _classMap.get(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. */
|
/** 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. */
|
/** 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.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.util.Map;
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
import com.samskivert.jdbc.JDBCUtil;
|
import com.samskivert.jdbc.JDBCUtil;
|
||||||
|
import com.samskivert.jdbc.LiaisonRegistry;
|
||||||
|
|
||||||
import static com.samskivert.jdbc.depot.Log.log;
|
import static com.samskivert.jdbc.depot.Log.log;
|
||||||
|
|
||||||
@@ -47,21 +48,15 @@ public abstract class EntityMigration extends Modifier
|
|||||||
_columnName = columnName;
|
_columnName = columnName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
if (!JDBCUtil.tableContainsColumn(conn, _tableName, _columnName)) {
|
if (!JDBCUtil.tableContainsColumn(conn, _tableName, _columnName)) {
|
||||||
// we'll accept this inconsistency
|
// we'll accept this inconsistency
|
||||||
log.warning(_tableName + "." + _columnName + " already dropped.");
|
log.warning(_tableName + "." + _columnName + " already dropped.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement stmt = conn.createStatement();
|
|
||||||
try {
|
|
||||||
log.info("Dropping '" + _columnName + "' from " + _tableName);
|
log.info("Dropping '" + _columnName + "' from " + _tableName);
|
||||||
return stmt.executeUpdate(
|
return liaison.dropColumn(conn, _tableName, _columnName) ? 1 : 0;
|
||||||
"alter table " + _tableName + " drop column " + _columnName);
|
|
||||||
} finally {
|
|
||||||
stmt.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String _columnName;
|
protected String _columnName;
|
||||||
@@ -78,7 +73,7 @@ public abstract class EntityMigration extends Modifier
|
|||||||
_newColumnName = newColumnName;
|
_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, _oldColumnName)) {
|
||||||
if (JDBCUtil.tableContainsColumn(conn, _tableName, _newColumnName)) {
|
if (JDBCUtil.tableContainsColumn(conn, _tableName, _newColumnName)) {
|
||||||
// we'll accept this inconsistency
|
// we'll accept this inconsistency
|
||||||
@@ -97,60 +92,46 @@ public abstract class EntityMigration extends Modifier
|
|||||||
_tableName + " already contains '" + _newColumnName + "'");
|
_tableName + " already contains '" + _newColumnName + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement stmt = conn.createStatement();
|
log.info("Renaming '" + _oldColumnName + "' to '" + _newColumnName + "' in: " +
|
||||||
try {
|
|
||||||
log.info("Changing '" + _oldColumnName + "' to '" + _newColumnDef + "' in " +
|
|
||||||
_tableName);
|
_tableName);
|
||||||
return stmt.executeUpdate("alter table " + _tableName + " change column " +
|
return liaison.renameColumn(conn, _tableName, _oldColumnName, _newColumnName) ? 1 : 0;
|
||||||
_oldColumnName + " " + _newColumnDef);
|
|
||||||
} finally {
|
|
||||||
stmt.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean runBeforeDefault () {
|
public boolean runBeforeDefault () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init (String tableName, HashMap<String,FieldMarshaller> marshallers) {
|
protected String _oldColumnName, _newColumnName;
|
||||||
super.init(tableName, marshallers);
|
|
||||||
_newColumnDef = marshallers.get(_newColumnName).getColumnDefinition();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String _oldColumnName, _newColumnName, _newColumnDef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 static class Retype extends EntityMigration
|
||||||
{
|
{
|
||||||
public Retype (int targetVersion, String columnName) {
|
public Retype (int targetVersion, String fieldName) {
|
||||||
super(targetVersion);
|
super(targetVersion);
|
||||||
_columnName = columnName;
|
_fieldName = fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
Statement stmt = conn.createStatement();
|
log.info("Updating type of '" + _fieldName + "' in " + _tableName);
|
||||||
try {
|
return liaison.changeColumn(conn, _tableName, _fieldName, _newColumnDef) ? 1 : 0;
|
||||||
log.info("Updating type of '" + _columnName + "' in " + _tableName);
|
|
||||||
return stmt.executeUpdate("alter table " + _tableName + " change column " +
|
|
||||||
_columnName + " " + _newColumnDef);
|
|
||||||
} finally {
|
|
||||||
stmt.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean runBeforeDefault () {
|
public boolean runBeforeDefault () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init (String tableName, HashMap<String,FieldMarshaller> marshallers) {
|
protected void init (String tableName, Map<String,FieldMarshaller> marshallers) {
|
||||||
super.init(tableName, 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
|
* migration has been determined to be runnable so one cannot rely on this method having been
|
||||||
* called in {@link #shouldRunMigration}.
|
* called in {@link #shouldRunMigration}.
|
||||||
*/
|
*/
|
||||||
protected void init (String tableName, HashMap<String,FieldMarshaller> marshallers)
|
protected void init (String tableName, Map<String,FieldMarshaller> marshallers)
|
||||||
{
|
{
|
||||||
_tableName = tableName;
|
_tableName = tableName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import java.sql.SQLException;
|
|||||||
import java.sql.Time;
|
import java.sql.Time;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
import com.samskivert.io.PersistenceException;
|
||||||
import com.samskivert.jdbc.depot.annotation.Column;
|
import com.samskivert.jdbc.depot.annotation.Column;
|
||||||
import com.samskivert.jdbc.depot.annotation.Computed;
|
import com.samskivert.jdbc.depot.annotation.Computed;
|
||||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||||
@@ -106,10 +107,20 @@ public abstract class FieldMarshaller<T>
|
|||||||
"Cannot marshall field of type '" + ftype.getName() + "'.");
|
"Cannot marshall field of type '" + ftype.getName() + "'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
marshaller.init(field);
|
marshaller.create(field);
|
||||||
return marshaller;
|
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.
|
* Returns the {@link Field} handled by this marshaller.
|
||||||
*/
|
*/
|
||||||
@@ -194,98 +205,30 @@ public abstract class FieldMarshaller<T>
|
|||||||
writeToObject(po, getFromSet(rset));
|
writeToObject(po, getFromSet(rset));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected void create (Field field)
|
||||||
* Returns the type used in the SQL column definition for this field.
|
|
||||||
*/
|
|
||||||
public abstract String getColumnType ();
|
|
||||||
|
|
||||||
protected void init (Field field)
|
|
||||||
{
|
{
|
||||||
_field = field;
|
_field = field;
|
||||||
_columnName = field.getName();
|
_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);
|
Column column = _field.getAnnotation(Column.class);
|
||||||
if (column != null) {
|
if (column != null) {
|
||||||
nullable = column.nullable();
|
|
||||||
unique = column.unique();
|
|
||||||
length = column.length();
|
|
||||||
defval = column.defaultValue();
|
|
||||||
if (!StringUtil.isBlank(column.name())) {
|
if (!StringUtil.isBlank(column.name())) {
|
||||||
_columnName = column.name();
|
_columnName = column.name();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_computed = field.getAnnotation(Computed.class);
|
_computed = field.getAnnotation(Computed.class);
|
||||||
// if this field is @Computed, it has no SQL definition
|
|
||||||
if (_computed != null) {
|
if (_computed != null) {
|
||||||
_columnDefinition = null;
|
|
||||||
return;
|
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) {
|
if (field.getAnnotation(Id.class) != null) {
|
||||||
// figure out how we're going to generate our primary key values
|
// figure out how we're going to generate our primary key values
|
||||||
_generatedValue = field.getAnnotation(GeneratedValue.class);
|
_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> {
|
protected static class BooleanMarshaller extends FieldMarshaller<Boolean> {
|
||||||
public String getColumnType () {
|
|
||||||
return "TINYINT";
|
|
||||||
}
|
|
||||||
public Boolean getFromObject (Object po)
|
public Boolean getFromObject (Object po)
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
return _field.getBoolean(po);
|
return _field.getBoolean(po);
|
||||||
@@ -305,9 +248,6 @@ public abstract class FieldMarshaller<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static class ByteMarshaller extends FieldMarshaller<Byte> {
|
protected static class ByteMarshaller extends FieldMarshaller<Byte> {
|
||||||
public String getColumnType () {
|
|
||||||
return "TINYINT";
|
|
||||||
}
|
|
||||||
public Byte getFromObject (Object po)
|
public Byte getFromObject (Object po)
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
return _field.getByte(po);
|
return _field.getByte(po);
|
||||||
@@ -327,9 +267,6 @@ public abstract class FieldMarshaller<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static class ShortMarshaller extends FieldMarshaller<Short> {
|
protected static class ShortMarshaller extends FieldMarshaller<Short> {
|
||||||
public String getColumnType () {
|
|
||||||
return "SMALLINT";
|
|
||||||
}
|
|
||||||
public Short getFromObject (Object po)
|
public Short getFromObject (Object po)
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
return _field.getShort(po);
|
return _field.getShort(po);
|
||||||
@@ -349,9 +286,6 @@ public abstract class FieldMarshaller<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static class IntMarshaller extends FieldMarshaller<Integer> {
|
protected static class IntMarshaller extends FieldMarshaller<Integer> {
|
||||||
public String getColumnType () {
|
|
||||||
return "INTEGER";
|
|
||||||
}
|
|
||||||
public Integer getFromObject (Object po)
|
public Integer getFromObject (Object po)
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
return _field.getInt(po);
|
return _field.getInt(po);
|
||||||
@@ -371,9 +305,6 @@ public abstract class FieldMarshaller<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static class LongMarshaller extends FieldMarshaller<Long> {
|
protected static class LongMarshaller extends FieldMarshaller<Long> {
|
||||||
public String getColumnType () {
|
|
||||||
return "BIGINT";
|
|
||||||
}
|
|
||||||
public Long getFromObject (Object po)
|
public Long getFromObject (Object po)
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
return _field.getLong(po);
|
return _field.getLong(po);
|
||||||
@@ -393,9 +324,6 @@ public abstract class FieldMarshaller<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static class FloatMarshaller extends FieldMarshaller<Float> {
|
protected static class FloatMarshaller extends FieldMarshaller<Float> {
|
||||||
public String getColumnType () {
|
|
||||||
return "FLOAT";
|
|
||||||
}
|
|
||||||
public Float getFromObject (Object po)
|
public Float getFromObject (Object po)
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
return _field.getFloat(po);
|
return _field.getFloat(po);
|
||||||
@@ -415,9 +343,6 @@ public abstract class FieldMarshaller<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static class DoubleMarshaller extends FieldMarshaller<Double> {
|
protected static class DoubleMarshaller extends FieldMarshaller<Double> {
|
||||||
public String getColumnType () {
|
|
||||||
return "DOUBLE";
|
|
||||||
}
|
|
||||||
public Double getFromObject (Object po)
|
public Double getFromObject (Object po)
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
return _field.getDouble(po);
|
return _field.getDouble(po);
|
||||||
@@ -437,37 +362,6 @@ public abstract class FieldMarshaller<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static class ObjectMarshaller extends FieldMarshaller<Object> {
|
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)
|
public Object getFromObject (Object po)
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
return _field.get(po);
|
return _field.get(po);
|
||||||
@@ -503,9 +397,6 @@ public abstract class FieldMarshaller<T>
|
|||||||
throws SQLException {
|
throws SQLException {
|
||||||
ps.setBytes(column, value);
|
ps.setBytes(column, value);
|
||||||
}
|
}
|
||||||
public String getColumnType () {
|
|
||||||
return "VARBINARY";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class IntArrayMarshaller extends FieldMarshaller<int[]> {
|
protected static class IntArrayMarshaller extends FieldMarshaller<int[]> {
|
||||||
@@ -525,9 +416,6 @@ public abstract class FieldMarshaller<T>
|
|||||||
throws SQLException {
|
throws SQLException {
|
||||||
ps.setObject(column, value);
|
ps.setObject(column, value);
|
||||||
}
|
}
|
||||||
public String getColumnType () {
|
|
||||||
return "BLOB";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class ByteEnumMarshaller extends FieldMarshaller<ByteEnum> {
|
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)
|
public ByteEnum getFromObject (Object po)
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
return (ByteEnum) _field.get(po);
|
return (ByteEnum) _field.get(po);
|
||||||
|
|||||||
@@ -33,14 +33,18 @@ import net.sf.ehcache.Cache;
|
|||||||
import net.sf.ehcache.Element;
|
import net.sf.ehcache.Element;
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.util.ArrayUtil;
|
||||||
|
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
import com.samskivert.jdbc.JDBCUtil;
|
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.FieldOverride;
|
||||||
import com.samskivert.jdbc.depot.clause.Join;
|
import com.samskivert.jdbc.depot.clause.Join;
|
||||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
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.clause.Where;
|
||||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
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
|
* This class implements the functionality required by {@link DepotRepository#findAll): fetch
|
||||||
@@ -58,18 +62,20 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
|||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
super(ctx, type);
|
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;
|
_clauses = clauses;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<T> invoke (Connection conn) throws SQLException {
|
public List<T> invoke (Connection conn, DatabaseLiaison liaison) throws SQLException
|
||||||
SQLQueryBuilder<T> keyFieldQuery = new SQLQueryBuilder<T>(
|
{
|
||||||
_ctx, _types, _marsh.getPrimaryKeyColumns(), _clauses);
|
|
||||||
PreparedStatement stmt = keyFieldQuery.prepare(conn);
|
|
||||||
|
|
||||||
Map<Key<T>, T> entities = new HashMap<Key<T>, T>();
|
Map<Key<T>, T> entities = new HashMap<Key<T>, T>();
|
||||||
List<Key<T>> allKeys = new ArrayList<Key<T>>();
|
List<Key<T>> allKeys = new ArrayList<Key<T>>();
|
||||||
List<Key<T>> fetchKeys = 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 {
|
try {
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
@@ -97,26 +103,28 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
|||||||
|
|
||||||
if (fetchKeys.size() > 0) {
|
if (fetchKeys.size() > 0) {
|
||||||
int jj = 0;
|
int jj = 0;
|
||||||
QueryClause[] newClauses = new QueryClause[_clauses.length + 1];
|
|
||||||
// a select subset of query clauses are preserved for the entity query
|
// 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 ++) {
|
for (int ii = 0; ii < _clauses.length; ii ++) {
|
||||||
if (_clauses[ii] instanceof Join || _clauses[ii] instanceof FieldOverride) {
|
if (_clauses[ii] instanceof Join || _clauses[ii] instanceof FieldOverride) {
|
||||||
newClauses[jj ++] = _clauses[ii];
|
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
|
// add our special key-matching where clause
|
||||||
newClauses[jj ++] = new Where(new Or(keyArray));
|
newClauses[jj ++] = new Where(new Or(keyArray));
|
||||||
newClauses = ArrayUtil.splice(newClauses, jj);
|
newClauses = ArrayUtil.splice(newClauses, jj);
|
||||||
|
|
||||||
// build the new query
|
// build the new query
|
||||||
SQLQueryBuilder<T> entityQuery = new SQLQueryBuilder<T>(
|
_builder.newQuery(new SelectClause<T>(_type, _marsh.getFieldNames(), newClauses));
|
||||||
_ctx, _types, _marsh.getFieldNames(), newClauses);
|
stmt = _builder.prepare(conn);
|
||||||
|
|
||||||
// and execute it
|
// and execute it
|
||||||
stmt = entityQuery.prepare(conn);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
for (Key<T> key : fetchKeys) {
|
for (Key<T> key : fetchKeys) {
|
||||||
@@ -138,7 +146,6 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DepotTypes<T> _types;
|
|
||||||
protected QueryClause[] _clauses;
|
protected QueryClause[] _clauses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,33 +158,32 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
|||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
super(ctx, type);
|
super(ctx, type);
|
||||||
DepotTypes<List<T>> types = SQLQueryBuilder.getDepotTypes(ctx, type, clauses);
|
SelectClause<T> select = new SelectClause<T>(type, _marsh.getFieldNames(), clauses);
|
||||||
_builder = new SQLQueryBuilder<List<T>>(ctx, types, _marsh.getFieldNames(), clauses);
|
_builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, select));
|
||||||
|
_builder.newQuery(select);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<T> invoke (Connection conn) throws SQLException {
|
public List<T> invoke (Connection conn, DatabaseLiaison liaison) throws SQLException
|
||||||
PreparedStatement stmt = _builder.prepare(conn);
|
{
|
||||||
|
|
||||||
List<T> result = new ArrayList<T>();
|
List<T> result = new ArrayList<T>();
|
||||||
|
PreparedStatement stmt = _builder.prepare(conn);
|
||||||
try {
|
try {
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
result.add(_marsh.createObject(rs));
|
result.add(_marsh.createObject(rs));
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
JDBCUtil.close(stmt);
|
JDBCUtil.close(stmt);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SQLQueryBuilder<List<T>> _builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FindAllQuery (PersistenceContext ctx, Class<T> type)
|
public FindAllQuery (PersistenceContext ctx, Class<T> type)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
|
_type = type;
|
||||||
_marsh = _ctx.getMarshaller(type);
|
_marsh = _ctx.getMarshaller(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,5 +222,7 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected PersistenceContext _ctx;
|
protected PersistenceContext _ctx;
|
||||||
|
protected SQLBuilder _builder;
|
||||||
protected DepotMarshaller<T> _marsh;
|
protected DepotMarshaller<T> _marsh;
|
||||||
|
protected Class<T> _type;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
import com.samskivert.io.PersistenceException;
|
||||||
|
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
import com.samskivert.jdbc.JDBCUtil;
|
import com.samskivert.jdbc.JDBCUtil;
|
||||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||||
|
import com.samskivert.jdbc.depot.clause.SelectClause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The implementation of {@link DepotRepository#find) functionality.
|
* The implementation of {@link DepotRepository#find) functionality.
|
||||||
@@ -39,21 +42,23 @@ public class FindOneQuery<T extends PersistentRecord>
|
|||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
_marsh = ctx.getMarshaller(type);
|
_marsh = ctx.getMarshaller(type);
|
||||||
DepotTypes<T> types = SQLQueryBuilder.getDepotTypes(ctx, type, clauses);
|
_select = new SelectClause<T>(type, _marsh.getFieldNames(), clauses);
|
||||||
_builder = new SQLQueryBuilder<T>(ctx, types, _marsh.getFieldNames(), clauses);
|
_builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select));
|
||||||
|
_builder.newQuery(_select);
|
||||||
}
|
}
|
||||||
|
|
||||||
// from Query
|
// from Query
|
||||||
public CacheKey getCacheKey ()
|
public CacheKey getCacheKey ()
|
||||||
{
|
{
|
||||||
if (_builder.where != null && _builder.where instanceof CacheKey) {
|
WhereClause where = _select.getWhereClause();
|
||||||
return (CacheKey) _builder.where;
|
if (where != null && where instanceof CacheKey) {
|
||||||
|
return (CacheKey) where;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from Query
|
// from Query
|
||||||
public T invoke (Connection conn) throws SQLException {
|
public T invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
PreparedStatement stmt = _builder.prepare(conn);
|
PreparedStatement stmt = _builder.prepare(conn);
|
||||||
try {
|
try {
|
||||||
T result = null;
|
T result = null;
|
||||||
@@ -96,5 +101,6 @@ public class FindOneQuery<T extends PersistentRecord>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected DepotMarshaller<T> _marsh;
|
protected DepotMarshaller<T> _marsh;
|
||||||
protected SQLQueryBuilder<T> _builder;
|
protected SelectClause<T> _select;
|
||||||
|
protected SQLBuilder _builder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,17 +21,22 @@
|
|||||||
package com.samskivert.jdbc.depot;
|
package com.samskivert.jdbc.depot;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
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.
|
* 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
|
// from interface KeyGenerator
|
||||||
public boolean isPostFactum ()
|
public boolean isPostFactum ()
|
||||||
{
|
{
|
||||||
@@ -39,27 +44,16 @@ public class IdentityKeyGenerator implements KeyGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// from interface KeyGenerator
|
// from interface KeyGenerator
|
||||||
public void init (Connection conn)
|
public void init (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
// nothing to do here
|
liaison.initializeGenerator(conn, _table, _column, _initialValue, _allocationSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// from interface KeyGenerator
|
// from interface KeyGenerator
|
||||||
public int nextGeneratedValue (Connection conn)
|
public int nextGeneratedValue (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
// load up the last inserted ID mysql style
|
return liaison.lastInsertedId(conn, _table, _column);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ package com.samskivert.jdbc.depot;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -31,20 +29,77 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.annotation.Id;
|
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.jdbc.depot.expression.SQLExpression;
|
||||||
import com.samskivert.util.StringUtil;
|
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
|
* 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
|
* {@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
|
* itself upon modification. This class is created by many {@link DepotMarshaller} methods as
|
||||||
* a convenience, and may also be instantiated explicitly.
|
* 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
|
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.
|
* 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)
|
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) {
|
if (fields.length != values.length) {
|
||||||
throw new IllegalArgumentException("Field and Value arrays must be of equal 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);
|
String[] keyFields = getKeyFields(pClass);
|
||||||
|
|
||||||
// now extract the values in field order and ensure none are extra or missing
|
// 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++) {
|
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
|
// 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]);
|
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
|
// finally make sure we were not given any fields that are not in fact primary key fields
|
||||||
if (map.size() > 0) {
|
if (map.size() > 0) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Non-key columns given: " + StringUtil.join(map.keySet().toArray(), ", "));
|
"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)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
{
|
{
|
||||||
classSet.add(_pClass);
|
classSet.add(condition.getPersistentClass());
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public void appendExpression (QueryBuilderContext<?> query, StringBuilder builder)
|
public void accept (ExpressionVisitor builder)
|
||||||
|
throws Exception
|
||||||
{
|
{
|
||||||
String[] keyFields = getKeyFields(_pClass);
|
builder.visit(this);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from CacheKey
|
// from CacheKey
|
||||||
public String getCacheId ()
|
public String getCacheId ()
|
||||||
{
|
{
|
||||||
return _pClass.getName();
|
return condition.getPersistentClass().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
// from CacheKey
|
// from CacheKey
|
||||||
@@ -171,12 +194,6 @@ public class Key<T extends PersistentRecord> extends Where
|
|||||||
ctx.cacheInvalidate(this);
|
ctx.cacheInvalidate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode ()
|
|
||||||
{
|
|
||||||
return _pClass.hashCode() ^ Arrays.hashCode(_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals (Object obj)
|
public boolean equals (Object obj)
|
||||||
{
|
{
|
||||||
@@ -186,17 +203,24 @@ public class Key<T extends PersistentRecord> extends Where
|
|||||||
if (obj == null || getClass() != obj.getClass()) {
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Key other = (Key) obj;
|
return condition.equals(((Key) obj).condition);
|
||||||
return _pClass == other._pClass && Arrays.equals(_values, other._values);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode ()
|
||||||
|
{
|
||||||
|
return condition.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString ()
|
public String toString ()
|
||||||
{
|
{
|
||||||
StringBuilder builder = new StringBuilder("[Key pClass=" + _pClass.getName());
|
StringBuilder builder =
|
||||||
String[] keyFields = getKeyFields(_pClass);
|
new StringBuilder("[Key pClass=").append(condition.getPersistentClass().getName());
|
||||||
|
String[] keyFields = getKeyFields(condition.getPersistentClass());
|
||||||
for (int ii = 0; ii < keyFields.length; ii ++) {
|
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("]");
|
builder.append("]");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
@@ -222,9 +246,6 @@ public class Key<T extends PersistentRecord> extends Where
|
|||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Class<T> _pClass;
|
|
||||||
protected Comparable[] _values;
|
|
||||||
|
|
||||||
/** A (never expiring) cache of primary key field names for all persistent classes (of which
|
/** A (never expiring) cache of primary key field names for all persistent classes (of which
|
||||||
* there are merely dozens, so we don't need to worry about expiring). */
|
* there are merely dozens, so we don't need to worry about expiring). */
|
||||||
protected static HashMap<Class<?>,String[]> _keyFields = new HashMap<Class<?>,String[]>();
|
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.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
|
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the interface to our primary key generators.
|
* 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
|
public KeyGenerator (GeneratedValue gv, String table, String column)
|
||||||
* run before. */
|
{
|
||||||
public boolean isPostFactum ();
|
_allocationSize = gv.allocationSize();
|
||||||
|
_initialValue = gv.initialValue();
|
||||||
/** Prepares the generator for operation. */
|
_table = table;
|
||||||
public void init (Connection conn) throws SQLException;
|
_column = column;
|
||||||
|
}
|
||||||
/** Fetch/generate the next primary key value. */
|
|
||||||
public int nextGeneratedValue (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 ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates a modification of persistent objects.
|
* 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
|
* A simple modifier that executes a single SQL statement. No cache flushing is done as a
|
||||||
* result of this operation.
|
* 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);
|
super(null);
|
||||||
_query = query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int invoke (Connection conn) throws SQLException {
|
public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||||
Statement stmt = conn.createStatement();
|
Statement stmt = conn.createStatement();
|
||||||
try {
|
try {
|
||||||
return stmt.executeUpdate(_query);
|
return stmt.executeUpdate(createQuery(liaison));
|
||||||
} finally {
|
} finally {
|
||||||
stmt.close();
|
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;
|
* Overriden to perform the actual database modifications represented by this object; should
|
||||||
* should return the number of modified rows.
|
* 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.
|
* Constructs a {@link Modifier} without a cache invalidator.
|
||||||
|
|||||||
@@ -20,21 +20,19 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot;
|
package com.samskivert.jdbc.depot;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.clause.Where;
|
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
|
* 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
|
* 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.
|
* 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
|
implements CacheInvalidator
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -69,8 +67,6 @@ public class MultiKey<T extends PersistentRecord> extends Where
|
|||||||
public MultiKey (Class<T> pClass, String[] sFields, Comparable[] sValues,
|
public MultiKey (Class<T> pClass, String[] sFields, Comparable[] sValues,
|
||||||
String mField, Comparable[] mValues)
|
String mField, Comparable[] mValues)
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
super(null);
|
|
||||||
if (sFields.length != sValues.length) {
|
if (sFields.length != sValues.length) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Key field and values arrays must be of equal length.");
|
"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)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
{
|
{
|
||||||
classSet.add(_pClass);
|
// nothing to add
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from CacheInvalidator
|
// from CacheInvalidator
|
||||||
@@ -142,8 +102,28 @@ public class MultiKey<T extends PersistentRecord> extends Where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Class<T> _pClass;
|
public Class<T> getPersistentClass ()
|
||||||
protected HashMap<String, Comparable> _map;
|
{
|
||||||
|
return _pClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Comparable> getSingleFieldsMap ()
|
||||||
|
{
|
||||||
|
return _map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMultiField ()
|
||||||
|
{
|
||||||
|
return _mField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparable[] getMultiValues ()
|
||||||
|
{
|
||||||
|
return _mValues;
|
||||||
|
}
|
||||||
|
|
||||||
protected String _mField;
|
protected String _mField;
|
||||||
protected Comparable[] _mValues;
|
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.util.StringUtil;
|
||||||
|
|
||||||
import com.samskivert.jdbc.ConnectionProvider;
|
import com.samskivert.jdbc.ConnectionProvider;
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
import com.samskivert.jdbc.DuplicateKeyException;
|
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.
|
* Defines a scope in which global annotations are shared.
|
||||||
@@ -124,6 +127,23 @@ public class PersistenceContext
|
|||||||
_ident = ident;
|
_ident = ident;
|
||||||
_conprov = conprov;
|
_conprov = conprov;
|
||||||
_cachemgr = CacheManager.getInstance();
|
_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);
|
Connection conn = _conprov.getConnection(_ident, isReadOnlyQuery);
|
||||||
try {
|
try {
|
||||||
if (isReadOnlyQuery) {
|
if (isReadOnlyQuery) {
|
||||||
// if this becomes more complex than this single statement,
|
// if this becomes more complex than this single statement, then this should turn
|
||||||
// then this should turn into a method call that contains
|
// into a method call that contains the complexity
|
||||||
// the complexity
|
return query.invoke(conn, _liaison);
|
||||||
return query.invoke(conn);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// if this becomes more complex than this single statement,
|
// if this becomes more complex than this single statement, then this should turn
|
||||||
// then this should turn into a method call that contains
|
// into a method call that contains the complexity
|
||||||
// the complexity
|
return modifier.invoke(conn, _liaison);
|
||||||
return modifier.invoke(conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SQLException sqe) {
|
} catch (SQLException sqe) {
|
||||||
if (!isReadOnlyQuery) {
|
if (!isReadOnlyQuery) {
|
||||||
// convert this exception to a DuplicateKeyException if
|
// convert this exception to a DuplicateKeyException if appropriate
|
||||||
// appropriate
|
if (_liaison.isDuplicateRowException(sqe)) {
|
||||||
String msg = sqe.getMessage();
|
throw new DuplicateKeyException(sqe.getMessage());
|
||||||
if (msg != null && msg.indexOf("Duplicate entry") != -1) {
|
|
||||||
throw new DuplicateKeyException(msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +441,7 @@ public class PersistenceContext
|
|||||||
_conprov.connectionFailed(_ident, isReadOnlyQuery, conn, sqe);
|
_conprov.connectionFailed(_ident, isReadOnlyQuery, conn, sqe);
|
||||||
conn = null;
|
conn = null;
|
||||||
|
|
||||||
if (retryOnTransientFailure && isTransientException(sqe)) {
|
if (retryOnTransientFailure && _liaison.isTransientException(sqe)) {
|
||||||
// the MySQL JDBC driver has the annoying habit of including
|
// the MySQL JDBC driver has the annoying habit of including
|
||||||
// the embedded exception stack trace in the message of their
|
// the embedded exception stack trace in the message of their
|
||||||
// outer exception; if I want a fucking stack trace, I'll call
|
// outer exception; if I want a fucking stack trace, I'll call
|
||||||
@@ -448,21 +464,10 @@ public class PersistenceContext
|
|||||||
return invoke(query, modifier, false);
|
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 String _ident;
|
||||||
protected ConnectionProvider _conprov;
|
protected ConnectionProvider _conprov;
|
||||||
protected CacheManager _cachemgr;
|
protected CacheManager _cachemgr;
|
||||||
|
protected DatabaseLiaison _liaison;
|
||||||
|
|
||||||
protected Map<String, Set<CacheListener<?>>> _listenerSets =
|
protected Map<String, Set<CacheListener<?>>> _listenerSets =
|
||||||
new HashMap<String, Set<CacheListener<?>>>();
|
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.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base of all read-only queries.
|
* The base of all read-only queries.
|
||||||
*/
|
*/
|
||||||
public interface Query<T>
|
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}
|
* 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
|
* 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.
|
* Performs the actual JDBC operations associated with this query.
|
||||||
*/
|
*/
|
||||||
public T invoke (Connection conn)
|
public T invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException;
|
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.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||||
import com.samskivert.jdbc.depot.annotation.TableGenerator;
|
import com.samskivert.jdbc.depot.annotation.TableGenerator;
|
||||||
|
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
import com.samskivert.jdbc.JDBCUtil;
|
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");
|
super(gv, table, column);
|
||||||
_pkColumnName = defStr(annotation.pkColumnName(), "sequence");
|
_valueTable = defStr(tg.table(), "IdSequences");
|
||||||
_pkColumnValue = defStr(annotation.pkColumnValue(), "default");
|
_pkColumnName = defStr(tg.pkColumnName(), "sequence");
|
||||||
_valueColumnName = defStr(annotation.valueColumnName(), "value");
|
_pkColumnValue = defStr(tg.pkColumnValue(), "default");
|
||||||
_allocationSize = annotation.allocationSize();
|
_valueColumnName = defStr(tg.valueColumnName(), "value");
|
||||||
_initialValue = annotation.initialValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from interface KeyGenerator
|
// from interface KeyGenerator
|
||||||
@@ -51,20 +53,23 @@ public class TableKeyGenerator implements KeyGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// from interface KeyGenerator
|
// from interface KeyGenerator
|
||||||
public void init (Connection conn)
|
public void init (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
// make sure our table exists
|
// make sure our table exists
|
||||||
JDBCUtil.createTableIfMissing(conn, _table, new String[] {
|
liaison.createTableIfMissing(
|
||||||
_pkColumnName + " VARCHAR(255) PRIMARY KEY",
|
conn, _valueTable,
|
||||||
_valueColumnName + " INTEGER NOT NULL"
|
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
|
// and also that there's a row in it for us
|
||||||
PreparedStatement stmt = null;
|
PreparedStatement stmt = null;
|
||||||
try {
|
try {
|
||||||
stmt = conn.prepareStatement(
|
stmt = conn.prepareStatement(
|
||||||
"SELECT * FROM " + _table + " WHERE " + _pkColumnName + " = ?");
|
" SELECT * FROM " + liaison.tableSQL(_valueTable) +
|
||||||
|
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ?");
|
||||||
stmt.setString(1, _pkColumnValue);
|
stmt.setString(1, _pkColumnValue);
|
||||||
if (stmt.executeQuery().next()) {
|
if (stmt.executeQuery().next()) {
|
||||||
return;
|
return;
|
||||||
@@ -72,8 +77,10 @@ public class TableKeyGenerator implements KeyGenerator
|
|||||||
|
|
||||||
JDBCUtil.close(stmt);
|
JDBCUtil.close(stmt);
|
||||||
stmt = null;
|
stmt = null;
|
||||||
stmt = conn.prepareStatement("INSERT INTO " + _table + " SET " +
|
stmt = conn.prepareStatement(
|
||||||
_pkColumnName + " = ?, " + _valueColumnName + " = ?");
|
" INSERT INTO " + liaison.tableSQL(_valueTable) + " (" +
|
||||||
|
liaison.columnSQL(_pkColumnName) + ", " + liaison.columnSQL(_valueColumnName) +
|
||||||
|
") VALUES (?, ?)");
|
||||||
stmt.setString(1, _pkColumnValue);
|
stmt.setString(1, _pkColumnValue);
|
||||||
stmt.setInt(2, _initialValue);
|
stmt.setInt(2, _initialValue);
|
||||||
stmt.executeUpdate();
|
stmt.executeUpdate();
|
||||||
@@ -84,27 +91,32 @@ public class TableKeyGenerator implements KeyGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// from interface KeyGenerator
|
// from interface KeyGenerator
|
||||||
public int nextGeneratedValue (Connection conn)
|
public int nextGeneratedValue (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
PreparedStatement stmt = null;
|
PreparedStatement stmt = null;
|
||||||
try {
|
try {
|
||||||
String query = "SELECT " + _valueColumnName + " FROM " + _table +
|
// TODO: Make this lockless!
|
||||||
" WHERE " + _pkColumnName + "= ? FOR UPDATE";
|
String query =
|
||||||
|
" SELECT " + liaison.columnSQL(_valueColumnName) +
|
||||||
|
" FROM " + liaison.tableSQL(_valueTable) +
|
||||||
|
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ? FOR UPDATE";
|
||||||
stmt = conn.prepareStatement(query);
|
stmt = conn.prepareStatement(query);
|
||||||
stmt.setString(1, _pkColumnValue);
|
stmt.setString(1, _pkColumnValue);
|
||||||
|
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
if (!rs.next()) {
|
if (!rs.next()) {
|
||||||
throw new SQLException("Failed to find next primary key value [table=" + _table +
|
throw new SQLException("Failed to find next primary key value " +
|
||||||
", column=" + _valueColumnName +
|
"[table=" + _valueTable + ", column=" + _valueColumnName +
|
||||||
", where=" + _pkColumnName + "=" + _pkColumnValue + "]");
|
", where=" + _pkColumnName + "=" + _pkColumnValue + "]");
|
||||||
}
|
}
|
||||||
int val = rs.getInt(1);
|
int val = rs.getInt(1);
|
||||||
JDBCUtil.close(stmt);
|
JDBCUtil.close(stmt);
|
||||||
|
|
||||||
stmt = conn.prepareStatement("UPDATE " + _table + " SET " + _valueColumnName + " = ? " +
|
stmt = conn.prepareStatement(
|
||||||
"WHERE " + _pkColumnName + " = ?");
|
" UPDATE " + liaison.tableSQL(_valueTable) +
|
||||||
|
" SET " + liaison.columnSQL(_valueColumnName) + " = ? " +
|
||||||
|
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ?");
|
||||||
stmt.setInt(1, val + _allocationSize);
|
stmt.setInt(1, val + _allocationSize);
|
||||||
stmt.setString(2, _pkColumnValue);
|
stmt.setString(2, _pkColumnValue);
|
||||||
stmt.executeUpdate();
|
stmt.executeUpdate();
|
||||||
@@ -126,10 +138,8 @@ public class TableKeyGenerator implements KeyGenerator
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String _table;
|
protected String _valueTable;
|
||||||
protected String _pkColumnName;
|
protected String _pkColumnName;
|
||||||
protected String _pkColumnValue;
|
protected String _pkColumnValue;
|
||||||
protected String _valueColumnName;
|
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
|
// License along with this library; if not, write to the Free Software
|
||||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
// 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;
|
boolean nullable () default false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SQL fragment that is used when generating the DDL for the column. Defaults to the
|
* The SQL type that is used when generating the DDL for the column.
|
||||||
* generated SQL to create a column of the inferred type.
|
|
||||||
*/
|
*/
|
||||||
String columnDefinition () default "";
|
String type() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The column length. (Applies to String and byte[] columns.)
|
* 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.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a field as computed, meaning it is ignored for schema purposes and it does not directly
|
* 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
|
* correspond to a column in a table.
|
||||||
* QueryBuilderContext}.
|
|
||||||
*/
|
*/
|
||||||
@Retention(value=RetentionPolicy.RUNTIME)
|
@Retention(value=RetentionPolicy.RUNTIME)
|
||||||
@Target({ ElementType.FIELD, ElementType.TYPE })
|
@Target({ ElementType.FIELD, ElementType.TYPE })
|
||||||
|
|||||||
@@ -37,8 +37,4 @@ public @interface Entity
|
|||||||
|
|
||||||
/** Defines indices to add to this entity's table. */
|
/** Defines indices to add to this entity's table. */
|
||||||
Index[] indices () default {};
|
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. */
|
/** Identifies the strategy to be used to generate this value. */
|
||||||
GenerationType strategy () default GenerationType.AUTO;
|
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
|
* the primary key column of the generator table
|
||||||
*/
|
*/
|
||||||
String pkColumnValue () default "";
|
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;
|
package com.samskivert.jdbc.depot.clause;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
import com.samskivert.io.PersistenceException;
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
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.LiteralExp;
|
||||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||||
|
|
||||||
@@ -68,11 +66,22 @@ public class FieldOverride extends QueryClause
|
|||||||
return _field;
|
return _field;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public SQLExpression getOverride ()
|
||||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
|
||||||
{
|
{
|
||||||
_override.appendExpression(query, builder);
|
return _override;
|
||||||
builder.append(" as ").append(_field);
|
}
|
||||||
|
|
||||||
|
// 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. */
|
/** The name of the field on the persistent object to override. */
|
||||||
@@ -80,4 +89,5 @@ public class FieldOverride extends QueryClause
|
|||||||
|
|
||||||
/** The overriding expression. */
|
/** The overriding expression. */
|
||||||
protected SQLExpression _override;
|
protected SQLExpression _override;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,21 +20,24 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.clause;
|
package com.samskivert.jdbc.depot.clause;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
|
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a FOR UPDATE clause.
|
* Represents a FOR UPDATE clause.
|
||||||
*/
|
*/
|
||||||
public class ForUpdate extends QueryClause
|
public class ForUpdate extends QueryClause
|
||||||
{
|
{
|
||||||
// from QueryClause
|
// from SQLExpression
|
||||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
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;
|
package com.samskivert.jdbc.depot.clause;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
import com.samskivert.io.PersistenceException;
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
|
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Completely overrides the FROM clause, if it exists.
|
* Completely overrides the FROM clause, if it exists.
|
||||||
@@ -54,27 +53,25 @@ public class FromOverride extends QueryClause
|
|||||||
_fromClasses.addAll(fromClasses);
|
_fromClasses.addAll(fromClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public List<Class<? extends PersistentRecord>> getFromClasses ()
|
||||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
|
||||||
{
|
{
|
||||||
classSet.addAll(_fromClasses);
|
return _fromClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
// from SQLExpression
|
||||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
{
|
{
|
||||||
builder.append(" from " );
|
classSet.addAll(getFromClasses());
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// from SQLExpression
|
||||||
|
public void accept (ExpressionVisitor builder)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
builder.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The classes of the tables we're selecting from. */
|
/** 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>>();
|
new ArrayList<Class<? extends PersistentRecord>>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,10 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.clause;
|
package com.samskivert.jdbc.depot.clause;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
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.SQLExpression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,28 +36,24 @@ public class GroupBy extends QueryClause
|
|||||||
_values = values;
|
_values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public SQLExpression[] getValues ()
|
||||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
|
||||||
{
|
{
|
||||||
builder.append(" group by ");
|
return _values;
|
||||||
for (int ii = 0; ii < _values.length; ii++) {
|
|
||||||
if (ii > 0) {
|
|
||||||
builder.append(", ");
|
|
||||||
}
|
|
||||||
_values[ii].appendExpression(query, builder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
// from SQLExpression
|
||||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
for (int ii = 0; ii < _values.length; ii++) {
|
builder.visit(this);
|
||||||
argIdx = _values[ii].bindExpressionArguments(pstmt, argIdx);
|
|
||||||
}
|
}
|
||||||
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. */
|
/** The expressions that are generated for the clause. */
|
||||||
protected SQLExpression[] _values;
|
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;
|
package com.samskivert.jdbc.depot.clause;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
import com.samskivert.io.PersistenceException;
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
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.SQLExpression;
|
||||||
import com.samskivert.jdbc.depot.operator.Conditionals.*;
|
import com.samskivert.jdbc.depot.operator.Conditionals.Equals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a JOIN.
|
* Represents a JOIN.
|
||||||
@@ -50,7 +48,7 @@ public class Join extends QueryClause
|
|||||||
public Join (ColumnExp primary, ColumnExp join)
|
public Join (ColumnExp primary, ColumnExp join)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
_joinClass = join.pClass;
|
_joinClass = join.getPersistentClass();
|
||||||
_joinCondition = new Equals(primary, join);
|
_joinCondition = new Equals(primary, join);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,12 +58,6 @@ public class Join extends QueryClause
|
|||||||
_joinCondition = joinCondition;
|
_joinCondition = joinCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
|
||||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
|
||||||
{
|
|
||||||
classSet.add(_joinClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the type of join to be performed.
|
* Configures the type of join to be performed.
|
||||||
*/
|
*/
|
||||||
@@ -75,30 +67,31 @@ public class Join extends QueryClause
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public Type getType ()
|
||||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
|
||||||
{
|
{
|
||||||
switch (_type) {
|
return _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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public Class<? extends PersistentRecord> getJoinClass ()
|
||||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
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. */
|
/** Indicates the type of join to be performed. */
|
||||||
|
|||||||
@@ -20,12 +20,10 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.clause;
|
package com.samskivert.jdbc.depot.clause;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
|
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a LIMIT/OFFSET clause, for pagination.
|
* Represents a LIMIT/OFFSET clause, for pagination.
|
||||||
@@ -38,19 +36,25 @@ public class Limit extends QueryClause
|
|||||||
_count = count;
|
_count = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public int getOffset ()
|
||||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
|
||||||
{
|
{
|
||||||
builder.append(" limit ? offset ? ");
|
return _offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public int getCount ()
|
||||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
{
|
||||||
throws SQLException
|
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. */
|
/** The first row of the result set to return. */
|
||||||
|
|||||||
@@ -20,13 +20,10 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.clause;
|
package com.samskivert.jdbc.depot.clause;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
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.LiteralExp;
|
||||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||||
|
|
||||||
@@ -46,22 +43,6 @@ public class OrderBy extends QueryClause
|
|||||||
return ascending(new LiteralExp("rand()"));
|
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.
|
* Creates and returns an ascending order by clause on the supplied expression.
|
||||||
*/
|
*/
|
||||||
@@ -84,27 +65,28 @@ public class OrderBy extends QueryClause
|
|||||||
_orders = orders;
|
_orders = orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public SQLExpression[] getValues ()
|
||||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
|
||||||
{
|
{
|
||||||
builder.append(" order by ");
|
return _values;
|
||||||
for (int ii = 0; ii < _values.length; ii++) {
|
|
||||||
if (ii > 0) {
|
|
||||||
builder.append(", ");
|
|
||||||
}
|
|
||||||
_values[ii].appendExpression(query, builder);
|
|
||||||
builder.append(" ").append(_orders[ii]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public Order[] getOrders ()
|
||||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
for (int ii = 0; ii < _values.length; ii++) {
|
return _orders;
|
||||||
argIdx = _values[ii].bindExpressionArguments(pstmt, argIdx);
|
}
|
||||||
|
|
||||||
|
// 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. */
|
/** 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. */
|
/** Whether the ordering is to be ascending or descending. */
|
||||||
protected Order[] _orders;
|
protected Order[] _orders;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,39 +20,11 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.clause;
|
package com.samskivert.jdbc.depot.clause;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a piece or modifier of an SQL query.
|
* 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;
|
package com.samskivert.jdbc.depot.clause;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
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.ColumnExp;
|
||||||
|
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||||
import com.samskivert.jdbc.depot.expression.ValueExp;
|
import com.samskivert.jdbc.depot.expression.ValueExp;
|
||||||
import com.samskivert.jdbc.depot.operator.Conditionals.Equals;
|
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
|
* Represents a where clause: the condition can be any comparison operator or logical combination
|
||||||
* thereof.
|
* 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)
|
public Where (ColumnExp column, Comparable value)
|
||||||
{
|
{
|
||||||
this(new ColumnExp[] { column }, new Comparable[] { value });
|
this(new ColumnExp[] { column }, new Comparable[] { value });
|
||||||
@@ -64,18 +57,6 @@ public class Where extends QueryClause
|
|||||||
new Comparable[] { value1, value2, value3 });
|
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)
|
public Where (ColumnExp[] columns, Comparable[] values)
|
||||||
{
|
{
|
||||||
this(toCondition(columns, values));
|
this(toCondition(columns, values));
|
||||||
@@ -86,18 +67,21 @@ public class Where extends QueryClause
|
|||||||
_condition = condition;
|
_condition = condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
public SQLExpression getCondition ()
|
||||||
public void appendClause (QueryBuilderContext<?> query, StringBuilder builder)
|
|
||||||
{
|
{
|
||||||
builder.append(" where ");
|
return _condition;
|
||||||
_condition.appendExpression(query, builder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from QueryClause
|
// from SQLExpression
|
||||||
public int bindClauseArguments (PreparedStatement pstmt, int argIdx)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
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)
|
protected static SQLExpression toCondition (ColumnExp[] columns, Comparable[] values)
|
||||||
|
|||||||
@@ -20,53 +20,48 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.expression;
|
package com.samskivert.jdbc.depot.expression;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.util.Collection;
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An expression identifying a column of a class, e.g. GameRecord.itemId. If no class is given,
|
* An expression that unambiguously identifies a field of a class, e.g. GameRecord.itemId.
|
||||||
* no disambiguation occurs in the generated SQL.
|
|
||||||
*/
|
*/
|
||||||
public class ColumnExp
|
public class ColumnExp
|
||||||
implements SQLExpression
|
implements SQLExpression
|
||||||
{
|
{
|
||||||
/** The table that hosts the column we reference, or null. */
|
public ColumnExp (Class<? extends PersistentRecord> pClass, String field)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
pClass = c;
|
_pClass = pClass;
|
||||||
this.pColumn = column;
|
_pField = field;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public void appendExpression (QueryBuilderContext<?> query, StringBuilder builder)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
{
|
{
|
||||||
if (pClass == null || query == null) {
|
builder.visit(this);
|
||||||
builder.append(pColumn);
|
|
||||||
} else {
|
|
||||||
String tRef = query.getTableAbbreviation(pClass);
|
|
||||||
builder.append(tRef).append(".").append(pColumn);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
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;
|
package com.samskivert.jdbc.depot.expression;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.util.Collection;
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An expression for a function, e.g. FLOOR(blah).
|
* An expression for a function, e.g. FLOOR(blah).
|
||||||
*/
|
*/
|
||||||
public class FunctionExp
|
public class FunctionExp implements SQLExpression
|
||||||
implements SQLExpression
|
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Create a new FunctionExp with the given function and arguments.
|
* Create a new FunctionExp with the given function and arguments.
|
||||||
@@ -41,27 +39,28 @@ public class FunctionExp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
public void accept (ExpressionVisitor builder)
|
||||||
|
throws Exception
|
||||||
{
|
{
|
||||||
builder.append(_function);
|
builder.visit(this);
|
||||||
builder.append("(");
|
|
||||||
for (int ii = 0; ii < _arguments.length; ii ++) {
|
|
||||||
if (ii > 0) {
|
|
||||||
builder.append(", ");
|
|
||||||
}
|
|
||||||
_arguments[ii].appendExpression(query, builder);
|
|
||||||
}
|
|
||||||
builder.append(")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
for (int ii = 0; ii < _arguments.length; ii ++) {
|
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 */
|
/** The literal name of this function, e.g. FLOOR */
|
||||||
|
|||||||
@@ -20,10 +20,9 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.expression;
|
package com.samskivert.jdbc.depot.expression;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.util.Collection;
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An expression for things we don't support natively, e.g. COUNT(*).
|
* An expression for things we don't support natively, e.g. COUNT(*).
|
||||||
@@ -38,16 +37,19 @@ public class LiteralExp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
{
|
{
|
||||||
builder.append(_text);
|
builder.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
return argIdx;
|
}
|
||||||
|
|
||||||
|
public String getText ()
|
||||||
|
{
|
||||||
|
return _text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The literal text of this expression, e.g. COUNT(*) */
|
/** The literal text of this expression, e.g. COUNT(*) */
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.expression;
|
package com.samskivert.jdbc.depot.expression;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.util.Collection;
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
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.
|
* Represents an SQL expression, e.g. column name, function, or constant.
|
||||||
@@ -31,16 +31,19 @@ import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|||||||
public interface SQLExpression
|
public interface SQLExpression
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Construct the SQL form of this expression. The implementor is invited to call methods on the
|
* Most uses of this class have been implemented with a visitor pattern. Create your own
|
||||||
* Query object to e.g. resolve the current table abbreviations associated with classes.
|
* {@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
|
* Adds all persistent classes that are brought into the SQL context by this clause: FROM
|
||||||
* SQL, precisely one parameter must be claimed and bound in this method, and argIdx
|
* clauses, JOINs, UPDATEs, anything that could create a new table abbreviation. This method
|
||||||
* incremented and returned.
|
* 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)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet);
|
||||||
throws SQLException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,9 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.expression;
|
package com.samskivert.jdbc.depot.expression;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.util.Collection;
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
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'.
|
* 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
|
public class ValueExp
|
||||||
implements SQLExpression
|
implements SQLExpression
|
||||||
{
|
{
|
||||||
public ValueExp (Comparable _value)
|
public ValueExp (Object _value)
|
||||||
{
|
{
|
||||||
this._value = _value;
|
this._value = _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
{
|
{
|
||||||
builder.append("?");
|
builder.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
pstmt.setObject(argIdx ++, _value);
|
}
|
||||||
return argIdx;
|
|
||||||
|
public Object getValue ()
|
||||||
|
{
|
||||||
|
return _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The value to be bound to the SQL parameters. */
|
/** 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;
|
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.expression.SQLExpression;
|
||||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
||||||
|
|
||||||
@@ -34,11 +33,6 @@ public abstract class Arithmetic
|
|||||||
/** The SQL '+' operator. */
|
/** The SQL '+' operator. */
|
||||||
public static class Add extends BinaryOperator
|
public static class Add extends BinaryOperator
|
||||||
{
|
{
|
||||||
public Add (String pColumn, Comparable value)
|
|
||||||
{
|
|
||||||
super(new ColumnExp(pColumn), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Add (SQLExpression column, Comparable value)
|
public Add (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -50,7 +44,7 @@ public abstract class Arithmetic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "+";
|
return "+";
|
||||||
}
|
}
|
||||||
@@ -59,11 +53,6 @@ public abstract class Arithmetic
|
|||||||
/** The SQL '-' operator. */
|
/** The SQL '-' operator. */
|
||||||
public static class Sub extends BinaryOperator
|
public static class Sub extends BinaryOperator
|
||||||
{
|
{
|
||||||
public Sub (String pColumn, Comparable value)
|
|
||||||
{
|
|
||||||
super(new ColumnExp(pColumn), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Sub (SQLExpression column, Comparable value)
|
public Sub (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -75,7 +64,7 @@ public abstract class Arithmetic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "-";
|
return "-";
|
||||||
}
|
}
|
||||||
@@ -84,11 +73,6 @@ public abstract class Arithmetic
|
|||||||
/** The SQL '*' operator. */
|
/** The SQL '*' operator. */
|
||||||
public static class Mul extends BinaryOperator
|
public static class Mul extends BinaryOperator
|
||||||
{
|
{
|
||||||
public Mul (String pColumn, Comparable value)
|
|
||||||
{
|
|
||||||
super(new ColumnExp(pColumn), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mul (SQLExpression column, Comparable value)
|
public Mul (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -100,7 +84,7 @@ public abstract class Arithmetic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "*";
|
return "*";
|
||||||
}
|
}
|
||||||
@@ -109,11 +93,6 @@ public abstract class Arithmetic
|
|||||||
/** The SQL '/' operator. */
|
/** The SQL '/' operator. */
|
||||||
public static class Div extends BinaryOperator
|
public static class Div extends BinaryOperator
|
||||||
{
|
{
|
||||||
public Div (String pColumn, Comparable value)
|
|
||||||
{
|
|
||||||
super(new ColumnExp(pColumn), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Div (SQLExpression column, Comparable value)
|
public Div (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -125,7 +104,7 @@ public abstract class Arithmetic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "/";
|
return "/";
|
||||||
}
|
}
|
||||||
@@ -134,11 +113,6 @@ public abstract class Arithmetic
|
|||||||
/** The SQL '&' operator. */
|
/** The SQL '&' operator. */
|
||||||
public static class BitAnd extends BinaryOperator
|
public static class BitAnd extends BinaryOperator
|
||||||
{
|
{
|
||||||
public BitAnd (String pColumn, Comparable value)
|
|
||||||
{
|
|
||||||
super(new ColumnExp(pColumn), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BitAnd (SQLExpression column, Comparable value)
|
public BitAnd (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -150,7 +124,7 @@ public abstract class Arithmetic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "&";
|
return "&";
|
||||||
}
|
}
|
||||||
@@ -159,11 +133,6 @@ public abstract class Arithmetic
|
|||||||
/** The SQL '|' operator. */
|
/** The SQL '|' operator. */
|
||||||
public static class BitOr extends BinaryOperator
|
public static class BitOr extends BinaryOperator
|
||||||
{
|
{
|
||||||
public BitOr (String pColumn, Comparable value)
|
|
||||||
{
|
|
||||||
super(new ColumnExp(pColumn), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BitOr (SQLExpression column, Comparable value)
|
public BitOr (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -175,7 +144,7 @@ public abstract class Arithmetic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "|";
|
return "|";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,12 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.operator;
|
package com.samskivert.jdbc.depot.operator;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.QueryBuilderContext;
|
|
||||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
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.SQLExpression;
|
||||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenient container for implementations of conditional operators. Classes that value brevity
|
* A convenient container for implementations of conditional operators. Classes that value brevity
|
||||||
@@ -56,31 +53,28 @@ public abstract class Conditionals
|
|||||||
_column = column;
|
_column = column;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
public ColumnExp getColumn()
|
||||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
|
||||||
{
|
{
|
||||||
_column.appendExpression(query, builder);
|
return _column;
|
||||||
builder.append(" is null");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
throws SQLException
|
{
|
||||||
|
builder.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from SQLExpression
|
||||||
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
{
|
{
|
||||||
return argIdx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ColumnExp _column;
|
protected ColumnExp _column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The SQL '=' operator. */
|
/** 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)
|
public Equals (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -92,20 +86,15 @@ public abstract class Conditionals
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "=";
|
return "=";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The SQL '<' operator. */
|
/** 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)
|
public LessThan (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -117,20 +106,15 @@ public abstract class Conditionals
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "<";
|
return "<";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The SQL '<=' operator. */
|
/** 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)
|
public LessThanEquals (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -142,20 +126,15 @@ public abstract class Conditionals
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "<=";
|
return "<=";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The SQL '>' operator. */
|
/** 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)
|
public GreaterThan (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -167,20 +146,15 @@ public abstract class Conditionals
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return ">";
|
return ">";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The SQL '>=' operator. */
|
/** 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)
|
public GreaterThanEquals (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -192,7 +166,7 @@ public abstract class Conditionals
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return ">=";
|
return ">=";
|
||||||
}
|
}
|
||||||
@@ -202,16 +176,6 @@ public abstract class Conditionals
|
|||||||
public static class In
|
public static class In
|
||||||
implements SQLOperator
|
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)
|
public In (Class<? extends PersistentRecord> pClass, String pColumn, Comparable... values)
|
||||||
{
|
{
|
||||||
this(new ColumnExp(pClass, pColumn), values);
|
this(new ColumnExp(pClass, pColumn), values);
|
||||||
@@ -232,107 +196,40 @@ public abstract class Conditionals
|
|||||||
_values = values;
|
_values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
public In (ColumnExp pColumn, Collection<? extends Comparable> values)
|
||||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
|
||||||
{
|
{
|
||||||
_column.appendExpression(query, builder);
|
this(pColumn, values.toArray(new Comparable[values.size()]));
|
||||||
builder.append(" in (");
|
|
||||||
for (int ii = 0; ii < _values.length; ii ++) {
|
|
||||||
if (ii > 0) {
|
|
||||||
builder.append(", ");
|
|
||||||
}
|
}
|
||||||
builder.append("?");
|
|
||||||
|
public ColumnExp getColumn ()
|
||||||
|
{
|
||||||
|
return _column;
|
||||||
}
|
}
|
||||||
builder.append(")");
|
|
||||||
|
public Comparable[] getValues ()
|
||||||
|
{
|
||||||
|
return _values;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
for (int ii = 0; ii < _values.length; ii++) {
|
builder.visit(this);
|
||||||
pstmt.setObject(argIdx ++, _values[ii]);
|
|
||||||
}
|
}
|
||||||
return argIdx;
|
|
||||||
|
// from SQLExpression
|
||||||
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
|
{
|
||||||
|
_column.addClasses(classSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ColumnExp _column;
|
protected ColumnExp _column;
|
||||||
protected Comparable[] _values;
|
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. */
|
/** 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)
|
public Like (SQLExpression column, Comparable value)
|
||||||
{
|
{
|
||||||
super(column, value);
|
super(column, value);
|
||||||
@@ -344,9 +241,64 @@ public abstract class Conditionals
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return " like ";
|
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;
|
package com.samskivert.jdbc.depot.operator;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.util.Collection;
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
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.SQLExpression;
|
||||||
import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenient container for implementations of logical operators. Classes that value brevity
|
* 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.
|
* 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)
|
public Or (SQLExpression... conditions)
|
||||||
{
|
{
|
||||||
@@ -45,7 +44,7 @@ public abstract class Logic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "or";
|
return "or";
|
||||||
}
|
}
|
||||||
@@ -54,7 +53,7 @@ public abstract class Logic
|
|||||||
/**
|
/**
|
||||||
* Represents a condition that is true iff all its subconditions are true.
|
* 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)
|
public And (SQLExpression... conditions)
|
||||||
{
|
{
|
||||||
@@ -62,7 +61,7 @@ public abstract class Logic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String operator()
|
public String operator()
|
||||||
{
|
{
|
||||||
return "and";
|
return "and";
|
||||||
}
|
}
|
||||||
@@ -76,23 +75,24 @@ public abstract class Logic
|
|||||||
{
|
{
|
||||||
public Not (SQLExpression condition)
|
public Not (SQLExpression condition)
|
||||||
{
|
{
|
||||||
super();
|
|
||||||
_condition = condition;
|
_condition = condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
public SQLExpression getCondition ()
|
||||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
|
||||||
{
|
{
|
||||||
builder.append(" not (");
|
return _condition;
|
||||||
_condition.appendExpression(query, builder);
|
|
||||||
builder.append(")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
return _condition.bindExpressionArguments(pstmt, argIdx);
|
builder.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from SQLExpression
|
||||||
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
|
{
|
||||||
|
_condition.addClasses(classSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SQLExpression _condition;
|
protected SQLExpression _condition;
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc.depot.operator;
|
package com.samskivert.jdbc.depot.operator;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.util.Collection;
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
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.SQLExpression;
|
||||||
import com.samskivert.jdbc.depot.expression.ValueExp;
|
import com.samskivert.jdbc.depot.expression.ValueExp;
|
||||||
|
|
||||||
@@ -42,37 +42,32 @@ public interface SQLOperator extends SQLExpression
|
|||||||
{
|
{
|
||||||
public MultiOperator (SQLExpression ... conditions)
|
public MultiOperator (SQLExpression ... conditions)
|
||||||
{
|
{
|
||||||
super();
|
|
||||||
_conditions = conditions;
|
_conditions = conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
{
|
{
|
||||||
for (int ii = 0; ii < _conditions.length; ii++) {
|
builder.visit(this);
|
||||||
if (ii > 0) {
|
|
||||||
builder.append(" ").append(operator()).append(" ");
|
|
||||||
}
|
|
||||||
builder.append("(");
|
|
||||||
_conditions[ii].appendExpression(query, builder);
|
|
||||||
builder.append(")");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
for (int ii = 0; ii < _conditions.length; ii ++) {
|
for (int ii = 0; ii < _conditions.length; ii ++) {
|
||||||
argIdx = _conditions[ii].bindExpressionArguments(pstmt, argIdx);
|
_conditions[ii].addClasses(classSet);
|
||||||
}
|
}
|
||||||
return argIdx;
|
}
|
||||||
|
|
||||||
|
public SQLExpression[] getConditions ()
|
||||||
|
{
|
||||||
|
return _conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the text infix to be used to join expressions together.
|
* Returns the text infix to be used to join expressions together.
|
||||||
*/
|
*/
|
||||||
protected abstract String operator ();
|
public abstract String operator ();
|
||||||
|
|
||||||
protected SQLExpression[] _conditions;
|
protected SQLExpression[] _conditions;
|
||||||
}
|
}
|
||||||
@@ -94,26 +89,32 @@ public interface SQLOperator extends SQLExpression
|
|||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public void appendExpression (QueryBuilderContext query, StringBuilder builder)
|
public void accept (ExpressionVisitor builder) throws Exception
|
||||||
{
|
{
|
||||||
_lhs.appendExpression(query, builder);
|
builder.visit(this);
|
||||||
builder.append(operator());
|
|
||||||
_rhs.appendExpression(query, builder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from SQLExpression
|
// from SQLExpression
|
||||||
public int bindExpressionArguments (PreparedStatement pstmt, int argIdx)
|
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
argIdx = _lhs.bindExpressionArguments(pstmt, argIdx);
|
_lhs.addClasses(classSet);
|
||||||
argIdx = _rhs.bindExpressionArguments(pstmt, argIdx);
|
_rhs.addClasses(classSet);
|
||||||
return argIdx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the string representation of the operator.
|
* 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 _lhs;
|
||||||
protected SQLExpression _rhs;
|
protected SQLExpression _rhs;
|
||||||
|
|||||||
Reference in New Issue
Block a user