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:
@@ -40,22 +40,6 @@
|
|||||||
</copy>
|
</copy>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- generates fields for persistent record classes -->
|
|
||||||
<target name="genrecord" depends="prepare">
|
|
||||||
<taskdef name="grecord" classname="com.samskivert.jdbc.depot.tools.GenRecordTask"
|
|
||||||
classpathref="classpath"/>
|
|
||||||
<!-- make sure the record class files are all compiled -->
|
|
||||||
<javac srcdir="src/java" destdir="${classes.dir}"
|
|
||||||
debug="on" optimize="${build.optimize}" deprecation="on">
|
|
||||||
<classpath refid="classpath"/>
|
|
||||||
<include name="**/*Record.java"/>
|
|
||||||
</javac>
|
|
||||||
<!-- now update the source files -->
|
|
||||||
<grecord classpathref="classpath">
|
|
||||||
<fileset dir="src/java" includes="**/*Record.java"/>
|
|
||||||
</grecord>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<!-- cleans out the intermediate build files -->
|
<!-- cleans out the intermediate build files -->
|
||||||
<target name="clean">
|
<target name="clean">
|
||||||
<delete dir="${classes.dir}"/>
|
<delete dir="${classes.dir}"/>
|
||||||
@@ -76,8 +60,6 @@
|
|||||||
<classpath refid="classpath"/>
|
<classpath refid="classpath"/>
|
||||||
<exclude name="com/samskivert/io/**" unless="build.io"/>
|
<exclude name="com/samskivert/io/**" unless="build.io"/>
|
||||||
<exclude name="com/samskivert/jdbc/**" unless="build.jdbc"/>
|
<exclude name="com/samskivert/jdbc/**" unless="build.jdbc"/>
|
||||||
<exclude name="com/samskivert/jdbc/depot/EHCacheAdapter.java" unless="build.depot.ehcache"/>
|
|
||||||
<exclude name="com/samskivert/jdbc/depot/tools/**" unless="build.depot.tools"/>
|
|
||||||
<exclude name="com/samskivert/net/MailUtil*" unless="build.mail"/>
|
<exclude name="com/samskivert/net/MailUtil*" unless="build.mail"/>
|
||||||
<exclude name="com/samskivert/servlet/**" unless="build.servlet"/>
|
<exclude name="com/samskivert/servlet/**" unless="build.servlet"/>
|
||||||
<exclude name="com/samskivert/swing/**" unless="build.swing"/>
|
<exclude name="com/samskivert/swing/**" unless="build.swing"/>
|
||||||
@@ -104,7 +86,6 @@
|
|||||||
<packageset dir="${src.dir}">
|
<packageset dir="${src.dir}">
|
||||||
<exclude name="com/samskivert/io/**" unless="build.io"/>
|
<exclude name="com/samskivert/io/**" unless="build.io"/>
|
||||||
<exclude name="com/samskivert/jdbc/**" unless="build.jdbc"/>
|
<exclude name="com/samskivert/jdbc/**" unless="build.jdbc"/>
|
||||||
<exclude name="com/samskivert/jdbc/depot/EHCacheAdapter.java" unless="ehcache.present"/>
|
|
||||||
<exclude name="com/samskivert/net/**" unless="build.net"/>
|
<exclude name="com/samskivert/net/**" unless="build.net"/>
|
||||||
<exclude name="com/samskivert/servlet/**" unless="build.servlet"/>
|
<exclude name="com/samskivert/servlet/**" unless="build.servlet"/>
|
||||||
<exclude name="com/samskivert/swing/**" unless="build.swing"/>
|
<exclude name="com/samskivert/swing/**" unless="build.swing"/>
|
||||||
@@ -143,7 +124,6 @@
|
|||||||
<include name="**/tests/**/*Test.java"/>
|
<include name="**/tests/**/*Test.java"/>
|
||||||
<exclude name="com/samskivert/io/**" unless="build.io"/>
|
<exclude name="com/samskivert/io/**" unless="build.io"/>
|
||||||
<exclude name="com/samskivert/jdbc/**" unless="build.jdbc"/>
|
<exclude name="com/samskivert/jdbc/**" unless="build.jdbc"/>
|
||||||
<exclude name="com/samskivert/jdbc/depot/EHCacheAdapter.java" unless="ehcache.present"/>
|
|
||||||
<exclude name="com/samskivert/net/**" unless="build.net"/>
|
<exclude name="com/samskivert/net/**" unless="build.net"/>
|
||||||
<exclude name="com/samskivert/servlet/**" unless="build.servlet"/>
|
<exclude name="com/samskivert/servlet/**" unless="build.servlet"/>
|
||||||
<exclude name="com/samskivert/swing/**" unless="build.swing"/>
|
<exclude name="com/samskivert/swing/**" unless="build.swing"/>
|
||||||
|
|||||||
@@ -59,14 +59,6 @@
|
|||||||
<echo level="info">---------------</echo>
|
<echo level="info">---------------</echo>
|
||||||
<echo level="info">Other libraries</echo>
|
<echo level="info">Other libraries</echo>
|
||||||
<echo level="info">---------------</echo>
|
<echo level="info">---------------</echo>
|
||||||
<available property="ant.present" classpathref="classpath" ignoresystemclasses="true"
|
|
||||||
classname="org.apache.tools.ant.Task"/>
|
|
||||||
<echo level="info">Ant: ${ant.present}</echo>
|
|
||||||
|
|
||||||
<available property="ehcache.present" classpathref="classpath"
|
|
||||||
classname="net.sf.ehcache.Element"/>
|
|
||||||
<echo level="info">EH Cache: ${ehcache.present}</echo>
|
|
||||||
|
|
||||||
<available property="junit.present" classpathref="classpath"
|
<available property="junit.present" classpathref="classpath"
|
||||||
classname="junit.framework.Test"/>
|
classname="junit.framework.Test"/>
|
||||||
<echo level="info">JUnit: ${junit.present}</echo>
|
<echo level="info">JUnit: ${junit.present}</echo>
|
||||||
@@ -100,22 +92,6 @@
|
|||||||
</condition>
|
</condition>
|
||||||
<echo level="info">com.samskivert.jdbc: ${build.jdbc}</echo>
|
<echo level="info">com.samskivert.jdbc: ${build.jdbc}</echo>
|
||||||
|
|
||||||
<condition property="build.depot.ehcache">
|
|
||||||
<and>
|
|
||||||
<isset property="build.jdbc"/>
|
|
||||||
<isset property="ehcache.present"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<echo level="info">com.samskivert.jdbc.depot.ehcace: ${build.depot.ehcache}</echo>
|
|
||||||
|
|
||||||
<condition property="build.depot.tools">
|
|
||||||
<and>
|
|
||||||
<isset property="build.jdbc"/>
|
|
||||||
<isset property="ant.present"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<echo level="info">com.samskivert.jdbc.depot.tools: ${build.depot.tools}</echo>
|
|
||||||
|
|
||||||
<condition property="build.servlet">
|
<condition property="build.servlet">
|
||||||
<and>
|
<and>
|
||||||
<isset property="build.io"/>
|
<isset property="build.io"/>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<!-- $Id$ -->
|
<!-- $Id$ -->
|
||||||
<!-- Exports various samskivert utilities to GWT. -->
|
<!-- Exports various samskivert utilities to GWT. -->
|
||||||
<module>
|
<module>
|
||||||
<source path="jdbc/depot"/>
|
|
||||||
<source path="text"/>
|
<source path="text"/>
|
||||||
</module>
|
</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