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

buckelieg.simpletools.db.SelectQuery Maven / Gradle / Ivy

/*
* Copyright 2016 Anatoly Kutyakov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package buckelieg.simpletools.db;

import org.apache.log4j.Logger;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.NotThreadSafe;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

@NotThreadSafe
@ParametersAreNonnullByDefault
class SelectQuery implements Iterable, Iterator, Spliterator, Select {

    private static final Logger LOG = Logger.getLogger(SelectQuery.class);

    final S statement;
    private final AtomicBoolean hasNext;
    private final AtomicBoolean hasMoved;
    ResultSet rs;
    private ImmutableResultSet wrapper;
    private int batchSize = -1;

    SelectQuery(S statement) {
        this.statement = Objects.requireNonNull(statement, "Statement must not be null");
        this.hasMoved = new AtomicBoolean();
        this.hasNext = new AtomicBoolean();
    }

    @Override
    public final Iterator iterator() {
        return this;
    }

    @Override
    public final boolean hasNext() {
        try {
            if (hasMoved.get()) {
                return hasNext.get();
            }
            hasNext.set(doHasNext());
            hasMoved.set(true);
        } catch (SQLException e) {
            hasNext.set(false);
        }
        if (!hasNext.get()) {
            close();
        }
        return hasNext.get();
    }

    protected boolean doHasNext() throws SQLException {
        return rs != null && rs.next();
    }

    @Override
    public final ResultSet next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        hasMoved.set(false);
        return wrapper;
    }

    @Nullable
    @Override
    public final  T single(Try._1 mapper) {
        try {
            return Objects.requireNonNull(mapper, "Mapper must be provided").doTry(execute().iterator().next());
        } catch (SQLException | NoSuchElementException e) {
            throw new SQLRuntimeException(e);
        } finally {
            close();
        }
    }

    @Nonnull
    @Override
    public final Iterable execute() {
        try {
            if (batchSize >= 0) {
                statement.setFetchSize(batchSize);
            }
            doExecute();
            if (rs != null) {
                this.wrapper = new ImmutableResultSet(rs);
            }
        } catch (SQLException e) {
            throw new SQLRuntimeException(e);
        }
        return this;
    }

    protected void doExecute() throws SQLException {
        this.rs = statement.executeQuery();
    }

    @Nonnull
    @Override
    public final Select batchSize(int size) {
        try {
            batchSize = rs != null && rs.getFetchSize() >= size ? rs.getFetchSize() : size > 0 ? size : 0; // 0 value is ignored by ResultSet.setFetchSize
        } catch (SQLException e) {
            throw new SQLRuntimeException(e);
        }
        return this;
    }

    @Override
    public final Spliterator spliterator() {
        return this;
    }

    @Override
    public final boolean tryAdvance(Consumer action) {
        Objects.requireNonNull(action);
        if (hasNext()) {
            action.accept(next());
            return true;
        }
        return false;
    }

    @Override
    public final Spliterator trySplit() {
        return null; // not splittable. Parallel streams would not gain any performance benefits yet. May be implemented in future
    }

    @Override
    public final long estimateSize() {
        return Long.MAX_VALUE;
    }

    @Override
    public final int characteristics() {
        return Spliterator.IMMUTABLE | Spliterator.ORDERED | Spliterator.NONNULL;
    }

    @Override
    public void forEachRemaining(Consumer action) {
        /*Objects.requireNonNull(action);
        while (hasNext()) {
            action.accept(next());
        }*/
        while (tryAdvance(action)) {
        }
    }

    final void close() {
        try {
            if (statement != null && !statement.isClosed()) {
                statement.close(); // by JDBC spec: subsequently closes all result sets opened by this statement
            }
        } catch (SQLException e) {
            throw new SQLRuntimeException(e);
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy