Package org.b3log.latke.repository.jdbc

Source Code of org.b3log.latke.repository.jdbc.JdbcRepository

/*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team
*
* 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 org.b3log.latke.repository.jdbc;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;
import org.b3log.latke.Keys;
import org.b3log.latke.cache.Cache;
import org.b3log.latke.cache.CacheFactory;
import org.b3log.latke.model.Pagination;
import org.b3log.latke.repository.Filter;
import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.CompositeFilter;
import org.b3log.latke.repository.PropertyFilter;
import org.b3log.latke.repository.Projection;
import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.Repository;
import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.repository.Transaction;
import org.b3log.latke.repository.jdbc.util.Connections;
import org.b3log.latke.repository.jdbc.util.JdbcRepositories;
import org.b3log.latke.repository.jdbc.util.JdbcUtil;
import org.b3log.latke.util.Ids;
import org.b3log.latke.util.Strings;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
* JDBC repository implementation.
*
* @author <a href="mailto:wmainlove@gmail.com">Love Yao</a>
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.7, Jul 17, 2012
*/
@SuppressWarnings("unchecked")
public final class JdbcRepository implements Repository {

    /**
     * Logger.
     */
    private static final Logger LOGGER = Logger.getLogger(JdbcRepository.class.getName());
    /**
     * Repository name.
     */
    private String name;
    /**
     * Is cache enabled?
     */
    private boolean cacheEnabled = true;
    /**
     * Writable?
     */
    private boolean writable = true;
    /**
     * Cache key prefix.
     */
    public static final String CACHE_KEY_PREFIX = "repository";
    /**
     * Repository cache count.
     */
    private static final String REPOSITORY_CACHE_COUNT = "#count";
    /**
     * Repository cache.
     * <p>
     * &lt;oId, JSONObject&gt;
     * </p>
     */
    public static final Cache<String, Serializable> CACHE;
    /**
     * Repository cache name.
     */
    public static final String REPOSITORY_CACHE_NAME = "repositoryCache";
    /**
     * The current transaction.
     */
    public static final ThreadLocal<JdbcTransaction> TX = new InheritableThreadLocal<JdbcTransaction>();

    static {
        CACHE = (Cache<String, Serializable>) CacheFactory.getCache(REPOSITORY_CACHE_NAME);
    }

    @Override
    public String add(final JSONObject jsonObject) throws RepositoryException {
        final JdbcTransaction currentTransaction = TX.get();
        if (null == currentTransaction) {
            throw new RepositoryException("Invoking add() outside a transaction");
        }

        final Connection connection = getConnection();
        final List<Object> paramList = new ArrayList<Object>();
        final StringBuilder sql = new StringBuilder();
        String id = null;

        try {
            id = buildAddSql(jsonObject, paramList, sql);
            JdbcUtil.executeSql(sql.toString(), paramList, connection);
        } catch (final SQLException se) {
            LOGGER.log(Level.SEVERE, "add:"
                                     + se.getMessage(), se);
            throw new JDBCRepositoryException(se);
        } catch (final Exception e) {
            LOGGER.log(Level.SEVERE, "add:"
                                     + e.getMessage(), e);
            throw new RepositoryException(e);
        }

        return id;
    }

    /**
     * buildAddSql.
     * @param jsonObject jsonObject
     * @param paramlist paramlist
     * @param sql sql
     * @return id
     * @throws Exception  exception
     */
    private String buildAddSql(final JSONObject jsonObject, final List<Object> paramlist, final StringBuilder sql)
            throws Exception {
        String ret;

        if (!jsonObject.has(Keys.OBJECT_ID)) {
            ret = Ids.genTimeMillisId();
            jsonObject.put(Keys.OBJECT_ID, ret);
        } else {
            ret = jsonObject.getString(Keys.OBJECT_ID);
        }

        setProperties(jsonObject, paramlist, sql);

        return ret;
    }

    /**
     * setProperties.
     *
     * @param jsonObject jsonObject
     * @param paramlist paramlist
     * @param sql sql
     * @throws Exception exception
     */
    private void setProperties(final JSONObject jsonObject, final List<Object> paramlist, final StringBuilder sql) throws Exception {
        final Iterator<String> keys = jsonObject.keys();

        final StringBuilder insertString = new StringBuilder();
        final StringBuilder wildcardString = new StringBuilder();

        boolean isFirst = true;
        String key;
        Object value;

        while (keys.hasNext()) {
            key = keys.next();

            if (isFirst) {
                insertString.append("(").append(key);
                wildcardString.append("(?");
                isFirst = false;
            } else {
                insertString.append(",").append(key);
                wildcardString.append(",?");
            }

            value = jsonObject.get(key);
            paramlist.add(value);

            if (!keys.hasNext()) {
                insertString.append(")");
                wildcardString.append(")");
            }
        }

        /*
         * TODO: Y, table name Prefix.
         */
        sql.append("insert into ").append(getName()).append(insertString).append(" value ").append(wildcardString);
    }

    @Override
    public void update(final String id, final JSONObject jsonObject) throws RepositoryException {
        if (Strings.isEmptyOrNull(id)) {
            return;
        }

        final JdbcTransaction currentTransaction = TX.get();

        if (null == currentTransaction) {
            throw new RepositoryException("Invoking update() outside a transaction");
        }

        final JSONObject oldJsonObject = get(id);

        final Connection connection = getConnection();
        final List<Object> paramList = new ArrayList<Object>();
        final StringBuilder sqlBuilder = new StringBuilder();
        try {
            update(id, oldJsonObject, jsonObject, paramList, sqlBuilder);

            final String sql = sqlBuilder.toString();
            if (Strings.isEmptyOrNull(sql)) {
                return;
            }

            JdbcUtil.executeSql(sql, paramList, connection);
        } catch (final SQLException se) {
            LOGGER.log(Level.SEVERE, "update:"
                                     + se.getMessage(), se);
            throw new JDBCRepositoryException(se);
        } catch (final Exception e) {
            LOGGER.log(Level.SEVERE, "update:"
                                     + e.getMessage(), e);
            throw new RepositoryException(e);
        }
    }

    /**
     *
     * update.
     *
     * @param id id
     * @param oldJsonObject oldJsonObject
     * @param jsonObject newJsonObject
     * @param paramList paramList
     * @param sql sql
     * @throws JSONException JSONException
     */
    private void update(final String id, final JSONObject oldJsonObject,
                        final JSONObject jsonObject, final List<Object> paramList,
                        final StringBuilder sql) throws JSONException {
        final JSONObject needUpdateJsonObject = getNeedUpdateJsonObject(oldJsonObject, jsonObject);

        if (needUpdateJsonObject.length() == 0) {
            LOGGER.log(Level.INFO, "nothing to update [{0}] for repository[{1}]", new Object[]{id, getName()});
            return;
        }

        setUpdateProperties(id, needUpdateJsonObject, paramList, sql);
    }

    /**
     * setUpdateProperties.
     *
     * @param id id
     * @param needUpdateJsonObject needUpdateJsonObject
     * @param paramList paramList
     * @param sql sql
     * @throws JSONException JSONException
     */
    private void setUpdateProperties(final String id, final JSONObject needUpdateJsonObject,
                                     final List<Object> paramList, final StringBuilder sql) throws JSONException {
        final Iterator<String> keys = needUpdateJsonObject.keys();
        String key;

        boolean isFirst = true;
        final StringBuilder wildcardString = new StringBuilder();

        while (keys.hasNext()) {
            key = keys.next();

            if (isFirst) {
                wildcardString.append(" set ").append(key).append("=?");
                isFirst = false;
            } else {
                wildcardString.append(",").append(key).append("=?");
            }

            paramList.add(needUpdateJsonObject.get(key));
        }

        sql.append("update ").append(getName()).append(wildcardString).append(" where ").
                append(JdbcRepositories.OID).append("=").append("?");
        paramList.add(id);
    }

    /**
     *
     * getNeedUpdateJsonObject.
     *
     * @param oldJsonObject oldJsonObject
     * @param jsonObject newJsonObject
     * @return JSONObject
     * @throws JSONException jsonObject
     */
    private JSONObject getNeedUpdateJsonObject(final JSONObject oldJsonObject, final JSONObject jsonObject) throws JSONException {
        final JSONObject needUpdateJsonObject = new JSONObject();

        final Iterator<String> keys = jsonObject.keys();

        String key;
        while (keys.hasNext()) {
            key = keys.next();

            if (jsonObject.get(key) == null
                && oldJsonObject.get(key) == null) {
                // ???????????????????????????
                needUpdateJsonObject.put(key, jsonObject.get(key));
            } else if (!jsonObject.getString(key).equals(
                    oldJsonObject.getString(key))) {
                needUpdateJsonObject.put(key, jsonObject.get(key));
            }
        }

        return needUpdateJsonObject;
    }

    @Override
    public void remove(final String id) throws RepositoryException {
        if (Strings.isEmptyOrNull(id)) {
            return;
        }

        final JdbcTransaction currentTransaction = TX.get();

        if (null == currentTransaction) {
            throw new RepositoryException("Invoking remove() outside a transaction");
        }

        final StringBuilder sql = new StringBuilder();
        final Connection connection = getConnection();

        try {
            remove(id, sql);
            JdbcUtil.executeSql(sql.toString(), connection);
        } catch (final SQLException se) {
            LOGGER.log(Level.SEVERE, "update:"
                                     + se.getMessage(), se);
            throw new JDBCRepositoryException(se);
        } catch (final Exception e) {
            LOGGER.log(Level.SEVERE, "remove:"
                                     + e.getMessage(), e);
            throw new RepositoryException(e);
        }
    }

    /**
     * remove record.
     *
     * @param id id
     * @param sql sql
     */
    private void remove(final String id, final StringBuilder sql) {
        sql.append("delete from ").append(getName()).append(" where ").append(JdbcRepositories.OID).append("='").
                append(id).append("'");
    }

    @Override
    public JSONObject get(final String id) throws RepositoryException {
        JSONObject ret = null;

        if (cacheEnabled) {
            final String cacheKey = CACHE_KEY_PREFIX
                                    + id;
            ret = (JSONObject) CACHE.get(cacheKey);
            if (null != ret) {
                LOGGER.log(Level.FINER, "Got an object[cacheKey={0}] from repository cache[name={1}]", new Object[]{cacheKey, getName()});
                return ret;
            }
        }

        final StringBuilder sql = new StringBuilder();
        final Connection connection = getConnection();

        try {
            get(sql);
            final ArrayList<Object> paramList = new ArrayList<Object>();
            paramList.add(id);
            ret = JdbcUtil.queryJsonObject(sql.toString(), paramList, connection, getName());

            if (cacheEnabled) {
                final String cacheKey = CACHE_KEY_PREFIX
                                        + id;
                CACHE.putAsync(cacheKey, ret);
                LOGGER.log(Level.FINER, "Added an object[cacheKey={0}] in repository cache[{1}]", new Object[]{cacheKey, getName()});
            }

        } catch (final SQLException e) {
            throw new JDBCRepositoryException(e);
        } catch (final Exception e) {
            LOGGER.log(Level.SEVERE, "get:"
                                     + e.getMessage(), e);
            throw new RepositoryException(e);
        } finally {
            closeQueryConnection(connection);
        }

        return ret;
    }

    /**
     * get.
     *
     * @param sql sql
     */
    private void get(final StringBuilder sql) {
        sql.append("select * from ").append(getName()).append(" where ").append(JdbcRepositories.OID).append("=").append("?");
    }

    @Override
    public Map<String, JSONObject> get(final Iterable<String> ids) throws RepositoryException {
        final Map<String, JSONObject> map = new HashMap<String, JSONObject>();
        JSONObject jsonObject;

        for (final String id : ids) {
            jsonObject = get(id);
            map.put(jsonObject.optString(JdbcRepositories.OID), jsonObject);
        }

        return map;
    }

    @Override
    public boolean has(final String id) throws RepositoryException {
        // final StringBuilder sql = new StringBuilder("select count("
        // + JdbcRepositories.OID +
        // ") from ").append(getName()).append(" where ").append(
        // JdbcRepositories.OID).append("=").append(id);
        //
        // return count(sql, new ArrayList<Object>()) > 0;

        // using get() method to get result.

        return null != get(id);
    }

    @Override
    public JSONObject get(final Query query) throws RepositoryException {
        JSONObject ret = new JSONObject();

        final String cacheKey = CACHE_KEY_PREFIX
                                + query.getCacheKey() + "_" + getName();
        if (cacheEnabled) {
            ret = (JSONObject) CACHE.get(cacheKey);
            if (null != ret) {
                LOGGER.log(Level.FINER, "Got query result[cacheKey={0}] from repository cache[name={1}]",
                           new Object[]{cacheKey, getName()});
                return ret;
            }

            ret = new JSONObject(); // Re-instantiates it if cache miss
        }

        final int currentPageNum = query.getCurrentPageNum();
        final int pageSize = query.getPageSize();
        // final Map<String, SortDirection> sorts = query.getSorts();
        // final Set<Projection> projections = query.getProjections();
        // Asssumes the application call need to ccount page
        int pageCount = -1;
        // If the application caller need not to count page, gets the page count
        // the caller specified
        if (null != query.getPageCount()) {
            pageCount = query.getPageCount();
        }

        final StringBuilder sql = new StringBuilder();
        final Connection connection = getConnection();
        final List<Object> paramList = new ArrayList<Object>();

        try {
            final int pageCnt = get(currentPageNum, pageSize, pageCount, query, sql, paramList);

            // page
            final JSONObject pagination = new JSONObject();
            pagination.put(Pagination.PAGINATION_PAGE_COUNT, pageCnt);
            ret.put(Pagination.PAGINATION, pagination);

            // result
            if (pageCnt == 0) {
                ret.put(Keys.RESULTS, new JSONArray());
                return ret;
            }

            final JSONArray jsonResults = JdbcUtil.queryJsonArray(sql.toString(), paramList, connection, getName());
            ret.put(Keys.RESULTS, jsonResults);

            if (cacheEnabled) {
                CACHE.putAsync(cacheKey, ret);
                LOGGER.log(Level.FINER, "Added query result[cacheKey={0}] in repository cache[{1}]",
                           new Object[]{cacheKey, getName()});
                try {
                    cacheQueryResults(ret.optJSONArray(Keys.RESULTS), query);
                } catch (final JSONException e) {
                    LOGGER.log(Level.WARNING, "Caches query results failed", e);
                }
            }

        } catch (final SQLException e) {
            throw new JDBCRepositoryException(e);
        } catch (final Exception e) {
            LOGGER.log(Level.SEVERE, "query: "
                                     + e.getMessage(), e);
            throw new RepositoryException(e);
        } finally {
            closeQueryConnection(connection);
        }

        return ret;
    }

    /**
     * Caches the specified query results with the specified query.
     *
     * @param results the specified query results
     * @param query the specified query
     * @throws JSONException json exception
     */
    private void cacheQueryResults(final JSONArray results, final org.b3log.latke.repository.Query query)
            throws JSONException {
        String cacheKey;
        for (int i = 0; i < results.length(); i++) {
            final JSONObject jsonObject = results.optJSONObject(i);

            // 1. Caching for get by id.
            cacheKey = CACHE_KEY_PREFIX + jsonObject.optString(Keys.OBJECT_ID);
            CACHE.putAsync(cacheKey, jsonObject);
            LOGGER.log(Level.FINER, "Added an object[cacheKey={0}] in repository cache[{1}] for default index[oId]",
                       new Object[]{cacheKey, getName()});

            // 2. Caching for get by query with filters (EQUAL operator) only
            final Set<String[]> indexes = query.getIndexes();
            final StringBuilder logMsgBuilder = new StringBuilder();
            for (final String[] index : indexes) {
                final org.b3log.latke.repository.Query futureQuery = new org.b3log.latke.repository.Query().setPageCount(1);
                for (int j = 0; j < index.length; j++) {
                    final String propertyName = index[j];

                    futureQuery.setFilter(new PropertyFilter(propertyName, FilterOperator.EQUAL, jsonObject.opt(propertyName)));
                    logMsgBuilder.append(propertyName).append(",");
                }

                if (logMsgBuilder.length() > 0) {
                    logMsgBuilder.deleteCharAt(logMsgBuilder.length() - 1);
                }

                cacheKey = CACHE_KEY_PREFIX + futureQuery.getCacheKey() + "_" + getName();

                final JSONObject futureQueryRet = new JSONObject();
                final JSONObject pagination = new JSONObject();
                futureQueryRet.put(Pagination.PAGINATION, pagination);
                pagination.put(Pagination.PAGINATION_PAGE_COUNT, 1);

                final JSONArray futureQueryResults = new JSONArray();
                futureQueryRet.put(Keys.RESULTS, futureQueryResults);
                futureQueryResults.put(jsonObject);

                CACHE.putAsync(cacheKey, futureQueryRet);
                LOGGER.log(Level.FINER, "Added an object[cacheKey={0}] in repository cache[{1}] for index[{2}] for future query[{3}]",
                           new Object[]{cacheKey, getName(), logMsgBuilder, futureQuery.toString()});
            }
        }
    }

    /**
     *
     * getQuery sql.
     *
     * @param currentPageNum currentPageNum
     * @param pageSize pageSize
     * @param pageCount pageCount
     * @param query query
     * @param sql sql
     * @param paramList paramList
     * @return pageCnt
     * @throws RepositoryException  RepositoryException
     */
    private int get(final int currentPageNum, final int pageSize, final int pageCount, final Query query, final StringBuilder sql,
                    final List<Object> paramList)
            throws RepositoryException {
        int ret = pageCount;

        final StringBuilder selectSql = new StringBuilder();
        final StringBuilder filterSql = new StringBuilder();
        final StringBuilder orderBySql = new StringBuilder();
        getSelectSql(selectSql, query.getProjections());
        getFilterSql(filterSql, paramList, query.getFilter());
        getOrderBySql(orderBySql, query.getSorts());

        if (-1 == pageCount) {
            final StringBuilder countSql = new StringBuilder("select count(" + JdbcRepositories.OID + ") from ").append(getName());

            if (StringUtils.isNotBlank(filterSql.toString())) {
                countSql.append(" where ").append(filterSql);
            }

            final long count = count(countSql, paramList);
            ret = (int) Math.ceil((double) count / (double) pageSize);

            if (ret == 0) {
                return 0;
            }
        }

        if (currentPageNum > ret) {
            LOGGER.log(Level.WARNING, "Current page num[{0}] > page count[{1}]", new Object[]{currentPageNum, ret});
        }

        getQuerySql(currentPageNum, pageSize, selectSql, filterSql, orderBySql, sql);

        return ret;
    }

    /**
     * get select sql.
     * if projections size = 0 ,return select count(*).
     * @param selectSql selectSql
     * @param projections projections
     */
    private void getSelectSql(final StringBuilder selectSql, final Set<Projection> projections) {
        if (projections == null || projections.isEmpty()) {
            selectSql.append(" select * ");
            return;
        }

        boolean isFisrt = true;
        selectSql.append(" select ");
        for (Projection projection : projections) {
            if (isFisrt) {
                selectSql.append(projection.getKey());
                isFisrt = false;
            } else {
                selectSql.append(",").append(projection.getKey());
            }
        }
    }

    /**
     * getQuerySql.
     *
     * @param currentPageNum currentPageNum
     * @param pageSize  pageSize
     * @param selectSql selectSql
     * @param filterSql filterSql
     * @param orderBySql orderBySql
     * @param sql sql
     */
    private void getQuerySql(final int currentPageNum, final int pageSize, final StringBuilder selectSql,
                             final StringBuilder filterSql, final StringBuilder orderBySql, final StringBuilder sql) {
        final int start = (currentPageNum - 1) * pageSize;
        final int end = start + pageSize;

        sql.append(JdbcFactory.createJdbcFactory().queryPage(start, end, selectSql.toString(), filterSql.toString(), orderBySql.toString(),
                                                             getName()));
    }

    /**
     *
     * get filterSql and paramList.
     *
     * @param filterSql filterSql
     * @param paramList paramList
     * @param filter filter
     * @throws RepositoryException RepositoryException
     */
    private void getFilterSql(final StringBuilder filterSql, final List<Object> paramList, final Filter filter)
            throws RepositoryException {
        if (null == filter) {
            return;
        }

        if (filter instanceof PropertyFilter) {
            processPropertyFilter(filterSql, paramList, (PropertyFilter) filter);
        } else { // CompositeFiler
            processCompositeFilter(filterSql, paramList, (CompositeFilter) filter);
        }
    }

    /**
     *
     * getOrderBySql.
     *
     * @param orderBySql orderBySql
     * @param sorts sorts
     */
    private void getOrderBySql(final StringBuilder orderBySql, final Map<String, SortDirection> sorts) {
        boolean isFirst = true;
        String querySortDirection;
        for (final Map.Entry<String, SortDirection> sort : sorts.entrySet()) {
            if (isFirst) {
                orderBySql.append(" order by ");
                isFirst = false;
            } else {
                orderBySql.append(",");
            }

            if (sort.getValue().equals(SortDirection.ASCENDING)) {
                querySortDirection = "asc";
            } else {
                querySortDirection = "desc";
            }

            orderBySql.append(sort.getKey()).append(" ").append(querySortDirection);
        }
    }

    @Override
    public List<JSONObject> getRandomly(final int fetchSize) throws RepositoryException {
        final List<JSONObject> jsonObjects = new ArrayList<JSONObject>();

        final StringBuilder sql = new StringBuilder();
        JSONArray jsonArray;

        final Connection connection = getConnection();
        getRandomly(fetchSize, sql);
        try {
            jsonArray = JdbcUtil.queryJsonArray(sql.toString(), new ArrayList<Object>(), connection, getName());

            for (int i = 0; i < jsonArray.length(); i++) {
                jsonObjects.add(jsonArray.getJSONObject(i));
            }
        } catch (final SQLException se) {
            LOGGER.log(Level.SEVERE, "update:" + se.getMessage(), se);
            throw new JDBCRepositoryException(se);
        } catch (final Exception e) {
            LOGGER.log(Level.SEVERE, "getRandomly:" + e.getMessage(), e);
            throw new RepositoryException(e);
        } finally {
            closeQueryConnection(connection);
        }

        return jsonObjects;
    }

    /**
     * getRandomly.
     *
     * @param fetchSize fetchSize
     * @param sql sql
     */
    private void getRandomly(final int fetchSize, final StringBuilder sql) {
        sql.append(JdbcFactory.createJdbcFactory().getRandomlySql(getName(), fetchSize));
    }

    @Override
    public long count() throws RepositoryException {
        final String cacheKey = CACHE_KEY_PREFIX + getName() + REPOSITORY_CACHE_COUNT;
        if (cacheEnabled) {
            final Object o = CACHE.get(cacheKey);
            if (null != o) {
                LOGGER.log(Level.FINER, "Got an object[cacheKey={0}] from repository cache[name={1}]", new Object[]{cacheKey, getName()});
                try {
                    return (Long) o;
                } catch (final Exception e) {
                    LOGGER.log(Level.SEVERE, e.getMessage(), e);

                    return -1;
                }
            }
        }

        final StringBuilder sql = new StringBuilder("select count(" + JdbcRepositories.OID + ") from ").append(getName());
        final long ret = count(sql, new ArrayList<Object>());

        if (cacheEnabled) {
            CACHE.putAsync(cacheKey, ret);
            LOGGER.log(Level.FINER, "Added an object[cacheKey={0}] in repository cache[{1}]", new Object[]{cacheKey, getName()});
        }

        return ret;
    }

    /**
     * count.
     *
     * @param sql sql
     * @param paramList paramList
     * @return count
     * @throws RepositoryException RepositoryException
     */
    private long count(final StringBuilder sql, final List<Object> paramList) throws RepositoryException {
        final Connection connection = getConnection();

        JSONObject jsonObject;
        long count;
        try {
            jsonObject = JdbcUtil.queryJsonObject(sql.toString(), paramList, connection, getName());
            count = jsonObject.getLong(jsonObject.keys().next().toString());
        } catch (final SQLException se) {
            LOGGER.log(Level.SEVERE, "update:"
                                     + se.getMessage(), se);
            throw new JDBCRepositoryException(se);
        } catch (final Exception e) {
            LOGGER.log(Level.SEVERE, "count :"
                                     + e.getMessage(), e);
            throw new RepositoryException(e);
        } finally {
            closeQueryConnection(connection);
        }

        return count;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Transaction beginTransaction() {
        final JdbcTransaction ret = TX.get();
        if (null != ret) {
            LOGGER.log(Level.FINER, "There is a transaction[isActive={0}] in current thread", ret.isActive());
            if (ret.isActive()) {
                return TX.get(); // Using 'the current transaction'
            }
        }

        JdbcTransaction jdbcTransaction = null;
        try {
            jdbcTransaction = new JdbcTransaction();
        } catch (final SQLException e) {
            LOGGER.severe("init jdbcTransaction wrong");
        }

        TX.set(jdbcTransaction);

        return jdbcTransaction;
    }

    @Override
    public boolean isCacheEnabled() {
        return cacheEnabled;
    }

    @Override
    public void setCacheEnabled(final boolean isCacheEnabled) {
        cacheEnabled = isCacheEnabled;
    }

    @Override
    public boolean isWritable() {
        return writable;
    }

    @Override
    public void setWritable(final boolean writable) {
        this.writable = writable;
    }

    @Override
    public Cache<String, Serializable> getCache() {
        // TODO Auto-generated method stub
        return CACHE;
    }

    /**
     * Constructs a JDBC repository with the specified name.
     *
     * @param name the specified name
     */
    public JdbcRepository(final String name) {
        this.name = name;
    }

    /**
     * dispose the resource when requestDestroyed .
     */
    public static void dispose() {
        final JdbcTransaction jdbcTransaction = TX.get();
        if (jdbcTransaction == null) {
            return;
        }

        if (jdbcTransaction.getConnection() != null) {
            jdbcTransaction.dispose();
        }

    }

    /**
     * getConnection.
     * default using current JdbcTransaction's connection,if null get a new one.
     *
     * @return {@link Connection}
     */
    private Connection getConnection() {
        final JdbcTransaction jdbcTransaction = TX.get();
        if (jdbcTransaction == null || !jdbcTransaction.isActive()) {
            try {
                return Connections.getConnection();
            } catch (final SQLException e) {
                LOGGER.log(Level.SEVERE, "Gets connection error", e);
            }
        }

        return jdbcTransaction.getConnection();
    }

    /**
     * closeQueryConnection,this connection not in JdbcTransaction,
     * should be closed in code.
     *
     * @param connection {@link Connection}
     * @throws RepositoryException  RepositoryException
   
     */
    private void closeQueryConnection(final Connection connection) throws RepositoryException {
        final JdbcTransaction jdbcTransaction = TX.get();
        if (jdbcTransaction == null || !jdbcTransaction.isActive()) {
            try {
                connection.close();
            } catch (final SQLException e) {
                LOGGER.log(Level.SEVERE, "Can not close database connection", e);
                throw new RepositoryException(e);
            }
        } else {
            LOGGER.log(Level.FINEST, "Do not close query connection caused by in a transaction");
        }
    }

    /**
     * Processes property filter.
     *
     * @param filterSql the specified filter SQL to build
     * @param paramList the specified parameter list
     * @param propertyFilter the specified property filter
     * @throws RepositoryException repository exception
     */
    private void processPropertyFilter(final StringBuilder filterSql, final List<Object> paramList, final PropertyFilter propertyFilter)
            throws RepositoryException {
        String filterOperator = null;

        switch (propertyFilter.getOperator()) {
            case EQUAL:
                filterOperator = "=";
                break;
            case GREATER_THAN:
                filterOperator = ">";
                break;
            case GREATER_THAN_OR_EQUAL:
                filterOperator = ">=";
                break;
            case LESS_THAN:
                filterOperator = "<";
                break;
            case LESS_THAN_OR_EQUAL:
                filterOperator = "<=";
                break;
            case NOT_EQUAL:
                filterOperator = "!=";
                break;
            case IN:
                filterOperator = "in";
                break;
            default:
                throw new RepositoryException("Unsupported filter operator[" + propertyFilter.getOperator() + "]");
        }

        if (FilterOperator.IN != propertyFilter.getOperator()) {
            filterSql.append(propertyFilter.getKey()).append(filterOperator).append("?");
            paramList.add(propertyFilter.getValue());
        } else {
            final Collection<Object> objects = (Collection<Object>) propertyFilter.getValue();

            boolean isSubFist = true;
            if (objects != null && objects.size() > 0) {
                filterSql.append(propertyFilter.getKey()).append(" in ");

                final Iterator<Object> obs = objects.iterator();
                while (obs.hasNext()) {
                    if (isSubFist) {
                        filterSql.append("(");
                        isSubFist = false;
                    } else {
                        filterSql.append(",");
                    }
                    filterSql.append("?");
                    paramList.add(obs.next());

                    if (!obs.hasNext()) {
                        filterSql.append(") ");
                    }
                }
            }
        }
    }

    /**
     * Processes composite filter.
     *
     * @param filterSql the specified filter SQL to build
     * @param paramList the specified parameter list
     * @param compositeFilter the specified composite filter
     * @throws RepositoryException repository exception
     */
    private void processCompositeFilter(final StringBuilder filterSql, final List<Object> paramList,
                                        final CompositeFilter compositeFilter) throws RepositoryException {
        final List<Filter> subFilters = compositeFilter.getSubFilters();

        if (2 > subFilters.size()) {
            throw new RepositoryException("At least two sub filters in a composite filter");
        }

        filterSql.append("(");

        final Iterator<Filter> iterator = subFilters.iterator();
        while (iterator.hasNext()) {
            final Filter filter = iterator.next();

            if (filter instanceof PropertyFilter) {
                processPropertyFilter(filterSql, paramList, (PropertyFilter) filter);
            } else { // CompositeFilter
                processCompositeFilter(filterSql, paramList, (CompositeFilter) filter);
            }

            if (iterator.hasNext()) {
                switch (compositeFilter.getOperator()) {
                    case AND:
                        filterSql.append(" and ");
                        break;
                    case OR:
                        filterSql.append(" or ");
                        break;
                    default:
                        throw new RepositoryException("Unsupported composite filter[operator=" + compositeFilter.getOperator() + "]");
                }
            }
        }

        filterSql.append(")");
    }
}
TOP

Related Classes of org.b3log.latke.repository.jdbc.JdbcRepository

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.