Index: src/net/lemnik/eodsql/Update.java
===================================================================
--- src/net/lemnik/eodsql/Update.java (revision 217)
+++ src/net/lemnik/eodsql/Update.java (working copy)
@@ -93,6 +93,13 @@
String sql() default "";
/**
+ * If true
, the update will be performed as a batch update. All parameters of the
+ * method need to be either arrays or lists of the same length in that case. The sql
+ * query applies to each element in the array / list individually.
+ */
+ public boolean batchUpdate() default false;
+
+ /**
*
* The specification of how auto-generated keys are to be returned for this
* @Update
query.
Index: src/net/lemnik/eodsql/impl/UpdateMethodImplementation.java
===================================================================
--- src/net/lemnik/eodsql/impl/UpdateMethodImplementation.java (revision 217)
+++ src/net/lemnik/eodsql/impl/UpdateMethodImplementation.java (working copy)
@@ -1,6 +1,9 @@
package net.lemnik.eodsql.impl;
+import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -34,7 +37,11 @@
*/
class UpdateMethodImplementation extends AbstractMethodImplementation {
private final GeneratedKeys keys;
-
+
+ private final boolean batchUpdate;
+
+ private final BatchParameterKinds batchParameterKinds;
+
UpdateMethodImplementation(final Method method) throws ParseException {
final Update update = method.getAnnotation(Update.class);
@@ -45,16 +52,51 @@
}
keys = update.keys();
+
+ batchUpdate = update.batchUpdate();
+ batchParameterKinds = batchUpdate ? new BatchParameterKinds(method) : null;
- query = Query.getQuery(queryString, method.getParameterTypes());
+ query = Query.getQuery(queryString, getParameterTypes(method));
final Map parameters = extractReturnTypeMapperParameters(update);
if(keys != GeneratedKeys.NO_KEYS_RETURNED) {
wrapper = ResultSetWrapper.get(method.getGenericReturnType(), parameters);
}
}
-
+
+ private Class>[] getParameterTypes(Method method) {
+ if (batchUpdate) {
+ final Type[] genericTypes = method.getGenericParameterTypes();
+ final Class>[] types = new Class>[genericTypes.length];
+ for (int i = 0; i < types.length; ++i) {
+ if (genericTypes[i] instanceof Class>) {
+ types[i] = (Class>) genericTypes[i];
+ if (types[i].isArray()) {
+ types[i] = types[i].getComponentType();
+ }
+ } else if (genericTypes[i] instanceof ParameterizedType) {
+ final ParameterizedType ptype = (ParameterizedType) genericTypes[i];
+ if (Iterable.class.isAssignableFrom((Class>) ptype.getRawType())) {
+ types[i] = (Class>) ptype.getActualTypeArguments()[0];
+ } else {
+ types[i] = (Class>) ptype.getRawType();
+ if (types[i].isArray()) {
+ types[i] = types[i].getComponentType();
+ }
+ }
+ } else if (genericTypes[i] instanceof GenericArrayType) {
+ types[i] = (Class>) ((GenericArrayType) genericTypes[i]).getGenericComponentType();
+ } else {
+ throw new InvalidQueryException("Unsupported type: " + genericTypes[i]);
+ }
+ }
+ return types;
+ } else {
+ return method.getParameterTypes();
+ }
+ }
+
private Map extractReturnTypeMapperParameters(final Update update) {
BindingType bindingType = BindingType.KEYS_BINDING;
@@ -87,10 +129,18 @@
}
context.setResource(new StatementResource(statement));
- fillPreparedStatementParameters(context, statement);
- statement.executeUpdate();
-
+ if (batchUpdate) {
+ for (Context ctx : new BatchContextProvider(batchParameterKinds, context)) {
+ fillPreparedStatementParameters(ctx, statement);
+ statement.addBatch();
+ }
+ statement.executeBatch();
+ } else {
+ fillPreparedStatementParameters(context, statement);
+ statement.executeUpdate();
+ }
+
if(keys != GeneratedKeys.NO_KEYS_RETURNED) {
final ResultSet results = statement.getGeneratedKeys();
context.setResource(new ResultSetResource(results));
@@ -124,6 +174,10 @@
"GeneratedKeys.NO_KEYS_RETURNED must have a return " +
"type of void or int.", method);
}
+
+ if (update.batchUpdate()) {
+ new BatchParameterKinds(method);
+ }
Query.validate(sql, method);
Index: src/net/lemnik/eodsql/impl/BatchContextProvider.java
--- src/net/lemnik/eodsql/impl/BatchContextProvider.java (revision 0)
+++ src/net/lemnik/eodsql/impl/BatchContextProvider.java (revision 0)
@@ -0,0 +1,59 @@
+package net.lemnik.eodsql.impl;
+
+import java.util.Iterator;
+
+import net.lemnik.eodsql.Update;
+import net.lemnik.eodsql.spi.Context;
+
+/**
+ * Created on 2009/05/23
+ * @author Bernd Rinn
+ */
+final class BatchContextProvider implements Iterable> {
+ private final BatchParameterKinds batchParameterKinds;
+ private final Context context;
+
+ BatchContextProvider(BatchParameterKinds batchParameterKinds, Context context) {
+ this.batchParameterKinds = batchParameterKinds;
+ this.context = context;
+ }
+
+ public Iterator> iterator() {
+ return new Iterator>() {
+ final Iterator>[] batchIterators = new Iterator>[batchParameterKinds.getSizeBatchParameters()];
+ final Iterator>[] iterators = new Iterator>[batchParameterKinds.getSize()];
+ {
+ int j = 0;
+ for (int i = 0; i < iterators.length; ++i) {
+ final BatchParameterKind kind = batchParameterKinds.get(i);
+ iterators[i] = kind.getIterator(context.getParameters()[i]);
+ if (kind != BatchParameterKind.OTHER)
+ {
+ batchIterators[j++] = iterators[i];
+ }
+ }
+ }
+ public boolean hasNext() {
+ boolean hasNext = batchIterators[0].hasNext();
+ for (int i = 1; i < batchIterators.length; ++i) {
+ if (batchIterators[i].hasNext() != hasNext) {
+ throw new IllegalStateException("Batch update: iterators disagree on batch size.");
+ }
+ }
+ return hasNext;
+ }
+
+ public Context next() {
+ final Object[] parameters = new Object[iterators.length];
+ for (int i = 0; i < parameters.length; ++i) {
+ parameters[i] = iterators[i].next();
+ }
+ return new Context(context.getAnnotation(), parameters);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+}
Index: src/net/lemnik/eodsql/impl/BatchParameterKind.java
--- src/net/lemnik/eodsql/impl/BatchParameterKind.java (revision 0)
+++ src/net/lemnik/eodsql/impl/BatchParameterKind.java (revision 0)
@@ -0,0 +1,66 @@
+package net.lemnik.eodsql.impl;
+
+import java.lang.reflect.Array;
+import java.util.Iterator;
+
+/**
+ * Created on 2009/05/23
+ * @author Bernd Rinn
+ */
+enum BatchParameterKind {
+ ITERABLE, ARRAY, OTHER;
+
+ static BatchParameterKind getKind(final Class> type) {
+ if (type.isArray()) {
+ return ARRAY;
+ } else if (Iterable.class.isAssignableFrom(type)) {
+ return ITERABLE;
+ } else {
+ return OTHER;
+ }
+ }
+
+ Iterator> getIterator(final Object obj) {
+ switch(this) {
+ case ITERABLE:
+ return ((Iterable>) obj).iterator();
+ case ARRAY:
+ return new Iterator()
+ {
+ int i = 0;
+ public boolean hasNext()
+ {
+ return i < Array.getLength(obj);
+ }
+
+ public Object next()
+ {
+ return Array.get(obj, i++);
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ default:
+ return new Iterator()
+ {
+ public boolean hasNext()
+ {
+ return true;
+ }
+
+ public Object next()
+ {
+ return obj;
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ }
+}
Index: src/net/lemnik/eodsql/impl/BatchParameterKinds.java
--- src/net/lemnik/eodsql/impl/BatchParameterKinds.java (revision 0)
+++ src/net/lemnik/eodsql/impl/BatchParameterKinds.java (revision 0)
@@ -0,0 +1,56 @@
+package net.lemnik.eodsql.impl;
+
+import java.lang.reflect.Method;
+
+/**
+ * Created on 2009/05/23
+ * @author Bernd Rinn
+ */
+final class BatchParameterKinds {
+ private final BatchParameterKind[] batchParameterKinds;
+
+ private final int batchParameterCount;
+
+ BatchParameterKinds(final Method method) {
+ batchParameterKinds = getParameterKinds(method);
+ batchParameterCount = countBatchParameters();
+ if (batchParameterCount == 0) {
+ throw new IllegalArgumentException("Batch update: no batch parameters found.");
+ }
+ }
+
+ private BatchParameterKind[] getParameterKinds(Method method) {
+ final Class>[] parameterTypes = method.getParameterTypes();
+ BatchParameterKind[] kinds = new BatchParameterKind[parameterTypes.length];
+ for (int i = 0; i < kinds.length; ++i) {
+ kinds[i] = BatchParameterKind.getKind(parameterTypes[i]);
+ }
+ return kinds;
+ }
+
+ private int countBatchParameters() {
+ int count = 0;
+ for (BatchParameterKind kind : batchParameterKinds) {
+ if (kind != BatchParameterKind.OTHER) {
+ ++count;
+ }
+ }
+ return count;
+ }
+
+ BatchParameterKind get(int i)
+ {
+ return batchParameterKinds[i];
+ }
+
+ int getSize()
+ {
+ return batchParameterKinds.length;
+ }
+
+ int getSizeBatchParameters()
+ {
+ return batchParameterCount;
+ }
+
+}