Java tutorial
/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 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.Latkes; import org.b3log.latke.RuntimeEnv; import org.b3log.latke.cache.Cache; import org.b3log.latke.cache.CacheFactory; import org.b3log.latke.model.Pagination; import org.b3log.latke.repository.CompositeFilter; import org.b3log.latke.repository.Filter; import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.Projection; import org.b3log.latke.repository.PropertyFilter; 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.1.3, Mar 6, 2013 */ @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> * <oId, JSONObject> * </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); final RuntimeEnv runtimeEnv = Latkes.getRuntimeEnv(); if (RuntimeEnv.LOCAL == runtimeEnv || RuntimeEnv.BAE == runtimeEnv) { int maxCnt = Integer.MAX_VALUE; final String maxPageCntStr = Latkes.getLocalProperty("cache.maxPageCnt"); if (!Strings.isEmptyOrNull(maxPageCntStr)) { maxCnt = Integer.parseInt(maxPageCntStr); } if (0 >= maxCnt) { Latkes.disableDataCache(); } else { CACHE.setMaxCount(maxCnt); LOGGER.log(Level.INFO, "Initialized data object cache[maxCount={0}]", maxCnt); } } } @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(")"); } } sql.append("insert into ").append(getName()).append(insertString).append(" values ").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 { if (null == oldJsonObject) { return jsonObject; } 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.optString(key).equals(oldJsonObject.optString(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); } } /** * Removes an 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() { final String tableNamePrefix = StringUtils.isNotBlank(Latkes.getLocalProperty("jdbc.tablePrefix")) ? Latkes.getLocalProperty("jdbc.tablePrefix") + "_" : ""; return tableNamePrefix + 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.log(Level.SEVERE, "Failed to initialize JDBC transaction", e); throw new IllegalStateException("Failed to initialize JDBC transaction"); } TX.set(jdbcTransaction); return jdbcTransaction; } @Override public boolean hasTransactionBegun() { return null != TX.get(); } @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.isEmpty()) { 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(") "); } } } else { // in () => 1!=1 filterSql.append("1!=1"); } } } /** * 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(")"); } }