All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.davidmoten.rx.jdbc.QueryUpdate Maven / Gradle / Ivy

There is a newer version: 0.7.19
Show newest version
package com.github.davidmoten.rx.jdbc;

import static com.github.davidmoten.rx.jdbc.Conditions.checkNotNull;
import static com.github.davidmoten.rx.jdbc.Queries.bufferedParameters;

import java.util.List;

import rx.Observable;
import rx.Observable.Operator;
import rx.functions.Func1;

/**
 * Always emits an Observable of size 1 containing the number of
 * affected records.
 */
final public class QueryUpdate implements Query {

    private final String sql;
    private final Observable parameters;
    private final QueryContext context;
    private final Observable depends;

    /**
     * Private constructor.
     * 
     * @param sql
     * @param parameters
     * @param depends
     * @param context
     */
    private QueryUpdate(String sql, Observable parameters, Observable depends,
            QueryContext context) {
        checkNotNull(sql);
        checkNotNull(parameters);
        checkNotNull(depends);
        checkNotNull(context);
        this.sql = sql;
        this.parameters = parameters;
        this.depends = depends;
        this.context = context;
    }

    @Override
    public String sql() {
        return 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;
    }

    /**
     * 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
     */
    public Observable count() {
        return bufferedParameters(this)
        // execute query for each set of parameters
                .concatMap(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 (sql.equals(QueryUpdateOperation.BEGIN_TRANSACTION)) {
                    context.beginTransactionSubscribe();
                }
                Observable result = executeOnce(params).subscribeOn(context.scheduler());
                if (sql.equals(QueryUpdateOperation.COMMIT)
                        || sql.equals(QueryUpdateOperation.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 executeOnce(final List parameters) {
        return QueryUpdateOperation.execute(this, parameters);
    }

    /**
     * Builds a {@link QueryUpdate}.
     */
    final public static class Builder {

        /**
         * Standard query builder.
         */
        private final QueryBuilder builder;

        /**
         * 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;
        }

        /**
         * 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 an {@link Observable} with the count of rows affected by the
         * update statement.
         * 
         * @return
         */
        public Observable count() {
            return new QueryUpdate(builder.sql(), builder.parameters(), builder.depends(),
                    builder.context()).count();
        }

        /**
         * Returns an {@link Operator} to allow the query to be pushed
         * parameters via the {@link Observable#lift(Operator)} method.
         * 
         * @return operator that acts on parameters
         */
        public Operator parameterOperator() {
            return new QueryUpdateOperator(this, OperatorType.PARAMETER);
        }

        /**
         * Returns an {@link Operator} to allow the query to be pushed
         * dependencies via the {@link Observable#lift(Operator)} method.
         * 
         * @return operator that acts on dependencies
         */
        public Operator dependsOnOperator() {
            return new QueryUpdateOperator(this, OperatorType.DEPENDENCY);
        }

        /**
         * Returns an {@link Operator} to allow the query to be run once per
         * parameter list in the source.
         * 
         * @return
         */
        public Operator, Observable> parameterListOperator() {
            return new QueryUpdateOperatorFromObservable(this);
        }

        public Builder clearParameters() {
            builder.clearParameters();
            return this;
        }
    }
}