och.comp.db.base.universal.UniversalQueries.java Source code

Java tutorial

Introduction

Here is the source code for och.comp.db.base.universal.UniversalQueries.java

Source

/*
 * Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com).
 *
 * 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 och.comp.db.base.universal;

import static java.util.Collections.*;
import static och.api.model.PropKey.*;
import static och.comp.db.base.exception.ConnectionProblemException.*;
import static och.comp.db.base.universal.field.RowField.*;
import static och.util.Util.*;
import static och.util.sql.SingleTx.*;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import och.comp.db.base.exception.ConnectionProblemException;
import och.comp.db.base.exception.UniversalSqlException;
import och.comp.db.base.universal.field.RowField;
import och.comp.db.base.universal.mapper.ReflectionMapper;
import och.comp.db.base.universal.query.SortCondition;
import och.comp.db.base.universal.query.WhereCondition;
import och.service.props.Props;
import och.util.model.Pair;

import org.apache.commons.logging.Log;

public class UniversalQueries {

    private static final ThreadLocal<String> lastQuery = new ThreadLocal<String>();

    Log log = getLog(getClass());

    String url;
    DataSource ds;
    ReflectionMapper mapper = new ReflectionMapper();
    Props props;

    public UniversalQueries(DataSource ds) {
        this(ds, null, null);
    }

    public UniversalQueries(DataSource ds, Props props) {
        this(ds, props, null);
    }

    public UniversalQueries(DataSource ds, Props props, String url) {
        this.ds = ds;
        this.url = url;
        this.props = props;
    }

    public List<Integer> update(BaseUpdateOp... updates) throws SQLException {
        try {
            return tryUpdate(updates);
        } catch (Exception e) {
            ConnectionProblemException connEx = tryFindConnProblem(url, e);
            if (connEx != null)
                throw connEx;
            else
                throw createOutException("can't update", lastQuery.get(), e);
        } finally {
            lastQuery.remove();
        }
    }

    public Integer updateOne(BaseUpdateOp update) throws SQLException {
        return update(update).get(0);
    }

    @SuppressWarnings("unchecked")
    public <T> List<T> select(SelectRows<T> select) throws SQLException {
        try {
            return (List<T>) trySelect(select);
        } catch (Exception e) {
            ConnectionProblemException connEx = tryFindConnProblem(url, e);
            if (connEx != null)
                throw connEx;
            else
                throw createOutException("can't select", lastQuery.get(), e);
        } finally {
            lastQuery.remove();
        }
    }

    private static UniversalSqlException createOutException(String preffix, String query, Exception e) {
        if (query == null)
            query = "";
        query = query.replace('\n', ' ');
        return new UniversalSqlException(preffix + ": query='" + query + "', errorMsg=" + e.getMessage(), e);
    }

    public <T> T selectOne(SelectRows<T> select) throws SQLException {
        List<T> list = select(select);
        return isEmpty(list) ? null : list.get(0);
    }

    private List<Integer> tryUpdate(BaseUpdateOp... updates) throws SQLException {

        List<Integer> out = new ArrayList<>();
        if (isEmpty(updates))
            return out;

        Connection conn = getSingleOrNewConnection(ds);
        conn.setAutoCommit(false);

        try {

            for (BaseUpdateOp update : updates) {
                Integer opResult;
                if (update instanceof UpdateRows) {
                    opResult = updateRows((UpdateRows) update, conn);
                } else if (update instanceof CreateRow) {
                    opResult = createRow((CreateRow) update, conn);
                } else if (update instanceof DeleteRows) {
                    opResult = deleteRows((DeleteRows) update, conn);
                } else {
                    opResult = 0;
                }
                out.add(opResult);
            }

            conn.commit();
            return out;

        } catch (SQLException e) {
            saveRealRollbackException(e);
            conn.rollback();
            throw e;

        } finally {
            try {
                conn.setAutoCommit(true);
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private int updateRows(UpdateRows update, Connection conn) throws SQLException {

        lastQuery.remove();

        int size = update.fields == null ? 0 : update.fields.length;
        if (size == 0)
            return 0;

        StringBuilder sb = new StringBuilder();
        sb.append("UPDATE ").append(update.table);
        sb.append("\n SET");
        boolean isFirst = true;
        for (RowField<?> q : update.fields) {
            if (!isFirst)
                sb.append(',');
            isFirst = false;
            sb.append(' ').append(q.fieldName()).append("=?");
        }
        WhereCondition condition = update.whereCondition;
        appendWhereCondition(sb, condition);

        String q = sb.toString();
        lastQuery.set(q);

        PreparedStatement ps = conn.prepareStatement(q);
        for (int i = 0; i < size; i++) {
            ps.setObject(i + 1, update.fields[i].value);
        }
        if (!isEmpty(condition) && !isEmpty(condition.values())) {
            RowField<?>[] values = condition.values();
            for (int i = 0; i < values.length; i++) {
                ps.setObject(size + i + 1, values[i].value);
            }
        }

        long start = System.currentTimeMillis();
        logQueryStart(q);

        int out = ps.executeUpdate();
        ps.close();

        logQueryEnd(start);

        return out;

    }

    private int deleteRows(DeleteRows update, Connection conn) throws SQLException {

        lastQuery.remove();

        StringBuilder sb = new StringBuilder();
        sb.append("DELETE FROM ").append(update.table);
        WhereCondition condition = update.whereCondition;
        appendWhereCondition(sb, condition);

        String q = sb.toString();
        lastQuery.set(q);

        PreparedStatement ps = conn.prepareStatement(q);
        if (!isEmpty(condition)) {
            RowField<?>[] values = condition.values();
            for (int i = 0; i < values.length; i++) {
                ps.setObject(i + 1, values[i].value);
            }
        }

        long start = System.currentTimeMillis();
        logQueryStart(q);

        int out = ps.executeUpdate();
        ps.close();

        logQueryEnd(start);

        return out;

    }

    private int createRow(CreateRow update, Connection conn) throws SQLException {

        lastQuery.remove();

        int size = update.fields == null ? 0 : update.fields.length;
        if (size == 0)
            return 0;

        StringBuilder sb = new StringBuilder();
        sb.append("INSERT INTO ").append(update.table).append(" (");
        {
            boolean isFirst = true;
            for (RowField<?> q : update.fields) {
                if (!isFirst)
                    sb.append(',');
                isFirst = false;
                sb.append(' ').append(q.fieldName());
            }
        }
        sb.append(")\n VALUES (");
        {
            boolean isFirst = true;
            for (int i = 0; i < size; i++) {
                if (!isFirst)
                    sb.append(',');
                isFirst = false;
                sb.append('?');
            }
        }
        sb.append(")");

        String q = sb.toString();
        lastQuery.set(q);

        PreparedStatement ps = conn.prepareStatement(q);
        for (int i = 0; i < size; i++) {
            ps.setObject(i + 1, update.fields[i].value);
        }

        long start = System.currentTimeMillis();
        logQueryStart(q);

        int out = ps.executeUpdate();
        ps.close();

        logQueryEnd(start);

        return out;

    }

    private List<Object> trySelect(SelectRows<?> select) throws Exception {

        lastQuery.remove();

        Class<?>[] fieldTypes = select.selectFields.fieldTypes;
        WhereCondition condition = select.whereCondition;

        if (isEmpty(fieldTypes))
            return emptyList();

        StringBuilder sb = new StringBuilder();
        sb.append("SELECT");
        {
            boolean isFirst = true;
            for (Class<?> type : fieldTypes) {
                if (!isFirst)
                    sb.append(',');
                else
                    isFirst = false;
                sb.append(' ').append(fieldName(type));
            }
        }
        sb.append("\n FROM ").append(select.table);
        appendWhereCondition(sb, condition);
        if (select.sortCondition != null)
            appendSortCondition(sb, select.sortCondition);
        if (select.limit != null)
            sb.append("\n LIMIT ").append(select.limit);
        if (select.offset != null)
            sb.append("\n OFFSET ").append(select.offset);
        sb.append(';');

        try (Connection conn = getSingleOrNewConnection(ds)) {

            String q = sb.toString();
            lastQuery.set(q);

            PreparedStatement ps = conn.prepareStatement(q);
            if (!isEmpty(condition)) {
                RowField<?>[] values = condition.values();
                for (int i = 0; i < values.length; i++) {
                    ps.setObject(i + 1, values[i].value);
                }
            }

            long startTime = System.currentTimeMillis();
            logQueryStart(q);

            ResultSet rs = ps.executeQuery();

            logQueryEnd(startTime);

            ArrayList<Object> out = new ArrayList<>();
            while (rs.next()) {
                Object entity = mapper.createEntity(rs, select.resultType, fieldTypes);
                out.add(entity);
            }

            ps.close();

            return out;
        }

    }

    public long nextSeqFor(Object tableName) throws SQLException {
        return nextSeq(tableName + "_seq");
    }

    public long nextSeq(String seqName) throws SQLException {
        try (Connection conn = getSingleOrNewConnection(ds)) {
            Statement st = conn.createStatement();
            ResultSet rs = st.executeQuery("SELECT nextval('" + seqName + "')");
            if (!rs.next())
                throw new IllegalStateException("ResultSet has no Sequence data for " + seqName);
            long out = rs.getLong(1);
            st.close();
            return out;
        }
    }

    private void appendSortCondition(StringBuilder sb, SortCondition sortCondition) {
        Pair<Class<? extends RowField<?>>, Boolean>[] columns = sortCondition.values();
        if (isEmpty(columns))
            return;
        sb.append("\n ORDER BY");
        boolean first = true;
        for (Pair<Class<? extends RowField<?>>, Boolean> pair : columns) {
            if (first)
                first = false;
            else
                sb.append(",");

            sb.append(' ').append(RowField.fieldName(pair.first)).append(" ");
            if (pair.second)
                sb.append("ASC");
            else
                sb.append("DESC");
        }

    }

    private void logQueryStart(String q) {
        if (printLogs())
            log.info("[SQL] Execute query: \n" + q);
    }

    private void logQueryEnd(long startTime) {
        if (printLogs())
            log.info("[SQL] Work time: " + (System.currentTimeMillis() - startTime) + "ms");
    }

    private boolean printLogs() {
        return props != null && props.getBoolVal(db_debug_LogSql);
    }

    private static void appendWhereCondition(StringBuilder sb, WhereCondition condition) {
        if (!isEmpty(condition)) {
            sb.append("\n WHERE ");
            condition.setWhereQuery(sb);
        }
    }

}