com.github.davidmoten.rx.jdbc.QueryUpdate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rxjava-jdbc Show documentation
Show all versions of rxjava-jdbc Show documentation
rx-java Observables for jdbc
package com.github.davidmoten.rx.jdbc;
import static com.github.davidmoten.rx.jdbc.Conditions.checkArgument;
import static com.github.davidmoten.rx.jdbc.Conditions.checkNotNull;
import static com.github.davidmoten.rx.jdbc.Queries.bufferedParameters;
import java.sql.ResultSet;
import java.util.List;
import com.github.davidmoten.rx.Transformers;
import com.github.davidmoten.rx.jdbc.NamedParameters.JdbcQuery;
import com.github.davidmoten.rx.jdbc.tuple.Tuple2;
import com.github.davidmoten.rx.jdbc.tuple.Tuple3;
import com.github.davidmoten.rx.jdbc.tuple.Tuple4;
import com.github.davidmoten.rx.jdbc.tuple.Tuple5;
import com.github.davidmoten.rx.jdbc.tuple.Tuple6;
import com.github.davidmoten.rx.jdbc.tuple.Tuple7;
import com.github.davidmoten.rx.jdbc.tuple.TupleN;
import com.github.davidmoten.rx.jdbc.tuple.Tuples;
import rx.Observable;
import rx.Observable.Transformer;
import rx.functions.Func1;
import rx.functions.Func2;
/**
* Always emits an Observable of size 1 containing the number of
* affected records.
*
* @param
* type of returned observable (Integer for count, custom for
* returning generated keys)
*/
final public class QueryUpdate implements Query {
private final JdbcQuery jdbcQuery;
private final Observable parameters;
private final QueryContext context;
private final Observable> depends;
// nullable!
private final ResultSetMapper extends T> returnGeneratedKeysFunction;
private static final Func1, List> toFinalArrayList = new Func1, List>() {
@Override
public List call(List list) {
return new ArrayListFinal(list);
}
};
/**
* Private constructor.
*
* @param sql
* @param parameters
* @param depends
* @param context
* @param returnGeneratedKeysFunction
* nullable!
*/
private QueryUpdate(String sql, Observable parameters, Observable> depends,
QueryContext context, ResultSetMapper extends T> returnGeneratedKeysFunction) {
checkNotNull(sql);
checkNotNull(parameters);
checkNotNull(depends);
checkNotNull(context);
checkArgument(context.batchSize() <= 1 || returnGeneratedKeysFunction == null,
"Cannot return generated keys from a batched update");
this.jdbcQuery = NamedParameters.parse(sql);
this.parameters = parameters;
this.depends = depends;
this.context = context;
this.returnGeneratedKeysFunction = returnGeneratedKeysFunction;
}
@Override
public String sql() {
return jdbcQuery.sql();
}
@Override
public Observable parameters() {
return parameters;
}
@Override
public QueryContext context() {
return context;
}
@Override
public String toString() {
return "QueryUpdate [sql=" + sql() + "]";
}
@Override
public Observable> depends() {
return depends;
}
@Override
public List names() {
return jdbcQuery.names();
}
/**
* Returns the results of an update query. Should be an {@link Observable}
* of size 1 containing the number of records affected by the update (or
* insert) statement.
*
* @param query
* @return
*/
@SuppressWarnings("unchecked")
public Observable count() {
return (Observable) QueryUpdate.get(this);
}
public ResultSetMapper extends T> returnGeneratedKeysFunction() {
return returnGeneratedKeysFunction;
}
boolean returnGeneratedKeys() {
return returnGeneratedKeysFunction != null;
}
static Observable get(QueryUpdate queryUpdate) {
if (queryUpdate.context().batchSize() > 1) {
return bufferedParameters(queryUpdate) //
// mark the last parameter list as such
.compose(Transformers.mapLast(toFinalArrayList))//
// execute query for each set of parameters
.concatMap(queryUpdate.executeOnce());
} else {
return bufferedParameters(queryUpdate) //
// execute query for each set of parameters
.concatMap(queryUpdate.executeOnce());
}
}
/**
* Returns a {@link Func1} that itself returns the results of pushing
* parameters through an update query.
*
* @param query
* @return
*/
private Func1, Observable> executeOnce() {
return new Func1, Observable>() {
@Override
public Observable call(final List params) {
if (jdbcQuery.sql().equals(QueryUpdateOnSubscribe.BEGIN_TRANSACTION)) {
context.beginTransactionSubscribe();
}
Observable result = createUpdate(params).subscribeOn(context.scheduler());
if (jdbcQuery.sql().equals(QueryUpdateOnSubscribe.COMMIT)
|| jdbcQuery.sql().equals(QueryUpdateOnSubscribe.ROLLBACK))
context.endTransactionSubscribe();
return result;
}
};
}
/**
* Returns the results of an update query. Should return an
* {@link Observable} of size one containing the rows affected count.
*
* @param query
* @param parameters
* @return
*/
private Observable createUpdate(final List parameters) {
return QueryUpdateOnSubscribe.create(this, parameters);
}
/**
* Builds a {@link QueryUpdate}.
*/
final public static class Builder {
private static final int DEFAULT_BATCH_SIZE = 1;
/**
* Standard query builder.
*/
private final QueryBuilder builder;
private int batchSize = DEFAULT_BATCH_SIZE;
/**
* Constructor.
*
* @param sql
* @param db
*/
public Builder(String sql, Database db) {
this.builder = new QueryBuilder(sql, db);
}
/**
* Appends the given parameters to the parameter list for the query. If
* there are more parameters than required for one execution of the
* query then more than one execution of the query will occur.
*
* @param parameters
* @return this
*/
public Builder parameters(Observable parameters) {
builder.parameters(parameters);
return this;
}
/**
* Appends the given parameter values to the parameter list for the
* query. If there are more parameters than required for one execution
* of the query then more than one execution of the query will occur.
*
* @param objects
* @return this
*/
public Builder parameters(Object... objects) {
builder.parameters(objects);
return this;
}
/**
* Appends a parameter to the parameter list for the query. If there are
* more parameters than required for one execution of the query then
* more than one execution of the query will occur.
*
* @param value
* @return this
*/
public Builder parameter(Object value) {
builder.parameter(value);
return this;
}
/**
* Sets a named parameter. If name is null throws a
* {@link NullPointerException}. If value is instance of Observable then
* throws an {@link IllegalArgumentException}.
*
* @param name
* the parameter name. Cannot be null.
* @param value
* the parameter value
*/
public Builder parameter(String name, Object value) {
builder.parameter(name, value);
return this;
}
/**
* Appends a parameter to the parameter list for the query for a CLOB
* parameter and handles null appropriately. If there are more
* parameters than required for one execution of the query then more
* than one execution of the query will occur.
*
* @param value
* the string to insert in the CLOB column
* @return this
*/
public Builder parameterClob(String value) {
builder.parameter(Database.toSentinelIfNull(value));
return this;
}
/**
* Appends a parameter to the parameter list for the query for a CLOB
* parameter and handles null appropriately. If there are more
* parameters than required for one execution of the query then more
* than one execution of the query will occur.
*
* @param value
* @return this
*/
public Builder parameterBlob(byte[] bytes) {
builder.parameter(Database.toSentinelIfNull(bytes));
return this;
}
/**
* Appends a dependency to the dependencies that have to complete their
* emitting before the query is executed.
*
* @param dependency
* @return this
*/
public Builder dependsOn(Observable> dependency) {
builder.dependsOn(dependency);
return this;
}
/**
* Appends a dependency on the result of the last transaction (
* true
for commit or false
for rollback) to
* the dependencies that have to complete their emitting before the
* query is executed.
*
* @return this
*/
public Builder dependsOnLastTransaction() {
builder.dependsOnLastTransaction();
return this;
}
/**
* Returns a builder used to specify how to process the generated keys
* {@link ResultSet}. Not all jdbc drivers support this functionality
* and some have limitations in their support (h2 for instance only
* returns the last generated key when multiple inserts happen in the
* one statement).
*
* @return a builder used to specify how to process the generated keys
* ResultSet
*/
public ReturnGeneratedKeysBuilder returnGeneratedKeys() {
Conditions.checkArgument(batchSize == 1,
"Cannot return generated keys if batchSize > 1");
return new ReturnGeneratedKeysBuilder(builder);
}
/**
* Returns an {@link Observable} with the count of rows affected by the
* update statement.
*
* @return Observable of counts of rows affected.
*/
public Observable count() {
QueryContext ctxt;
if (batchSize > 1) {
ctxt = builder.context().batched(batchSize);
} else {
ctxt = builder.context();
}
return new QueryUpdate(builder.sql(), builder.parameters(), builder.depends(),
ctxt, null).count();
}
/**
* Executes the update query immediately, blocking till completion and
* returns total of counts of records affected.
*
* @return total of counts of records affected by update queries
*/
public int execute() {
return count().reduce(0, TotalHolder.TOTAL).toBlocking().single();
}
private static final class TotalHolder {
static final Func2 TOTAL = new Func2() {
@Override
public Integer call(Integer a, Integer b) {
return a + b;
}
};
}
/**
* Returns an {@link Transformer} to allow the query to be pushed
* parameters via the {@link Observable#compose(Transformer)} method.
*
* @return Transformer that acts on parameters
*/
public Transformer
© 2015 - 2025 Weber Informatics LLC | Privacy Policy