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; + } + +}