Depot now lives in its own project.
git-svn-id: https://samskivert.googlecode.com/svn/trunk@2480 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<!-- $Id$ -->
|
||||
<!-- Exports various samskivert utilities to GWT. -->
|
||||
<module>
|
||||
<source path="jdbc/depot"/>
|
||||
<source path="text"/>
|
||||
</module>
|
||||
|
||||
@@ -1,307 +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.nio.ByteBuffer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.jdbc.depot.clause.DeleteClause;
|
||||
import com.samskivert.jdbc.depot.clause.FieldDefinition;
|
||||
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.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.EpochSeconds;
|
||||
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.Exists;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.FullTextMatch;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.In;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.IsNull;
|
||||
import com.samskivert.jdbc.depot.operator.Logic.Not;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Implements the base functionality of the argument-binding pass of {@link SQLBuilder}. Dialectal
|
||||
* subclasses of this should be created and returned from {@link SQLBuilder#getBindVisitor}.
|
||||
*
|
||||
* This class is intimately paired with {#link BuildVisitor}.
|
||||
*/
|
||||
public class BindVisitor implements ExpressionVisitor
|
||||
{
|
||||
public void visit (FromOverride override)
|
||||
{
|
||||
// nothing needed
|
||||
}
|
||||
|
||||
public void visit (FieldDefinition fieldOverride)
|
||||
{
|
||||
// nothing needed
|
||||
}
|
||||
|
||||
public void visit (Key.Expression<? extends PersistentRecord> key)
|
||||
{
|
||||
for (Comparable<?> value : key.getValues()) {
|
||||
if (value != null) {
|
||||
writeValueToStatement(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (MultiKey<? extends PersistentRecord> key)
|
||||
{
|
||||
for (Map.Entry<String, Comparable<?>> entry : key.getSingleFieldsMap().entrySet()) {
|
||||
if (entry.getValue() != null) {
|
||||
writeValueToStatement(entry.getValue());
|
||||
}
|
||||
}
|
||||
Comparable<?>[] values = key.getMultiValues();
|
||||
for (int ii = 0; ii < values.length; ii++) {
|
||||
writeValueToStatement(values[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (FunctionExp functionExp)
|
||||
{
|
||||
visit(functionExp.getArguments());
|
||||
}
|
||||
|
||||
public void visit (EpochSeconds epochSeconds)
|
||||
{
|
||||
epochSeconds.getArgument().accept(this);
|
||||
}
|
||||
|
||||
public void visit (MultiOperator multiOperator)
|
||||
{
|
||||
visit(multiOperator.getConditions());
|
||||
}
|
||||
|
||||
public void visit (BinaryOperator binaryOperator)
|
||||
{
|
||||
binaryOperator.getLeftHandSide().accept(this);
|
||||
binaryOperator.getRightHandSide().accept(this);
|
||||
}
|
||||
|
||||
public void visit (IsNull isNull)
|
||||
{
|
||||
}
|
||||
|
||||
public void visit (In in)
|
||||
{
|
||||
Comparable<?>[] values = in.getValues();
|
||||
for (int ii = 0; ii < values.length; ii++) {
|
||||
writeValueToStatement(values[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (FullTextMatch match)
|
||||
{
|
||||
// we never get here
|
||||
}
|
||||
|
||||
public void visit (ColumnExp columnExp)
|
||||
{
|
||||
// no arguments
|
||||
}
|
||||
|
||||
public void visit (Not not)
|
||||
{
|
||||
not.getCondition().accept(this);
|
||||
}
|
||||
|
||||
public void visit (GroupBy groupBy)
|
||||
{
|
||||
visit(groupBy.getValues());
|
||||
}
|
||||
|
||||
public void visit (ForUpdate forUpdate)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public void visit (OrderBy orderBy)
|
||||
{
|
||||
visit(orderBy.getValues());
|
||||
}
|
||||
|
||||
public void visit (WhereClause where)
|
||||
{
|
||||
where.getWhereExpression().accept(this);
|
||||
}
|
||||
|
||||
public void visit (Join join)
|
||||
{
|
||||
join.getJoinCondition().accept(this);
|
||||
}
|
||||
|
||||
public void visit (Limit limit)
|
||||
{
|
||||
try {
|
||||
_stmt.setInt(_argIdx++, limit.getCount());
|
||||
_stmt.setInt(_argIdx++, limit.getOffset());
|
||||
} catch (SQLException sqe) {
|
||||
throw new DatabaseException("Failed to configure statement with limit clause " +
|
||||
"[count=" + limit.getCount() +
|
||||
", offset=" + limit.getOffset() + "]", sqe);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (LiteralExp literalExp)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public void visit (ValueExp valueExp)
|
||||
{
|
||||
writeValueToStatement(valueExp.getValue());
|
||||
}
|
||||
|
||||
public void visit (Exists<? extends PersistentRecord> exists)
|
||||
{
|
||||
exists.getSubClause().accept(this);
|
||||
}
|
||||
|
||||
public void visit (SelectClause<? extends PersistentRecord> selectClause)
|
||||
{
|
||||
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)
|
||||
{
|
||||
DepotMarshaller<?> marsh = _types.getMarshaller(updateClause.getPersistentClass());
|
||||
|
||||
// bind the update arguments
|
||||
Object pojo = updateClause.getPojo();
|
||||
if (pojo != null) {
|
||||
for (String field : updateClause.getFields()) {
|
||||
try {
|
||||
marsh.getFieldMarshaller(field).getAndWriteToStatement(_stmt, _argIdx++, pojo);
|
||||
} catch (Exception e) {
|
||||
throw new DatabaseException(
|
||||
"Failed to read field from persistent record and write it to prepared " +
|
||||
"statement [field=" + field + "]", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
visit(updateClause.getValues());
|
||||
}
|
||||
updateClause.getWhereClause().accept(this);
|
||||
}
|
||||
|
||||
public void visit (InsertClause<? extends PersistentRecord> insertClause)
|
||||
{
|
||||
DepotMarshaller<?> marsh = _types.getMarshaller(insertClause.getPersistentClass());
|
||||
Object pojo = insertClause.getPojo();
|
||||
Set<String> idFields = insertClause.getIdentityFields();
|
||||
for (String field : marsh.getColumnFieldNames()) {
|
||||
if (!idFields.contains(field)) {
|
||||
try {
|
||||
marsh.getFieldMarshaller(field).getAndWriteToStatement(_stmt, _argIdx++, pojo);
|
||||
} catch (Exception e) {
|
||||
throw new DatabaseException(
|
||||
"Failed to read field from persistent record and write it to prepared " +
|
||||
"statement [field=" + field + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (DeleteClause<? extends PersistentRecord> deleteClause)
|
||||
{
|
||||
deleteClause.getWhereClause().accept(this);
|
||||
}
|
||||
|
||||
protected BindVisitor (DepotTypes types, Connection conn, PreparedStatement stmt)
|
||||
{
|
||||
_types = types;
|
||||
_conn = conn;
|
||||
_stmt = stmt;
|
||||
_argIdx = 1;
|
||||
}
|
||||
|
||||
protected void visit (SQLExpression[] expressions)
|
||||
{
|
||||
for (int ii = 0; ii < expressions.length; ii ++) {
|
||||
expressions[ii].accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
// write the value to the next argument slot in the prepared statement
|
||||
protected void writeValueToStatement (Object value)
|
||||
{
|
||||
try {
|
||||
// TODO: how can we abstract this fieldless marshalling
|
||||
if (value instanceof ByteEnum) {
|
||||
// byte enums require special conversion
|
||||
_stmt.setByte(_argIdx++, ((ByteEnum)value).toByte());
|
||||
} else if (value instanceof int[]) {
|
||||
// int arrays require conversion to byte arrays
|
||||
int[] data = (int[])value;
|
||||
ByteBuffer bbuf = ByteBuffer.allocate(data.length * 4);
|
||||
bbuf.asIntBuffer().put(data);
|
||||
_stmt.setObject(_argIdx++, bbuf.array());
|
||||
} else {
|
||||
_stmt.setObject(_argIdx++, value);
|
||||
}
|
||||
} catch (SQLException sqe) {
|
||||
throw new DatabaseException("Failed to write value to statement [idx=" + (_argIdx-1) +
|
||||
", value=" + StringUtil.safeToString(value) + "]", sqe);
|
||||
}
|
||||
}
|
||||
|
||||
protected DepotTypes _types;
|
||||
protected Connection _conn;
|
||||
protected PreparedStatement _stmt;
|
||||
protected int _argIdx;
|
||||
}
|
||||
@@ -1,611 +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.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.jdbc.depot.annotation.Computed;
|
||||
import com.samskivert.jdbc.depot.clause.DeleteClause;
|
||||
import com.samskivert.jdbc.depot.clause.FieldDefinition;
|
||||
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.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.EpochSeconds;
|
||||
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.Exists;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.FullTextMatch;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.In;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.IsNull;
|
||||
import com.samskivert.jdbc.depot.operator.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)
|
||||
{
|
||||
_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 (FieldDefinition definition)
|
||||
{
|
||||
definition.getDefinition().accept(this);
|
||||
if (_enableAliasing) {
|
||||
_builder.append(" as ");
|
||||
appendIdentifier(definition.getField());
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (WhereClause where)
|
||||
{
|
||||
_builder.append(" where ");
|
||||
where.getWhereExpression().accept(this);
|
||||
}
|
||||
|
||||
public void visit (Key.Expression<? extends PersistentRecord> key)
|
||||
{
|
||||
Class<? extends PersistentRecord> pClass = key.getPersistentClass();
|
||||
String[] keyFields = KeyUtil.getKeyFields(pClass);
|
||||
List<Comparable<?>> values = key.getValues();
|
||||
for (int ii = 0; ii < keyFields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(" and ");
|
||||
}
|
||||
// A Key's WHERE clause must mirror what's actually retrieved for the persistent
|
||||
// object, so we turn on overrides here just as we do when expanding SELECT fields
|
||||
boolean saved = _enableOverrides;
|
||||
_enableOverrides = true;
|
||||
appendRhsColumn(pClass, keyFields[ii]);
|
||||
_enableOverrides = saved;
|
||||
_builder.append(values.get(ii) == null ? " is null " : " = ? ");
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (MultiKey<? extends PersistentRecord> key)
|
||||
{
|
||||
_builder.append(" where ");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, Comparable<?>> entry : key.getSingleFieldsMap().entrySet()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
_builder.append(" and ");
|
||||
}
|
||||
// A MultiKey's WHERE clause must mirror what's actually retrieved for the persistent
|
||||
// object, so we turn on overrides here just as we do when expanding SELECT fields
|
||||
boolean saved = _enableOverrides;
|
||||
_enableOverrides = true;
|
||||
appendRhsColumn(key.getPersistentClass(), entry.getKey());
|
||||
_enableOverrides = saved;
|
||||
_builder.append(entry.getValue() == null ? " is null " : " = ? ");
|
||||
}
|
||||
if (!first) {
|
||||
_builder.append(" and ");
|
||||
}
|
||||
appendRhsColumn(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)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
_builder.append('(');
|
||||
binaryOperator.getLeftHandSide().accept(this);
|
||||
_builder.append(binaryOperator.operator());
|
||||
binaryOperator.getRightHandSide().accept(this);
|
||||
_builder.append(')');
|
||||
}
|
||||
|
||||
public void visit (IsNull isNull)
|
||||
{
|
||||
isNull.getColumn().accept(this);
|
||||
_builder.append(" is null");
|
||||
}
|
||||
|
||||
public void visit (In in)
|
||||
{
|
||||
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 abstract void visit (EpochSeconds seconds);
|
||||
|
||||
public abstract void visit (FullTextMatch match);
|
||||
|
||||
public void visit (ColumnExp columnExp)
|
||||
{
|
||||
appendRhsColumn(columnExp.getPersistentClass(), columnExp.getField());
|
||||
}
|
||||
|
||||
public void visit (Not not)
|
||||
{
|
||||
_builder.append(" not (");
|
||||
not.getCondition().accept(this);
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
public void visit (GroupBy groupBy)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_builder.append(" for update ");
|
||||
}
|
||||
|
||||
public void visit (OrderBy orderBy)
|
||||
{
|
||||
_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 (Join join)
|
||||
{
|
||||
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)
|
||||
{
|
||||
_builder.append(" limit ? offset ? ");
|
||||
}
|
||||
|
||||
public void visit (LiteralExp literalExp)
|
||||
{
|
||||
_builder.append(literalExp.getText());
|
||||
}
|
||||
|
||||
public void visit (ValueExp valueExp)
|
||||
{
|
||||
_builder.append("?");
|
||||
}
|
||||
|
||||
public void visit (Exists<? extends PersistentRecord> exists)
|
||||
{
|
||||
_builder.append("exists ");
|
||||
exists.getSubClause().accept(this);
|
||||
}
|
||||
|
||||
public void visit (SelectClause<? extends PersistentRecord> selectClause)
|
||||
{
|
||||
Class<? extends PersistentRecord> pClass = selectClause.getPersistentClass();
|
||||
boolean isInner = _innerClause;
|
||||
_innerClause = true;
|
||||
|
||||
if (isInner) {
|
||||
_builder.append("(");
|
||||
}
|
||||
_builder.append("select ");
|
||||
|
||||
if (_definitions.containsKey(pClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can not yet nest SELECTs on the same persistent record.");
|
||||
}
|
||||
|
||||
Map<String, FieldDefinition> definitionMap = new HashMap<String, FieldDefinition>();
|
||||
for (FieldDefinition definition : selectClause.getFieldDefinitions()) {
|
||||
definitionMap.put(definition.getField(), definition);
|
||||
}
|
||||
_definitions.put(pClass, definitionMap);
|
||||
|
||||
try {
|
||||
// iterate over the fields we're filling in and figure out whence each one comes
|
||||
boolean skip = true;
|
||||
|
||||
// while expanding column names in the SELECT query, do aliasing and expansion
|
||||
_enableAliasing = _enableOverrides = true;
|
||||
|
||||
for (String field : selectClause.getFields()) {
|
||||
if (!skip) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
skip = false;
|
||||
|
||||
int len = _builder.length();
|
||||
appendRhsColumn(pClass, field);
|
||||
|
||||
// if nothing was added, don't add a comma
|
||||
if (_builder.length() == len) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// then stop
|
||||
_enableAliasing = _enableOverrides = false;
|
||||
|
||||
if (selectClause.getFromOverride() != null) {
|
||||
selectClause.getFromOverride().accept(this);
|
||||
|
||||
} else {
|
||||
Computed computed = _types.getMarshaller(pClass).getComputed();
|
||||
Class<? extends PersistentRecord> tClass;
|
||||
if (computed != null && !PersistentRecord.class.equals(computed.shadowOf())) {
|
||||
tClass = computed.shadowOf();
|
||||
} else if (_types.getTableName(pClass) != null) {
|
||||
tClass = pClass;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Query on @Computed entity with no FromOverrideClause.");
|
||||
}
|
||||
_builder.append(" from ");
|
||||
appendTableName(tClass);
|
||||
_builder.append(" as ");
|
||||
appendTableAbbreviation(tClass);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} finally {
|
||||
_definitions.remove(pClass);
|
||||
}
|
||||
if (isInner) {
|
||||
_builder.append(")");
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (UpdateClause<? extends PersistentRecord> updateClause)
|
||||
{
|
||||
if (updateClause.getWhereClause() == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"I dare not currently perform UPDATE without a WHERE clause.");
|
||||
}
|
||||
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(", ");
|
||||
}
|
||||
appendLhsColumn(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)
|
||||
{
|
||||
_builder.append("delete from ");
|
||||
appendTableName(deleteClause.getPersistentClass());
|
||||
_builder.append(" as ");
|
||||
appendTableAbbreviation(deleteClause.getPersistentClass());
|
||||
_builder.append(" ");
|
||||
deleteClause.getWhereClause().accept(this);
|
||||
}
|
||||
|
||||
public void visit (InsertClause<? extends PersistentRecord> insertClause)
|
||||
{
|
||||
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(", ");
|
||||
}
|
||||
appendLhsColumn(pClass, fields[ii]);
|
||||
}
|
||||
_builder.append(") values(");
|
||||
|
||||
Set<String> idFields = insertClause.getIdentityFields();
|
||||
for (int ii = 0; ii < fields.length; ii++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
if (idFields.contains(fields[ii])) {
|
||||
_builder.append("DEFAULT");
|
||||
} else {
|
||||
_builder.append("?");
|
||||
}
|
||||
}
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
protected abstract void appendIdentifier (String field);
|
||||
|
||||
protected void appendTableName (Class<? extends PersistentRecord> type)
|
||||
{
|
||||
appendIdentifier(_types.getTableName(type));
|
||||
}
|
||||
|
||||
protected void appendTableAbbreviation (Class<? extends PersistentRecord> type)
|
||||
{
|
||||
appendIdentifier(_types.getTableAbbreviation(type));
|
||||
}
|
||||
|
||||
// Constructs a name used for assignment in e.g. INSERT/UPDATE. This is the SQL
|
||||
// equivalent of an lvalue; something that can appear to the left of an equals sign.
|
||||
// We do not prepend this identifier with a table abbreviation, nor do we expand
|
||||
// field overrides, shadowOf declarations, or the like: it is just a column name.
|
||||
protected void appendLhsColumn (Class<? extends PersistentRecord> type, String field)
|
||||
{
|
||||
DepotMarshaller<?> dm = _types.getMarshaller(type);
|
||||
if (dm == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown field on persistent record [record=" + type + ", field=" + field + "]");
|
||||
}
|
||||
|
||||
FieldMarshaller<?> fm = dm.getFieldMarshaller(field);
|
||||
appendIdentifier(fm.getColumnName());
|
||||
}
|
||||
|
||||
// Appends an expression for the given field on the given persistent record; this can
|
||||
// appear in a SELECT list, in WHERE clauses, etc, etc.
|
||||
protected void appendRhsColumn (Class<? extends PersistentRecord> type, String field)
|
||||
{
|
||||
DepotMarshaller<?> dm = _types.getMarshaller(type);
|
||||
if (dm == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown field on persistent record [record=" + type + ", field=" + field + "]");
|
||||
}
|
||||
|
||||
// first, see if there's a field definition
|
||||
FieldMarshaller<?> fm = dm.getFieldMarshaller(field);
|
||||
Map<String, FieldDefinition> fieldDefs = _definitions.get(type);
|
||||
if (fieldDefs != null) {
|
||||
FieldDefinition fieldDef = fieldDefs.get(field);
|
||||
if (fieldDef != null) {
|
||||
boolean useOverride;
|
||||
if (fieldDef instanceof FieldOverride) {
|
||||
if (fm.getComputed() != null && dm.getComputed() != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"FieldOverride cannot be used on @Computed field: " + field);
|
||||
}
|
||||
useOverride = _enableOverrides;
|
||||
} else if (fm.getComputed() == null && dm.getComputed() == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"FieldDefinition must not be used on concrete field: " + field);
|
||||
} else {
|
||||
useOverride = true;
|
||||
}
|
||||
|
||||
if (useOverride) {
|
||||
// If a FieldOverride's target is in turn another FieldOverride, the second one
|
||||
// is ignored. As an example, when creating ItemRecords from CloneRecords, we
|
||||
// make Item.itemId = Clone.itemId. We also make Item.parentId = Item.itemId
|
||||
// and would be dismayed to find Item.parentID = Item.itemId = Clone.itemId.
|
||||
boolean saved = _enableOverrides;
|
||||
_enableOverrides = false;
|
||||
fieldDef.accept(this);
|
||||
_enableOverrides = saved;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Computed entityComputed = dm.getComputed();
|
||||
|
||||
// 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 = type;
|
||||
} else if (!PersistentRecord.class.equals(entityComputed.shadowOf())) {
|
||||
tableClass = entityComputed.shadowOf();
|
||||
} else {
|
||||
tableClass = null;
|
||||
}
|
||||
|
||||
// handle the field-level @Computed annotation, if there is one
|
||||
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());
|
||||
if (_enableAliasing) {
|
||||
_builder.append(" as ");
|
||||
appendIdentifier(field);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// or if we can simply ignore the field
|
||||
if (!fieldComputed.required()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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(".");
|
||||
appendIdentifier(fm.getColumnName());
|
||||
return;
|
||||
}
|
||||
|
||||
// else owie
|
||||
throw new IllegalArgumentException(
|
||||
"Persistent field has no definition [class=" + type + ", field=" + field + "]");
|
||||
}
|
||||
|
||||
protected BuildVisitor (DepotTypes types)
|
||||
{
|
||||
_types = types;
|
||||
}
|
||||
|
||||
protected DepotTypes _types;
|
||||
|
||||
/** A StringBuilder to hold the constructed SQL. */
|
||||
protected StringBuilder _builder = new StringBuilder();
|
||||
|
||||
/** A mapping of field overrides per persistent record. */
|
||||
protected Map<Class<? extends PersistentRecord>, Map<String, FieldDefinition>> _definitions=
|
||||
new HashMap<Class<? extends PersistentRecord>, Map<String,FieldDefinition>>();
|
||||
|
||||
/** A flag that's set to true for inner SELECT's */
|
||||
protected boolean _innerClause = false;
|
||||
|
||||
protected boolean _enableOverrides = false;
|
||||
|
||||
protected boolean _enableAliasing = false;
|
||||
}
|
||||
@@ -1,39 +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;
|
||||
|
||||
/**
|
||||
* An enum value can be used as a field in a persistent object if it implements this interface and
|
||||
* also declares a public static method with the following signature:
|
||||
*
|
||||
* <code>
|
||||
* public static YourEnum fromByte (byte value)
|
||||
* </code>
|
||||
*
|
||||
* which must return the appropriate instance of your enum for the supplied byte.
|
||||
*/
|
||||
public interface ByteEnum
|
||||
{
|
||||
/**
|
||||
* Returns the byte value to which to map this enum value.
|
||||
*/
|
||||
public byte toByte ();
|
||||
}
|
||||
@@ -1,77 +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.io.Serializable;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are responsible for all the caching needs of Depot.
|
||||
*
|
||||
* The cache consists of many {@link CacheBin}s. Each bin its own key space. Look-ups and storage
|
||||
* occur on a per-bin per-key basis.
|
||||
*/
|
||||
public interface CacheAdapter
|
||||
{
|
||||
/** The encapsulated result of a cache lookup. */
|
||||
public interface CachedValue<T>
|
||||
{
|
||||
/** Returns the cached value, which can be null. */
|
||||
public T getValue ();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to a specific bin within the cache; this is the type where most of the actual
|
||||
* caching functionality occurs.
|
||||
*/
|
||||
public interface CacheBin<T>
|
||||
{
|
||||
/**
|
||||
* Searches this bin using the given key and returns the resulting {@link CachedValue}, or
|
||||
* null if nothing exists in the cache for this key.
|
||||
*/
|
||||
public CachedValue<T> lookup (Serializable key);
|
||||
|
||||
/**
|
||||
* Stores a new value in this cache bin under the given key.
|
||||
*/
|
||||
public void store (Serializable key, T value);
|
||||
|
||||
/**
|
||||
* Removes the cache entry, if any, associated with the given key.
|
||||
*/
|
||||
public void remove (Serializable key);
|
||||
|
||||
/**
|
||||
* Provides a way to enumerate the currently cached entries in this bin.
|
||||
*/
|
||||
public Iterable<Serializable> enumerateKeys ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the {@link CacheBin} associated with the given ID, creating one on the fly if needed.
|
||||
*/
|
||||
public <T> CacheBin<T> getCache (String id);
|
||||
|
||||
/**
|
||||
* Shut down all operations, e.g. persisting memory contents to disk.
|
||||
*/
|
||||
public void shutdown ();
|
||||
}
|
||||
@@ -1,61 +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.io.Serializable;
|
||||
|
||||
/**
|
||||
* Implementors of this interface performs perform cache invalidation for calls to
|
||||
* {@link DepotRepository#updateLiteral}, {@link DepotRepository#updatePartial} and
|
||||
* {@link DepotRepository#deleteAll}.
|
||||
*/
|
||||
public interface CacheInvalidator
|
||||
{
|
||||
public static abstract class TraverseWithFilter<T extends Serializable>
|
||||
implements CacheInvalidator
|
||||
{
|
||||
public TraverseWithFilter (Class<T> pClass) {
|
||||
this(pClass.getName());
|
||||
}
|
||||
|
||||
public TraverseWithFilter (String cacheId) {
|
||||
_cacheId = cacheId;
|
||||
}
|
||||
|
||||
public void invalidate (PersistenceContext ctx) {
|
||||
ctx.cacheTraverse(_cacheId, new PersistenceContext.CacheEvictionFilter<T>() {
|
||||
@Override protected boolean testForEviction (Serializable key, T record) {
|
||||
return TraverseWithFilter.this.testForEviction(key, record);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract boolean testForEviction (Serializable key, T record);
|
||||
|
||||
protected String _cacheId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must invalidate all cache entries that depend on the records being modified or deleted.
|
||||
* This method is called just before the database statement is executed.
|
||||
*/
|
||||
public void invalidate (PersistenceContext ctx);
|
||||
}
|
||||
@@ -1,42 +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.io.Serializable;
|
||||
|
||||
/**
|
||||
* This interface uniquely identifies a single persistent entry for caching purposes.
|
||||
* Queries that are given a {@link CacheKey} consult the cache before they hit the
|
||||
* database.
|
||||
*/
|
||||
public interface CacheKey
|
||||
{
|
||||
/**
|
||||
* Returns the id of the cache in whose scope this key makes sense.
|
||||
*/
|
||||
public String getCacheId ();
|
||||
|
||||
/**
|
||||
* Returns the actual opaque serializable cache key under which results are stored
|
||||
* in the cache identified by {@link #getCacheId}.
|
||||
*/
|
||||
public Serializable getCacheKey ();
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2001-2008 Michael Bayne
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
/**
|
||||
* Encapsulates a migration of data between entities that should be run only once and using the
|
||||
* same safeguards applied to entity migrations. Note: this should not be used for schema
|
||||
* migrations, use {@link SchemaMigration} for that. Data migrations are registered on a specific
|
||||
* repository via {@link DepotRepository#registerMigration} and should be registered in the
|
||||
* repository's constructor as they will be invoked (if appropriate) in the repository's {@link
|
||||
* DepotRepository#init} method.
|
||||
*
|
||||
* <p> In general one will register an anonymous inner class in a repository's constructor and can
|
||||
* then access methods in the repository directly:
|
||||
*
|
||||
* <pre>
|
||||
* public class FooRepository extends DepotRepository {
|
||||
* public FooRepository (PersistenceContext ctx) {
|
||||
* super(ctx);
|
||||
* registerMigration(new DataMigration("2008_09_25_referral_to_tracking_id") {
|
||||
* public void invoke () throws DatabaseException {
|
||||
* // feel free to use load() findAll(), update(), etc.
|
||||
* }
|
||||
* });
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public abstract class DataMigration
|
||||
{
|
||||
/**
|
||||
* Creates a data migration with the specified unique identifier. The identifier must be unique
|
||||
* across all users of the database and for all time, so be careful. Best to include the date
|
||||
* and pertintent information, e.g. "2008_09_25_referral_to_tracking_id".
|
||||
*/
|
||||
public DataMigration (String ident)
|
||||
{
|
||||
_ident = ident;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier of this migration.
|
||||
*/
|
||||
public String getIdent ()
|
||||
{
|
||||
return _ident;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effects the data migration.
|
||||
*/
|
||||
public abstract void invoke () throws DatabaseException;
|
||||
|
||||
/** The unique identifier for this migration. */
|
||||
protected String _ident;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2001-2008 Michael Bayne
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
/**
|
||||
* Represents a failure reported by the underlying database.
|
||||
*/
|
||||
public class DatabaseException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Constructs a database exception with the specified error message.
|
||||
*/
|
||||
public DatabaseException (String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a database exception with the specified error message and the chained causing
|
||||
* event.
|
||||
*/
|
||||
public DatabaseException (String message, Throwable cause)
|
||||
{
|
||||
super(message);
|
||||
initCause(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a database exception with the specified chained causing event.
|
||||
*/
|
||||
public DatabaseException (Throwable cause)
|
||||
{
|
||||
initCause(cause);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,73 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2001-2008 Michael Bayne
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.samskivert.jdbc.depot.annotation.Column;
|
||||
import com.samskivert.jdbc.depot.annotation.Id;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
|
||||
/**
|
||||
* Maintains a record of all successfully invoked data migrations.
|
||||
*/
|
||||
public class DepotMigrationHistoryRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The column identifier for the {@link #ident} field. */
|
||||
public static final String IDENT = "ident";
|
||||
|
||||
/** The qualified column identifier for the {@link #ident} field. */
|
||||
public static final ColumnExp IDENT_C =
|
||||
new ColumnExp(DepotMigrationHistoryRecord.class, IDENT);
|
||||
|
||||
/** The column identifier for the {@link #whenCompleted} field. */
|
||||
public static final String WHEN_COMPLETED = "whenCompleted";
|
||||
|
||||
/** The qualified column identifier for the {@link #whenCompleted} field. */
|
||||
public static final ColumnExp WHEN_COMPLETED_C =
|
||||
new ColumnExp(DepotMigrationHistoryRecord.class, WHEN_COMPLETED);
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** Our schema version. Probably not likely to change. */
|
||||
public static final int SCHEMA_VERSION = 1;
|
||||
|
||||
/** The unique identifier for this migration. */
|
||||
@Id public String ident;
|
||||
|
||||
/** The time at which the migration was completed. */
|
||||
@Column(nullable=true)
|
||||
public Timestamp whenCompleted;
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Create and return a primary {@link Key} to identify a {@link DepotMigrationHistoryRecord}
|
||||
* with the supplied key values.
|
||||
*/
|
||||
public static Key<DepotMigrationHistoryRecord> getKey (String ident)
|
||||
{
|
||||
return new Key<DepotMigrationHistoryRecord>(
|
||||
DepotMigrationHistoryRecord.class,
|
||||
new String[] { IDENT },
|
||||
new Comparable[] { ident });
|
||||
}
|
||||
// AUTO-GENERATED: METHODS END
|
||||
}
|
||||
@@ -1,988 +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.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
|
||||
import com.samskivert.jdbc.ConnectionProvider;
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
|
||||
import com.samskivert.jdbc.depot.Modifier.*;
|
||||
import com.samskivert.jdbc.depot.clause.DeleteClause;
|
||||
import com.samskivert.jdbc.depot.clause.FieldOverride;
|
||||
import com.samskivert.jdbc.depot.clause.InsertClause;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.clause.SelectClause;
|
||||
import com.samskivert.jdbc.depot.clause.UpdateClause;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.expression.ValueExp;
|
||||
|
||||
import static com.samskivert.jdbc.depot.Log.log;
|
||||
|
||||
/**
|
||||
* Provides a base for classes that provide access to persistent objects. Also defines the
|
||||
* mechanism by which all persistent queries and updates are routed through the distributed cache.
|
||||
*/
|
||||
public abstract class DepotRepository
|
||||
{
|
||||
/**
|
||||
* Creates a repository with the supplied persistence context. Any schema migrations needed by
|
||||
* this repository should be registered in its constructor. A repository should <em>not</em>
|
||||
* perform any actual database operations in its constructor, only register schema
|
||||
* migrations. Initialization related database operations should be performed in {@link #init}.
|
||||
*/
|
||||
protected DepotRepository (PersistenceContext context)
|
||||
{
|
||||
_ctx = context;
|
||||
_ctx.repositoryCreated(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a repository with the supplied connection provider and its own private persistence
|
||||
* context. This should generally not be used for new systems, and is only included to
|
||||
* facilitate the integration of small numbers of Depot-based repositories into systems using
|
||||
* the older samskivert SimpleRepository system.
|
||||
*/
|
||||
protected DepotRepository (ConnectionProvider conprov)
|
||||
{
|
||||
_ctx = new PersistenceContext();
|
||||
_ctx.init(getClass().getName(), conprov, null);
|
||||
_ctx.repositoryCreated(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves all persistent records registered to this repository (via {@link
|
||||
* #getManagedRecords}. This will be done before the repository is initialized via {@link
|
||||
* #init}.
|
||||
*/
|
||||
protected void resolveRecords ()
|
||||
throws DatabaseException
|
||||
{
|
||||
Set<Class<? extends PersistentRecord>> classes =
|
||||
new HashSet<Class<? extends PersistentRecord>>();
|
||||
getManagedRecords(classes);
|
||||
for (Class<? extends PersistentRecord> rclass : classes) {
|
||||
_ctx.getMarshaller(rclass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a place where a repository can perform any initialization that requires database
|
||||
* operations.
|
||||
*/
|
||||
protected void init ()
|
||||
throws DatabaseException
|
||||
{
|
||||
// run any registered data migrations
|
||||
for (DataMigration migration : _dataMigs) {
|
||||
runMigration(migration);
|
||||
}
|
||||
_dataMigs = null; // note that we've been initialized
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a data migration for this repository. This migration will only be run once and its
|
||||
* unique identifier will be stored persistently to ensure that it is never run again on the
|
||||
* same database. Nonetheless, migrations should strive to be idempotent because someone might
|
||||
* come along and create a brand new system installation and all registered migrations will be
|
||||
* run once on the freshly created database. As with all database migrations, understand
|
||||
* clearly how the process works and think about edge cases when creating a migration.
|
||||
*
|
||||
* <p> See {@link PersistenceContext#registerMigration} for details on how schema migrations
|
||||
* operate and how they might interact with data migrations.
|
||||
*/
|
||||
protected void registerMigration (DataMigration migration)
|
||||
{
|
||||
if (_dataMigs == null) {
|
||||
// we've already been initialized, so we have to run this migration immediately
|
||||
runMigration(migration);
|
||||
} else {
|
||||
_dataMigs.add(migration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the persistent classes used by this repository to the supplied set.
|
||||
*/
|
||||
protected abstract void getManagedRecords (Set<Class<? extends PersistentRecord>> classes);
|
||||
|
||||
/**
|
||||
* Loads the persistent object that matches the specified primary key.
|
||||
*/
|
||||
protected <T extends PersistentRecord> T load (Class<T> type, Comparable<?> primaryKey,
|
||||
QueryClause... clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
clauses = ArrayUtil.append(clauses, _ctx.getMarshaller(type).makePrimaryKey(primaryKey));
|
||||
return load(type, clauses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the persistent object that matches the specified primary key.
|
||||
*/
|
||||
protected <T extends PersistentRecord> T load (Class<T> type, String ix, Comparable<?> val,
|
||||
QueryClause... clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
clauses = ArrayUtil.append(clauses, new Key<T>(type, ix, val));
|
||||
return load(type, clauses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the persistent object that matches the specified two-column primary key.
|
||||
*/
|
||||
protected <T extends PersistentRecord> T load (Class<T> type, String ix1, Comparable<?> val1,
|
||||
String ix2, Comparable<?> val2,
|
||||
QueryClause... clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
clauses = ArrayUtil.append(clauses, new Key<T>(type, ix1, val1, ix2, val2));
|
||||
return load(type, clauses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the persistent object that matches the specified three-column primary key.
|
||||
*/
|
||||
protected <T extends PersistentRecord> T load (Class<T> type, String ix1, Comparable<?> val1,
|
||||
String ix2, Comparable<?> val2, String ix3,
|
||||
Comparable<?> val3, QueryClause... clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
clauses = ArrayUtil.append(clauses, new Key<T>(type, ix1, val1, ix2, val2, ix3, val3));
|
||||
return load(type, clauses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the first persistent object that matches the supplied query clauses.
|
||||
*/
|
||||
protected <T extends PersistentRecord> T load (
|
||||
Class<T> type, Collection<? extends QueryClause> clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
return load(type, clauses.toArray(new QueryClause[clauses.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the first persistent object that matches the supplied query clauses.
|
||||
*/
|
||||
protected <T extends PersistentRecord> T load (Class<T> type, QueryClause... clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
return _ctx.invoke(new FindOneQuery<T>(_ctx, type, clauses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up all persistent records that match the supplied set of raw primary keys.
|
||||
*/
|
||||
protected <T extends PersistentRecord> List<T> loadAll (
|
||||
Class<T> type, Collection<? extends Comparable<?>> primaryKeys)
|
||||
throws DatabaseException
|
||||
{
|
||||
// convert the raw keys into real key records
|
||||
DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
|
||||
List<Key<T>> keys = new ArrayList<Key<T>>();
|
||||
for (Comparable<?> key : primaryKeys) {
|
||||
keys.add(marsh.makePrimaryKey(key));
|
||||
}
|
||||
return loadAll(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up all persistent records that match the supplied set of primary keys.
|
||||
*/
|
||||
protected <T extends PersistentRecord> List<T> loadAll (Collection<Key<T>> keys)
|
||||
throws DatabaseException
|
||||
{
|
||||
return (keys.size() == 0) ? Collections.<T>emptyList() :
|
||||
_ctx.invoke(new FindAllQuery.WithKeys<T>(_ctx, keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* A varargs version of {@link #findAll(Class,Collection)}.
|
||||
*/
|
||||
protected <T extends PersistentRecord> List<T> findAll (Class<T> type, QueryClause... clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
return findAll(type, Arrays.asList(clauses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all persistent objects that match the specified clauses.
|
||||
*
|
||||
* We have two strategies for doing this: one performs the query as-is, the second executes two
|
||||
* passes: first fetching only key columns and consulting the cache for each such key; then, in
|
||||
* the second pass, fetching the full entity only for keys that were not found in the cache.
|
||||
*
|
||||
* The more complex strategy could save a lot of data shuffling. On the other hand, its
|
||||
* complexity is an inherent drawback, and it does execute two separate database queries for
|
||||
* what the simple method does in one.
|
||||
*/
|
||||
protected <T extends PersistentRecord> List<T> findAll (
|
||||
Class<T> type, Collection<? extends QueryClause> clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
return findAll(type, false, clauses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all persistent objects that match the specified clauses.
|
||||
*
|
||||
* @param skipCache if true, our normal mixed select strategy that allows cached records to be
|
||||
* loaded from the cache will not be used even if it otherwise could. See {@link
|
||||
* #findAll(Class,Collection)} for details on the mixed strategy.
|
||||
*/
|
||||
protected <T extends PersistentRecord> List<T> findAll (
|
||||
Class<T> type, boolean skipCache, Collection<? extends QueryClause> clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
|
||||
boolean useExplicit = skipCache || (marsh.getTableName() == null) ||
|
||||
!marsh.hasPrimaryKey() || !_ctx.isUsingCache();
|
||||
|
||||
// queries on @Computed records or the presence of FieldOverrides use the simple algorithm
|
||||
for (QueryClause clause : clauses) {
|
||||
useExplicit |= (clause instanceof FieldOverride);
|
||||
}
|
||||
|
||||
return _ctx.invoke(useExplicit ? new FindAllQuery.Explicitly<T>(_ctx, type, clauses) :
|
||||
new FindAllQuery.WithCache<T>(_ctx, type, clauses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up and returns {@link Key} records for all rows that match the supplied query clauses.
|
||||
*
|
||||
* @param forUpdate if true, the query will be run using a read-write connection to ensure that
|
||||
* it talks to the master database, if false, the query will be run on a read-only connection
|
||||
* and may load keys from a slave. For performance reasons, you should always pass false unless
|
||||
* you know you will be modifying the database as a result of this query and absolutely need
|
||||
* the latest data.
|
||||
*/
|
||||
protected <T extends PersistentRecord> List<Key<T>> findAllKeys (
|
||||
Class<T> type, boolean forUpdate, QueryClause... clause)
|
||||
throws DatabaseException
|
||||
{
|
||||
return findAllKeys(type, forUpdate, Arrays.asList(clause));
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up and returns {@link Key} records for all rows that match the supplied query clauses.
|
||||
*
|
||||
* @param forUpdate if true, the query will be run using a read-write connection to ensure that
|
||||
* it talks to the master database, if false, the query will be run on a read-only connection
|
||||
* and may load keys from a slave. For performance reasons, you should always pass false unless
|
||||
* you know you will be modifying the database as a result of this query and absolutely need
|
||||
* the latest data.
|
||||
*/
|
||||
protected <T extends PersistentRecord> List<Key<T>> findAllKeys (
|
||||
Class<T> type, boolean forUpdate, Collection<? extends QueryClause> clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
final List<Key<T>> keys = new ArrayList<Key<T>>();
|
||||
final DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
|
||||
SelectClause<T> select = new SelectClause<T>(type, marsh.getPrimaryKeyFields(), clauses);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, select));
|
||||
builder.newQuery(select);
|
||||
|
||||
if (forUpdate) {
|
||||
_ctx.invoke(new Modifier(null) {
|
||||
@Override public Integer invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
keys.add(marsh.makePrimaryKey(rs));
|
||||
}
|
||||
return 0;
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
_ctx.invoke(new Query.Trivial<Void>() {
|
||||
@Override
|
||||
public Void invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
keys.add(marsh.makePrimaryKey(rs));
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the supplied persistent object into the database, assigning its primary key (if it
|
||||
* has one) in the process.
|
||||
*
|
||||
* @return the number of rows modified by this action, this should always be one.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int insert (T record)
|
||||
throws DatabaseException
|
||||
{
|
||||
@SuppressWarnings("unchecked") final Class<T> pClass = (Class<T>) record.getClass();
|
||||
final DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||
Key<T> key = marsh.getPrimaryKey(record, false);
|
||||
|
||||
DepotTypes types = DepotTypes.getDepotTypes(_ctx);
|
||||
types.addClass(_ctx, pClass);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(types);
|
||||
|
||||
// key will be null if record was supplied without a primary key
|
||||
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
// if needed, update our modifier's key so that it can cache our results
|
||||
Set<String> identityFields = Collections.emptySet();
|
||||
if (_key == null) {
|
||||
// set any auto-generated column values
|
||||
identityFields = marsh.generateFieldValues(conn, liaison, _result, false);
|
||||
updateKey(marsh.getPrimaryKey(_result, false));
|
||||
}
|
||||
|
||||
builder.newQuery(new InsertClause<T>(pClass, _result, identityFields));
|
||||
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
int mods = stmt.executeUpdate();
|
||||
// run any post-factum value generators and potentially generate our key
|
||||
if (_key == null) {
|
||||
marsh.generateFieldValues(conn, liaison, _result, true);
|
||||
updateKey(marsh.getPrimaryKey(_result, false));
|
||||
}
|
||||
return mods;
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all fields of the supplied persistent object, using its primary key to identify the
|
||||
* row to be updated.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int update (T record)
|
||||
throws DatabaseException
|
||||
{
|
||||
@SuppressWarnings("unchecked") Class<T> pClass = (Class<T>) record.getClass();
|
||||
requireNotComputed(pClass, "update");
|
||||
|
||||
DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||
Key<T> key = marsh.getPrimaryKey(record);
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("Can't update record with null primary key.");
|
||||
}
|
||||
|
||||
UpdateClause<T> update = new UpdateClause<T>(pClass, key, marsh._columnFields, record);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, update));
|
||||
builder.newQuery(update);
|
||||
|
||||
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates just the specified fields of the supplied persistent object, using its primary key
|
||||
* to identify the row to be updated. This method currently flushes the associated record from
|
||||
* the cache, but in the future it should be modified to update the modified fields in the
|
||||
* cached value iff the record exists in the cache.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int update (T record, final String... modifiedFields)
|
||||
throws DatabaseException
|
||||
{
|
||||
@SuppressWarnings("unchecked") Class<T> pClass = (Class<T>) record.getClass();
|
||||
requireNotComputed(pClass, "updatePartial");
|
||||
|
||||
DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||
Key<T> key = marsh.getPrimaryKey(record);
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("Can't update record with null primary key.");
|
||||
}
|
||||
|
||||
UpdateClause<T> update = new UpdateClause<T>(pClass, key, modifiedFields, record);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, update));
|
||||
builder.newQuery(update);
|
||||
|
||||
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
// clear out _result so that we don't rewrite this partial record to the cache
|
||||
_result = null;
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied primary key.
|
||||
*
|
||||
* @param type the type of the persistent object to be modified.
|
||||
* @param primaryKey the primary key to match in the update.
|
||||
* @param updates an mapping from the names of the fields/columns ti the values to be assigned.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updatePartial (
|
||||
Class<T> type, Comparable<?> primaryKey, Map<String,Object> updates)
|
||||
throws DatabaseException
|
||||
{
|
||||
Object[] fieldsValues = new Object[updates.size()*2];
|
||||
int idx = 0;
|
||||
for (Map.Entry<String,Object> entry : updates.entrySet()) {
|
||||
fieldsValues[idx++] = entry.getKey();
|
||||
fieldsValues[idx++] = entry.getValue();
|
||||
}
|
||||
return updatePartial(type, primaryKey, fieldsValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied primary key.
|
||||
*
|
||||
* @param type the type of the persistent object to be modified.
|
||||
* @param primaryKey the primary key to match in the update.
|
||||
* @param fieldsValues an array containing the names of the fields/columns and the values to be
|
||||
* assigned, in key, value, key, value, etc. order.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updatePartial (
|
||||
Class<T> type, Comparable<?> primaryKey, Object... fieldsValues)
|
||||
throws DatabaseException
|
||||
{
|
||||
return updatePartial(_ctx.getMarshaller(type).makePrimaryKey(primaryKey), fieldsValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied two-column
|
||||
* primary key.
|
||||
*
|
||||
* @param type the type of the persistent object to be modified.
|
||||
* @param fieldsValues an array containing the names of the fields/columns and the values to be
|
||||
* assigned, in key, value, key, value, etc. order.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updatePartial (
|
||||
Class<T> type, String ix1, Comparable<?> val1, String ix2, Comparable<?> val2,
|
||||
Object... fieldsValues)
|
||||
throws DatabaseException
|
||||
{
|
||||
return updatePartial(new Key<T>(type, ix1, val1, ix2, val2), fieldsValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied three-column
|
||||
* primary key.
|
||||
*
|
||||
* @param type the type of the persistent object to be modified.
|
||||
* @param fieldsValues an array containing the names of the fields/columns and the values to be
|
||||
* assigned, in key, value, key, value, etc. order.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updatePartial (
|
||||
Class<T> type, String ix1, Comparable<?> val1, String ix2, Comparable<?> val2,
|
||||
String ix3, Comparable<?> val3, Object... fieldsValues)
|
||||
throws DatabaseException
|
||||
{
|
||||
return updatePartial(new Key<T>(type, ix1, val1, ix2, val2, ix3, val3), fieldsValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied key.
|
||||
*
|
||||
* @param key the key for the persistent objects to be modified.
|
||||
* @param fieldsValues an array containing the names of the fields/columns and the values to be
|
||||
* assigned, in key, value, key, value, etc. order.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updatePartial (Key<T> key, Object... fieldsValues)
|
||||
throws DatabaseException
|
||||
{
|
||||
return updatePartial(key.getPersistentClass(), key, key, fieldsValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied key. This
|
||||
* method currently flushes the associated record from the cache, but in the future it should
|
||||
* be modified to update the modified fields in the cached value iff the record exists in the
|
||||
* cache.
|
||||
*
|
||||
* @param type the type of the persistent object to be modified.
|
||||
* @param key the key to match in the update.
|
||||
* @param invalidator a cache invalidator that will be run prior to the update to flush the
|
||||
* relevant persistent objects from the cache, or null if no invalidation is needed.
|
||||
* @param fieldsValues an array containing the names of the fields/columns and the values to be
|
||||
* assigned, in key, value, key, value, etc. order.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updatePartial (
|
||||
Class<T> type, final WhereClause key, CacheInvalidator invalidator, Object... fieldsValues)
|
||||
throws DatabaseException
|
||||
{
|
||||
if (invalidator instanceof ValidatingCacheInvalidator) {
|
||||
((ValidatingCacheInvalidator)invalidator).validateFlushType(type); // sanity check
|
||||
}
|
||||
key.validateQueryType(type); // and another
|
||||
|
||||
// separate the arguments into keys and values
|
||||
final String[] fields = new String[fieldsValues.length/2];
|
||||
final SQLExpression[] values = new SQLExpression[fields.length];
|
||||
for (int ii = 0, idx = 0; ii < fields.length; ii++) {
|
||||
fields[ii] = (String)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);
|
||||
|
||||
return _ctx.invoke(new Modifier(invalidator) {
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied primary
|
||||
* key. The values in this case must be literal SQL to be inserted into the update statement.
|
||||
* In general this is used when you want to do something like the following:
|
||||
*
|
||||
* <pre>
|
||||
* update FOO set BAR = BAR + 1;
|
||||
* update BAZ set BIF = NOW();
|
||||
* </pre>
|
||||
*
|
||||
* @param type the type of the persistent object to be modified.
|
||||
* @param primaryKey the key to match in the update.
|
||||
* @param fieldsValues an array containing the names of the fields/columns and the values to be
|
||||
* assigned, in key, literal value, key, literal value, etc. order.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updateLiteral (
|
||||
Class<T> type, Comparable<?> primaryKey, Map<String, SQLExpression> fieldsValues)
|
||||
throws DatabaseException
|
||||
{
|
||||
Key<T> key = _ctx.getMarshaller(type).makePrimaryKey(primaryKey);
|
||||
return updateLiteral(type, key, key, fieldsValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied two-column
|
||||
* primary key. The values in this case must be literal SQL to be inserted into the update
|
||||
* statement. In general this is used when you want to do something like the following:
|
||||
*
|
||||
* <pre>
|
||||
* update FOO set BAR = BAR + 1;
|
||||
* update BAZ set BIF = NOW();
|
||||
* </pre>
|
||||
*
|
||||
* @param type the type of the persistent object to be modified.
|
||||
* @param fieldsValues an array containing the names of the fields/columns and the values to be
|
||||
* assigned, in key, literal value, key, literal value, etc. order.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updateLiteral (
|
||||
Class<T> type, String ix1, Comparable<?> val1, String ix2, Comparable<?> val2,
|
||||
Map<String, SQLExpression> fieldsValues)
|
||||
throws DatabaseException
|
||||
{
|
||||
Key<T> key = new Key<T>(type, ix1, val1, ix2, val2);
|
||||
return updateLiteral(type, key, key, fieldsValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied three-column
|
||||
* primary key. The values in this case must be literal SQL to be inserted into the update
|
||||
* statement. In general this is used when you want to do something like the following:
|
||||
*
|
||||
* <pre>
|
||||
* update FOO set BAR = BAR + 1;
|
||||
* update BAZ set BIF = NOW();
|
||||
* </pre>
|
||||
*
|
||||
* @param type the type of the persistent object to be modified.
|
||||
* @param fieldsValues an array containing the names of the fields/columns and the values to be
|
||||
* assigned, in key, literal value, key, literal value, etc. order.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updateLiteral (
|
||||
Class<T> type, String ix1, Comparable<?> val1, String ix2, Comparable<?> val2,
|
||||
String ix3, Comparable<?> val3, Map<String, SQLExpression> fieldsValues)
|
||||
throws DatabaseException
|
||||
{
|
||||
Key<T> key = new Key<T>(type, ix1, val1, ix2, val2, ix3, val3);
|
||||
return updateLiteral(type, key, key, fieldsValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the specified columns for all persistent objects matching the supplied primary
|
||||
* key. The values in this case must be literal SQL to be inserted into the update statement.
|
||||
* In general this is used when you want to do something like the following:
|
||||
*
|
||||
* <pre>
|
||||
* update FOO set BAR = BAR + 1;
|
||||
* update BAZ set BIF = NOW();
|
||||
* </pre>
|
||||
*
|
||||
* @param type the type of the persistent object to be modified.
|
||||
* @param key the key to match in the update.
|
||||
* @param fieldsValues an array containing the names of the fields/columns and the values to be
|
||||
* assigned, in key, literal value, key, literal value, etc. order.
|
||||
*
|
||||
* @return the number of rows modified by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int updateLiteral (
|
||||
Class<T> type, final WhereClause key, CacheInvalidator invalidator,
|
||||
Map<String, SQLExpression> fieldsValues)
|
||||
throws DatabaseException
|
||||
{
|
||||
requireNotComputed(type, "updateLiteral");
|
||||
|
||||
if (invalidator instanceof ValidatingCacheInvalidator) {
|
||||
((ValidatingCacheInvalidator)invalidator).validateFlushType(type); // sanity check
|
||||
}
|
||||
key.validateQueryType(type); // and another
|
||||
|
||||
// separate the arguments into keys and values
|
||||
final String[] fields = new String[fieldsValues.size()];
|
||||
final SQLExpression[] values = new SQLExpression[fields.length];
|
||||
int ii = 0;
|
||||
for (Map.Entry<String, SQLExpression> entry : fieldsValues.entrySet()) {
|
||||
fields[ii] = entry.getKey();
|
||||
values[ii] = entry.getValue();
|
||||
ii ++;
|
||||
}
|
||||
|
||||
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) {
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the supplied persisent object in the database. If it has no primary key assigned (it
|
||||
* is null or zero), it will be inserted directly. Otherwise an update will first be attempted
|
||||
* and if that matches zero rows, the object will be inserted.
|
||||
*
|
||||
* @return true if the record was created, false if it was updated.
|
||||
*/
|
||||
protected <T extends PersistentRecord> boolean store (T record)
|
||||
throws DatabaseException
|
||||
{
|
||||
@SuppressWarnings("unchecked") final Class<T> pClass = (Class<T>) record.getClass();
|
||||
requireNotComputed(pClass, "store");
|
||||
|
||||
final DepotMarshaller<T> marsh = _ctx.getMarshaller(pClass);
|
||||
Key<T> key = marsh.hasPrimaryKey() ? marsh.getPrimaryKey(record) : null;
|
||||
final UpdateClause<T> update =
|
||||
new UpdateClause<T>(pClass, key, marsh._columnFields, record);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, update));
|
||||
|
||||
// if our primary key isn't null, we start by trying to update rather than insert
|
||||
if (key != null) {
|
||||
builder.newQuery(update);
|
||||
}
|
||||
|
||||
final boolean[] created = new boolean[1];
|
||||
_ctx.invoke(new CachingModifier<T>(record, key, key) {
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
if (_key != null) {
|
||||
// run the update
|
||||
stmt = builder.prepare(conn);
|
||||
int mods = stmt.executeUpdate();
|
||||
if (mods > 0) {
|
||||
// if it succeeded, we're done
|
||||
return mods;
|
||||
}
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
|
||||
// if the update modified zero rows or the primary key was unset, insert
|
||||
Set<String> identityFields = Collections.emptySet();
|
||||
if (_key == null) {
|
||||
// first, set any auto-generated column values
|
||||
identityFields = marsh.generateFieldValues(conn, liaison, _result, false);
|
||||
// update our modifier's key so that it can cache our results
|
||||
updateKey(marsh.getPrimaryKey(_result, false));
|
||||
}
|
||||
|
||||
builder.newQuery(new InsertClause<T>(pClass, _result, identityFields));
|
||||
|
||||
stmt = builder.prepare(conn);
|
||||
int mods = stmt.executeUpdate();
|
||||
|
||||
// run any post-factum value generators and potentially generate our key
|
||||
if (_key == null) {
|
||||
marsh.generateFieldValues(conn, liaison, _result, true);
|
||||
updateKey(marsh.getPrimaryKey(_result, false));
|
||||
}
|
||||
created[0] = true;
|
||||
return mods;
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
return created[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all persistent objects from the database with a primary key matching the primary key
|
||||
* of the supplied object.
|
||||
*
|
||||
* @return the number of rows deleted by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int delete (T record)
|
||||
throws DatabaseException
|
||||
{
|
||||
@SuppressWarnings("unchecked") Class<T> type = (Class<T>)record.getClass();
|
||||
Key<T> primaryKey = _ctx.getMarshaller(type).getPrimaryKey(record);
|
||||
if (primaryKey == null) {
|
||||
throw new IllegalArgumentException("Can't delete record with null primary key.");
|
||||
}
|
||||
return delete(type, primaryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all persistent objects from the database with a primary key matching the supplied
|
||||
* primary key.
|
||||
*
|
||||
* @return the number of rows deleted by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int delete (Class<T> type, Comparable<?> primaryKeyValue)
|
||||
throws DatabaseException
|
||||
{
|
||||
return delete(type, _ctx.getMarshaller(type).makePrimaryKey(primaryKeyValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all persistent objects from the database with a primary key matching the supplied
|
||||
* primary key.
|
||||
*
|
||||
* @return the number of rows deleted by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int delete (Class<T> type, Key<T> primaryKey)
|
||||
throws DatabaseException
|
||||
{
|
||||
return deleteAll(type, primaryKey, primaryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all persistent objects from the database that match the supplied where clause.
|
||||
*
|
||||
* @return the number of rows deleted by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int deleteAll (Class<T> type, final WhereClause where)
|
||||
throws DatabaseException
|
||||
{
|
||||
if (_ctx.getMarshaller(type).hasPrimaryKey()) {
|
||||
// look up the primary keys for all rows matching our where clause and delete using those
|
||||
KeySet<T> pwhere = new KeySet<T>(type, findAllKeys(type, true, where));
|
||||
return deleteAll(type, pwhere, pwhere);
|
||||
} else {
|
||||
// otherwise just do the delete directly as we can't have cached a record that has no
|
||||
// primary key in the first place
|
||||
return deleteAll(type, where, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all persistent objects from the database that match the supplied key.
|
||||
*
|
||||
* @return the number of rows deleted by this action.
|
||||
*/
|
||||
protected <T extends PersistentRecord> int deleteAll (
|
||||
Class<T> type, final WhereClause where, CacheInvalidator invalidator)
|
||||
throws DatabaseException
|
||||
{
|
||||
if (invalidator instanceof ValidatingCacheInvalidator) {
|
||||
((ValidatingCacheInvalidator)invalidator).validateFlushType(type); // sanity check
|
||||
}
|
||||
where.validateQueryType(type); // and another
|
||||
|
||||
DeleteClause<T> delete = new DeleteClause<T>(type, where);
|
||||
final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, delete));
|
||||
builder.newQuery(delete);
|
||||
|
||||
return _ctx.invoke(new Modifier(invalidator) {
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
PreparedStatement stmt = builder.prepare(conn);
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// make sure the given type corresponds to a concrete class
|
||||
protected void requireNotComputed (Class<? extends PersistentRecord> type, String action)
|
||||
throws DatabaseException
|
||||
{
|
||||
DepotMarshaller<?> marsh = _ctx.getMarshaller(type);
|
||||
if (marsh == null) {
|
||||
throw new DatabaseException("Unknown persistent type [class=" + type + "]");
|
||||
}
|
||||
if (marsh.getTableName() == null) {
|
||||
throw new DatabaseException(
|
||||
"Can't " + action + " computed entities [class=" + type + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the supplied migration has not already been run, it will be run and if it completes, we
|
||||
* will note in the DepotMigrationHistory table that it has been run.
|
||||
*/
|
||||
protected void runMigration (DataMigration migration)
|
||||
throws DatabaseException
|
||||
{
|
||||
// attempt to get a lock to run this migration (or detect that it has already been run)
|
||||
DepotMigrationHistoryRecord record;
|
||||
while (true) {
|
||||
// check to see if the migration has already been completed
|
||||
record = load(DepotMigrationHistoryRecord.class, migration.getIdent());
|
||||
if (record != null && record.whenCompleted != null) {
|
||||
return; // great, no need to do anything
|
||||
}
|
||||
|
||||
// if no record exists at all, try to insert one and thereby obtain the migration lock
|
||||
if (record == null) {
|
||||
try {
|
||||
record = new DepotMigrationHistoryRecord();
|
||||
record.ident = migration.getIdent();
|
||||
insert(record);
|
||||
break; // we got the lock, break out of this loop and run the migration
|
||||
} catch (DuplicateKeyException dke) {
|
||||
// someone beat us to the punch, so we have to wait for them to finish
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't get the lock, so wait 5 seconds and then check to see if the other process
|
||||
// finished the update or failed in which case we'll try to grab the lock ourselves
|
||||
try {
|
||||
log.info("Waiting on migration lock for " + migration.getIdent() + ".");
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException ie) {
|
||||
throw new DatabaseException("Interrupted while waiting on migration lock.");
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Running data migration", "ident", migration.getIdent());
|
||||
try {
|
||||
// run the migration
|
||||
migration.invoke();
|
||||
|
||||
// report to the world that we've done so
|
||||
record.whenCompleted = new Timestamp(System.currentTimeMillis());
|
||||
update(record);
|
||||
|
||||
} finally {
|
||||
// clear out our migration history record if we failed to get the job done
|
||||
if (record.whenCompleted == null) {
|
||||
try {
|
||||
delete(record);
|
||||
} catch (Throwable dt) {
|
||||
log.warning("Oh noez! Failed to delete history record for failed migration. " +
|
||||
"All clients will loop forever waiting for the lock.",
|
||||
"ident", migration.getIdent(), dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected PersistenceContext _ctx;
|
||||
protected List<DataMigration> _dataMigs = new ArrayList<DataMigration>();
|
||||
}
|
||||
@@ -1,221 +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.SQLException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Maintains a record of the persistent classes brought into the context of the associated SQL,
|
||||
* i.e. any class associated with a concrete table that would appear in FROM or JOIN clauses or as
|
||||
* the target of an UPDATE or an INSERT or any other place where a table abbreviation could be
|
||||
* constructed.
|
||||
*
|
||||
* The main motivation for breaking this functionality out into its own class is to encapsulate the
|
||||
* operation that throws {@link DatabaseException} as separate from the operations that throw
|
||||
* {@link SQLException}. Once this class has been constructed, it may be used to create {@link
|
||||
* SQLBuilder} instances without any {@link DatabaseException} worries.
|
||||
*/
|
||||
public class DepotTypes
|
||||
{
|
||||
/** A trivial instance that is accessible in places where we want the dialectal benefits of the
|
||||
* SQLBuilder without really requiring per-persistent-class context. */
|
||||
public static DepotTypes TRIVIAL = new DepotTypes();
|
||||
|
||||
/**
|
||||
* Conveniently constructs a {@link DepotTypes} object given {@link QueryClause} objects, which
|
||||
* are interrogated for their class definition sets through {@link SQLExpression#addClasses}.
|
||||
*/
|
||||
public static <T extends PersistentRecord> DepotTypes getDepotTypes (
|
||||
PersistenceContext ctx, Collection<? extends QueryClause> clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
Set<Class<? extends PersistentRecord>> classSet =
|
||||
new HashSet<Class<? extends PersistentRecord>>();
|
||||
for (QueryClause clause : clauses) {
|
||||
if (clause != null) {
|
||||
clause.addClasses(classSet);
|
||||
}
|
||||
}
|
||||
return new DepotTypes(ctx, classSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* A varargs version of {@link #getDepotTypes(PersistenceContext,Collection)}.
|
||||
*/
|
||||
public static <T extends PersistentRecord> DepotTypes getDepotTypes (
|
||||
PersistenceContext ctx, QueryClause... clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
return getDepotTypes(ctx, Arrays.asList(clauses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DepotTypes with the given {@link PersistenceContext} and a collection of
|
||||
* persistent record classes.
|
||||
*/
|
||||
public DepotTypes (PersistenceContext ctx, Collection<Class<? extends PersistentRecord>> others)
|
||||
throws DatabaseException
|
||||
{
|
||||
for (Class<? extends PersistentRecord> c : others) {
|
||||
addClass(ctx, c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DepotTypes with the given {@link PersistenceContext} and the given
|
||||
* persistent record.
|
||||
*/
|
||||
public DepotTypes (PersistenceContext ctx, Class<? extends PersistentRecord> pClass)
|
||||
throws DatabaseException
|
||||
{
|
||||
addClass(ctx, pClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full table name of the given persistent class, which must have been previously
|
||||
* registered with this object.
|
||||
*/
|
||||
public String getTableName (Class<? extends PersistentRecord> cl)
|
||||
{
|
||||
return getMarshaller(cl).getTableName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current abbreviation by which we refer to the table associated with the given
|
||||
* persistent record -- which must have been previously registered with this object. If the
|
||||
* useTableAbbreviations flag is false, we return the full table name instead.
|
||||
*
|
||||
* @exception IllegalArgumentException thrown if the specified class is not known.
|
||||
*/
|
||||
public String getTableAbbreviation (Class<? extends PersistentRecord> cl)
|
||||
{
|
||||
if (_useTableAbbreviations) {
|
||||
Integer ix = _classIx.get(cl);
|
||||
if (ix == null) {
|
||||
throw new IllegalArgumentException("Unknown persistence class: " + cl);
|
||||
}
|
||||
return "T" + (ix+1);
|
||||
}
|
||||
return getTableName(cl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated database column of the given field of the given persistent class,
|
||||
* throwing an exception if the record has not been registered with this object, or if the
|
||||
* field is unknown on the record.
|
||||
*
|
||||
* @exception IllegalArgumentException thrown if the specified field is not part of the
|
||||
* specified persistent class.
|
||||
*/
|
||||
public String getColumnName (Class<? extends PersistentRecord> cl, String field)
|
||||
{
|
||||
FieldMarshaller<?> fm = getMarshaller(cl).getFieldMarshaller(field);
|
||||
if (fm == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Field not known on class [field=" + field + ", class=" + cl + "]");
|
||||
}
|
||||
return fm.getColumnName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link DepotMarshaller} associated with the given persistent class, if it's been
|
||||
* registered with this object.
|
||||
*
|
||||
* @exception IllegalArgumentException thrown if the specified class is not known.
|
||||
*/
|
||||
public DepotMarshaller<?> getMarshaller (Class<? extends PersistentRecord> cl)
|
||||
{
|
||||
DepotMarshaller<?> marsh = _classMap.get(cl);
|
||||
if (marsh == null) {
|
||||
throw new IllegalArgumentException("Persistent class not known: " + cl);
|
||||
}
|
||||
return marsh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new persistent class with this object.
|
||||
*/
|
||||
public void addClass (PersistenceContext ctx, Class <? extends PersistentRecord> type)
|
||||
throws DatabaseException
|
||||
{
|
||||
if (_classMap.containsKey(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add the class in question
|
||||
DepotMarshaller<?> marsh = ctx.getMarshaller(type);
|
||||
_classMap.put(type, marsh);
|
||||
_classIx.put(type, _classIx.size());
|
||||
|
||||
// if this class is @Computed and has a shadow, add its shadow
|
||||
if (marsh.getComputed() != null &&
|
||||
!PersistentRecord.class.equals(marsh.getComputed().shadowOf())) {
|
||||
addClass(ctx, marsh.getComputed().shadowOf());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the useTableAbbreviations flag, which governs the behaviour when
|
||||
* referencing columns during SQL construction. Normally, this flag is on, and tables are
|
||||
* referenced as e.g. T1.itemId, but there are cases of weak/broken SQL where abbreviations
|
||||
* may not be brought into play. In these cases we prepend the full table name.
|
||||
*/
|
||||
public boolean getUseTableAbbreviations ()
|
||||
{
|
||||
return _useTableAbbreviations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the useTableAbbreviations flag, which governs the behaviour when
|
||||
* referencing columns during SQL construction. Normally, this flag is on, and tables are
|
||||
* referenced as e.g. T1.itemId, but there are cases of weak/broken SQL where abbreviations
|
||||
* may not be brought into play. In these cases we prepend the full table name.
|
||||
*/
|
||||
public void setUseTableAbbreviations (boolean doUse)
|
||||
{
|
||||
_useTableAbbreviations = doUse;
|
||||
}
|
||||
|
||||
// constructor used to create TRIVIAL
|
||||
protected DepotTypes ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Classes mapped to integers, used for table abbreviation indexing. */
|
||||
protected Map<Class<?>, Integer> _classIx = new HashMap<Class<?>, Integer>();
|
||||
|
||||
/** Classes mapped to marshallers, used for table names and field lists. */
|
||||
protected Map<Class<?>, DepotMarshaller<?>> _classMap =
|
||||
new HashMap<Class<?>, DepotMarshaller<?>>();
|
||||
|
||||
/** When false, override the normal table abbreviations and return full table names instead. */
|
||||
protected boolean _useTableAbbreviations = true;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2001-2008 Michael Bayne
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
/**
|
||||
* Thrown when an insert or update results in a duplicate key on a column that has a uniqueness
|
||||
* constraint.
|
||||
*/
|
||||
public class DuplicateKeyException extends DatabaseException
|
||||
{
|
||||
public DuplicateKeyException (String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,169 +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.io.Serializable;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sf.ehcache.distribution.CacheManagerPeerListener;
|
||||
import net.sf.ehcache.distribution.CacheManagerPeerProvider;
|
||||
import net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator;
|
||||
|
||||
import static com.samskivert.jdbc.depot.Log.log;
|
||||
|
||||
/**
|
||||
* An implementation of {@link CacheAdapter} for ehcache.
|
||||
*/
|
||||
public class EHCacheAdapter
|
||||
implements CacheAdapter
|
||||
{
|
||||
/**
|
||||
* Creates an adapter using the supplied cache manager. Note: this adapter does not shut down
|
||||
* the supplied manager when it is shutdown. The caller is responsible for shutting down the
|
||||
* cache manager when it knows that Depot and any other clients no longer need it.
|
||||
*/
|
||||
public EHCacheAdapter (CacheManager cachemgr)
|
||||
{
|
||||
_cachemgr = cachemgr;
|
||||
|
||||
CacheManagerPeerListener listener = _cachemgr.getCachePeerListener();
|
||||
CacheManagerPeerProvider provider = _cachemgr.getCachePeerProvider();
|
||||
if ((provider != null) != (listener != null)) {
|
||||
// we want either both listener and provider, or neither
|
||||
log.warning("EHCache misconfigured, distributed mode disabled [listener =" +
|
||||
listener + ", provider=" + provider);
|
||||
_distributed = false;
|
||||
|
||||
} else {
|
||||
_distributed = (listener != null);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> CacheBin<T> getCache (String id)
|
||||
{
|
||||
return new EHCacheBin<T>(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* The main ehcache-bridging class, a {@link CacheBin} interface against {@link Cache}.
|
||||
*/
|
||||
protected class EHCacheBin<T> implements CacheBin<T>
|
||||
{
|
||||
// from CacheBin
|
||||
public CachedValue<T> lookup (Serializable key)
|
||||
{
|
||||
Element hit = _cache.get(key);
|
||||
if (hit == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Serializable rawValue = hit.getValue();
|
||||
@SuppressWarnings("unchecked")
|
||||
final T value = (T) (rawValue instanceof NullValue ? null : rawValue);
|
||||
return new CachedValue<T>() {
|
||||
public T getValue () {
|
||||
return value;
|
||||
}
|
||||
@Override public String toString () {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// from CacheBin
|
||||
public void store (Serializable key, T value)
|
||||
{
|
||||
_cache.put(new Element(key, value != null ? value : NULL));
|
||||
}
|
||||
|
||||
// from CacheBin
|
||||
public void remove (Serializable key)
|
||||
{
|
||||
_cache.remove(key);
|
||||
}
|
||||
|
||||
// from CacheBin
|
||||
public Iterable<Serializable> enumerateKeys ()
|
||||
{
|
||||
@SuppressWarnings("unchecked") Iterable<Serializable> keys = _cache.getKeys();
|
||||
return keys;
|
||||
}
|
||||
|
||||
protected EHCacheBin (String id)
|
||||
{
|
||||
synchronized (_cachemgr) {
|
||||
_cache = _cachemgr.getCache(id);
|
||||
if (_cache == null) {
|
||||
// create the cache programatically with reasonable settings
|
||||
// TODO: we will eventually need this to be configurable in .properties
|
||||
_cache = new Cache(id,
|
||||
1000, // keep 1000 elements in RAM
|
||||
true, // overflow the rest to disk
|
||||
false, // don't keep records around eternally
|
||||
300, // keep them for 5 minutes after they're created
|
||||
20); // or 20 seconds after last access
|
||||
|
||||
if (_distributed) {
|
||||
// a programatically created cache has to have its replicator event
|
||||
// listener programatically added.
|
||||
_cache.getCacheEventNotificationService().registerListener(
|
||||
new RMIAsynchronousCacheReplicator(true, true, true, true, 1000));
|
||||
}
|
||||
_cachemgr.addCache(_cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Cache _cache;
|
||||
}
|
||||
|
||||
// from CacheAdapter
|
||||
public void shutdown ()
|
||||
{
|
||||
}
|
||||
|
||||
protected boolean _distributed;
|
||||
protected CacheManager _cachemgr;
|
||||
|
||||
// this is just for convenience and memory use; we don't rely on pointer equality anywhere
|
||||
protected static Serializable NULL = new NullValue() {};
|
||||
|
||||
/** A class to represent an explicitly Serializable concept of null for EHCache. */
|
||||
protected static class NullValue implements Serializable
|
||||
{
|
||||
@Override public String toString ()
|
||||
{
|
||||
return "<EHCache Null>";
|
||||
}
|
||||
|
||||
@Override public boolean equals (Object other)
|
||||
{
|
||||
return other != null && other.getClass().equals(NullValue.class);
|
||||
}
|
||||
|
||||
@Override public int hashCode ()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,477 +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.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Date;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.samskivert.jdbc.ColumnDefinition;
|
||||
import com.samskivert.jdbc.depot.annotation.Column;
|
||||
import com.samskivert.jdbc.depot.annotation.Computed;
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Handles the marshalling and unmarshalling of a particular field of a persistent object.
|
||||
*
|
||||
* @see DepotMarshaller
|
||||
*/
|
||||
public abstract class FieldMarshaller<T>
|
||||
{
|
||||
/**
|
||||
* Creates and returns a field marshaller for the specified field. Throws an exception if the
|
||||
* field in question cannot be marshalled.
|
||||
*/
|
||||
public static FieldMarshaller<?> createMarshaller (Field field)
|
||||
{
|
||||
Class<?> ftype = field.getType();
|
||||
FieldMarshaller<?> marshaller;
|
||||
|
||||
// primitive types
|
||||
if (ftype.equals(Boolean.TYPE)) {
|
||||
marshaller = new BooleanMarshaller();
|
||||
} else if (ftype.equals(Byte.TYPE)) {
|
||||
marshaller = new ByteMarshaller();
|
||||
} else if (ftype.equals(Short.TYPE)) {
|
||||
marshaller = new ShortMarshaller();
|
||||
} else if (ftype.equals(Integer.TYPE)) {
|
||||
marshaller = new IntMarshaller();
|
||||
} else if (ftype.equals(Long.TYPE)) {
|
||||
marshaller = new LongMarshaller();
|
||||
} else if (ftype.equals(Float.TYPE)) {
|
||||
marshaller = new FloatMarshaller();
|
||||
} else if (ftype.equals(Double.TYPE)) {
|
||||
marshaller = new DoubleMarshaller();
|
||||
|
||||
// "natural" types
|
||||
} else if (ftype.equals(Byte.class) ||
|
||||
ftype.equals(Short.class) ||
|
||||
ftype.equals(Integer.class) ||
|
||||
ftype.equals(Long.class) ||
|
||||
ftype.equals(Float.class) ||
|
||||
ftype.equals(Double.class) ||
|
||||
ftype.equals(String.class)) {
|
||||
marshaller = new ObjectMarshaller();
|
||||
|
||||
// some primitive array types
|
||||
} else if (ftype.equals(byte[].class)) {
|
||||
marshaller = new ByteArrayMarshaller();
|
||||
|
||||
} else if (ftype.equals(int[].class)) {
|
||||
marshaller = new IntArrayMarshaller();
|
||||
|
||||
// SQL types
|
||||
} else if (ftype.equals(Date.class) ||
|
||||
ftype.equals(Time.class) ||
|
||||
ftype.equals(Timestamp.class) ||
|
||||
ftype.equals(Blob.class) ||
|
||||
ftype.equals(Clob.class)) {
|
||||
marshaller = new ObjectMarshaller();
|
||||
|
||||
// special Enum types
|
||||
} else if (ByteEnum.class.isAssignableFrom(ftype)) {
|
||||
marshaller = new ByteEnumMarshaller(ftype);
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot marshall field of type '" + ftype.getName() + "'.");
|
||||
}
|
||||
|
||||
marshaller.create(field);
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this field marshaller with a SQL builder which it uses to construct its column
|
||||
* definition according to the appropriate database dialect.
|
||||
*/
|
||||
public void init (SQLBuilder builder)
|
||||
throws DatabaseException
|
||||
{
|
||||
_columnDefinition = builder.buildColumnDefinition(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Field} handled by this marshaller.
|
||||
*/
|
||||
public Field getField ()
|
||||
{
|
||||
return _field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Computed annotation on this field, if any.
|
||||
*/
|
||||
public Computed getComputed ()
|
||||
{
|
||||
return _computed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GeneratedValue annotation on this field, if any.
|
||||
*/
|
||||
public GeneratedValue getGeneratedValue ()
|
||||
{
|
||||
return _generatedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the table column used to store this field.
|
||||
*/
|
||||
public String getColumnName ()
|
||||
{
|
||||
return _columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SQL used to define this field's column.
|
||||
*/
|
||||
public ColumnDefinition getColumnDefinition ()
|
||||
{
|
||||
return _columnDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this field from the given persistent object.
|
||||
*/
|
||||
public abstract T getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Sets the specified column of the given prepared statement to the given value.
|
||||
*/
|
||||
public abstract void writeToStatement (PreparedStatement ps, int column, T value)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Reads the value of our field from the supplied persistent object and sets that value into
|
||||
* the specified column of the supplied prepared statement.
|
||||
*/
|
||||
public void getAndWriteToStatement (PreparedStatement ps, int column, Object po)
|
||||
throws SQLException, IllegalAccessException
|
||||
{
|
||||
writeToStatement(ps, column, getFromObject(po));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns this field from the result set.
|
||||
*/
|
||||
public abstract T getFromSet (ResultSet rs)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Writes the given value to the given persistent value.
|
||||
*/
|
||||
public abstract void writeToObject (Object po, T value)
|
||||
throws IllegalArgumentException, IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Reads the specified column from the supplied result set and writes it to the appropriate
|
||||
* field of the persistent object.
|
||||
*/
|
||||
public void getAndWriteToObject (ResultSet rset, Object po)
|
||||
throws SQLException, IllegalAccessException
|
||||
{
|
||||
writeToObject(po, getFromSet(rset));
|
||||
}
|
||||
|
||||
protected void create (Field field)
|
||||
{
|
||||
_field = field;
|
||||
_columnName = field.getName();
|
||||
|
||||
Column column = _field.getAnnotation(Column.class);
|
||||
if (column != null) {
|
||||
if (!StringUtil.isBlank(column.name())) {
|
||||
_columnName = column.name();
|
||||
}
|
||||
}
|
||||
|
||||
_computed = field.getAnnotation(Computed.class);
|
||||
if (_computed != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// figure out how we're going to generate our primary key values
|
||||
_generatedValue = field.getAnnotation(GeneratedValue.class);
|
||||
}
|
||||
|
||||
protected static class BooleanMarshaller extends FieldMarshaller<Boolean> {
|
||||
@Override public Boolean getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getBoolean(po);
|
||||
}
|
||||
@Override public Boolean getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return rs.getBoolean(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, Boolean value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.setBoolean(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, Boolean value)
|
||||
throws SQLException {
|
||||
ps.setBoolean(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ByteMarshaller extends FieldMarshaller<Byte> {
|
||||
@Override public Byte getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getByte(po);
|
||||
}
|
||||
@Override public Byte getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return rs.getByte(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, Byte value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.setByte(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, Byte value)
|
||||
throws SQLException {
|
||||
ps.setByte(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ShortMarshaller extends FieldMarshaller<Short> {
|
||||
@Override public Short getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getShort(po);
|
||||
}
|
||||
@Override public Short getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return rs.getShort(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, Short value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.setShort(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, Short value)
|
||||
throws SQLException {
|
||||
ps.setShort(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class IntMarshaller extends FieldMarshaller<Integer> {
|
||||
@Override public Integer getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getInt(po);
|
||||
}
|
||||
@Override public Integer getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return rs.getInt(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, Integer value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.setInt(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, Integer value)
|
||||
throws SQLException {
|
||||
ps.setInt(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class LongMarshaller extends FieldMarshaller<Long> {
|
||||
@Override public Long getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getLong(po);
|
||||
}
|
||||
@Override public Long getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return rs.getLong(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, Long value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.setLong(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, Long value)
|
||||
throws SQLException {
|
||||
ps.setLong(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class FloatMarshaller extends FieldMarshaller<Float> {
|
||||
@Override public Float getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getFloat(po);
|
||||
}
|
||||
@Override public Float getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return rs.getFloat(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, Float value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.setFloat(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, Float value)
|
||||
throws SQLException {
|
||||
ps.setFloat(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DoubleMarshaller extends FieldMarshaller<Double> {
|
||||
@Override public Double getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.getDouble(po);
|
||||
}
|
||||
@Override public Double getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return rs.getDouble(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, Double value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.setDouble(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, Double value)
|
||||
throws SQLException {
|
||||
ps.setDouble(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ObjectMarshaller extends FieldMarshaller<Object> {
|
||||
@Override public Object getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return _field.get(po);
|
||||
}
|
||||
@Override public Object getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return rs.getObject(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, Object value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.set(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, Object value)
|
||||
throws SQLException {
|
||||
ps.setObject(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ByteArrayMarshaller extends FieldMarshaller<byte[]> {
|
||||
@Override public byte[] getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return (byte[]) _field.get(po);
|
||||
}
|
||||
@Override public byte[] getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return rs.getBytes(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, byte[] value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.set(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, byte[] value)
|
||||
throws SQLException {
|
||||
ps.setBytes(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class IntArrayMarshaller extends FieldMarshaller<byte[]> {
|
||||
@Override public byte[] getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
int[] values = (int[]) _field.get(po);
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
ByteBuffer bbuf = ByteBuffer.allocate(values.length * 4);
|
||||
bbuf.asIntBuffer().put(values);
|
||||
return bbuf.array();
|
||||
|
||||
}
|
||||
@Override public byte[] getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
return (byte[]) rs.getObject(getColumnName());
|
||||
}
|
||||
@Override public void writeToObject (Object po, byte[] data)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
int[] value = null;
|
||||
if (data != null) {
|
||||
value = new int[data.length/4];
|
||||
ByteBuffer.wrap(data).asIntBuffer().get(value);
|
||||
}
|
||||
_field.set(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, byte[] value)
|
||||
throws SQLException {
|
||||
ps.setObject(column, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ByteEnumMarshaller extends FieldMarshaller<ByteEnum> {
|
||||
public ByteEnumMarshaller (Class<?> clazz) {
|
||||
try {
|
||||
_factmeth = clazz.getMethod("fromByte", new Class[] { Byte.TYPE });
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(
|
||||
"Could not locate fromByte() method on enum field " + _field.getType() + ".");
|
||||
}
|
||||
if (!Modifier.isPublic(_factmeth.getModifiers()) ||
|
||||
!Modifier.isStatic(_factmeth.getModifiers())) {
|
||||
throw new IllegalArgumentException(
|
||||
_field.getType() + ".fromByte() must be public and static.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public ByteEnum getFromObject (Object po)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
return (ByteEnum) _field.get(po);
|
||||
}
|
||||
@Override public ByteEnum getFromSet (ResultSet rs)
|
||||
throws SQLException {
|
||||
try {
|
||||
return (ByteEnum) _factmeth.invoke(null, rs.getByte(getColumnName()));
|
||||
} catch (SQLException se) {
|
||||
throw se;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
@Override public void writeToObject (Object po, ByteEnum value)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
_field.set(po, value);
|
||||
}
|
||||
@Override public void writeToStatement (PreparedStatement ps, int column, ByteEnum value)
|
||||
throws SQLException {
|
||||
ps.setByte(column, value.toByte());
|
||||
}
|
||||
|
||||
protected Method _factmeth;
|
||||
}
|
||||
|
||||
protected Field _field;
|
||||
protected String _columnName;
|
||||
protected ColumnDefinition _columnDefinition;
|
||||
protected Computed _computed;
|
||||
protected GeneratedValue _generatedValue;
|
||||
}
|
||||
@@ -1,303 +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.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
|
||||
import com.samskivert.jdbc.depot.clause.FieldOverride;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.clause.SelectClause;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.*;
|
||||
|
||||
import static com.samskivert.jdbc.depot.Log.log;
|
||||
|
||||
/**
|
||||
* This class implements the functionality required by {@link DepotRepository#findAll}: fetch
|
||||
* a collection of persistent objects using one of two included strategies.
|
||||
*/
|
||||
public abstract class FindAllQuery<T extends PersistentRecord>
|
||||
implements Query<List<T>>
|
||||
{
|
||||
/**
|
||||
* The two-pass collection query implementation. See {@link DepotRepository#findAll} for
|
||||
* details.
|
||||
*/
|
||||
public static class WithCache<T extends PersistentRecord> extends FindAllQuery<T>
|
||||
{
|
||||
public WithCache (PersistenceContext ctx, Class<T> type,
|
||||
Collection<? extends QueryClause> clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
super(ctx, type);
|
||||
|
||||
if (_marsh.getComputed() != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"This algorithm doesn't work on @Computed records.");
|
||||
}
|
||||
for (QueryClause clause : clauses) {
|
||||
if (clause instanceof FieldOverride) {
|
||||
throw new IllegalArgumentException(
|
||||
"This algorithm doesn't work with FieldOverrides.");
|
||||
}
|
||||
}
|
||||
|
||||
SelectClause<T> select =
|
||||
new SelectClause<T>(_type, _marsh.getPrimaryKeyFields(), clauses);
|
||||
_builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, select));
|
||||
_builder.newQuery(select);
|
||||
}
|
||||
|
||||
public List<T> invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
Map<Key<T>, T> entities = new HashMap<Key<T>, T>();
|
||||
List<Key<T>> allKeys = new ArrayList<Key<T>>();
|
||||
Set<Key<T>> fetchKeys = new HashSet<Key<T>>();
|
||||
|
||||
PreparedStatement stmt = _builder.prepare(conn);
|
||||
String stmtString = stmt.toString(); // for debugging
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
Key<T> key = _marsh.makePrimaryKey(rs);
|
||||
allKeys.add(key);
|
||||
|
||||
// TODO: All this cache fiddling needs to move to PersistenceContext?
|
||||
CacheAdapter.CachedValue<T> hit = _ctx.cacheLookup(key);
|
||||
if (hit != null) {
|
||||
T value = hit.getValue();
|
||||
if (value != null) {
|
||||
@SuppressWarnings("unchecked") T newValue = (T) value.clone();
|
||||
entities.put(key, newValue);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fetchKeys.add(key);
|
||||
}
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
|
||||
return loadAndResolve(conn, allKeys, fetchKeys, entities, stmtString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The two-pass collection query implementation. See {@link DepotRepository#findAll} for
|
||||
* details.
|
||||
*/
|
||||
public static class WithKeys<T extends PersistentRecord> extends FindAllQuery<T>
|
||||
{
|
||||
public WithKeys (PersistenceContext ctx, Collection<Key<T>> keys)
|
||||
throws DatabaseException
|
||||
{
|
||||
super(ctx, keys.iterator().next().getPersistentClass());
|
||||
_keys = keys;
|
||||
_builder = ctx.getSQLBuilder(new DepotTypes(ctx, _type));
|
||||
}
|
||||
|
||||
public List<T> invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
Map<Key<T>, T> entities = new HashMap<Key<T>, T>();
|
||||
Set<Key<T>> fetchKeys = new HashSet<Key<T>>();
|
||||
for (Key<T> key : _keys) {
|
||||
// TODO: All this cache fiddling needs to move to PersistenceContext?
|
||||
CacheAdapter.CachedValue<T> hit = _ctx.cacheLookup(key);
|
||||
if (hit != null) {
|
||||
T value = hit.getValue();
|
||||
if (value != null) {
|
||||
@SuppressWarnings("unchecked") T newValue = (T) value.clone();
|
||||
entities.put(key, newValue);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fetchKeys.add(key);
|
||||
}
|
||||
|
||||
return loadAndResolve(conn, _keys, fetchKeys, entities, null);
|
||||
}
|
||||
|
||||
protected Collection<Key<T>> _keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* The single-pass collection query implementation. See {@link DepotRepository#findAll} for
|
||||
* details.
|
||||
*/
|
||||
public static class Explicitly<T extends PersistentRecord> extends FindAllQuery<T>
|
||||
{
|
||||
public Explicitly (PersistenceContext ctx, Class<T> type,
|
||||
Collection<? extends QueryClause> clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
super(ctx, type);
|
||||
SelectClause<T> select = new SelectClause<T>(type, _marsh.getFieldNames(), clauses);
|
||||
_builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, select));
|
||||
_builder.newQuery(select);
|
||||
}
|
||||
|
||||
public List<T> invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
List<T> result = new ArrayList<T>();
|
||||
PreparedStatement stmt = _builder.prepare(conn);
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
result.add(_marsh.createObject(rs));
|
||||
}
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public FindAllQuery (PersistenceContext ctx, Class<T> type)
|
||||
throws DatabaseException
|
||||
{
|
||||
_ctx = ctx;
|
||||
_type = type;
|
||||
_marsh = _ctx.getMarshaller(type);
|
||||
}
|
||||
|
||||
// from Query
|
||||
public CacheKey getCacheKey ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// from Query
|
||||
public void updateCache (PersistenceContext ctx, List<T> result) {
|
||||
if (_marsh.hasPrimaryKey()) {
|
||||
for (T bit : result) {
|
||||
ctx.cacheStore(_marsh.getPrimaryKey(bit), bit.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// from Query
|
||||
public List<T> transformCacheHit (CacheKey key, List<T> bits)
|
||||
{
|
||||
if (bits == null) {
|
||||
return bits;
|
||||
}
|
||||
|
||||
List<T> result = new ArrayList<T>();
|
||||
for (T bit : bits) {
|
||||
if (bit != null) {
|
||||
@SuppressWarnings("unchecked") T cbit = (T) bit.clone();
|
||||
result.add(cbit);
|
||||
} else {
|
||||
result.add(null);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected List<T> loadAndResolve (Connection conn, Collection<Key<T>> allKeys,
|
||||
Set<Key<T>> fetchKeys, Map<Key<T>, T> entities,
|
||||
String origStmt)
|
||||
throws SQLException
|
||||
{
|
||||
// if we're fetching a huge number of records, we have to do it in multiple queries
|
||||
if (fetchKeys.size() > In.MAX_KEYS) {
|
||||
int keyCount = fetchKeys.size();
|
||||
do {
|
||||
Set<Key<T>> keys = new HashSet<Key<T>>();
|
||||
Iterator<Key<T>> iter = fetchKeys.iterator();
|
||||
for (int ii = 0; ii < Math.min(keyCount, In.MAX_KEYS); ii++) {
|
||||
keys.add(iter.next());
|
||||
iter.remove();
|
||||
}
|
||||
keyCount -= keys.size();
|
||||
loadRecords(conn, keys, entities, origStmt);
|
||||
} while (keyCount > 0);
|
||||
|
||||
} else if (fetchKeys.size() > 0) {
|
||||
loadRecords(conn, fetchKeys, entities, origStmt);
|
||||
}
|
||||
|
||||
List<T> result = new ArrayList<T>();
|
||||
for (Key<T> key : allKeys) {
|
||||
T value = entities.get(key);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void loadRecords (Connection conn, Set<Key<T>> keys, Map<Key<T>, T> entities,
|
||||
String origStmt)
|
||||
throws SQLException
|
||||
{
|
||||
_builder.newQuery(new SelectClause<T>(_type, _marsh.getFieldNames(),
|
||||
new KeySet<T>(_type, keys)));
|
||||
PreparedStatement stmt = _builder.prepare(conn);
|
||||
try {
|
||||
Set<Key<T>> got = new HashSet<Key<T>>();
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
int cnt = 0, dups = 0;
|
||||
while (rs.next()) {
|
||||
T obj = _marsh.createObject(rs);
|
||||
Key<T> key = _marsh.getPrimaryKey(obj);
|
||||
if (entities.put(key, obj) != null) {
|
||||
dups++;
|
||||
}
|
||||
got.add(key);
|
||||
cnt++;
|
||||
}
|
||||
// if we get more results than we planned, or if we're doing a two-phase query and got
|
||||
// fewer, then complain
|
||||
if (cnt > keys.size() || (origStmt != null && cnt < keys.size())) {
|
||||
log.warning("Row count mismatch in second pass", "origQuery", origStmt,
|
||||
"wanted", keys, "got", got, "dups", dups, new Exception());
|
||||
}
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
protected PersistenceContext _ctx;
|
||||
protected SQLBuilder _builder;
|
||||
protected DepotMarshaller<T> _marsh;
|
||||
protected Class<T> _type;
|
||||
}
|
||||
@@ -1,110 +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.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.clause.SelectClause;
|
||||
|
||||
/**
|
||||
* The implementation of {@link DepotRepository#load} functionality.
|
||||
*/
|
||||
public class FindOneQuery<T extends PersistentRecord>
|
||||
implements Query<T>
|
||||
{
|
||||
public FindOneQuery (PersistenceContext ctx, Class<T> type, QueryClause[] clauses)
|
||||
throws DatabaseException
|
||||
{
|
||||
_marsh = ctx.getMarshaller(type);
|
||||
_select = new SelectClause<T>(type, _marsh.getFieldNames(), clauses);
|
||||
WhereClause where = _select.getWhereClause();
|
||||
if (where != null) {
|
||||
_select.getWhereClause().validateQueryType(type); // sanity check
|
||||
}
|
||||
_builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select));
|
||||
_builder.newQuery(_select);
|
||||
}
|
||||
|
||||
// from Query
|
||||
public CacheKey getCacheKey ()
|
||||
{
|
||||
WhereClause where = _select.getWhereClause();
|
||||
if (where != null && where instanceof CacheKey) {
|
||||
return (CacheKey) where;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// from Query
|
||||
public T invoke (Connection conn, DatabaseLiaison liaison) throws SQLException
|
||||
{
|
||||
PreparedStatement stmt = _builder.prepare(conn);
|
||||
try {
|
||||
T result = null;
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
result = _marsh.createObject(rs);
|
||||
}
|
||||
// TODO: if (rs.next()) issue warning?
|
||||
rs.close();
|
||||
return result;
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
// from Query
|
||||
public void updateCache (PersistenceContext ctx, T result)
|
||||
{
|
||||
CacheKey key = getCacheKey();
|
||||
if (key == null) {
|
||||
// no row-specific cache key was given
|
||||
if (result == null || !_marsh.hasPrimaryKey()) {
|
||||
return;
|
||||
}
|
||||
// if we can, create a key from what was actually returned
|
||||
key = _marsh.getPrimaryKey(result);
|
||||
}
|
||||
ctx.cacheStore(key, (result != null) ? result.clone() : null);
|
||||
}
|
||||
|
||||
// from Query
|
||||
public T transformCacheHit (CacheKey key, T value)
|
||||
{
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
// we do not want to return a reference to the actual cached entity so we clone it
|
||||
@SuppressWarnings("unchecked") T cvalue = (T) value.clone();
|
||||
return cvalue;
|
||||
}
|
||||
|
||||
protected DepotMarshaller<T> _marsh;
|
||||
protected SelectClause<T> _select;
|
||||
protected SQLBuilder _builder;
|
||||
}
|
||||
@@ -1,65 +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.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
|
||||
/**
|
||||
* Generates primary keys using an identity column.
|
||||
*/
|
||||
public class IdentityValueGenerator extends ValueGenerator
|
||||
{
|
||||
public IdentityValueGenerator (GeneratedValue gv, DepotMarshaller<?> dm, FieldMarshaller<?> fm)
|
||||
{
|
||||
super(gv, dm, fm);
|
||||
}
|
||||
|
||||
@Override // from ValueGenerator
|
||||
public boolean isPostFactum ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override // from ValueGenerator
|
||||
public void create (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
liaison.createGenerator(conn, _dm.getTableName(), _fm.getColumnName(), _initialValue);
|
||||
}
|
||||
|
||||
@Override // from ValueGenerator
|
||||
public int nextGeneratedValue (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
return liaison.lastInsertedId(conn, _dm.getTableName(), _fm.getColumnName());
|
||||
}
|
||||
|
||||
@Override // from ValueGenerator
|
||||
public void delete (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
liaison.deleteGenerator(conn, _dm.getTableName(), _fm.getColumnName());
|
||||
}
|
||||
}
|
||||
@@ -1,246 +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.io.Serializable;
|
||||
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.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* A special form of {@link WhereClause} that uniquely specifies a single database row and
|
||||
* thus also a single persistent object. Because it implements both {@link CacheKey} and
|
||||
* {@link CacheInvalidator} it also uniquely indexes into the cache and knows how to invalidate
|
||||
* itself upon modification. This class is created by many {@link DepotMarshaller} methods as
|
||||
* a convenience, and may also be instantiated explicitly.
|
||||
*/
|
||||
public class Key<T extends PersistentRecord> extends WhereClause
|
||||
implements SQLExpression, CacheKey, ValidatingCacheInvalidator, Serializable
|
||||
{
|
||||
/** Handles the matching of the key columns to its bound values. This is needed so that we can
|
||||
* combine a buncy of keys into a {@link KeySet}. */
|
||||
public static class Expression<U extends PersistentRecord> implements SQLExpression
|
||||
{
|
||||
public Expression (Class<U> pClass, List<Comparable<?>> values) {
|
||||
_pClass = pClass;
|
||||
_values = values;
|
||||
}
|
||||
public Class<U> getPersistentClass () {
|
||||
return _pClass;
|
||||
}
|
||||
public List<Comparable<?>> getValues () {
|
||||
return _values;
|
||||
}
|
||||
public void accept (ExpressionVisitor builder) {
|
||||
builder.visit(this);
|
||||
}
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet) {
|
||||
classSet.add(getPersistentClass());
|
||||
}
|
||||
protected Class<U> _pClass;
|
||||
protected List<Comparable<?>> _values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new single-column {@code Key} with the given value.
|
||||
*/
|
||||
public Key (Class<T> pClass, String ix, Comparable<?> val)
|
||||
{
|
||||
this(pClass, new String[] { ix }, new Comparable[] { val });
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new two-column {@code Key} with the given values.
|
||||
*/
|
||||
public Key (Class<T> pClass, String ix1, Comparable<?> val1,
|
||||
String ix2, Comparable<?> val2)
|
||||
{
|
||||
this(pClass, new String[] { ix1, ix2 }, new Comparable[] { val1, val2 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new three-column {@code Key} with the given values.
|
||||
*/
|
||||
public Key (Class<T> pClass, String ix1, Comparable<?> val1,
|
||||
String ix2, Comparable<?> val2, String ix3, Comparable<?> val3)
|
||||
{
|
||||
this(pClass, new String[] { ix1, ix2, ix3 }, new Comparable[] { val1, val2, val3 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new multi-column {@code Key} with the given values.
|
||||
*/
|
||||
public Key (Class<T> pClass, String[] fields, Comparable<?>[] values)
|
||||
{
|
||||
if (fields.length != values.length) {
|
||||
throw new IllegalArgumentException("Field and Value arrays must be of equal length.");
|
||||
}
|
||||
|
||||
// keep this for posterity
|
||||
_pClass = pClass;
|
||||
|
||||
// build a local map of field name -> field value
|
||||
Map<String, Comparable<?>> map = new HashMap<String, Comparable<?>>();
|
||||
for (int i = 0; i < fields.length; i ++) {
|
||||
map.put(fields[i], values[i]);
|
||||
}
|
||||
|
||||
// look up the cached primary key fields for this object
|
||||
String[] keyFields = KeyUtil.getKeyFields(pClass);
|
||||
|
||||
// now extract the values in field order and ensure none are extra or missing
|
||||
_values = new ArrayList<Comparable<?>>();
|
||||
for (int ii = 0; ii < keyFields.length; ii++) {
|
||||
Comparable<?> nugget = map.remove(keyFields[ii]);
|
||||
if (nugget == null) {
|
||||
// make sure we were provided with a value for this primary key field
|
||||
throw new IllegalArgumentException("Missing value for key field: " + keyFields[ii]);
|
||||
}
|
||||
if (nugget instanceof Serializable) {
|
||||
_values.add(nugget);
|
||||
continue;
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Non-serializable argument [key=" + keyFields[ii] + ", arg=" + nugget + "]");
|
||||
}
|
||||
|
||||
// finally make sure we were not given any fields that are not in fact primary key fields
|
||||
if (map.size() > 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Non-key columns given: " + StringUtil.join(map.keySet().toArray(), ", "));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the persistent class for which we represent a key.
|
||||
*/
|
||||
public Class<T> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values bound to this key.
|
||||
*/
|
||||
public List<Comparable<?>> getValues ()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
// from WhereClause
|
||||
public SQLExpression getWhereExpression ()
|
||||
{
|
||||
return new Expression<T>(_pClass, _values);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_pClass);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from CacheKey
|
||||
public String getCacheId ()
|
||||
{
|
||||
return _pClass.getName();
|
||||
}
|
||||
|
||||
// from CacheKey
|
||||
public Serializable getCacheKey ()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
// from ValidatingCacheInvalidator
|
||||
public void validateFlushType (Class<?> pClass)
|
||||
{
|
||||
if (!pClass.equals(_pClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Class mismatch between persistent record and cache invalidator " +
|
||||
"[record=" + pClass.getSimpleName() + ", invtype=" + _pClass.getSimpleName() + "].");
|
||||
}
|
||||
}
|
||||
|
||||
// from CacheInvalidator
|
||||
public void invalidate (PersistenceContext ctx)
|
||||
{
|
||||
ctx.cacheInvalidate(this);
|
||||
}
|
||||
|
||||
@Override // from WhereClause
|
||||
public void validateQueryType (Class<?> pClass)
|
||||
{
|
||||
super.validateQueryType(pClass);
|
||||
validateTypesMatch(pClass, _pClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj)
|
||||
{
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return _values.equals(((Key<?>) obj)._values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
return _values.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(_pClass.getSimpleName());
|
||||
builder.append("(");
|
||||
String[] keyFields = KeyUtil.getKeyFields(_pClass);
|
||||
for (int ii = 0; ii < keyFields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append(keyFields[ii]).append("=").append(_values.get(ii));
|
||||
}
|
||||
builder.append(")");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/** The persistent record type for which we are a key. */
|
||||
protected final Class<T> _pClass;
|
||||
|
||||
/** The expression that identifies our row. */
|
||||
protected final ArrayList<Comparable<?>> _values; // must declare as ArrayList for Serializable
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2001-2008 Michael Bayne
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.LiteralExp;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals;
|
||||
import com.samskivert.jdbc.depot.operator.Logic;
|
||||
|
||||
/**
|
||||
* Contains a set of primary keys that match a set of persistent records. This is used internally
|
||||
* in Depot when decomposing queries into two parts: first a query for the primary keys that
|
||||
* identify the records that match a free-form query and then another query that operates on the
|
||||
* previously identified keys. The keys obtained in the first query are used to create a KeySet and
|
||||
* modifications and deletons using this set will automatically flush the appropriate records from
|
||||
* the cache.
|
||||
*/
|
||||
public class KeySet<T extends PersistentRecord> extends WhereClause
|
||||
implements SQLExpression, ValidatingCacheInvalidator
|
||||
{
|
||||
/**
|
||||
* Creates a set from the supplied primary keys.
|
||||
*/
|
||||
public KeySet (Class<T> pClass, Collection<Key<T>> keys)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_keys = keys;
|
||||
|
||||
String[] keyFields = KeyUtil.getKeyFields(pClass);
|
||||
if (keys.size() == 0) {
|
||||
_condition = new LiteralExp("false");
|
||||
|
||||
} else if (keyFields.length == 1) {
|
||||
// TODO: remove when we update to 1.6 and change Postgres In handling
|
||||
if (keys.size() > Conditionals.In.MAX_KEYS) {
|
||||
throw new IllegalArgumentException("Cannot create where clause for more than " +
|
||||
Conditionals.In.MAX_KEYS + " at a time.");
|
||||
}
|
||||
|
||||
// Single-column keys result in the compact IN(keyVal1, keyVal2, ...)
|
||||
Comparable<?> first = keys.iterator().next().getValues().get(0);
|
||||
Comparable<?>[] keyFieldValues;
|
||||
if (first instanceof Integer) {
|
||||
keyFieldValues = new Integer[keys.size()];
|
||||
} else if (first instanceof Integer) {
|
||||
keyFieldValues = new String[keys.size()];
|
||||
} else {
|
||||
keyFieldValues = new Comparable<?>[keys.size()];
|
||||
}
|
||||
int ii = 0;
|
||||
for (Key<T> key : keys) {
|
||||
keyFieldValues[ii++] = key.getValues().get(0);
|
||||
}
|
||||
_condition = new Conditionals.In(pClass, keyFields[0], keyFieldValues);
|
||||
|
||||
} else {
|
||||
// TODO: is there a maximum size of an or query? 32768?
|
||||
|
||||
// Multi-column keys result in OR'd AND's, of unknown efficiency (TODO check).
|
||||
SQLExpression[] keyArray = new SQLExpression[keys.size()];
|
||||
int ii = 0;
|
||||
for (Key<T> key : keys) {
|
||||
keyArray[ii++] = key.getWhereExpression();
|
||||
}
|
||||
_condition = new Logic.Or(keyArray);
|
||||
}
|
||||
}
|
||||
|
||||
// from WhereClause
|
||||
public SQLExpression getWhereExpression ()
|
||||
{
|
||||
return _condition;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_pClass);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from ValidatingCacheInvalidator
|
||||
public void validateFlushType (Class<?> pClass)
|
||||
{
|
||||
if (!pClass.equals(_pClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Class mismatch between persistent record and cache invalidator " +
|
||||
"[record=" + pClass.getSimpleName() + ", invtype=" + _pClass.getSimpleName() + "].");
|
||||
}
|
||||
}
|
||||
|
||||
// from CacheInvalidator
|
||||
public void invalidate (PersistenceContext ctx)
|
||||
{
|
||||
for (Key<T> key : _keys) {
|
||||
ctx.cacheInvalidate(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from WhereClause
|
||||
public void validateQueryType (Class<?> pClass)
|
||||
{
|
||||
super.validateQueryType(pClass);
|
||||
validateTypesMatch(pClass, _pClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj)
|
||||
{
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return _condition.equals(((KeySet<?>) obj)._condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
return _condition.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return _keys.toString();
|
||||
}
|
||||
|
||||
protected Class<T> _pClass;
|
||||
protected Collection<Key<T>> _keys;
|
||||
protected SQLExpression _condition;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2001-2008 Michael Bayne
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.jdbc.depot.annotation.Id;
|
||||
|
||||
/**
|
||||
* Simple utility methods used by {@link Key} and {@link KeySet}.
|
||||
*/
|
||||
public class KeyUtil
|
||||
{
|
||||
/**
|
||||
* Returns an array containing the names of the primary key fields for the supplied persistent
|
||||
* class. The values are introspected and cached for the lifetime of the VM.
|
||||
*/
|
||||
public static String[] getKeyFields (Class<?> pClass)
|
||||
{
|
||||
String[] fields = _keyFields.get(pClass);
|
||||
if (fields == null) {
|
||||
List<String> kflist = new ArrayList<String>();
|
||||
for (Field field : pClass.getFields()) {
|
||||
// look for @Id fields
|
||||
if (field.getAnnotation(Id.class) != null) {
|
||||
kflist.add(field.getName());
|
||||
}
|
||||
}
|
||||
_keyFields.put(pClass, fields = kflist.toArray(new String[kflist.size()]));
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
/** A (never expiring) cache of primary key field names for all persistent classes (of which
|
||||
* there are merely dozens, so we don't need to worry about expiring). */
|
||||
protected static Map<Class<?>,String[]> _keyFields = new HashMap<Class<?>,String[]>();
|
||||
}
|
||||
@@ -1,32 +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 com.samskivert.util.Logger;
|
||||
|
||||
/**
|
||||
* Contains a reference to the log object used by this package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
/** We dispatch our log messages through this logger. */
|
||||
public static Logger log = Logger.getLogger("com.samskivert.jdbc.depot");
|
||||
}
|
||||
@@ -1,144 +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.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
|
||||
/**
|
||||
* Encapsulates a modification of persistent objects.
|
||||
*/
|
||||
public abstract class Modifier implements Operation<Integer>
|
||||
{
|
||||
/**
|
||||
* A simple modifier that executes a single SQL statement. No cache flushing is done as a
|
||||
* result of this operation.
|
||||
*/
|
||||
public abstract static class Simple extends Modifier
|
||||
{
|
||||
public Simple () {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
return stmt.executeUpdate(createQuery(liaison));
|
||||
} finally {
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String createQuery (DatabaseLiaison liaison);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience modifier that can perform cache updates in addition to invalidation:
|
||||
* - Before {@link #invoke}, the {@link CacheInvalidator} is run, if given.
|
||||
* - After {@link #invoke}, the cache is updated with the modified object,
|
||||
* presuming both _key and _result are non-null. These variables may be set or modified
|
||||
* during execution in addition to being supplied to the constructor.
|
||||
*/
|
||||
public static abstract class CachingModifier<T extends PersistentRecord> extends Modifier
|
||||
{
|
||||
/**
|
||||
* Construct a new CachingModifier with the given result, cache key, and invalidator,
|
||||
* all of which are optional, and may also be set during execution.
|
||||
*/
|
||||
protected CachingModifier (T result, CacheKey key, CacheInvalidator invalidator)
|
||||
{
|
||||
super(invalidator);
|
||||
_result = result;
|
||||
_key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this {@link CachingModifier}'s cache key, e.g. during insertion when a
|
||||
* persistent object first receives a generated key.
|
||||
*/
|
||||
protected void updateKey (CacheKey key)
|
||||
{
|
||||
if (key != null) {
|
||||
_key = key;
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Modifier
|
||||
public void cacheUpdate (PersistenceContext ctx)
|
||||
{
|
||||
super.cacheUpdate(ctx);
|
||||
// if we have both a key and a record, cache
|
||||
if (_key != null && _result != null) {
|
||||
ctx.cacheStore(_key, _result.clone());
|
||||
}
|
||||
}
|
||||
|
||||
protected CacheKey _key;
|
||||
protected T _result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriden to perform the actual database modifications represented by this object; should
|
||||
* return the number of modified rows.
|
||||
*/
|
||||
public abstract Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException;
|
||||
|
||||
/**
|
||||
* Constructs a {@link Modifier} without a cache invalidator.
|
||||
*/
|
||||
public Modifier ()
|
||||
{
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link Modifier} with the given cache invalidator.
|
||||
*/
|
||||
public Modifier (CacheInvalidator invalidator)
|
||||
{
|
||||
_invalidator = invalidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do any cache invalidation needed for this modification. This method is called just
|
||||
* before the database statement is executed.
|
||||
*/
|
||||
public void cacheInvalidation (PersistenceContext ctx)
|
||||
{
|
||||
if (_invalidator != null) {
|
||||
_invalidator.invalidate(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do any cache updates needed for this modification. This method is called just after
|
||||
* the successful execution of the database statement.
|
||||
*/
|
||||
public void cacheUpdate (PersistenceContext ctx)
|
||||
{
|
||||
}
|
||||
|
||||
protected CacheInvalidator _invalidator;
|
||||
}
|
||||
@@ -1,155 +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.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* A special form of {@link Where} clause that specifies an explicit range of database rows. It
|
||||
* does not implement {@link CacheKey} but it does implement {@link CacheInvalidator} which means
|
||||
* it can be sent into e.g. {@link DepotRepository#deleteAll} and have it clean up after itself.
|
||||
*/
|
||||
public class MultiKey<T extends PersistentRecord> extends WhereClause
|
||||
implements ValidatingCacheInvalidator
|
||||
{
|
||||
/**
|
||||
* Constructs a new single-column {@code MultiKey} with the given value range.
|
||||
*/
|
||||
public MultiKey (Class<T> pClass, String ix, Comparable<?>... val)
|
||||
{
|
||||
this(pClass, new String[0], new Comparable[0], ix, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new two-column {@code MultiKey} with the given value range.
|
||||
*/
|
||||
public MultiKey (Class<T> pClass, String ix1, Comparable<?> val1, String ix2,
|
||||
Comparable<?>... val2)
|
||||
{
|
||||
this(pClass, new String[] { ix1 }, new Comparable[] { val1 }, ix2, val2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new three-column {@code MultiKey} with the given value range.
|
||||
*/
|
||||
public MultiKey (Class<T> pClass, String ix1, Comparable<?> val1,
|
||||
String ix2, Comparable<?> val2, String ix3, Comparable<?>... val3)
|
||||
{
|
||||
this(pClass, new String[] { ix1, ix2 }, new Comparable[] { val1, val2 }, ix3, val3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new multi-column {@code MultiKey} with the given value range.
|
||||
* See {@link Key#Key(Class,String[],Comparable[])} for somewhat relevant comments.
|
||||
*/
|
||||
public MultiKey (Class<T> pClass, String[] sFields, Comparable<?>[] sValues,
|
||||
String mField, Comparable<?>[] mValues)
|
||||
{
|
||||
if (sFields.length != sValues.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Key field and values arrays must be of equal length.");
|
||||
}
|
||||
_pClass = pClass;
|
||||
_mField = mField;
|
||||
_mValues = mValues;
|
||||
_map = new HashMap<String, Comparable<?>>();
|
||||
for (int i = 0; i < sFields.length; i ++) {
|
||||
_map.put(sFields[i], sValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public Class<T> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public Map<String, Comparable<?>> getSingleFieldsMap ()
|
||||
{
|
||||
return _map;
|
||||
}
|
||||
|
||||
public String getMultiField ()
|
||||
{
|
||||
return _mField;
|
||||
}
|
||||
|
||||
public Comparable<?>[] getMultiValues ()
|
||||
{
|
||||
return _mValues;
|
||||
}
|
||||
|
||||
// from WhereClause
|
||||
public SQLExpression getWhereExpression ()
|
||||
{
|
||||
throw new RuntimeException("Not used");
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
// nothing to add
|
||||
}
|
||||
|
||||
// from ValidatingCacheInvalidator
|
||||
public void validateFlushType (Class<?> pClass)
|
||||
{
|
||||
if (!pClass.equals(_pClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Class mismatch between persistent record and cache invalidator " +
|
||||
"[record=" + pClass.getSimpleName() +
|
||||
", invtype=" + _pClass.getSimpleName() + "].");
|
||||
}
|
||||
}
|
||||
|
||||
// from CacheInvalidator
|
||||
public void invalidate (PersistenceContext ctx)
|
||||
{
|
||||
HashMap<String, Comparable<?>> newMap = new HashMap<String, Comparable<?>>(_map);
|
||||
for (int i = 0; i < _mValues.length; i ++) {
|
||||
newMap.put(_mField, _mValues[i]);
|
||||
ctx.cacheInvalidate(new SimpleCacheKey(_pClass, newMap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from WhereClause
|
||||
public void validateQueryType (Class<?> pClass)
|
||||
{
|
||||
super.validateQueryType(pClass);
|
||||
validateTypesMatch(pClass, _pClass);
|
||||
}
|
||||
|
||||
protected String _mField;
|
||||
protected Comparable<?>[] _mValues;
|
||||
protected Class<T> _pClass;
|
||||
protected HashMap<String, Comparable<?>> _map;
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
//
|
||||
// $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.Connection;
|
||||
import java.sql.Date;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
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.annotation.FullTextIndex;
|
||||
import com.samskivert.jdbc.depot.clause.DeleteClause;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.EpochSeconds;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.FullTextMatch;
|
||||
|
||||
import static com.samskivert.Log.log;
|
||||
|
||||
public class MySQLBuilder
|
||||
extends SQLBuilder
|
||||
{
|
||||
public class MSBuildVisitor extends BuildVisitor
|
||||
{
|
||||
@Override public void visit (FullTextMatch match)
|
||||
{
|
||||
_builder.append("match(");
|
||||
Class<? extends PersistentRecord> pClass = match.getPersistentRecord();
|
||||
String[] fields =
|
||||
_types.getMarshaller(pClass).getFullTextIndex(match.getName()).fields();
|
||||
for (int ii = 0; ii < fields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
_builder.append(", ");
|
||||
}
|
||||
new ColumnExp(pClass, fields[ii]).accept(this);
|
||||
}
|
||||
_builder.append(") against (? in boolean mode)");
|
||||
}
|
||||
|
||||
@Override public void visit (DeleteClause<? extends PersistentRecord> deleteClause)
|
||||
{
|
||||
_builder.append("delete from ");
|
||||
appendTableName(deleteClause.getPersistentClass());
|
||||
_builder.append(" ");
|
||||
|
||||
// MySQL can't do DELETE FROM SomeTable AS T1, so we turn off abbreviations briefly.
|
||||
boolean savedFlag = _types.getUseTableAbbreviations();
|
||||
_types.setUseTableAbbreviations(false);
|
||||
try {
|
||||
deleteClause.getWhereClause().accept(this);
|
||||
} finally {
|
||||
_types.setUseTableAbbreviations(savedFlag);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit (EpochSeconds epochSeconds)
|
||||
{
|
||||
_builder.append("unix_timestamp(");
|
||||
epochSeconds.getArgument().accept(this);
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
protected MSBuildVisitor (DepotTypes types)
|
||||
{
|
||||
super(types);
|
||||
}
|
||||
|
||||
@Override protected void appendTableName (Class<? extends PersistentRecord> type)
|
||||
{
|
||||
_builder.append(_types.getTableName(type));
|
||||
}
|
||||
|
||||
@Override protected void appendTableAbbreviation (Class<? extends PersistentRecord> type)
|
||||
{
|
||||
_builder.append(_types.getTableAbbreviation(type));
|
||||
}
|
||||
|
||||
@Override protected void appendIdentifier (String field)
|
||||
{
|
||||
_builder.append(field);
|
||||
}
|
||||
}
|
||||
|
||||
public class MSBindVisitor extends BindVisitor
|
||||
{
|
||||
protected MSBindVisitor (DepotTypes types, Connection conn, PreparedStatement stmt) {
|
||||
super(types, conn, stmt);
|
||||
}
|
||||
|
||||
@Override public void visit (FullTextMatch match) {
|
||||
try {
|
||||
_stmt.setString(_argIdx++, match.getQuery());
|
||||
} catch (SQLException sqe) {
|
||||
throw new DatabaseException("Failed to configure full-text match column " +
|
||||
"[idx=" + (_argIdx-1) + ", match=" + match + "]", sqe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MySQLBuilder (DepotTypes types)
|
||||
{
|
||||
super(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getFtsIndexes (
|
||||
Iterable<String> columns, Iterable<String> indexes, Set<String> target)
|
||||
{
|
||||
for (String index : indexes) {
|
||||
if (index.startsWith("ftsIx_")) {
|
||||
target.add(index.substring("ftsIx_".length()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PersistentRecord> boolean addFullTextSearch (
|
||||
Connection conn, DepotMarshaller<T> marshaller, FullTextIndex fts)
|
||||
throws SQLException
|
||||
{
|
||||
Class<T> pClass = marshaller.getPersistentClass();
|
||||
StringBuilder update = new StringBuilder("ALTER TABLE ").
|
||||
append(marshaller.getTableName()).append(" ADD FULLTEXT INDEX ftsIx_").
|
||||
append(fts.name()).append(" (");
|
||||
String[] fields = fts.fields();
|
||||
for (int ii = 0; ii < fields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
update.append(", ");
|
||||
}
|
||||
update.append(_types.getColumnName(pClass, fields[ii]));
|
||||
}
|
||||
update.append(")");
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
log.info("Adding full-text search index: ftsIx_" + fts.name());
|
||||
stmt.executeUpdate(update.toString());
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrivateColumn (String column)
|
||||
{
|
||||
// The MySQL builder does not yet have any private columns.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBooleanDefault ()
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BuildVisitor getBuildVisitor ()
|
||||
{
|
||||
return new MSBuildVisitor(_types);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BindVisitor getBindVisitor (Connection conn, PreparedStatement stmt)
|
||||
{
|
||||
return new MSBindVisitor(_types, conn, stmt);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> String getColumnType (FieldMarshaller<?> fm, int length)
|
||||
{
|
||||
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)) {
|
||||
if (length < (1 << 15)) {
|
||||
return "VARCHAR(" + length + ")";
|
||||
}
|
||||
return "TEXT";
|
||||
} 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 ||
|
||||
fm instanceof IntArrayMarshaller) {
|
||||
if (fm instanceof IntArrayMarshaller) {
|
||||
length *= 4;
|
||||
}
|
||||
// semi-arbitrarily use VARBINARY() up to 32767
|
||||
if (length < (1 << 15)) {
|
||||
return "VARBINARY(" + length + ")";
|
||||
}
|
||||
// use BLOB to 65535
|
||||
if (length < (1 << 16)) {
|
||||
return "BLOB";
|
||||
}
|
||||
if (length < (1 << 24)) {
|
||||
return "MEDIUMBLOB";
|
||||
}
|
||||
return "LONGBLOB";
|
||||
} else if (fm instanceof ByteEnumMarshaller) {
|
||||
return "TINYINT";
|
||||
} else if (fm instanceof BooleanMarshaller) {
|
||||
return "TINYINT";
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown field marshaller type: " + fm.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2001-2008 Michael Bayne
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.samskivert.jdbc.depot;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
|
||||
/**
|
||||
* An abstraction that encompasses both {@link Query} and {@link Modifier} operations.
|
||||
*/
|
||||
public interface Operation<T>
|
||||
{
|
||||
/**
|
||||
* Performs the actual JDBC interactions associated with this operation.
|
||||
*/
|
||||
public T invoke (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException;
|
||||
}
|
||||
@@ -1,607 +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.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.samskivert.jdbc.ConnectionProvider;
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.LiaisonRegistry;
|
||||
import com.samskivert.jdbc.MySQLLiaison;
|
||||
import com.samskivert.jdbc.PostgreSQLLiaison;
|
||||
|
||||
import com.samskivert.jdbc.depot.annotation.TableGenerator;
|
||||
|
||||
import static com.samskivert.jdbc.depot.Log.log;
|
||||
|
||||
/**
|
||||
* Defines a scope in which global annotations are shared.
|
||||
*/
|
||||
public class PersistenceContext
|
||||
{
|
||||
/** Allow toggling of query logging and other debug output via a system property. */
|
||||
public static final boolean DEBUG = Boolean.getBoolean("com.samskivert.jdbc.depot.debug");
|
||||
|
||||
/** Map {@link TableGenerator} instances by name. */
|
||||
public HashMap<String, TableGenerator> tableGenerators = new HashMap<String, TableGenerator>();
|
||||
|
||||
/**
|
||||
* A cache listener is notified when cache entries change. Its purpose is typically to do
|
||||
* further invalidation of dependent entries in other caches.
|
||||
*/
|
||||
public static interface CacheListener<T>
|
||||
{
|
||||
/**
|
||||
* The given entry (which is never null) has just been evicted from the cache slot
|
||||
* indicated by the given key.
|
||||
*
|
||||
* This method is most commonly used to trigger custom cache invalidation of records that
|
||||
* depend on the one that was just invalidated.
|
||||
*/
|
||||
public void entryInvalidated (CacheKey key, T oldEntry);
|
||||
|
||||
/**
|
||||
* The given entry, which may be an explicit null, has just been placed into the cache
|
||||
* under the given key. The previous cache entry, if any, is also supplied.
|
||||
*
|
||||
* This method is most likely used by repositories to index entries by attribute for quick
|
||||
* cache invalidation when brute force is unrealistically time consuming.
|
||||
*/
|
||||
public void entryCached (CacheKey key, T newEntry, T oldEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback for {@link #cacheTraverse}; this is called for each entry in a given cache.
|
||||
*/
|
||||
public static interface CacheTraverser<T extends Serializable>
|
||||
{
|
||||
/**
|
||||
* Performs whatever cache-related tasks need doing for this cache entry. This method
|
||||
* is called for each cache entry in a full-cache enumeration.
|
||||
*/
|
||||
public void visitCacheEntry (
|
||||
PersistenceContext ctx, String cacheId, Serializable key, T record);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple implementation of {@link CacheTraverser} that selectively deletes entries in
|
||||
* a cache depending on the return value of {@link #testForEviction}.
|
||||
*/
|
||||
public static abstract class CacheEvictionFilter<T extends Serializable>
|
||||
implements CacheTraverser<T>
|
||||
{
|
||||
// from CacheTraverser
|
||||
public void visitCacheEntry (
|
||||
PersistenceContext ctx, String cacheId, Serializable key, T record)
|
||||
{
|
||||
if (testForEviction(key, record)) {
|
||||
ctx.cacheInvalidate(cacheId, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether or not this entry should be evicted and returns true if yes, false if
|
||||
* no.
|
||||
*/
|
||||
protected abstract boolean testForEviction (Serializable key, T record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache adapter used by this context or null if caching is disabled.
|
||||
*/
|
||||
public CacheAdapter getCacheAdapter ()
|
||||
{
|
||||
return _cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an uninitialized persistence context. {@link #init} must later be called on this
|
||||
* context to prepare it for operation.
|
||||
*/
|
||||
public PersistenceContext ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes a persistence context. See {@link #init}.
|
||||
*/
|
||||
public PersistenceContext (String ident, ConnectionProvider conprov, CacheAdapter adapter)
|
||||
{
|
||||
init(ident, conprov, adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this context with its connection provider and cache adapter.
|
||||
*
|
||||
* @param ident the identifier to provide to the connection provider when requesting a
|
||||
* connection.
|
||||
* @param conprov provides JDBC {@link Connection} instances.
|
||||
* @param adapter an optional adapter to a cache management system.
|
||||
*/
|
||||
public void init (String ident, ConnectionProvider conprov, CacheAdapter adapter)
|
||||
{
|
||||
_ident = ident;
|
||||
_conprov = conprov;
|
||||
_liaison = LiaisonRegistry.getLiaison(conprov.getURL(ident));
|
||||
_cache = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts this persistence context down, shutting down any caching system in use and shutting
|
||||
* down the JDBC connection pool.
|
||||
*/
|
||||
public void shutdown ()
|
||||
{
|
||||
try {
|
||||
if (_cache != null) {
|
||||
_cache.shutdown();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.warning("Failure shutting down Depot cache.", t);
|
||||
}
|
||||
_conprov.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a schema migration for the specified entity class.
|
||||
*
|
||||
* <p> This method must be called <b>before</b> an Entity is used by any repository. Thus you
|
||||
* should register all migrations in the constructor of the repository that declares them in
|
||||
* its {@link DepotRepository#getManagedRecords} method.
|
||||
*
|
||||
* <p> Note that the migration process is as follows:
|
||||
*
|
||||
* <ul><li> Note the difference between the entity's declared version and the version recorded
|
||||
* in the database.
|
||||
* <li> Run all registered pre-migrations
|
||||
* <li> Perform all default migrations (column and index additions, index removals)
|
||||
* <li> Run all registered post-migrations </ul>
|
||||
*
|
||||
* Thus you must either be prepared for the entity to be at <b>any</b> version prior to your
|
||||
* migration target version because we may start up, find the schema at version 1 and the
|
||||
* Entity class at version 8 and do all "standard" migrations in one fell swoop. So if a column
|
||||
* got added in version 2 and renamed in version 6 and your migration was registered for
|
||||
* version 6 to do that migration, it must be prepared for the column not to exist at all.
|
||||
*
|
||||
* <p> If you want a completely predictable migration process, never use the default migrations
|
||||
* and register a pre-migration for every single schema migration and they will then be
|
||||
* guaranteed to be run in registration order and with predictable pre- and post-conditions.
|
||||
*
|
||||
* <p> Note that if {@link PersistenceContext#initializeRepositories} is used, then all schema
|
||||
* migrations for all known repositories will be run and then all data migrations for all known
|
||||
* repositories will be run. This is recommeneded because schema migrations may fail and it is
|
||||
* generally better to have not yet done the data migration rather than having schema and data
|
||||
* migrations interleaved and potentially leaving the database in a strange state.
|
||||
*/
|
||||
public <T extends PersistentRecord> void registerMigration (
|
||||
Class<T> type, SchemaMigration migration)
|
||||
{
|
||||
getRawMarshaller(type).registerMigration(migration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the marshaller for the specified persistent object class, creating and initializing
|
||||
* it if necessary.
|
||||
*/
|
||||
public <T extends PersistentRecord> DepotMarshaller<T> getMarshaller (Class<T> type)
|
||||
throws DatabaseException
|
||||
{
|
||||
checkAreInitialized(); // le check du sanity
|
||||
DepotMarshaller<T> marshaller = getRawMarshaller(type);
|
||||
try {
|
||||
if (!marshaller.isInitialized()) {
|
||||
// initialize the marshaller which may create or migrate the table for its
|
||||
// underlying persistent object
|
||||
marshaller.init(this);
|
||||
if (marshaller.getTableName() != null && _warnOnLazyInit) {
|
||||
log.warning("Record initialized lazily", "type", type.getName(),
|
||||
new Exception());
|
||||
}
|
||||
}
|
||||
} catch (DatabaseException pe) {
|
||||
throw (DatabaseException)new DatabaseException(
|
||||
"Failed to initialize marshaller [type=" + type + "].").initCause(pe);
|
||||
}
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a non-modifying query and returns its result.
|
||||
*/
|
||||
public <T> T invoke (Query<T> query)
|
||||
throws DatabaseException
|
||||
{
|
||||
CacheKey key = query.getCacheKey();
|
||||
// if there is a cache key, check the cache
|
||||
if (key != null && _cache != null) {
|
||||
CacheAdapter.CachedValue<T> cacheHit = cacheLookup(key);
|
||||
if (cacheHit != null) {
|
||||
log.debug("cache hit [key=" + key + ", hit=" + cacheHit + "]");
|
||||
T value = cacheHit.getValue();
|
||||
value = query.transformCacheHit(key, value);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
log.debug("transformCacheHit returned null; rejecting cached value.");
|
||||
}
|
||||
log.debug("cache miss [key=" + key + "]");
|
||||
}
|
||||
// otherwise, perform the query
|
||||
T result = invoke(query, true);
|
||||
// and let the caller figure out if it wants to cache itself somehow
|
||||
query.updateCache(this, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a modifying query and returns the number of rows modified.
|
||||
*/
|
||||
public int invoke (Modifier modifier)
|
||||
throws DatabaseException
|
||||
{
|
||||
modifier.cacheInvalidation(this);
|
||||
int rows = invoke(modifier, true);
|
||||
if (rows > 0) {
|
||||
modifier.cacheUpdate(this);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is a {@link CacheAdapter} configured, false otherwise.
|
||||
*/
|
||||
public boolean isUsingCache ()
|
||||
{
|
||||
return _cache != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up an entry in the cache by the given key.
|
||||
*/
|
||||
public <T> CacheAdapter.CachedValue<T> cacheLookup (CacheKey key)
|
||||
{
|
||||
if (_cache == null) {
|
||||
return null;
|
||||
}
|
||||
CacheAdapter.CacheBin<T> bin = _cache.getCache(key.getCacheId());
|
||||
return bin.lookup(key.getCacheKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a new entry indexed by the given key.
|
||||
*/
|
||||
public <T> void cacheStore (CacheKey key, T entry)
|
||||
{
|
||||
if (_cache == null) {
|
||||
return;
|
||||
}
|
||||
if (key == null) {
|
||||
log.warning("Cache key must not be null [entry=" + entry + "]");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
log.debug("storing [key=" + key + ", value=" + entry + "]");
|
||||
|
||||
CacheAdapter.CacheBin<T> bin = _cache.getCache(key.getCacheId());
|
||||
CacheAdapter.CachedValue<T> element = bin.lookup(key.getCacheKey());
|
||||
T oldEntry = (element != null ? element.getValue() : null);
|
||||
|
||||
// update the cache
|
||||
bin.store(key.getCacheKey(), entry);
|
||||
|
||||
// then do cache invalidations
|
||||
Set<CacheListener<?>> listeners = _listenerSets.get(key.getCacheId());
|
||||
if (listeners != null && listeners.size() > 0) {
|
||||
for (CacheListener<?> listener : listeners) {
|
||||
log.debug("cascading [listener=" + listener + "]");
|
||||
@SuppressWarnings("unchecked")
|
||||
CacheListener<T> casted = (CacheListener<T>)listener;
|
||||
casted.entryCached(key, entry, oldEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evicts the cache entry indexed under the given key, if there is one. The eviction may
|
||||
* trigger further cache invalidations.
|
||||
*/
|
||||
public void cacheInvalidate (CacheKey key)
|
||||
{
|
||||
if (key == null) {
|
||||
log.warning("Cache key to invalidate must not be null.");
|
||||
Thread.dumpStack();
|
||||
} else {
|
||||
cacheInvalidate(key.getCacheId(), key.getCacheKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evicts the cache entry indexed under the given class and cache key, if there is one. The
|
||||
* eviction may trigger further cache invalidations.
|
||||
*/
|
||||
public void cacheInvalidate (Class<? extends PersistentRecord> pClass, Serializable cacheKey)
|
||||
{
|
||||
cacheInvalidate(pClass.getName(), cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evicts the cache entry indexed under the given cache id and cache key, if there is one. The
|
||||
* eviction may trigger further cache invalidations.
|
||||
*/
|
||||
public <T extends Serializable> void cacheInvalidate (String cacheId, Serializable cacheKey)
|
||||
{
|
||||
if (_cache == null) {
|
||||
return;
|
||||
}
|
||||
log.debug("invalidating [cacheId=" + cacheId + ", cacheKey=" + cacheKey + "]");
|
||||
|
||||
CacheAdapter.CacheBin<T> bin = _cache.getCache(cacheId);
|
||||
CacheAdapter.CachedValue<T> element = bin.lookup(cacheKey);
|
||||
if (element != null) {
|
||||
// find the old entry, if any
|
||||
T oldEntry = element.getValue();
|
||||
if (oldEntry != null) {
|
||||
// if there was one, do (possibly cascading) cache invalidations
|
||||
Set<CacheListener<?>> listeners = _listenerSets.get(cacheId);
|
||||
if (listeners != null && listeners.size() > 0) {
|
||||
CacheKey key = new SimpleCacheKey(cacheId, cacheKey);
|
||||
for (CacheListener<?> listener : listeners) {
|
||||
log.debug("cascading [listener=" + listener + "]");
|
||||
@SuppressWarnings("unchecked") CacheListener<T> casted =
|
||||
(CacheListener<T>)listener;
|
||||
casted.entryInvalidated(key, oldEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then remove the keyed entry from the cache system
|
||||
bin.remove(cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Brutally iterates over the entire contents of the cache associated with the given class,
|
||||
* invoking the callback for each cache entry.
|
||||
*/
|
||||
public <T extends Serializable> void cacheTraverse (
|
||||
Class<? extends PersistentRecord> pClass, CacheTraverser<T> filter)
|
||||
{
|
||||
cacheTraverse(pClass.getName(), filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Brutally iterates over the entire contents of the cache identified by the given cache id,
|
||||
* invoking the callback for each cache entry.
|
||||
*/
|
||||
public <T extends Serializable> void cacheTraverse (String cacheId, CacheTraverser<T> filter)
|
||||
{
|
||||
if (_cache == null) {
|
||||
return;
|
||||
}
|
||||
CacheAdapter.CacheBin<T> bin = _cache.getCache(cacheId);
|
||||
if (bin != null) {
|
||||
for (Object key : bin.enumerateKeys()) {
|
||||
CacheAdapter.CachedValue<T> element = bin.lookup((Serializable) key);
|
||||
T value;
|
||||
if (element != null && (value = element.getValue()) != null) {
|
||||
filter.visitCacheEntry(this, cacheId, (Serializable) key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new cache listener with the cache associated with the given class.
|
||||
*/
|
||||
public <T extends Serializable> void addCacheListener (
|
||||
Class<T> pClass, CacheListener<T> listener)
|
||||
{
|
||||
addCacheListener(pClass.getName(), listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new cache listener with the identified cache.
|
||||
*/
|
||||
public <T extends Serializable> void addCacheListener (
|
||||
String cacheId, CacheListener<T> listener)
|
||||
{
|
||||
Set<CacheListener<?>> listenerSet = _listenerSets.get(cacheId);
|
||||
if (listenerSet == null) {
|
||||
listenerSet = new HashSet<CacheListener<?>>();
|
||||
_listenerSets.put(cacheId, listenerSet);
|
||||
}
|
||||
listenerSet.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all repositories that have been created and registered with this persistence
|
||||
* context. Any repositories that are constructed after this call will be immediately
|
||||
* initialized at the time they are constructed (which is probably undesirable). When a
|
||||
* repository is initialized, schema migrations for all of its managed persistent records are
|
||||
* run. It is best to do all schema migrations when the system initializes which is why lazy
|
||||
* initialization of repositories is undesirable.
|
||||
*
|
||||
* @param warnOnLazyInit if true, any repositories are constructed after this method is called
|
||||
* will result in a warning so that the application developer can restructure their code to
|
||||
* ensure that those repositories are properly created prior to this call.
|
||||
*/
|
||||
public void initializeRepositories (boolean warnOnLazyInit)
|
||||
throws DatabaseException
|
||||
{
|
||||
// resolve all persistent records and trigger all schema migrations
|
||||
for (DepotRepository repo : _repositories) {
|
||||
repo.resolveRecords();
|
||||
}
|
||||
// then run all repository initialization methods, triggering all data migrations
|
||||
for (DepotRepository repo : _repositories) {
|
||||
repo.init();
|
||||
}
|
||||
// now potentially issue a warning if we lazily initialize any other persistent record
|
||||
_warnOnLazyInit = warnOnLazyInit;
|
||||
// finally note that we've now been initialized
|
||||
_repositories = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a depot repository is created. We register all persistent record classes used by
|
||||
* the repository so that systems that desire it can force the resolution of all database
|
||||
* tables rather than allowing resolution to happen on demand.
|
||||
*/
|
||||
protected void repositoryCreated (DepotRepository repo)
|
||||
{
|
||||
if (_repositories == null) {
|
||||
if (_warnOnLazyInit) {
|
||||
log.warning("Repository created lazily: " + repo.getClass().getName());
|
||||
}
|
||||
repo.resolveRecords();
|
||||
repo.init();
|
||||
} else {
|
||||
_repositories.add(repo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up and creates, but does not initialize, the marshaller for the specified Entity type.
|
||||
*/
|
||||
protected <T extends PersistentRecord> DepotMarshaller<T> getRawMarshaller (Class<T> type)
|
||||
{
|
||||
@SuppressWarnings("unchecked") DepotMarshaller<T> marshaller =
|
||||
(DepotMarshaller<T>)_marshallers.get(type);
|
||||
if (marshaller == null) {
|
||||
_marshallers.put(type, marshaller = new DepotMarshaller<T>(type, this));
|
||||
}
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal invoke method that takes care of transient retries for both queries and modifiers.
|
||||
*/
|
||||
protected <T> T invoke (Operation<T> op, boolean retryOnTransientFailure)
|
||||
throws DatabaseException
|
||||
{
|
||||
checkAreInitialized(); // le check du sanity
|
||||
|
||||
boolean isReadOnly = !(op instanceof Modifier);
|
||||
Connection conn;
|
||||
try {
|
||||
conn = _conprov.getConnection(_ident, isReadOnly);
|
||||
} catch (PersistenceException pe) {
|
||||
throw new DatabaseException(pe.getMessage(), pe.getCause());
|
||||
}
|
||||
|
||||
// TEMP: we synchronize on the connection to cooperate with SimpleRepository when used in
|
||||
// conjunction with a StaticConnectionProvider; at some point we'll switch to standard JDBC
|
||||
// connection pooling which will block in getConnection() instead of returning a connection
|
||||
// that someone else may be using
|
||||
synchronized (conn) {
|
||||
try {
|
||||
// if this becomes more complex than this single statement, then this should turn
|
||||
// into a method call that contains the complexity
|
||||
return op.invoke(conn, _liaison);
|
||||
|
||||
} catch (SQLException sqe) {
|
||||
if (!isReadOnly) {
|
||||
// convert this exception to a DuplicateKeyException if appropriate
|
||||
if (_liaison.isDuplicateRowException(sqe)) {
|
||||
throw new DuplicateKeyException(sqe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// let the provider know that the connection failed
|
||||
_conprov.connectionFailed(_ident, isReadOnly, conn, sqe);
|
||||
conn = null;
|
||||
|
||||
if (retryOnTransientFailure && _liaison.isTransientException(sqe)) {
|
||||
// the MySQL JDBC driver has the annoying habit of including the embedded
|
||||
// exception stack trace in the message of their outer exception; if I want a
|
||||
// fucking stack trace, I'll call printStackTrace() thanksverymuch
|
||||
String msg = StringUtil.split(String.valueOf(sqe), "\n")[0];
|
||||
log.info("Transient failure executing op, retrying [error=" + msg + "].");
|
||||
|
||||
} else {
|
||||
throw new DatabaseException("Operation failure " + op, sqe);
|
||||
}
|
||||
|
||||
} finally {
|
||||
_conprov.releaseConnection(_ident, isReadOnly, conn);
|
||||
}
|
||||
}
|
||||
|
||||
// if we got here, we want to retry a transient failure
|
||||
return invoke(op, false);
|
||||
}
|
||||
|
||||
protected void checkAreInitialized ()
|
||||
{
|
||||
if (_conprov == null) {
|
||||
throw new IllegalStateException(
|
||||
"This persistence context has not yet been initialized. You are probably " +
|
||||
"doing something too early, like in a repository's constructor. Don't do that.");
|
||||
}
|
||||
}
|
||||
|
||||
protected String _ident;
|
||||
protected ConnectionProvider _conprov;
|
||||
protected DatabaseLiaison _liaison;
|
||||
protected boolean _warnOnLazyInit;
|
||||
|
||||
/** The object through which all our caching is relayed, or null, for no caching. */
|
||||
protected CacheAdapter _cache;
|
||||
|
||||
/** Tracks repositories during the pre-initialization phase. */
|
||||
protected List<DepotRepository> _repositories = new ArrayList<DepotRepository>();
|
||||
|
||||
/** A mapping from persistent record class to resolved marshaller. */
|
||||
protected Map<Class<?>, DepotMarshaller<?>> _marshallers =
|
||||
new HashMap<Class<?>, DepotMarshaller<?>>();
|
||||
|
||||
/** A mapping of cache listeners by cache id. */
|
||||
protected Map<String, Set<CacheListener<?>>> _listenerSets =
|
||||
new HashMap<String, Set<CacheListener<?>>>();
|
||||
}
|
||||
@@ -1,41 +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.io.Serializable;
|
||||
|
||||
/**
|
||||
* The base class for all persistent records used in Depot. Persistent records must be cloneable
|
||||
* and serializable; this class is used to enforce those requirements.
|
||||
*/
|
||||
public class PersistentRecord
|
||||
implements Cloneable, Serializable
|
||||
{
|
||||
@Override // from Object
|
||||
public PersistentRecord clone ()
|
||||
{
|
||||
try {
|
||||
return (PersistentRecord) super.clone();
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
throw new RuntimeException(cnse); // this should never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
//
|
||||
// $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.Connection;
|
||||
import java.sql.Date;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
import com.samskivert.jdbc.LiaisonRegistry;
|
||||
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.annotation.FullTextIndex;
|
||||
import com.samskivert.jdbc.depot.expression.EpochSeconds;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.FullTextMatch;
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import static com.samskivert.Log.log;
|
||||
|
||||
public class PostgreSQLBuilder
|
||||
extends SQLBuilder
|
||||
{
|
||||
public class PGBuildVisitor extends BuildVisitor
|
||||
{
|
||||
@Override public void visit (FullTextMatch match) {
|
||||
appendIdentifier("ftsCol_" + match.getName());
|
||||
_builder.append(" @@ TO_TSQUERY('default', ?)");
|
||||
}
|
||||
|
||||
@Override public void visit (EpochSeconds epochSeconds) {
|
||||
_builder.append("date_part('epoch', ");
|
||||
epochSeconds.getArgument().accept(this);
|
||||
_builder.append(")");
|
||||
}
|
||||
|
||||
// TODO: enable when we can require 1.6 support
|
||||
// @Override public void visit (In in) {
|
||||
// in.getColumn().accept(this);
|
||||
// _builder.append(" = any (?)");
|
||||
// }
|
||||
|
||||
protected PGBuildVisitor (DepotTypes types) {
|
||||
super(types);
|
||||
}
|
||||
|
||||
@Override protected void appendIdentifier (String field) {
|
||||
_builder.append("\"").append(field).append("\"");
|
||||
}
|
||||
}
|
||||
|
||||
public class PGBindVisitor extends BindVisitor
|
||||
{
|
||||
@Override public void visit (FullTextMatch match) {
|
||||
// The tsearch2 engine takes queries on the form
|
||||
// (foo&bar)|goop
|
||||
// so in this first simple implementation, we just take the user query, chop it into
|
||||
// words by space/punctuation and 'or' those together like so:
|
||||
// 'ho! who goes there?' -> 'ho|who|goes|there'
|
||||
String[] searchTerms = match.getQuery().toLowerCase().split("\\W+");
|
||||
if (searchTerms.length > 0 && searchTerms[0].length() == 0) {
|
||||
searchTerms = ArrayUtil.splice(searchTerms, 0, 1);
|
||||
}
|
||||
String query = StringUtil.join(searchTerms, "|");
|
||||
try {
|
||||
_stmt.setString(_argIdx++, query);
|
||||
} catch (SQLException sqe) {
|
||||
throw new DatabaseException("Failed to configure full-text match column " +
|
||||
"[idx=" + (_argIdx-1) + ", query=" + query + "]", sqe);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: enable when we can require 1.6 support
|
||||
// @Override public void visit (In in) {
|
||||
// Comparable<?>[] values = in.getValues();
|
||||
// try {
|
||||
// _stmt.setObject(
|
||||
// _argIdx++, _conn.createArrayOf(getElementType(values), (Object[])values));
|
||||
// } catch (SQLException sqe) {
|
||||
// throw new DatabaseException(
|
||||
// "Failed to write value to statement [idx=" + (_argIdx-1) +
|
||||
// ", values=" + StringUtil.safeToString(values) + "]", sqe);
|
||||
// }
|
||||
// }
|
||||
|
||||
// protected String getElementType (Comparable<?>[] values) {
|
||||
// if (values instanceof Integer[]) {
|
||||
// return "integer";
|
||||
// } else if (values instanceof String[]) {
|
||||
// return "character varying";
|
||||
// } else {
|
||||
// throw new DatabaseException(
|
||||
// "Don't know how to make Postgres array for " + values.getClass());
|
||||
// }
|
||||
// }
|
||||
|
||||
protected PGBindVisitor (DepotTypes types, Connection conn, PreparedStatement stmt) {
|
||||
super(types, conn, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
public PostgreSQLBuilder (DepotTypes types)
|
||||
{
|
||||
super(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getFtsIndexes (
|
||||
Iterable<String> columns, Iterable<String> indexes, Set<String> target)
|
||||
{
|
||||
for (String column : columns) {
|
||||
if (column.startsWith("ftsCol_")) {
|
||||
target.add(column.substring("ftsCol_".length()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PersistentRecord> boolean addFullTextSearch (
|
||||
Connection conn, DepotMarshaller<T> marshaller, FullTextIndex fts)
|
||||
throws SQLException
|
||||
{
|
||||
Class<T> pClass = marshaller.getPersistentClass();
|
||||
DatabaseLiaison liaison = LiaisonRegistry.getLiaison(conn);
|
||||
|
||||
String[] fields = fts.fields();
|
||||
|
||||
String table = marshaller.getTableName();
|
||||
String column = "ftsCol_" + fts.name();
|
||||
String index = table + "_ftsIx_" + fts.name();
|
||||
String trigger = table + "_ftsTrig_" + fts.name();
|
||||
|
||||
// build the UPDATE
|
||||
StringBuilder initColumn = new StringBuilder("UPDATE ").
|
||||
append(liaison.tableSQL(table)).append(" SET ").append(liaison.columnSQL(column)).
|
||||
append(" = TO_TSVECTOR('default', ");
|
||||
|
||||
for (int ii = 0; ii < fields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
initColumn.append(" || ' ' || ");
|
||||
}
|
||||
initColumn.append("COALESCE(").
|
||||
append(liaison.columnSQL(_types.getColumnName(pClass, fields[ii]))).
|
||||
append(", '')");
|
||||
}
|
||||
initColumn.append(")");
|
||||
|
||||
// build the CREATE TRIGGER
|
||||
StringBuilder createTrigger = new StringBuilder("CREATE TRIGGER ").
|
||||
append(liaison.columnSQL(trigger)).append(" BEFORE UPDATE OR INSERT ON ").
|
||||
append(liaison.tableSQL(table)).append(" FOR EACH ROW EXECUTE PROCEDURE tsearch2(").
|
||||
append(liaison.columnSQL(column)).append(", ");
|
||||
|
||||
for (int ii = 0; ii < fields.length; ii ++) {
|
||||
if (ii > 0) {
|
||||
createTrigger.append(", ");
|
||||
}
|
||||
createTrigger.append(liaison.columnSQL(_types.getColumnName(pClass, fields[ii])));
|
||||
}
|
||||
createTrigger.append(")");
|
||||
|
||||
// build the CREATE INDEX
|
||||
StringBuilder createIndex = new StringBuilder("CREATE INDEX ").
|
||||
append(liaison.columnSQL(index)).append(" ON " ).append(liaison.tableSQL(table)).
|
||||
append(" USING GIST(").append(liaison.columnSQL(column)).append(")");
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
log.info(
|
||||
"Adding full-text search column, index and trigger: " + column + ", " +
|
||||
index + ", " + trigger);
|
||||
liaison.addColumn(conn, table, column, "TSVECTOR", true);
|
||||
stmt.executeUpdate(initColumn.toString());
|
||||
stmt.executeUpdate(createIndex.toString());
|
||||
stmt.executeUpdate(createTrigger.toString());
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrivateColumn (String column)
|
||||
{
|
||||
// filter out any column that we created as part of FTS support
|
||||
return column.startsWith("ftsCol_");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BuildVisitor getBuildVisitor ()
|
||||
{
|
||||
return new PGBuildVisitor(_types);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BindVisitor getBindVisitor (Connection conn, PreparedStatement stmt)
|
||||
{
|
||||
return new PGBindVisitor(_types, conn, stmt);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> String getColumnType (FieldMarshaller<?> fm, int length)
|
||||
{
|
||||
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 "REAL";
|
||||
} else if (fm instanceof DoubleMarshaller) {
|
||||
return "DOUBLE PRECISION";
|
||||
} 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)) {
|
||||
if (length < (1 << 15)) {
|
||||
return "VARCHAR(" + length + ")";
|
||||
}
|
||||
return "TEXT";
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +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.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.depot.PersistenceContext.CacheListener;
|
||||
|
||||
/**
|
||||
* The base of all read-only queries.
|
||||
*/
|
||||
public interface Query<T> extends Operation<T>
|
||||
{
|
||||
/** A simple base class for non-complex queries. */
|
||||
public abstract class Trivial<T> implements Query<T>
|
||||
{
|
||||
public abstract T invoke (Connection conn, DatabaseLiaison liaison) throws SQLException;
|
||||
|
||||
public CacheKey getCacheKey () {
|
||||
return null;
|
||||
}
|
||||
|
||||
public T transformCacheHit (CacheKey key, T value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void updateCache (PersistenceContext ctx, T result) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Any query may elect to utilize the built-in cache by returning a non-null {@link CacheKey}
|
||||
* in this method. This is done automatically by the {@link DepotRepository} when looking up
|
||||
* single entities by primary key, but even entire collections can be cached under a single
|
||||
* key.
|
||||
*
|
||||
* Great care must be taken to invalidate such cached collections when their constituent
|
||||
* entities are invalidated. This is generally done using {@link CacheListener} and
|
||||
* {@link CacheInvalidator}.
|
||||
*/
|
||||
public CacheKey getCacheKey ();
|
||||
|
||||
/**
|
||||
* Overriden by subclasses to perform special operations when the query would return a cache
|
||||
* hit. The value may be mutated, modified, or null may be return to force a database hit.
|
||||
*/
|
||||
public T transformCacheHit (CacheKey key, T value);
|
||||
|
||||
/**
|
||||
* Overriden by subclasses to perform case-by-case cache updates.
|
||||
*/
|
||||
public void updateCache (PersistenceContext ctx, T result);
|
||||
}
|
||||
@@ -1,230 +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.lang.reflect.Field;
|
||||
import java.util.Set;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.ColumnDefinition;
|
||||
import com.samskivert.jdbc.depot.annotation.Column;
|
||||
import com.samskivert.jdbc.depot.annotation.FullTextIndex;
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
import com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
import static com.samskivert.jdbc.depot.Log.log;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
_clause.accept(_buildVisitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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} and {@link Modifier#invoke}.
|
||||
*/
|
||||
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(conn, stmt);
|
||||
_clause.accept(_bindVisitor);
|
||||
|
||||
if (PersistenceContext.DEBUG) {
|
||||
log.info("SQL: " + stmt.toString());
|
||||
}
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
protected String nullify (String str) {
|
||||
return (str != null && str.length() > 0) ? str : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ColumnDefinition buildColumnDefinition (FieldMarshaller<?> fm)
|
||||
{
|
||||
// if this field is @Computed, it has no SQL definition
|
||||
if (fm.getComputed() != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Field field = fm.getField();
|
||||
|
||||
String type = null;
|
||||
boolean nullable = false;
|
||||
boolean unique = false;
|
||||
String defaultValue = null;
|
||||
int length = 255;
|
||||
|
||||
Column column = field.getAnnotation(Column.class);
|
||||
if (column != null) {
|
||||
type = nullify(column.type());
|
||||
nullable = column.nullable();
|
||||
unique = column.unique();
|
||||
defaultValue = nullify(column.defaultValue());
|
||||
length = column.length();
|
||||
}
|
||||
|
||||
// handle primary keyness
|
||||
GeneratedValue genValue = fm.getGeneratedValue();
|
||||
if (genValue != null) {
|
||||
switch (genValue.strategy()) {
|
||||
case AUTO:
|
||||
case IDENTITY:
|
||||
type = "SERIAL";
|
||||
unique = 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 (type == null) {
|
||||
type = getColumnType(fm, length);
|
||||
}
|
||||
|
||||
// sanity check nullability
|
||||
if (nullable && field.getType().isPrimitive()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Primitive Java type cannot be nullable [field=" + field.getName() + "]");
|
||||
}
|
||||
|
||||
// Java primitive types cannot be null, so we provide a default value for these columns
|
||||
// that matches Java's default for primitive types; however, if the column has a generated
|
||||
// value, don't provide a default because that will anger the database Gods
|
||||
if (defaultValue == null && genValue == null) {
|
||||
if (field.getType().equals(Byte.TYPE) ||
|
||||
field.getType().equals(Short.TYPE) ||
|
||||
field.getType().equals(Integer.TYPE) ||
|
||||
field.getType().equals(Long.TYPE) ||
|
||||
field.getType().equals(Float.TYPE) ||
|
||||
field.getType().equals(Double.TYPE) ||
|
||||
ByteEnum.class.isAssignableFrom(field.getType())) {
|
||||
defaultValue = "0";
|
||||
|
||||
} else if (field.getType().equals(Boolean.TYPE)) {
|
||||
defaultValue = getBooleanDefault();
|
||||
}
|
||||
}
|
||||
|
||||
return new ColumnDefinition(type, nullable, unique, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add full-text search capabilities, as defined by the provided {@link FullTextIndex}, on
|
||||
* the table associated with the given {@link DepotMarshaller}. This is a highly database
|
||||
* specific operation and must thus be implemented by each dialect subclass.
|
||||
*
|
||||
* @see FullTextIndex
|
||||
*/
|
||||
public abstract <T extends PersistentRecord> boolean addFullTextSearch (
|
||||
Connection conn, DepotMarshaller<T> marshaller, FullTextIndex fts)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Return true if the supplied column is an internal consideration of this {@link SQLBuilder},
|
||||
* e.g. PostgreSQL's full text search data is stored in a table column that should otherwise
|
||||
* not be visible to Depot; this method helps mask it.
|
||||
*/
|
||||
public abstract boolean isPrivateColumn (String column);
|
||||
|
||||
/**
|
||||
* Figure out what full text search indexes already exist on this table and add the names of
|
||||
* those indexes to the supplied target set.
|
||||
*/
|
||||
public abstract void getFtsIndexes (
|
||||
Iterable<String> columns, Iterable<String> indexes, Set<String> target);
|
||||
|
||||
/**
|
||||
* Returns the boolean literal that corresponds to false.
|
||||
*/
|
||||
protected String getBooleanDefault ()
|
||||
{
|
||||
return "false";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (Connection conn, PreparedStatement stmt);
|
||||
|
||||
/**
|
||||
* Overridden by subclasses to figure the dialect-specific SQL type of the given field.
|
||||
* @param length
|
||||
*/
|
||||
protected abstract <T> String getColumnType (FieldMarshaller<?> fm, int length);
|
||||
|
||||
/** The class that maps persistent classes to marshallers. */
|
||||
protected DepotTypes _types;
|
||||
|
||||
protected QueryClause _clause;
|
||||
protected BuildVisitor _buildVisitor;
|
||||
protected BindVisitor _bindVisitor;
|
||||
}
|
||||
@@ -1,246 +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.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.jdbc.ColumnDefinition;
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.depot.annotation.Column;
|
||||
|
||||
import static com.samskivert.jdbc.depot.Log.log;
|
||||
|
||||
/**
|
||||
* Encapsulates the migration of a persistent record's database schema. These can be registered
|
||||
* with the {@link PersistenceContext} to effect hand-coded migrations between entity versions. The
|
||||
* modifier should override {@link #invoke} to perform its migrations. See {@link
|
||||
* PersistenceContext#registerMigration} for details on the migration process.
|
||||
*
|
||||
* <p> Note: these should only be used for actual schema changes (column additions, removals,
|
||||
* renames, retypes, etc.). It should not be used for data migration, use {@link DataMigration} for
|
||||
* that.
|
||||
*/
|
||||
public abstract class SchemaMigration extends Modifier
|
||||
{
|
||||
/**
|
||||
* A convenient migration for dropping a column from an entity.
|
||||
*/
|
||||
public static class Drop extends SchemaMigration
|
||||
{
|
||||
public Drop (int targetVersion, String columnName) {
|
||||
super(targetVersion);
|
||||
_columnName = columnName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
if (!liaison.tableContainsColumn(conn, _tableName, _columnName)) {
|
||||
// we'll accept this inconsistency
|
||||
log.warning(_tableName + "." + _columnName + " already dropped.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log.info("Dropping '" + _columnName + "' from " + _tableName);
|
||||
return liaison.dropColumn(conn, _tableName, _columnName) ? 1 : 0;
|
||||
}
|
||||
|
||||
protected String _columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient migration for renaming a column in an entity.
|
||||
*/
|
||||
public static class Rename extends SchemaMigration
|
||||
{
|
||||
public Rename (int targetVersion, String oldColumnName, String newColumnName) {
|
||||
super(targetVersion);
|
||||
_oldColumnName = oldColumnName;
|
||||
_newColumnName = newColumnName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
if (!liaison.tableContainsColumn(conn, _tableName, _oldColumnName)) {
|
||||
if (liaison.tableContainsColumn(conn, _tableName, _newColumnName)) {
|
||||
// we'll accept this inconsistency
|
||||
log.warning(_tableName + "." + _oldColumnName + " already renamed to " +
|
||||
_newColumnName + ".");
|
||||
return 0;
|
||||
}
|
||||
// but this is not OK
|
||||
throw new IllegalArgumentException(
|
||||
_tableName + " does not contain '" + _oldColumnName + "'");
|
||||
}
|
||||
|
||||
// nor is this
|
||||
if (liaison.tableContainsColumn(conn, _tableName, _newColumnName)) {
|
||||
throw new IllegalArgumentException(
|
||||
_tableName + " already contains '" + _newColumnName + "'");
|
||||
}
|
||||
|
||||
log.info("Renaming '" + _oldColumnName + "' to '" + _newColumnName + "' in: " +
|
||||
_tableName);
|
||||
return liaison.renameColumn(
|
||||
conn, _tableName, _oldColumnName, _newColumnName, _newColumnDef) ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override public boolean runBeforeDefault () {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init (String tableName, Map<String, FieldMarshaller<?>> marshallers) {
|
||||
super.init(tableName, marshallers);
|
||||
FieldMarshaller<?> fm = marshallers.get(_newColumnName);
|
||||
if (fm == null) {
|
||||
throw new IllegalArgumentException(
|
||||
_tableName + " does not contain '" + _newColumnName + "' field.");
|
||||
}
|
||||
_newColumnDef = fm.getColumnDefinition();
|
||||
}
|
||||
|
||||
protected String _oldColumnName, _newColumnName;
|
||||
protected ColumnDefinition _newColumnDef;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 SchemaMigration
|
||||
{
|
||||
public Retype (int targetVersion, String fieldName) {
|
||||
super(targetVersion);
|
||||
_fieldName = fieldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
log.info("Updating type of '" + _fieldName + "' in " + _tableName);
|
||||
return liaison.changeColumn(conn, _tableName, _fieldName, _newColumnDef.getType(),
|
||||
_newColumnDef.isNullable(), _newColumnDef.isUnique(),
|
||||
_newColumnDef.getDefaultValue()) ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override public boolean runBeforeDefault () {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init (String tableName, Map<String, FieldMarshaller<?>> marshallers) {
|
||||
super.init(tableName, marshallers);
|
||||
_columnName = marshallers.get(_fieldName).getColumnName();
|
||||
_newColumnDef = marshallers.get(_fieldName).getColumnDefinition();
|
||||
}
|
||||
|
||||
protected String _fieldName, _columnName;
|
||||
protected ColumnDefinition _newColumnDef;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient migration for adding a new column that requires a default value to be specified
|
||||
* during the addition. Normally Depot will automatically handle column addition, but if you
|
||||
* have a column that normally does not have a default value but needs one when it is added to
|
||||
* a table with existing rows, you can use this migration.
|
||||
*
|
||||
* @see Column#defaultValue
|
||||
*/
|
||||
public static class Add extends SchemaMigration
|
||||
{
|
||||
public Add (int targetVersion, String fieldName, String defaultValue) {
|
||||
super(targetVersion);
|
||||
_fieldName = fieldName;
|
||||
_defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
|
||||
// override the default value in the column definition with the one provided
|
||||
ColumnDefinition defColumnDef = new ColumnDefinition(
|
||||
_newColumnDef.getType(), _newColumnDef.isNullable(),
|
||||
_newColumnDef.isUnique(), _defaultValue);
|
||||
// first add the column with the overridden default value
|
||||
if (liaison.addColumn(conn, _tableName, _fieldName, defColumnDef, true)) {
|
||||
// then change the column to the permanent default value
|
||||
liaison.changeColumn(conn, _tableName, _fieldName, _newColumnDef.getType(),
|
||||
null, null, _newColumnDef.getDefaultValue());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public boolean runBeforeDefault () {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init (String tableName, Map<String, FieldMarshaller<?>> marshallers) {
|
||||
super.init(tableName, marshallers);
|
||||
_columnName = marshallers.get(_fieldName).getColumnName();
|
||||
_newColumnDef = marshallers.get(_fieldName).getColumnDefinition();
|
||||
}
|
||||
|
||||
protected String _fieldName, _columnName, _defaultValue;
|
||||
protected ColumnDefinition _newColumnDef;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this method returns true, this migration will be run <b>before</b> the default
|
||||
* migrations, if false it will be run after.
|
||||
*/
|
||||
public boolean runBeforeDefault ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* When an Entity is being migrated, this method will be called to check whether this migration
|
||||
* should be run. The default implementation runs as long as the currentVersion is less than
|
||||
* the target version supplied to the migration at construct time.
|
||||
*/
|
||||
public boolean shouldRunMigration (int currentVersion, int targetVersion)
|
||||
{
|
||||
return (currentVersion < _targetVersion);
|
||||
}
|
||||
|
||||
protected SchemaMigration (int targetVersion)
|
||||
{
|
||||
super();
|
||||
_targetVersion = targetVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called to provide the migration with the name of the entity table and access to its
|
||||
* field marshallers prior to being invoked. This will <em>only</em> be called after this
|
||||
* migration has been determined to be runnable so one cannot rely on this method having been
|
||||
* called in {@link #shouldRunMigration}.
|
||||
*/
|
||||
protected void init (String tableName, Map<String, FieldMarshaller<?>> marshallers)
|
||||
{
|
||||
_tableName = tableName;
|
||||
}
|
||||
|
||||
protected int _targetVersion;
|
||||
protected String _tableName;
|
||||
}
|
||||
@@ -1,113 +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.io.Serializable;
|
||||
|
||||
/**
|
||||
* Convenience class that implements {@link CacheKey} as simply as possibly. This class is
|
||||
* typically used when the caller wants to cache a non-obvious query such as a collection,
|
||||
* and needs to specify their own cache key and file it under a hand-picked cache id.
|
||||
*/
|
||||
public class SimpleCacheKey
|
||||
implements CacheKey
|
||||
{
|
||||
/**
|
||||
* Construct a {@link SimpleCacheKey} for a query that has no parameters whatsoever.
|
||||
*/
|
||||
public SimpleCacheKey (String cacheId)
|
||||
{
|
||||
this(cacheId, Boolean.TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link SimpleCacheKey} associated with the given persistent class with
|
||||
* the given cache key.
|
||||
*/
|
||||
public SimpleCacheKey (Class<?> cacheClass, Serializable cacheKey)
|
||||
{
|
||||
this(cacheClass.getName(), cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link SimpleCacheKey} for the given cache id with the given cache key.
|
||||
*/
|
||||
public SimpleCacheKey (String cacheId, Serializable value)
|
||||
{
|
||||
_cacheId = cacheId;
|
||||
_cacheKey = value;
|
||||
}
|
||||
|
||||
// from CacheKey
|
||||
public String getCacheId ()
|
||||
{
|
||||
return _cacheId;
|
||||
}
|
||||
|
||||
// from CacheKey
|
||||
public Serializable getCacheKey ()
|
||||
{
|
||||
return _cacheKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
final int PRIME = 31;
|
||||
int result = 1;
|
||||
result = PRIME * result + ((_cacheId == null) ? 0 : _cacheId.hashCode());
|
||||
result = PRIME * result + ((_cacheKey == null) ? 0 : _cacheKey.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj)
|
||||
{
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
SimpleCacheKey other = (SimpleCacheKey) obj;
|
||||
if (_cacheId == null) {
|
||||
if (other._cacheId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!_cacheId.equals(other._cacheId)) {
|
||||
return false;
|
||||
}
|
||||
if (_cacheKey == null) {
|
||||
if (other._cacheKey != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!_cacheKey.equals(other._cacheKey)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return "[cacheId=" + _cacheId + ", value=" + _cacheKey + "]";
|
||||
}
|
||||
|
||||
protected String _cacheId;
|
||||
protected Serializable _cacheKey;
|
||||
}
|
||||
@@ -1,174 +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.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
import com.samskivert.jdbc.depot.annotation.TableGenerator;
|
||||
|
||||
import com.samskivert.jdbc.ColumnDefinition;
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
|
||||
/**
|
||||
* Generates primary keys using an external table .
|
||||
*/
|
||||
public class TableValueGenerator extends ValueGenerator
|
||||
{
|
||||
public TableValueGenerator (
|
||||
TableGenerator tg, GeneratedValue gv, DepotMarshaller<?> dm, FieldMarshaller<?> fm)
|
||||
{
|
||||
super(gv, dm, fm);
|
||||
_valueTable = defStr(tg.table(), "IdSequences");
|
||||
_pkColumnName = defStr(tg.pkColumnName(), "sequence");
|
||||
_pkColumnValue = defStr(tg.pkColumnValue(), "default");
|
||||
_valueColumnName = defStr(tg.valueColumnName(), "value");
|
||||
}
|
||||
|
||||
@Override // from ValueGenerator
|
||||
public boolean isPostFactum ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override // from ValueGenerator
|
||||
public void create (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
// make sure our table exists
|
||||
liaison.createTableIfMissing(
|
||||
conn, _valueTable,
|
||||
new String[] { _pkColumnName, _valueColumnName },
|
||||
new ColumnDefinition[] {
|
||||
new ColumnDefinition("VARCHAR(255)", true, false, null),
|
||||
new ColumnDefinition("INTEGER")
|
||||
},
|
||||
null,
|
||||
new String[] { _pkColumnName });
|
||||
|
||||
// and also that there's a row in it for us
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
stmt = conn.prepareStatement(
|
||||
" SELECT * FROM " + liaison.tableSQL(_valueTable) +
|
||||
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ?");
|
||||
stmt.setString(1, _pkColumnValue);
|
||||
if (stmt.executeQuery().next()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JDBCUtil.close(stmt);
|
||||
stmt = null;
|
||||
|
||||
int initialValue = _initialValue;
|
||||
if (_migrateIfExists) {
|
||||
Integer max = getFieldMaximum(conn, liaison);
|
||||
if (max != null) {
|
||||
initialValue = 1 + max.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
stmt = conn.prepareStatement(
|
||||
" INSERT INTO " + liaison.tableSQL(_valueTable) + " (" +
|
||||
liaison.columnSQL(_pkColumnName) + ", " + liaison.columnSQL(_valueColumnName) +
|
||||
") VALUES (?, ?)");
|
||||
stmt.setString(1, _pkColumnValue);
|
||||
stmt.setInt(2, initialValue);
|
||||
stmt.executeUpdate();
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from ValueGenerator
|
||||
public void delete (Connection conn, DatabaseLiaison liaison) throws SQLException
|
||||
{
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
stmt = conn.prepareStatement(
|
||||
" DELETE FROM " + liaison.tableSQL(_valueTable) +
|
||||
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ?");
|
||||
stmt.setString(1, _pkColumnValue);
|
||||
stmt.executeUpdate();
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from ValueGenerator
|
||||
public int nextGeneratedValue (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
// TODO: Make this lockless!
|
||||
String query =
|
||||
" SELECT " + liaison.columnSQL(_valueColumnName) +
|
||||
" FROM " + liaison.tableSQL(_valueTable) +
|
||||
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ? FOR UPDATE";
|
||||
stmt = conn.prepareStatement(query);
|
||||
stmt.setString(1, _pkColumnValue);
|
||||
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
if (!rs.next()) {
|
||||
throw new SQLException("Failed to find next primary key value " +
|
||||
"[table=" + _valueTable + ", column=" + _valueColumnName +
|
||||
", where=" + _pkColumnName + "=" + _pkColumnValue + "]");
|
||||
}
|
||||
int val = rs.getInt(1);
|
||||
JDBCUtil.close(stmt);
|
||||
|
||||
stmt = conn.prepareStatement(
|
||||
" UPDATE " + liaison.tableSQL(_valueTable) +
|
||||
" SET " + liaison.columnSQL(_valueColumnName) + " = ? " +
|
||||
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ?");
|
||||
stmt.setInt(1, val + _allocationSize);
|
||||
stmt.setString(2, _pkColumnValue);
|
||||
stmt.executeUpdate();
|
||||
return val;
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to return a value or a default fallback.
|
||||
*/
|
||||
protected static String defStr (String value, String def)
|
||||
{
|
||||
if (value == null || value.trim().length() == 0) {
|
||||
return def;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected String _valueTable;
|
||||
protected String _pkColumnName;
|
||||
protected String _pkColumnValue;
|
||||
protected String _valueColumnName;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 2001-2007 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;
|
||||
|
||||
/**
|
||||
* An augmented cache invalidator interface for invalidators that can ensure that they are
|
||||
* operating on the proper persistent record class.
|
||||
*/
|
||||
public interface ValidatingCacheInvalidator extends CacheInvalidator
|
||||
{
|
||||
/**
|
||||
* Validates that this invalidator operates on the supplied persistent record class. This helps
|
||||
* to catch programmer errors where one record type is used for a query clause and another is
|
||||
* used for the cache invalidator.
|
||||
*
|
||||
* @exception IllegalArgumentException thrown if the supplied persistent record class does not
|
||||
* match the class that this invalidator will flush from the cache.
|
||||
*/
|
||||
public void validateFlushType (Class<?> pClass);
|
||||
}
|
||||
@@ -1,124 +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.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import com.samskivert.jdbc.DatabaseLiaison;
|
||||
import com.samskivert.jdbc.JDBCUtil;
|
||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||
|
||||
import static com.samskivert.jdbc.depot.Log.log;
|
||||
|
||||
/**
|
||||
* Defines the interface to our value generators.
|
||||
*/
|
||||
public abstract class ValueGenerator
|
||||
{
|
||||
public ValueGenerator (GeneratedValue gv, DepotMarshaller<?> dm, FieldMarshaller<?> fm)
|
||||
{
|
||||
_allocationSize = gv.allocationSize();
|
||||
_initialValue = gv.initialValue();
|
||||
_migrateIfExists = gv.migrateIfExists();
|
||||
_dm = dm;
|
||||
_fm = fm;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, this key generator will be run after the insert statement, if false, it will be run
|
||||
* before.
|
||||
*/
|
||||
public abstract boolean isPostFactum ();
|
||||
|
||||
/**
|
||||
* Ensures the generator is prepared for operation, creating it if necessary. Care is taken to
|
||||
* only run this the first time a column is created. However, if a column with a value
|
||||
* generator is renamed, we can't distinguish that from a newly created column and we call this
|
||||
* method again on the renamed column. If it's possible to not fail in that circumstance, try
|
||||
* to avoid doing so.
|
||||
*/
|
||||
public abstract void create (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Fetch/generate the next primary key value.
|
||||
*/
|
||||
public abstract int nextGeneratedValue (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Delete all database entities associated with this value generator.
|
||||
*/
|
||||
public abstract void delete (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* Scans the table associated with this {@link ValueGenerator} and returns either null, if
|
||||
* there are no rows, or an {@link Integer} containing the largest numerical value our
|
||||
* field attains.
|
||||
*/
|
||||
protected Integer getFieldMaximum (Connection conn, DatabaseLiaison liaison)
|
||||
throws SQLException
|
||||
{
|
||||
String column = _fm.getColumnName();
|
||||
String table = _dm.getTableName();
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
try {
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
" SELECT COUNT(*), MAX(" + liaison.columnSQL(column) + ") " +
|
||||
" FROM " + liaison.tableSQL(table));
|
||||
if (!rs.next()) {
|
||||
log.warning("Query on count()/max() bizarrely returned no rows.");
|
||||
return null;
|
||||
}
|
||||
|
||||
int cnt = rs.getInt(1);
|
||||
if (cnt > 0) {
|
||||
return Integer.valueOf(rs.getInt(2));
|
||||
}
|
||||
return null;
|
||||
|
||||
} finally {
|
||||
JDBCUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
public DepotMarshaller<?> getDepotMarshaller ()
|
||||
{
|
||||
return _dm;
|
||||
}
|
||||
|
||||
public FieldMarshaller<?> getFieldMarshaller ()
|
||||
{
|
||||
return _fm;
|
||||
}
|
||||
|
||||
protected int _initialValue;
|
||||
protected int _allocationSize;
|
||||
protected boolean _migrateIfExists;
|
||||
|
||||
protected DepotMarshaller<?> _dm;
|
||||
protected FieldMarshaller<?> _fm;
|
||||
}
|
||||
@@ -1,60 +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 com.samskivert.jdbc.depot.clause.QueryClause;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Currently only exists as a type without any functionality of its own.
|
||||
*/
|
||||
public abstract class WhereClause extends QueryClause
|
||||
{
|
||||
/**
|
||||
* Returns the condition associated with this where clause.
|
||||
*/
|
||||
public abstract SQLExpression getWhereExpression ();
|
||||
|
||||
/**
|
||||
* Validates that the supplied persistent record type is the type matched by this where clause.
|
||||
* Not all clauses will be able to perform this validation, but those that can, should do so to
|
||||
* help alleviate programmer error.
|
||||
*
|
||||
* @exception IllegalArgumentException thrown if the supplied class is known not to by the type
|
||||
* matched by this where clause.
|
||||
*/
|
||||
public void validateQueryType (Class<?> pClass)
|
||||
{
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function for implementing {@link #validateQueryType}.
|
||||
*/
|
||||
protected void validateTypesMatch (Class<?> qClass, Class<?> kClass)
|
||||
{
|
||||
if (!qClass.equals(kClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Class mismatch between persistent record and key in query " +
|
||||
"[qtype=" + qClass.getSimpleName() + ", ktype=" + kClass.getSimpleName() + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Is used to specify a mapped column for a persistent property or field. If no Column annotation
|
||||
* is specified, the default values are applied.
|
||||
*/
|
||||
@Target(value=ElementType.FIELD)
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
public @interface Column
|
||||
{
|
||||
/**
|
||||
* The name of the column. Defaults to the field name.
|
||||
*/
|
||||
String name () default "";
|
||||
|
||||
/**
|
||||
* Whether the property is a unique key. This is a shortcut for the UniqueConstraint annotation
|
||||
* at the table level and is useful for when the unique key constraint is only a single
|
||||
* field. This constraint applies in addition to any constraint entailed by primary key mapping
|
||||
* and to constraints specified at the table level.
|
||||
*/
|
||||
boolean unique () default false;
|
||||
|
||||
/**
|
||||
* Whether the database column is nullable. <em>Note:</em> this default differs from the value
|
||||
* used by the EJB3 persistence framework.
|
||||
*/
|
||||
boolean nullable () default false;
|
||||
|
||||
/**
|
||||
* The SQL type that is used when generating the DDL for the column.
|
||||
*/
|
||||
String type() default "";
|
||||
|
||||
/**
|
||||
* The column length. (Applies to String and byte[] columns.)
|
||||
*/
|
||||
int length () default 255;
|
||||
|
||||
/**
|
||||
* The SQL literal value to be used when defining this column's default value. The value must
|
||||
* be quoted and escaped if it is not a SQL primitive datatype. For example:
|
||||
* <code>'2006-01-01'</code> or <code>25</code> or <code>NULL</code>.
|
||||
*/
|
||||
String defaultValue () default "";
|
||||
}
|
||||
@@ -1,64 +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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.clause.FieldOverride;
|
||||
import com.samskivert.jdbc.depot.clause.FromOverride;
|
||||
import com.samskivert.jdbc.depot.clause.Join;
|
||||
|
||||
/**
|
||||
* Marks a field as computed, meaning it is ignored for schema purposes and it does not directly
|
||||
* correspond to a column in a table.
|
||||
*/
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.TYPE })
|
||||
public @interface Computed
|
||||
{
|
||||
/** If this value is false, the field is not populated at all. */
|
||||
boolean required () default true;
|
||||
|
||||
/** A non-empty value here is taken as literal SQL and used to populate the computed field. */
|
||||
String fieldDefinition () default "";
|
||||
|
||||
/**
|
||||
* A computed record can shadow a concrete record, which causes any field the former has in
|
||||
* common with the latter and which is not otherwise overriden to inherit its definition. The
|
||||
* shadowed class may also be given at the field level.
|
||||
*
|
||||
* The purpose of shadowing is largely to avoid having to supply a {@link FieldOverride} when
|
||||
* querying for objects that that contain large subsets of some other persistent object's
|
||||
* fields -- in other words, when you use a computed entity to query only some of the columns
|
||||
* from a table.
|
||||
*
|
||||
* The shadowed class must have been brought into the query using e.g. {@link FromOverride}
|
||||
* or {@link Join} clauses. The referenced fields must be simple concrete columns in a table;
|
||||
* they must themselves be computed or overridden or shadowing.
|
||||
*
|
||||
* TODO: Do in fact let the shadowed field be computed, overriden or shadowing.
|
||||
*/
|
||||
Class<? extends PersistentRecord> shadowOf () default PersistentRecord.class;
|
||||
}
|
||||
@@ -1,48 +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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies the primary field(s) of an entity.
|
||||
*/
|
||||
@Target(value=ElementType.TYPE)
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
public @interface Entity
|
||||
{
|
||||
/** The name of an entity. Defaults to the unqualified name of the entity class. */
|
||||
String name () default "";
|
||||
|
||||
/** Unique constraints that are to be placed on this entity's table. These constraints apply in
|
||||
* addition to any constraints specified by the Column annotation and constraints entailed by
|
||||
* primary key mappings. Defaults to no additional constraints. */
|
||||
UniqueConstraint[] uniqueConstraints () default {};
|
||||
|
||||
/** Indices to add to this entity's table. */
|
||||
Index[] indices () default {};
|
||||
|
||||
/** Full-text search indexes defined on this entity, if any. Defaults to none. */
|
||||
FullTextIndex[] fullTextIndices () default {};
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// samskivert library - useful routines for java programs
|
||||
// Copyright (C) 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.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation is used to specify that a full text index is to be included in the generated DDL
|
||||
* for a table.
|
||||
*/
|
||||
@Target(value={})
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
public @interface FullTextIndex
|
||||
{
|
||||
/**
|
||||
* An identifier for this index, unique with the scope of the record.
|
||||
*/
|
||||
public String name ();
|
||||
|
||||
/**
|
||||
* An array of the field names that should be indexed.
|
||||
*/
|
||||
public String[] fields ();
|
||||
}
|
||||
@@ -1,64 +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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Provides for the specification of generation strategies for the values of primary keys. The
|
||||
* GeneratedValue annotation may be applied to a primary field in conjunction with the {@link Id}
|
||||
* annotation.
|
||||
*/
|
||||
@Target(value=ElementType.FIELD)
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
public @interface GeneratedValue
|
||||
{
|
||||
/** Identifies which generator should be used to generate this value when using a table or
|
||||
* sequence generator. */
|
||||
String generator () default "";
|
||||
|
||||
/** Identifies the strategy to be used to generate this value. */
|
||||
GenerationType strategy () default GenerationType.AUTO;
|
||||
|
||||
/**
|
||||
* The initial value to be used when allocating id numbers from the generator. The default
|
||||
* initial value is 1. <em>Note:</em> this default differs from the value used by the EJB3
|
||||
* persistence framework.
|
||||
*/
|
||||
int initialValue () default 1;
|
||||
|
||||
/**
|
||||
* If there are rows in our corresponding table, this boolean determines whether or not to
|
||||
* attempt to initialize the generator to the maximum value of our associated field over
|
||||
* those rows. This attribute does not exist in the EJB3 framework.
|
||||
*/
|
||||
boolean migrateIfExists () default true;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -1,51 +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.annotation;
|
||||
|
||||
/**
|
||||
* Defines the types of primary key generation.
|
||||
*/
|
||||
public enum GenerationType
|
||||
{
|
||||
/**
|
||||
* Indicates that the persistence provider must assign primary keys for the entity using an
|
||||
* underlying database table to ensure uniqueness.
|
||||
*/
|
||||
TABLE,
|
||||
|
||||
/**
|
||||
* Indicates that the persistence provider must assign primary keys for the entity using
|
||||
* database sequences.
|
||||
*/
|
||||
SEQUENCE,
|
||||
|
||||
/**
|
||||
* Indicates that the persistence provider must assign primary keys for the entity using
|
||||
* database identity column.
|
||||
*/
|
||||
IDENTITY,
|
||||
|
||||
/**
|
||||
* Indicates that the persistence provider should pick an appropriate strategy for the
|
||||
* particular database.
|
||||
*/
|
||||
AUTO;
|
||||
}
|
||||
@@ -1,35 +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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies the primary key property or field of an entity.
|
||||
*/
|
||||
@Target(value=ElementType.FIELD)
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
public @interface Id
|
||||
{
|
||||
}
|
||||
@@ -1,42 +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.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Defines an index on an entity table.
|
||||
*/
|
||||
@Target(value={})
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
public @interface Index
|
||||
{
|
||||
/** Defines the name of the index. */
|
||||
String name ();
|
||||
|
||||
/** Does this index enforce a uniqueness constraint? */
|
||||
boolean unique () default false;
|
||||
|
||||
/** Defines the fields on which the index operates. */
|
||||
String[] fields () default {};
|
||||
}
|
||||
@@ -1,66 +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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation defines a primary key generator that may be referenced by name when a generator
|
||||
* element is specified for the GeneratedValue annotation. A table generator may be specified on
|
||||
* the entity class or on the primary key field. The scope of the generator name is global to the
|
||||
* persistence unit (across all generator types).
|
||||
*/
|
||||
@Target(value={ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
public @interface TableGenerator
|
||||
{
|
||||
/**
|
||||
* A unique generator name that can be referenced by one or more classes to be the
|
||||
* generator for id values.
|
||||
*/
|
||||
String name ();
|
||||
|
||||
/**
|
||||
* Name of table that stores the generated id values. Defaults to a name chosen by persistence
|
||||
* provider.
|
||||
*/
|
||||
String table () default "";
|
||||
|
||||
/**
|
||||
* Name of the primary key column in the table Defaults to a provider-chosen name.
|
||||
*/
|
||||
String pkColumnName () default "";
|
||||
|
||||
/**
|
||||
* Name of the column that stores the last value generated Defaults to a provider-chosen name.
|
||||
*/
|
||||
String valueColumnName () default "";
|
||||
|
||||
/**
|
||||
* The primary key value in the generator table that distinguishes this set of generated values
|
||||
* from others that may be stored in the table Defaults to a provider-chosen value to store in
|
||||
* the primary key column of the generator table
|
||||
*/
|
||||
String pkColumnValue () default "";
|
||||
}
|
||||
@@ -1,36 +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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation specifies that the property or field is not persistent. It is used to annotate
|
||||
* a property or field of an entity class, mapped superclass, or embeddable class.
|
||||
*/
|
||||
@Target(value=ElementType.FIELD)
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
public @interface Transient
|
||||
{
|
||||
}
|
||||
@@ -1,39 +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.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation is used to specify that a unique constraint is to be included in the
|
||||
* generated DDL for a table.
|
||||
*/
|
||||
@Target(value={})
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
public @interface UniqueConstraint
|
||||
{
|
||||
/**
|
||||
* An array of the field names that make up the constraint
|
||||
*/
|
||||
public String[] fieldNames () default {};
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
//
|
||||
// $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)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
/** The type of persistent record on which we operate. */
|
||||
protected Class<? extends PersistentRecord> _pClass;
|
||||
|
||||
/** The where clause. */
|
||||
protected WhereClause _where;
|
||||
}
|
||||
@@ -1,89 +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.clause;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.LiteralExp;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Supplies a definition for a computed field of the persistent object we're creating.
|
||||
*
|
||||
* Thus the select portion of a query can include a reference to a different column in a different
|
||||
* table through a {@link ColumnExp}, or a literal expression such as COUNT(*) through a
|
||||
* {@link LiteralExp}.
|
||||
*
|
||||
* @see FieldOverride
|
||||
*/
|
||||
public class FieldDefinition extends QueryClause
|
||||
{
|
||||
public FieldDefinition (String field, String str)
|
||||
{
|
||||
this(field, new LiteralExp(str));
|
||||
}
|
||||
|
||||
public FieldDefinition (String field, Class<? extends PersistentRecord> pClass, String pCol)
|
||||
{
|
||||
this(field, new ColumnExp(pClass, pCol));
|
||||
}
|
||||
|
||||
public FieldDefinition (String field, SQLExpression override)
|
||||
{
|
||||
_field = field;
|
||||
_definition = override;
|
||||
}
|
||||
|
||||
/**
|
||||
* The field we're defining. The Query object uses this for indexing.
|
||||
*/
|
||||
public String getField ()
|
||||
{
|
||||
return _field;
|
||||
}
|
||||
|
||||
public SQLExpression getDefinition ()
|
||||
{
|
||||
return _definition;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_definition.addClasses(classSet);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor visitor)
|
||||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
/** The name of the field on the persistent object to override. */
|
||||
protected String _field;
|
||||
|
||||
/** The defining expression. */
|
||||
protected SQLExpression _definition;
|
||||
|
||||
}
|
||||
@@ -1,52 +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.clause;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.LiteralExp;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Redirects one field of the persistent object we're creating from its default associated column
|
||||
* to a general {@link SQLExpression}.
|
||||
*
|
||||
* Thus the select portion of a query can include a reference to a different column in a different
|
||||
* table through a {@link ColumnExp}, or a literal expression such as COUNT(*) through a
|
||||
* {@link LiteralExp}.
|
||||
*/
|
||||
public class FieldOverride extends FieldDefinition
|
||||
{
|
||||
public FieldOverride (String field, String str)
|
||||
{
|
||||
super(field, str);
|
||||
}
|
||||
|
||||
public FieldOverride (String field, Class<? extends PersistentRecord> pClass, String pCol)
|
||||
{
|
||||
super(field, pClass, pCol);
|
||||
}
|
||||
|
||||
public FieldOverride (String field, SQLExpression override)
|
||||
{
|
||||
super(field, override);
|
||||
}
|
||||
}
|
||||
@@ -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.clause;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Represents a FOR UPDATE clause.
|
||||
*/
|
||||
public class ForUpdate extends QueryClause
|
||||
{
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,72 +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.clause;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Completely overrides the FROM clause, if it exists.
|
||||
*/
|
||||
public class FromOverride extends QueryClause
|
||||
{
|
||||
public FromOverride (Class<? extends PersistentRecord> fromClass)
|
||||
{
|
||||
_fromClasses.add(fromClass);
|
||||
}
|
||||
|
||||
public FromOverride (Class<? extends PersistentRecord> fromClass1,
|
||||
Class<? extends PersistentRecord> fromClass2)
|
||||
{
|
||||
_fromClasses.add(fromClass1);
|
||||
_fromClasses.add(fromClass2);
|
||||
}
|
||||
|
||||
public FromOverride (Collection<Class<? extends PersistentRecord>> fromClasses)
|
||||
{
|
||||
_fromClasses.addAll(fromClasses);
|
||||
}
|
||||
|
||||
public List<Class<? extends PersistentRecord>> getFromClasses ()
|
||||
{
|
||||
return _fromClasses;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.addAll(getFromClasses());
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
/** The classes of the tables we're selecting from. */
|
||||
protected List<Class<? extends PersistentRecord>> _fromClasses =
|
||||
new ArrayList<Class<? extends PersistentRecord>>();
|
||||
}
|
||||
@@ -1,59 +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.clause;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Represents a GROUP BY clause.
|
||||
*/
|
||||
public class GroupBy extends QueryClause
|
||||
{
|
||||
public GroupBy (SQLExpression... values)
|
||||
{
|
||||
_values = values;
|
||||
}
|
||||
|
||||
public SQLExpression[] getValues ()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
// I can't imagine a GROUP BY clause bringing in new tables... ?
|
||||
}
|
||||
|
||||
/** The expressions that are generated for the clause. */
|
||||
protected SQLExpression[] _values;
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
//
|
||||
// $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 java.util.Set;
|
||||
|
||||
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, Set<String> identityFields)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_pojo = pojo;
|
||||
_idFields = identityFields;
|
||||
}
|
||||
|
||||
public Class<? extends PersistentRecord> getPersistentClass ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public Object getPojo ()
|
||||
{
|
||||
return _pojo;
|
||||
}
|
||||
|
||||
public Set<String> getIdentityFields ()
|
||||
{
|
||||
return _idFields;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
protected Class<? extends PersistentRecord> _pClass;
|
||||
|
||||
/** The object from which to fetch values, or null. */
|
||||
protected Object _pojo;
|
||||
|
||||
protected Set<String> _idFields;
|
||||
}
|
||||
@@ -1,103 +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.clause;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.Equals;
|
||||
|
||||
/**
|
||||
* Represents a JOIN.
|
||||
*/
|
||||
public class Join extends QueryClause
|
||||
{
|
||||
/** Indicates the join type to be used. The default is INNER. */
|
||||
public static enum Type { INNER, LEFT_OUTER, RIGHT_OUTER };
|
||||
|
||||
public Join (Class<? extends PersistentRecord> pClass, String pCol,
|
||||
Class<? extends PersistentRecord> joinClass, String jCol)
|
||||
{
|
||||
_joinClass = joinClass;
|
||||
_joinCondition = new Equals(new ColumnExp(joinClass, jCol), new ColumnExp(pClass, pCol));
|
||||
}
|
||||
|
||||
public Join (ColumnExp primary, ColumnExp join)
|
||||
{
|
||||
_joinClass = join.getPersistentClass();
|
||||
_joinCondition = new Equals(primary, join);
|
||||
}
|
||||
|
||||
public Join (Class<? extends PersistentRecord> joinClass, SQLExpression joinCondition)
|
||||
{
|
||||
_joinClass = joinClass;
|
||||
_joinCondition = joinCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the type of join to be performed.
|
||||
*/
|
||||
public Join setType (Type type)
|
||||
{
|
||||
_type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Type getType ()
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
public Class<? extends PersistentRecord> getJoinClass ()
|
||||
{
|
||||
return _joinClass;
|
||||
}
|
||||
|
||||
public SQLExpression getJoinCondition ()
|
||||
{
|
||||
return _joinCondition;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
classSet.add(_joinClass);
|
||||
_joinCondition.addClasses(classSet);
|
||||
}
|
||||
|
||||
/** Indicates the type of join to be performed. */
|
||||
protected Type _type = Type.INNER;
|
||||
|
||||
/** The class of the table we're to join against. */
|
||||
protected Class<? extends PersistentRecord> _joinClass;
|
||||
|
||||
/** The condition used to join in the new table. */
|
||||
protected SQLExpression _joinCondition;
|
||||
}
|
||||
@@ -1,65 +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.clause;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Represents a LIMIT/OFFSET clause, for pagination.
|
||||
*/
|
||||
public class Limit extends QueryClause
|
||||
{
|
||||
public Limit (int offset, int count)
|
||||
{
|
||||
_offset = offset;
|
||||
_count = count;
|
||||
}
|
||||
|
||||
public int getOffset ()
|
||||
{
|
||||
return _offset;
|
||||
}
|
||||
|
||||
public int getCount ()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
}
|
||||
|
||||
/** The first row of the result set to return. */
|
||||
protected int _offset;
|
||||
|
||||
/** The number of rows, at most, to return. */
|
||||
protected int _count;
|
||||
}
|
||||
@@ -1,98 +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.clause;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.LiteralExp;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Represents an ORDER BY clause.
|
||||
*/
|
||||
public class OrderBy extends QueryClause
|
||||
{
|
||||
/** Indicates the order of the clause. */
|
||||
public enum Order { ASC, DESC };
|
||||
|
||||
/**
|
||||
* Creates and returns a random order by clause.
|
||||
*/
|
||||
public static OrderBy random ()
|
||||
{
|
||||
return ascending(new LiteralExp("rand()"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an ascending order by clause on the supplied expression.
|
||||
*/
|
||||
public static OrderBy ascending (SQLExpression value)
|
||||
{
|
||||
return new OrderBy(new SQLExpression[] { value } , new Order[] { Order.ASC });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a descending order by clause on the supplied expression.
|
||||
*/
|
||||
public static OrderBy descending (SQLExpression value)
|
||||
{
|
||||
return new OrderBy(new SQLExpression[] { value }, new Order[] { Order.DESC });
|
||||
}
|
||||
|
||||
public OrderBy (SQLExpression[] values, Order[] orders)
|
||||
{
|
||||
_values = values;
|
||||
_orders = orders;
|
||||
}
|
||||
|
||||
public SQLExpression[] getValues ()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
public Order[] getOrders ()
|
||||
{
|
||||
return _orders;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
for (SQLExpression expression : _values) {
|
||||
expression.addClasses(classSet);
|
||||
}
|
||||
}
|
||||
|
||||
/** The expressions that are generated for the clause. */
|
||||
protected SQLExpression[] _values;
|
||||
|
||||
/** Whether the ordering is to be ascending or descending. */
|
||||
protected Order[] _orders;
|
||||
|
||||
}
|
||||
@@ -1,30 +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.clause;
|
||||
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* Represents a piece or modifier of an SQL query.
|
||||
*/
|
||||
public abstract class QueryClause implements SQLExpression
|
||||
{
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
//
|
||||
// $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.Arrays;
|
||||
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, Collection<? extends 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 FieldDefinition) {
|
||||
_disMap.put(((FieldDefinition) clause).getField(), ((FieldDefinition) 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;
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown clause provided in select " + clause + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A varargs version of the constructor.
|
||||
*/
|
||||
public SelectClause (Class<T> pClass, String[] fields, QueryClause... clauses)
|
||||
{
|
||||
this(pClass, fields, Arrays.asList(clauses));
|
||||
}
|
||||
|
||||
public FieldDefinition lookupDefinition (String field)
|
||||
{
|
||||
return _disMap.get(field);
|
||||
}
|
||||
|
||||
public Collection<FieldDefinition> getFieldDefinitions ()
|
||||
{
|
||||
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 (FieldDefinition override : _disMap.values()) {
|
||||
override.addClasses(classSet);
|
||||
}
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
/** Persistent class fields mapped to field override clauses. */
|
||||
protected Map<String, FieldDefinition> _disMap = new HashMap<String, FieldDefinition>();
|
||||
|
||||
/** 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;
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
//
|
||||
// $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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -1,99 +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.clause;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.WhereClause;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.expression.ValueExp;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.Equals;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.IsNull;
|
||||
import com.samskivert.jdbc.depot.operator.Logic.And;
|
||||
|
||||
/**
|
||||
* Represents a where clause: the condition can be any comparison operator or logical combination
|
||||
* thereof.
|
||||
*/
|
||||
public class Where extends WhereClause
|
||||
{
|
||||
public Where (ColumnExp column, Comparable<?> value)
|
||||
{
|
||||
this(new ColumnExp[] { column }, new Comparable<?>[] { value });
|
||||
}
|
||||
|
||||
public Where (ColumnExp index1, Comparable<?> value1,
|
||||
ColumnExp index2, Comparable<?> value2)
|
||||
{
|
||||
this(new ColumnExp[] { index1, index2 }, new Comparable<?>[] { value1, value2 });
|
||||
}
|
||||
|
||||
public Where (ColumnExp index1, Comparable<?> value1,
|
||||
ColumnExp index2, Comparable<?> value2,
|
||||
ColumnExp index3, Comparable<?> value3)
|
||||
{
|
||||
this(new ColumnExp[] { index1, index2, index3 },
|
||||
new Comparable<?>[] { value1, value2, value3 });
|
||||
}
|
||||
|
||||
public Where (ColumnExp[] columns, Comparable<?>[] values)
|
||||
{
|
||||
this(toCondition(columns, values));
|
||||
}
|
||||
|
||||
public Where (SQLExpression condition)
|
||||
{
|
||||
_condition = condition;
|
||||
}
|
||||
|
||||
// from WhereClause
|
||||
public SQLExpression getWhereExpression ()
|
||||
{
|
||||
return _condition;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_condition.addClasses(classSet);
|
||||
}
|
||||
|
||||
protected static SQLExpression toCondition (ColumnExp[] columns, Comparable<?>[] values)
|
||||
{
|
||||
SQLExpression[] comparisons = new SQLExpression[columns.length];
|
||||
for (int ii = 0; ii < columns.length; ii ++) {
|
||||
comparisons[ii] = (values[ii] == null) ? new IsNull(columns[ii]) :
|
||||
new Equals(columns[ii], new ValueExp(values[ii]));
|
||||
}
|
||||
return new And(comparisons);
|
||||
}
|
||||
|
||||
protected SQLExpression _condition;
|
||||
}
|
||||
@@ -1,67 +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.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* An expression that unambiguously identifies a field of a class, e.g. GameRecord.itemId.
|
||||
*/
|
||||
public class ColumnExp
|
||||
implements SQLExpression
|
||||
{
|
||||
public ColumnExp (Class<? extends PersistentRecord> pClass, String field)
|
||||
{
|
||||
super();
|
||||
_pClass = pClass;
|
||||
_pField = field;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -1,59 +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.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* An expression for extracting the seconds since the epoch from a date expression.
|
||||
*/
|
||||
public class EpochSeconds implements SQLExpression
|
||||
{
|
||||
/**
|
||||
* Create a new EpochSeconds with the given argument.
|
||||
*/
|
||||
public EpochSeconds (SQLExpression arg)
|
||||
{
|
||||
_arg = arg;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_arg.addClasses(classSet);
|
||||
}
|
||||
|
||||
public SQLExpression getArgument ()
|
||||
{
|
||||
return _arg;
|
||||
}
|
||||
|
||||
/** The argument. */
|
||||
protected SQLExpression _arg;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
//
|
||||
// $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;
|
||||
import com.samskivert.jdbc.depot.MultiKey;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.WhereClause;
|
||||
|
||||
import com.samskivert.jdbc.depot.clause.DeleteClause;
|
||||
import com.samskivert.jdbc.depot.clause.FieldDefinition;
|
||||
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.operator.Conditionals.Exists;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.In;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.IsNull;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals.FullTextMatch;
|
||||
import com.samskivert.jdbc.depot.operator.Logic.Not;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.MultiOperator;
|
||||
|
||||
/**
|
||||
* Enumerates visitation methods for every possible SQL expression type.
|
||||
*/
|
||||
public interface ExpressionVisitor
|
||||
{
|
||||
public void visit (FieldDefinition fieldOverride);
|
||||
public void visit (FunctionExp functionExp);
|
||||
public void visit (EpochSeconds epochSeconds);
|
||||
public void visit (FromOverride fromOverride);
|
||||
public void visit (MultiOperator multiOperator);
|
||||
public void visit (BinaryOperator binaryOperator);
|
||||
public void visit (IsNull isNull);
|
||||
public void visit (In in);
|
||||
public void visit (FullTextMatch match);
|
||||
public void visit (ColumnExp columnExp);
|
||||
public void visit (Not not);
|
||||
public void visit (GroupBy groupBy);
|
||||
public void visit (ForUpdate forUpdate);
|
||||
public void visit (OrderBy orderBy);
|
||||
public void visit (Join join);
|
||||
public void visit (Limit limit);
|
||||
public void visit (LiteralExp literalExp);
|
||||
public void visit (ValueExp valueExp);
|
||||
public void visit (WhereClause where);
|
||||
public void visit (Key.Expression<? extends PersistentRecord> key);
|
||||
public void visit (MultiKey<? extends PersistentRecord> key);
|
||||
public void visit (Exists<? extends PersistentRecord> exists);
|
||||
public void visit (SelectClause<? extends PersistentRecord> selectClause);
|
||||
public void visit (UpdateClause<? extends PersistentRecord> updateClause);
|
||||
public void visit (DeleteClause<? extends PersistentRecord> deleteClause);
|
||||
public void visit (InsertClause<? extends PersistentRecord> insertClause);
|
||||
}
|
||||
@@ -1,70 +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.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* An expression for a function, e.g. FLOOR(blah).
|
||||
*/
|
||||
public class FunctionExp implements SQLExpression
|
||||
{
|
||||
/**
|
||||
* Create a new FunctionExp with the given function and arguments.
|
||||
*/
|
||||
public FunctionExp (String function, SQLExpression... arguments)
|
||||
{
|
||||
_function = function;
|
||||
_arguments = arguments;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
for (int ii = 0; ii < _arguments.length; ii ++) {
|
||||
_arguments[ii].addClasses(classSet);
|
||||
}
|
||||
}
|
||||
|
||||
public String getFunction ()
|
||||
{
|
||||
return _function;
|
||||
}
|
||||
|
||||
public SQLExpression[] getArguments ()
|
||||
{
|
||||
return _arguments;
|
||||
}
|
||||
|
||||
/** The literal name of this function, e.g. FLOOR */
|
||||
protected String _function;
|
||||
|
||||
/** The arguments to this function */
|
||||
protected SQLExpression[] _arguments;
|
||||
}
|
||||
@@ -1,57 +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.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* An expression for things we don't support natively, e.g. COUNT(*).
|
||||
*/
|
||||
public class LiteralExp
|
||||
implements SQLExpression
|
||||
{
|
||||
public LiteralExp (String text)
|
||||
{
|
||||
super();
|
||||
_text = text;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
}
|
||||
|
||||
public String getText ()
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
|
||||
/** The literal text of this expression, e.g. COUNT(*) */
|
||||
protected String _text;
|
||||
}
|
||||
@@ -1,48 +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.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.SQLBuilder;
|
||||
|
||||
/**
|
||||
* Represents an SQL expression, e.g. column name, function, or constant.
|
||||
*/
|
||||
public interface SQLExpression
|
||||
{
|
||||
/**
|
||||
* Most uses of this class have been implemented with a visitor pattern. Create your own
|
||||
* {@link ExpressionVisitor} and call this method with it.
|
||||
*
|
||||
* @see SQLBuilder
|
||||
*/
|
||||
public void accept (ExpressionVisitor builder);
|
||||
|
||||
/**
|
||||
* Adds all persistent classes that are brought into the SQL context by this clause: FROM
|
||||
* clauses, JOINs, UPDATEs, anything that could create a new table abbreviation. This method
|
||||
* should recurse into any subordinate state that may in turn bring in new classes so that
|
||||
* sub-queries work correctly.
|
||||
*/
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet);
|
||||
}
|
||||
@@ -1,56 +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.expression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
|
||||
/**
|
||||
* A Java value that is bound as a parameter to the query, e.g. 1 or 'abc'.
|
||||
*/
|
||||
public class ValueExp
|
||||
implements SQLExpression
|
||||
{
|
||||
public ValueExp (Object _value)
|
||||
{
|
||||
this._value = _value;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
}
|
||||
|
||||
public Object getValue ()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
/** The value to be bound to the SQL parameters. */
|
||||
protected Object _value;
|
||||
}
|
||||
@@ -1,145 +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.operator;
|
||||
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
|
||||
|
||||
/**
|
||||
* A convenient container for implementations of arithmetic operators. Classes that value brevity
|
||||
* that feel otherwise will use Arithmetic.Add() and Arithmetic.Sub().
|
||||
*/
|
||||
public abstract class Arithmetic
|
||||
{
|
||||
/** The SQL '+' operator. */
|
||||
public static class Add extends BinaryOperator
|
||||
{
|
||||
public Add (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public Add (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "+";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '-' operator. */
|
||||
public static class Sub extends BinaryOperator
|
||||
{
|
||||
public Sub (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public Sub (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '*' operator. */
|
||||
public static class Mul extends BinaryOperator
|
||||
{
|
||||
public Mul (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public Mul (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "*";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '/' operator. */
|
||||
public static class Div extends BinaryOperator
|
||||
{
|
||||
public Div (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public Div (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return " / "; // Pad with spaces to work-around a MySQL bug.
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '&' operator. */
|
||||
public static class BitAnd extends BinaryOperator
|
||||
{
|
||||
public BitAnd (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public BitAnd (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "&";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '|' operator. */
|
||||
public static class BitOr extends BinaryOperator
|
||||
{
|
||||
public BitOr (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public BitOr (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "|";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,336 +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.operator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.clause.SelectClause;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* A convenient container for implementations of conditional operators. Classes that value brevity
|
||||
* classes that feel otherwise will use Conditionals.Equals() and Conditionals.In().
|
||||
*/
|
||||
public abstract class Conditionals
|
||||
{
|
||||
/** The SQL 'is null' operator. */
|
||||
public static class IsNull
|
||||
implements SQLOperator
|
||||
{
|
||||
public IsNull (String pColumn)
|
||||
{
|
||||
this(new ColumnExp(null, pColumn));
|
||||
}
|
||||
|
||||
public IsNull (Class<? extends PersistentRecord> pClass, String pColumn)
|
||||
{
|
||||
this(new ColumnExp(pClass, pColumn));
|
||||
}
|
||||
|
||||
public IsNull (ColumnExp column)
|
||||
{
|
||||
_column = column;
|
||||
}
|
||||
|
||||
public ColumnExp getColumn()
|
||||
{
|
||||
return _column;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
}
|
||||
|
||||
protected ColumnExp _column;
|
||||
}
|
||||
|
||||
/** The SQL '=' operator. */
|
||||
public static class Equals extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public Equals (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public Equals (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "=";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '!=' operator. */
|
||||
public static class NotEquals extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public NotEquals (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public NotEquals (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "!=";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '<' operator. */
|
||||
public static class LessThan extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public LessThan (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public LessThan (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "<";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '<=' operator. */
|
||||
public static class LessThanEquals extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public LessThanEquals (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public LessThanEquals (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "<=";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '>' operator. */
|
||||
public static class GreaterThan extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public GreaterThan (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public GreaterThan (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return ">";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL '>=' operator. */
|
||||
public static class GreaterThanEquals extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public GreaterThanEquals (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public GreaterThanEquals (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return ">=";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL 'in (...)' operator. */
|
||||
public static class In
|
||||
implements SQLOperator
|
||||
{
|
||||
/** The maximum number of keys allowed in an IN() clause. */
|
||||
public static final int MAX_KEYS = Short.MAX_VALUE;
|
||||
|
||||
public In (Class<? extends PersistentRecord> pClass, String pColumn, Comparable<?>... values)
|
||||
{
|
||||
this(new ColumnExp(pClass, pColumn), values);
|
||||
}
|
||||
|
||||
public In (Class<? extends PersistentRecord> pClass, String pColumn,
|
||||
Collection<? extends Comparable<?>> values)
|
||||
{
|
||||
this(new ColumnExp(pClass, pColumn), values.toArray(new Comparable<?>[values.size()]));
|
||||
}
|
||||
|
||||
public In (ColumnExp column, Comparable<?>... values)
|
||||
{
|
||||
if (values.length == 0) {
|
||||
throw new IllegalArgumentException("In() condition needs at least one value.");
|
||||
}
|
||||
_column = column;
|
||||
_values = values;
|
||||
}
|
||||
|
||||
public In (ColumnExp pColumn, Collection<? extends Comparable<?>> values)
|
||||
{
|
||||
this(pColumn, values.toArray(new Comparable<?>[values.size()]));
|
||||
}
|
||||
|
||||
public ColumnExp getColumn ()
|
||||
{
|
||||
return _column;
|
||||
}
|
||||
|
||||
public Comparable<?>[] getValues ()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_column.addClasses(classSet);
|
||||
}
|
||||
|
||||
protected ColumnExp _column;
|
||||
protected Comparable<?>[] _values;
|
||||
}
|
||||
|
||||
/** The SQL ' like ' operator. */
|
||||
public static class Like extends SQLOperator.BinaryOperator
|
||||
{
|
||||
public Like (SQLExpression column, Comparable<?> value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
public Like (SQLExpression column, SQLExpression value)
|
||||
{
|
||||
super(column, value);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return " like ";
|
||||
}
|
||||
}
|
||||
|
||||
/** The SQL ' exists' operator. */
|
||||
public static class Exists<T extends PersistentRecord> implements SQLOperator
|
||||
{
|
||||
public Exists (SelectClause<T> clause)
|
||||
{
|
||||
_clause = clause;
|
||||
}
|
||||
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_clause.addClasses(classSet);
|
||||
}
|
||||
|
||||
public SelectClause<T> getSubClause ()
|
||||
{
|
||||
return _clause;
|
||||
}
|
||||
|
||||
protected SelectClause<T> _clause;
|
||||
}
|
||||
|
||||
/**
|
||||
* An attempt at a dialect-agnostic full-text search condition, such as MySQL's MATCH() and
|
||||
* PostgreSQL's @@ TO_TSQUERY(...) abilities.
|
||||
*/
|
||||
public static class FullTextMatch
|
||||
implements SQLOperator
|
||||
{
|
||||
public FullTextMatch (Class<? extends PersistentRecord> pClass, String name, String query)
|
||||
{
|
||||
_pClass = pClass;
|
||||
_name = name;
|
||||
_query = query;
|
||||
}
|
||||
|
||||
public Class<? extends PersistentRecord> getPersistentRecord ()
|
||||
{
|
||||
return _pClass;
|
||||
}
|
||||
|
||||
public String getQuery ()
|
||||
{
|
||||
return _query;
|
||||
}
|
||||
|
||||
public String getName ()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
}
|
||||
|
||||
protected Class<? extends PersistentRecord> _pClass;
|
||||
protected String _name;
|
||||
protected String _query;
|
||||
}
|
||||
}
|
||||
@@ -1,107 +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.operator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
|
||||
/**
|
||||
* A convenient container for implementations of logical operators. Classes that value brevity
|
||||
* feel otherwise will use Logic.And() and Logic.Not().
|
||||
*/
|
||||
public abstract class Logic
|
||||
{
|
||||
/**
|
||||
* Represents a condition that is false iff all its subconditions are false.
|
||||
*/
|
||||
public static class Or extends SQLOperator.MultiOperator
|
||||
{
|
||||
public Or (Collection<? extends SQLExpression> conditions)
|
||||
{
|
||||
super(conditions.toArray(new SQLExpression[conditions.size()]));
|
||||
}
|
||||
|
||||
public Or (SQLExpression... conditions)
|
||||
{
|
||||
super(conditions);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "or";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a condition that is true iff all its subconditions are true.
|
||||
*/
|
||||
public static class And extends SQLOperator.MultiOperator
|
||||
{
|
||||
public And (Collection<? extends SQLExpression> conditions)
|
||||
{
|
||||
super(conditions.toArray(new SQLExpression[conditions.size()]));
|
||||
}
|
||||
|
||||
public And (SQLExpression... conditions)
|
||||
{
|
||||
super(conditions);
|
||||
}
|
||||
|
||||
@Override public String operator()
|
||||
{
|
||||
return "and";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the truth negation of another conditon.
|
||||
*/
|
||||
public static class Not
|
||||
implements SQLOperator
|
||||
{
|
||||
public Not (SQLExpression condition)
|
||||
{
|
||||
_condition = condition;
|
||||
}
|
||||
|
||||
public SQLExpression getCondition ()
|
||||
{
|
||||
return _condition;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_condition.addClasses(classSet);
|
||||
}
|
||||
|
||||
protected SQLExpression _condition;
|
||||
}
|
||||
}
|
||||
@@ -1,122 +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.operator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.expression.ExpressionVisitor;
|
||||
import com.samskivert.jdbc.depot.expression.SQLExpression;
|
||||
import com.samskivert.jdbc.depot.expression.ValueExp;
|
||||
|
||||
/**
|
||||
* A common interface for operator hierarchies in SQL. The main purpose of breaking this out from
|
||||
* SQLExpression is to capture the recursive nature of e.g. the logical operators, which work on
|
||||
* other SQLOperators but not general SQLExpressions.
|
||||
*/
|
||||
public interface SQLOperator extends SQLExpression
|
||||
{
|
||||
/**
|
||||
* Represents an operator with any number of operands.
|
||||
*/
|
||||
public abstract static class MultiOperator
|
||||
implements SQLOperator
|
||||
{
|
||||
public MultiOperator (SQLExpression ... conditions)
|
||||
{
|
||||
_conditions = conditions;
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
for (int ii = 0; ii < _conditions.length; ii ++) {
|
||||
_conditions[ii].addClasses(classSet);
|
||||
}
|
||||
}
|
||||
|
||||
public SQLExpression[] getConditions ()
|
||||
{
|
||||
return _conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text infix to be used to join expressions together.
|
||||
*/
|
||||
public abstract String operator ();
|
||||
|
||||
protected SQLExpression[] _conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the real work for simple binary operators such as Equals.
|
||||
*/
|
||||
public abstract static class BinaryOperator implements SQLOperator
|
||||
{
|
||||
public BinaryOperator (SQLExpression lhs, SQLExpression rhs)
|
||||
{
|
||||
_lhs = lhs;
|
||||
_rhs = rhs;
|
||||
}
|
||||
|
||||
public BinaryOperator (SQLExpression lhs, Comparable<?> rhs)
|
||||
{
|
||||
this(lhs, new ValueExp(rhs));
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void accept (ExpressionVisitor builder)
|
||||
{
|
||||
builder.visit(this);
|
||||
}
|
||||
|
||||
// from SQLExpression
|
||||
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
|
||||
{
|
||||
_lhs.addClasses(classSet);
|
||||
_rhs.addClasses(classSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the operator.
|
||||
*/
|
||||
public abstract String operator();
|
||||
|
||||
public SQLExpression getLeftHandSide ()
|
||||
{
|
||||
return _lhs;
|
||||
}
|
||||
|
||||
public SQLExpression getRightHandSide ()
|
||||
{
|
||||
return _rhs;
|
||||
}
|
||||
|
||||
protected SQLExpression _lhs;
|
||||
protected SQLExpression _rhs;
|
||||
}
|
||||
}
|
||||
@@ -1,128 +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.tests;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.samskivert.jdbc.depot.Key;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.annotation.Entity;
|
||||
import com.samskivert.jdbc.depot.annotation.Id;
|
||||
import com.samskivert.jdbc.depot.annotation.Index;
|
||||
import com.samskivert.jdbc.depot.expression.ColumnExp;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* A test persistent object.
|
||||
*/
|
||||
@Entity(indices={ @Index(name="createdIndex", fields={"created"}) })
|
||||
public class TestRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The column identifier for the {@link #recordId} field. */
|
||||
public static final String RECORD_ID = "recordId";
|
||||
|
||||
/** The qualified column identifier for the {@link #recordId} field. */
|
||||
public static final ColumnExp RECORD_ID_C =
|
||||
new ColumnExp(TestRecord.class, RECORD_ID);
|
||||
|
||||
/** The column identifier for the {@link #name} field. */
|
||||
public static final String NAME = "name";
|
||||
|
||||
/** The qualified column identifier for the {@link #name} field. */
|
||||
public static final ColumnExp NAME_C =
|
||||
new ColumnExp(TestRecord.class, NAME);
|
||||
|
||||
/** The column identifier for the {@link #age} field. */
|
||||
public static final String AGE = "age";
|
||||
|
||||
/** The qualified column identifier for the {@link #age} field. */
|
||||
public static final ColumnExp AGE_C =
|
||||
new ColumnExp(TestRecord.class, AGE);
|
||||
|
||||
/** The column identifier for the {@link #homeTown} field. */
|
||||
public static final String HOME_TOWN = "homeTown";
|
||||
|
||||
/** The qualified column identifier for the {@link #homeTown} field. */
|
||||
public static final ColumnExp HOME_TOWN_C =
|
||||
new ColumnExp(TestRecord.class, HOME_TOWN);
|
||||
|
||||
/** The column identifier for the {@link #created} field. */
|
||||
public static final String CREATED = "created";
|
||||
|
||||
/** The qualified column identifier for the {@link #created} field. */
|
||||
public static final ColumnExp CREATED_C =
|
||||
new ColumnExp(TestRecord.class, CREATED);
|
||||
|
||||
/** The column identifier for the {@link #lastModified} field. */
|
||||
public static final String LAST_MODIFIED = "lastModified";
|
||||
|
||||
/** The qualified column identifier for the {@link #lastModified} field. */
|
||||
public static final ColumnExp LAST_MODIFIED_C =
|
||||
new ColumnExp(TestRecord.class, LAST_MODIFIED);
|
||||
|
||||
/** The column identifier for the {@link #numbers} field. */
|
||||
public static final String NUMBERS = "numbers";
|
||||
|
||||
/** The qualified column identifier for the {@link #numbers} field. */
|
||||
public static final ColumnExp NUMBERS_C =
|
||||
new ColumnExp(TestRecord.class, NUMBERS);
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
public static final int SCHEMA_VERSION = 3;
|
||||
|
||||
@Id
|
||||
public int recordId;
|
||||
|
||||
public String name;
|
||||
|
||||
public int age;
|
||||
|
||||
public String homeTown;
|
||||
|
||||
public Date created;
|
||||
|
||||
public Timestamp lastModified;
|
||||
|
||||
public int[] numbers;
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return StringUtil.fieldsToString(this);
|
||||
}
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Create and return a primary {@link Key} to identify a {@link TestRecord}
|
||||
* with the supplied key values.
|
||||
*/
|
||||
public static Key<TestRecord> getKey (int recordId)
|
||||
{
|
||||
return new Key<TestRecord>(
|
||||
TestRecord.class,
|
||||
new String[] { RECORD_ID },
|
||||
new Comparable[] { recordId });
|
||||
}
|
||||
// AUTO-GENERATED: METHODS END
|
||||
}
|
||||
@@ -1,126 +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.tests;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.Timestamp;
|
||||
// import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.util.RandomUtil;
|
||||
|
||||
import com.samskivert.jdbc.StaticConnectionProvider;
|
||||
import com.samskivert.jdbc.depot.DepotRepository;
|
||||
// import com.samskivert.jdbc.depot.Key;
|
||||
// import com.samskivert.jdbc.depot.KeySet;
|
||||
import com.samskivert.jdbc.depot.PersistenceContext;
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.SchemaMigration;
|
||||
import com.samskivert.jdbc.depot.clause.Where;
|
||||
import com.samskivert.jdbc.depot.expression.LiteralExp;
|
||||
import com.samskivert.jdbc.depot.operator.Conditionals;
|
||||
|
||||
/**
|
||||
* A test tool for the Depot repository services.
|
||||
*/
|
||||
public class TestRepository extends DepotRepository
|
||||
{
|
||||
public static void main (String[] args)
|
||||
throws Exception
|
||||
{
|
||||
PersistenceContext perCtx = new PersistenceContext();
|
||||
perCtx.init("test", new StaticConnectionProvider("depot.properties"), null);
|
||||
|
||||
// tests a bogus rename migration
|
||||
// perCtx.registerMigration(TestRecord.class, new SchemaMigration.Rename(1, "foo", "bar"));
|
||||
|
||||
// tests a custom add column migration
|
||||
perCtx.registerMigration(TestRecord.class,
|
||||
new SchemaMigration.Add(2, TestRecord.HOME_TOWN, "'Anytown USA'"));
|
||||
|
||||
TestRepository repo = new TestRepository(perCtx);
|
||||
|
||||
repo.delete(TestRecord.class, 1);
|
||||
|
||||
Date now = new Date(System.currentTimeMillis());
|
||||
Timestamp tnow = new Timestamp(System.currentTimeMillis());
|
||||
|
||||
TestRecord record = new TestRecord();
|
||||
record.recordId = 1;
|
||||
record.name = "Elvis";
|
||||
record.age = 99;
|
||||
record.created = now;
|
||||
record.homeTown = "Right here";
|
||||
record.lastModified = tnow;
|
||||
record.numbers = new int[] { 9, 0, 2, 1, 0 };
|
||||
|
||||
repo.insert(record);
|
||||
System.out.println(repo.load(TestRecord.class, record.recordId));
|
||||
|
||||
// record.age = 25;
|
||||
// record.name = "Bob";
|
||||
// record.numbers = new int[] { 1, 2, 3, 4, 5 };
|
||||
// repo.update(record, TestRecord.AGE, TestRecord.NAME, TestRecord.NUMBERS);
|
||||
|
||||
repo.updatePartial(TestRecord.class, record.recordId,
|
||||
TestRecord.AGE, 25, TestRecord.NAME, "Bob",
|
||||
TestRecord.NUMBERS, new int[] { 1, 2, 3, 4, 5 });
|
||||
System.out.println(repo.load(TestRecord.class, record.recordId));
|
||||
|
||||
for (int ii = 2; ii < CREATE_RECORDS; ii++) {
|
||||
record = new TestRecord();
|
||||
record.recordId = ii;
|
||||
record.name = "Spam!";
|
||||
record.age = RandomUtil.getInt(150);
|
||||
record.homeTown = "Over there";
|
||||
record.numbers = new int[] { 5, 4, 3, 2, 1 };
|
||||
record.created = now;
|
||||
record.lastModified = tnow;
|
||||
repo.insert(record);
|
||||
}
|
||||
|
||||
System.out.println("Have " + repo.findAll(TestRecord.class).size() + " records.");
|
||||
repo.deleteAll(TestRecord.class, new Where(new Conditionals.LessThan(
|
||||
TestRecord.RECORD_ID_C, CREATE_RECORDS/2)));
|
||||
System.out.println("Now have " + repo.findAll(TestRecord.class).size() + " records.");
|
||||
repo.deleteAll(TestRecord.class, new Where(new LiteralExp("true")));
|
||||
// // TODO: try to break our In() clause
|
||||
// Set<Key<TestRecord>> ids = new HashSet<Key<TestRecord>>();
|
||||
// for (int ii = 1; ii <= Conditionals.In.MAX_KEYS*2+3; ii++) {
|
||||
// ids.add(TestRecord.getKey(ii));
|
||||
// }
|
||||
// repo.deleteAll(TestRecord.class, new KeySet<TestRecord>(TestRecord.class, ids));
|
||||
System.out.println("Now have " + repo.findAll(TestRecord.class).size() + " records.");
|
||||
}
|
||||
|
||||
public TestRepository (PersistenceContext perCtx)
|
||||
{
|
||||
super(perCtx);
|
||||
}
|
||||
|
||||
@Override // from DepotRepository
|
||||
protected void getManagedRecords (Set<Class<? extends PersistentRecord>> classes)
|
||||
{
|
||||
classes.add(TestRecord.class);
|
||||
}
|
||||
|
||||
protected static final int CREATE_RECORDS = 150;
|
||||
}
|
||||
@@ -1,493 +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.tools;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.DirectoryScanner;
|
||||
import org.apache.tools.ant.Task;
|
||||
import org.apache.tools.ant.types.FileSet;
|
||||
import org.apache.tools.ant.types.Reference;
|
||||
import org.apache.tools.ant.util.ClasspathUtils;
|
||||
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
|
||||
import com.samskivert.jdbc.depot.PersistentRecord;
|
||||
import com.samskivert.jdbc.depot.annotation.Id;
|
||||
import com.samskivert.jdbc.depot.annotation.Transient;
|
||||
import com.samskivert.util.ClassUtil;
|
||||
import com.samskivert.util.GenUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.velocity.VelocityUtil;
|
||||
|
||||
/**
|
||||
* An ant task that updates the column constants for a persistent record.
|
||||
*/
|
||||
public class GenRecordTask extends Task
|
||||
{
|
||||
/**
|
||||
* Adds a nested fileset element which enumerates record source files.
|
||||
*/
|
||||
public void addFileset (FileSet set)
|
||||
{
|
||||
_filesets.add(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures that classpath that we'll use to load record classes.
|
||||
*/
|
||||
public void setClasspathref (Reference pathref)
|
||||
{
|
||||
_cloader = ClasspathUtils.getClassLoaderForPath(getProject(), pathref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute () throws BuildException
|
||||
{
|
||||
if (_cloader == null) {
|
||||
String errmsg = "This task requires a 'classpathref' attribute " +
|
||||
"to be set to the project's classpath.";
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
|
||||
try {
|
||||
_velocity = VelocityUtil.createEngine();
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Failure initializing Velocity", e);
|
||||
}
|
||||
|
||||
// resolve the PersistentRecord class using our classloader
|
||||
try {
|
||||
_prclass = _cloader.loadClass(PersistentRecord.class.getName());
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Can't resolve InvocationListener", e);
|
||||
}
|
||||
|
||||
for (FileSet fs : _filesets) {
|
||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
||||
File fromDir = fs.getDir(getProject());
|
||||
String[] srcFiles = ds.getIncludedFiles();
|
||||
for (int f = 0; f < srcFiles.length; f++) {
|
||||
processRecord(new File(fromDir, srcFiles[f]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a distributed object source file.
|
||||
*/
|
||||
protected void processRecord (File source)
|
||||
{
|
||||
// System.err.println("Processing " + source + "...");
|
||||
|
||||
// load up the file and determine it's package and classname
|
||||
String name = null;
|
||||
try {
|
||||
name = readClassName(source);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to parse " + source + ": " + e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
processRecord(source, _cloader.loadClass(name));
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
System.err.println("Failed to load " + name + ".\n" +
|
||||
"Missing class: " + cnfe.getMessage());
|
||||
System.err.println("Be sure to set the 'classpathref' attribute to a classpath\n" +
|
||||
"that contains your projects invocation service classes.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
/** Processes a resolved persistent record class instance. */
|
||||
protected void processRecord (File source, Class<?> rclass)
|
||||
{
|
||||
// make sure we extend persistent record
|
||||
if (!_prclass.isAssignableFrom(rclass)) {
|
||||
// System.err.println("Skipping " + rclass.getName() + "...");
|
||||
return;
|
||||
}
|
||||
boolean isAbstract = Modifier.isAbstract(rclass.getModifiers());
|
||||
|
||||
// determine our primary key fields for getKey() generation (if we're not an abstract)
|
||||
List<Field> kflist = new ArrayList<Field>();
|
||||
if (!isAbstract) {
|
||||
// determine which fields make up our primary key; we'd just use Class.getFields() but
|
||||
// that returns things in a random order whereas ClassUtil returns fields in
|
||||
// declaration order starting from the top-most class and going down the line
|
||||
for (Field field : ClassUtil.getFields(rclass)) {
|
||||
if (hasAnnotation(field, Id.class)) {
|
||||
kflist.add(field);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// determine which fields we need to generate constants for
|
||||
List<Field> flist = new ArrayList<Field>();
|
||||
for (Field field : rclass.getFields()) {
|
||||
if (isPersistentField(field)) {
|
||||
flist.add(field);
|
||||
}
|
||||
}
|
||||
Set<Field> declared = new HashSet<Field>();
|
||||
for (Field field : rclass.getDeclaredFields()) {
|
||||
if (isPersistentField(field)) {
|
||||
declared.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
// slurp our source file into newline separated strings
|
||||
String[] lines = null;
|
||||
try {
|
||||
BufferedReader bin = new BufferedReader(new FileReader(source));
|
||||
ArrayList<String> llist = new ArrayList<String>();
|
||||
String line = null;
|
||||
while ((line = bin.readLine()) != null) {
|
||||
llist.add(line);
|
||||
}
|
||||
lines = llist.toArray(new String[llist.size()]);
|
||||
bin.close();
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error reading '" + source + "': " + ioe);
|
||||
return;
|
||||
}
|
||||
|
||||
// now determine where to insert our static field declarations
|
||||
int bstart = -1, bend = -1;
|
||||
int nstart = -1, nend = -1;
|
||||
int mstart = -1, mend = -1;
|
||||
for (int ii = 0; ii < lines.length; ii++) {
|
||||
String line = lines[ii].trim();
|
||||
|
||||
// look for the start of the class body
|
||||
if (NAME_PATTERN.matcher(line).find()) {
|
||||
if (line.endsWith("{")) {
|
||||
bstart = ii+1;
|
||||
} else {
|
||||
// search down a few lines for the open brace
|
||||
for (int oo = 1; oo < 10; oo++) {
|
||||
if (get(lines, ii+oo).trim().endsWith("{")) {
|
||||
bstart = ii+oo+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// track the last } on a line by itself and we'll call that the end of the class body
|
||||
} else if (line.equals("}")) {
|
||||
bend = ii;
|
||||
|
||||
// look for our field and method markers
|
||||
} else if (line.equals(FIELDS_START)) {
|
||||
nstart = ii;
|
||||
} else if (line.equals(FIELDS_END)) {
|
||||
nend = ii+1;
|
||||
} else if (line.equals(METHODS_START)) {
|
||||
mstart = ii;
|
||||
} else if (line.equals(METHODS_END)) {
|
||||
mend = ii+1;
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check the markers
|
||||
if (check(source, "fields start", nstart, "fields end", nend) ||
|
||||
check(source, "fields end", nend, "fields start", nstart) ||
|
||||
check(source, "methods start", mstart, "methods end", mend) ||
|
||||
check(source, "methods end", mend, "methods start", mstart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we have no previous markers then stuff the fields at the top of the class body and the
|
||||
// methods at the bottom
|
||||
if (nstart == -1) {
|
||||
nstart = bstart;
|
||||
nend = bstart;
|
||||
}
|
||||
if (mstart == -1) {
|
||||
mstart = bend;
|
||||
mend = bend;
|
||||
}
|
||||
|
||||
// get the unqualified class name
|
||||
String rname = rclass.getName();
|
||||
rname = rname.substring(rname.lastIndexOf(".")+1);
|
||||
|
||||
// generate our fields section
|
||||
StringBuilder fsection = new StringBuilder();
|
||||
for (int ii = 0; ii < flist.size(); ii++) {
|
||||
Field f = flist.get(ii);
|
||||
String fname = f.getName();
|
||||
|
||||
// create our velocity context
|
||||
VelocityContext ctx = new VelocityContext();
|
||||
ctx.put("record", rname);
|
||||
ctx.put("field", fname);
|
||||
ctx.put("capfield", StringUtil.unStudlyName(fname).toUpperCase());
|
||||
|
||||
// now generate our bits
|
||||
if (declared.contains(f)) {
|
||||
if (fsection.length() > 0) {
|
||||
fsection.append("\n");
|
||||
}
|
||||
fsection.append(mergeTemplate(NAME_TMPL, ctx));
|
||||
}
|
||||
if (!isAbstract) {
|
||||
if (fsection.length() > 0) {
|
||||
fsection.append("\n");
|
||||
}
|
||||
fsection.append(mergeTemplate(COL_TMPL, ctx));
|
||||
}
|
||||
}
|
||||
|
||||
// generate our methods section
|
||||
StringBuilder msection = new StringBuilder();
|
||||
|
||||
// add a getKey() method, if applicable
|
||||
if (kflist.size() > 0) {
|
||||
// create our velocity context
|
||||
VelocityContext ctx = new VelocityContext();
|
||||
ctx.put("record", rname);
|
||||
|
||||
StringBuilder argList = new StringBuilder();
|
||||
StringBuilder argNameList = new StringBuilder();
|
||||
StringBuilder fieldNameList = new StringBuilder();
|
||||
for (Field keyField : kflist) {
|
||||
if (argList.length() > 0) {
|
||||
argList.append(", ");
|
||||
argNameList.append(", ");
|
||||
fieldNameList.append(", ");
|
||||
}
|
||||
String name = keyField.getName();
|
||||
argList.append(GenUtil.simpleName(keyField)).append(" ").append(name);
|
||||
argNameList.append(name);
|
||||
fieldNameList.append(StringUtil.unStudlyName(name));
|
||||
}
|
||||
|
||||
ctx.put("argList", argList.toString());
|
||||
ctx.put("argNameList", argNameList.toString());
|
||||
ctx.put("fieldNameList", fieldNameList.toString());
|
||||
|
||||
// generate our bits and append them as appropriate to the string buffers
|
||||
msection.append(mergeTemplate(KEY_TMPL, ctx));
|
||||
}
|
||||
|
||||
// now bolt everything back together into a class declaration
|
||||
try {
|
||||
BufferedWriter bout = new BufferedWriter(new FileWriter(source));
|
||||
for (int ii = 0; ii < nstart; ii++) {
|
||||
writeln(bout, lines[ii]);
|
||||
}
|
||||
|
||||
if (fsection.length() > 0) {
|
||||
String prev = get(lines, nstart-1);
|
||||
if (!StringUtil.isBlank(prev) && !prev.equals("{")) {
|
||||
bout.newLine();
|
||||
}
|
||||
writeln(bout, " " + FIELDS_START);
|
||||
bout.write(fsection.toString());
|
||||
writeln(bout, " " + FIELDS_END);
|
||||
if (!StringUtil.isBlank(get(lines, nend))) {
|
||||
bout.newLine();
|
||||
}
|
||||
}
|
||||
for (int ii = nend; ii < mstart; ii++) {
|
||||
writeln(bout, lines[ii]);
|
||||
}
|
||||
|
||||
if (msection.length() > 0) {
|
||||
if (!StringUtil.isBlank(get(lines, mstart-1))) {
|
||||
bout.newLine();
|
||||
}
|
||||
writeln(bout, " " + METHODS_START);
|
||||
bout.write(msection.toString());
|
||||
writeln(bout, " " + METHODS_END);
|
||||
String next = get(lines, mend);
|
||||
if (!StringUtil.isBlank(next) && !next.equals("}")) {
|
||||
bout.newLine();
|
||||
}
|
||||
}
|
||||
for (int ii = mend; ii < lines.length; ii++) {
|
||||
writeln(bout, lines[ii]);
|
||||
}
|
||||
|
||||
bout.close();
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error writing to '" + source + "': " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the supplied field is part of a persistent record (is a public, non-static,
|
||||
* non-transient field).
|
||||
*/
|
||||
protected boolean isPersistentField (Field field)
|
||||
{
|
||||
int mods = field.getModifiers();
|
||||
return Modifier.isPublic(mods) && !Modifier.isStatic(mods) &&
|
||||
!Modifier.isTransient(mods) && !hasAnnotation(field, Transient.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely gets the <code>index</code>th line, returning the empty string if we exceed the
|
||||
* length of the array.
|
||||
*/
|
||||
protected String get (String[] lines, int index)
|
||||
{
|
||||
return (index < lines.length) ? lines[index] : "";
|
||||
}
|
||||
|
||||
/** Helper function for sanity checking marker existence. */
|
||||
protected boolean check (File source, String mname, int mline, String fname, int fline)
|
||||
{
|
||||
if (mline == -1 && fline != -1) {
|
||||
System.err.println("Found " + fname + " marker (at line " + (fline+1) + ") but no " +
|
||||
mname + " marker in '" + source + "'.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Helper function for writing a string and a newline to a writer. */
|
||||
protected void writeln (BufferedWriter bout, String line)
|
||||
throws IOException
|
||||
{
|
||||
bout.write(line);
|
||||
bout.newLine();
|
||||
}
|
||||
|
||||
/** Helper function for generating our boilerplate code. */
|
||||
protected String mergeTemplate (String tmpl, VelocityContext ctx)
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
_velocity.mergeTemplate(tmpl, "UTF-8", ctx, writer);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed processing template [tmpl=" + tmpl + "]");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
protected static boolean hasAnnotation (Field field, Class<?> annotation)
|
||||
{
|
||||
// iterate becase getAnnotation() fails if we're dealing with multiple classloaders
|
||||
for (Annotation a : field.getAnnotations()) {
|
||||
if (annotation.getName().equals(a.annotationType().getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in the supplied source file and locates the package and class or interface name and
|
||||
* returns a fully qualified class name.
|
||||
*/
|
||||
protected static String readClassName (File source)
|
||||
throws IOException
|
||||
{
|
||||
// load up the file and determine it's package and classname
|
||||
String pkgname = null, name = null;
|
||||
BufferedReader bin = new BufferedReader(new FileReader(source));
|
||||
String line;
|
||||
while ((line = bin.readLine()) != null) {
|
||||
Matcher pm = PACKAGE_PATTERN.matcher(line);
|
||||
if (pm.find()) {
|
||||
pkgname = pm.group(1);
|
||||
}
|
||||
Matcher nm = NAME_PATTERN.matcher(line);
|
||||
if (nm.find()) {
|
||||
name = nm.group(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
bin.close();
|
||||
|
||||
// make sure we found something
|
||||
if (name == null) {
|
||||
throw new IOException("Unable to locate class or interface name in " + source + ".");
|
||||
}
|
||||
|
||||
// prepend the package name to get a name we can Class.forName()
|
||||
if (pkgname != null) {
|
||||
name = pkgname + "." + name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/** A list of filesets that contain tile images. */
|
||||
protected ArrayList<FileSet> _filesets = new ArrayList<FileSet>();
|
||||
|
||||
/** Used to do our own classpath business. */
|
||||
protected ClassLoader _cloader;
|
||||
|
||||
/** Used to generate source files from templates. */
|
||||
protected VelocityEngine _velocity;
|
||||
|
||||
/** {@link PersistentRecord} resolved with the proper classloader so that we can compare it to
|
||||
* loaded derived classes. */
|
||||
protected Class<?> _prclass;
|
||||
|
||||
/** Specifies the path to the name code template. */
|
||||
protected static final String NAME_TMPL = "com/samskivert/jdbc/depot/tools/record_name.tmpl";
|
||||
|
||||
/** Specifies the path to the column code template. */
|
||||
protected static final String COL_TMPL = "com/samskivert/jdbc/depot/tools/record_column.tmpl";
|
||||
|
||||
/** Specifies the path to the key code template. */
|
||||
protected static final String KEY_TMPL = "com/samskivert/jdbc/depot/tools/record_key.tmpl";
|
||||
|
||||
// markers
|
||||
protected static final String MARKER = "// AUTO-GENERATED: ";
|
||||
protected static final String FIELDS_START = MARKER + "FIELDS START";
|
||||
protected static final String FIELDS_END = MARKER + "FIELDS END";
|
||||
protected static final String METHODS_START = MARKER + "METHODS START";
|
||||
protected static final String METHODS_END = MARKER + "METHODS END";
|
||||
|
||||
/** A regular expression for matching the package declaration. */
|
||||
protected static final Pattern PACKAGE_PATTERN = Pattern.compile("^\\s*package\\s+(\\S+)\\W");
|
||||
|
||||
/** A regular expression for matching the class or interface declaration. */
|
||||
protected static final Pattern NAME_PATTERN = Pattern.compile(
|
||||
"^\\s*public\\s+(?:abstract\\s+)?(interface|class)\\s+(\\w+)(\\W|$)");
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
/** The qualified column identifier for the {@link #$field} field. */
|
||||
public static final ColumnExp ${capfield}_C =
|
||||
new ColumnExp(${record}.class, $capfield);
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Create and return a primary {@link Key} to identify a {@link $record}
|
||||
* with the supplied key values.
|
||||
*/
|
||||
public static Key<${record}> getKey (${argList})
|
||||
{
|
||||
return new Key<${record}>(
|
||||
${record}.class,
|
||||
new String[] { $fieldNameList },
|
||||
new Comparable[] { $argNameList });
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
/** The column identifier for the {@link #$field} field. */
|
||||
public static final String $capfield = "$field";
|
||||
Reference in New Issue
Block a user