/**
* Copyright 2014 Duan Bingnan
*
* 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.pinus4j.datalayer.query.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.pinus4j.api.SQL;
import org.pinus4j.api.query.IQuery;
import org.pinus4j.api.query.impl.DefaultQueryImpl.OrderBy;
import org.pinus4j.cluster.resources.IDBResource;
import org.pinus4j.cluster.resources.ShardingDBResource;
import org.pinus4j.constant.Const;
import org.pinus4j.datalayer.AbstractDataLayer;
import org.pinus4j.datalayer.SQLBuilder;
import org.pinus4j.datalayer.SlowQueryLogger;
import org.pinus4j.datalayer.query.IDataQuery;
import org.pinus4j.entity.meta.EntityPK;
import org.pinus4j.entity.meta.PKName;
import org.pinus4j.entity.meta.PKValue;
import org.pinus4j.utils.JdbcUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* 分库分表查询抽象类. 此类封装了分库分表查询的公共操作. 子类可以针对主库、从库实现相关的查询.
*
* @author duanbn
*/
public abstract class AbstractJdbcQuery extends AbstractDataLayer implements IDataQuery {
// //////////////////////////////////////////////////////////////////////////////////////
// count相关
// //////////////////////////////////////////////////////////////////////////////////////
/**
* 获取全局表的count数
*
* @param conn
* @param clazz
* @return count数
*/
private Number _selectCount(IDBResource dbResource, Class<?> clazz) throws SQLException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
Connection conn = dbResource.getConnection();
String sql = null;
if (dbResource.isGlobal())
sql = SQLBuilder.buildSelectCountGlobalSql(clazz);
else
sql = SQLBuilder.buildSelectCountSql(clazz, ((ShardingDBResource) dbResource).getTableIndex());
ps = conn.prepareStatement(sql);
long begin = System.currentTimeMillis();
rs = ps.executeQuery();
long constTime = System.currentTimeMillis() - begin;
if (constTime > Const.SLOWQUERY_COUNT) {
SlowQueryLogger.write(conn, sql, constTime);
}
long count = -1;
if (rs.next()) {
count = rs.getLong(1);
}
return count;
} finally {
JdbcUtil.close(ps, rs);
}
}
/**
* 带缓存的获取全局表count
*
* @param conn
* @param clusterName
* @param clazz
* @return count数
* @throws SQLException
*/
protected Number selectCountWithCache(IDBResource dbResource, Class<?> clazz, boolean useCache) throws SQLException {
String clusterName = dbResource.getClusterName();
String tableName = entityMetaManager.getTableName(clazz);
// 操作缓存
if (isCacheAvailable(clazz, useCache)) {
long count = 0;
if (dbResource.isGlobal())
count = primaryCache.getCountGlobal(clusterName, tableName);
else
count = primaryCache.getCount((ShardingDBResource) dbResource);
if (count > 0) {
return count;
}
}
long count = 0;
count = _selectCount(dbResource, clazz).longValue();
// 操作缓存
if (isCacheAvailable(clazz, useCache) && count > 0) {
if (dbResource.isGlobal())
primaryCache.setCountGlobal(clusterName, tableName, count);
else
primaryCache.setCount((ShardingDBResource) dbResource, count);
}
return count;
}
protected <T> Number selectCountByQuery(IQuery<T> query, IDBResource dbResource, Class<T> clazz)
throws SQLException {
long count = -1;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Connection conn = dbResource.getConnection();
SQL sql = null;
if (dbResource.isGlobal())
sql = SQLBuilder.buildSelectCountByQuery(clazz, -1, query);
else
sql = SQLBuilder.buildSelectCountByQuery(clazz, ((ShardingDBResource) dbResource).getTableIndex(),
query);
ps = conn.prepareStatement(sql.getSql());
fillParam(ps, sql);
long begin = System.currentTimeMillis();
rs = ps.executeQuery();
long constTime = System.currentTimeMillis() - begin;
if (constTime > Const.SLOWQUERY_COUNT) {
SlowQueryLogger.write(conn, sql, constTime);
}
if (rs.next()) {
count = rs.getLong(1);
}
} finally {
JdbcUtil.close(ps, rs);
}
return count;
}
// //////////////////////////////////////////////////////////////////////////////////////
// findByPks相关
// //////////////////////////////////////////////////////////////////////////////////////
private <T> Map<EntityPK, T> _selectByPks(IDBResource dbResource, Class<T> clazz, EntityPK[] pks,
List<OrderBy> order) throws SQLException {
Map<EntityPK, T> result = Maps.newLinkedHashMap();
PreparedStatement ps = null;
ResultSet rs = null;
try {
Connection conn = dbResource.getConnection();
SQL sql = null;
if (dbResource.isGlobal())
sql = SQLBuilder.buildSelectByPks(pks, order, clazz, -1);
else
sql = SQLBuilder.buildSelectByPks(pks, order, clazz, ((ShardingDBResource) dbResource).getTableIndex());
ps = conn.prepareStatement(sql.getSql());
fillParam(ps, sql);
long begin = System.currentTimeMillis();
rs = ps.executeQuery();
long constTime = System.currentTimeMillis() - begin;
if (constTime > Const.SLOWQUERY_PKS) {
SlowQueryLogger.write(conn, sql, constTime);
}
result = SQLBuilder.createResultObjectAsMap(clazz, rs);
} finally {
JdbcUtil.close(ps, rs);
}
return result;
}
protected <T> Map<EntityPK, T> selectByPksWithCache(IDBResource dbResource, Class<T> clazz, EntityPK[] pks,
List<OrderBy> order, boolean useCache) throws SQLException {
Map<EntityPK, T> result = Maps.newLinkedHashMap();
if (pks == null || pks.length == 0) {
return result;
}
if (!isCacheAvailable(clazz, useCache)) {
return _selectByPks(dbResource, clazz, pks, order);
}
String tableName = entityMetaManager.getTableName(clazz);
String clusterName = dbResource.getClusterName();
Map<EntityPK, T> hitResult = null;
if (dbResource.isGlobal())
hitResult = primaryCache.getGlobal(clusterName, tableName, pks);
else
hitResult = primaryCache.get((ShardingDBResource) dbResource, pks);
if (hitResult == null || hitResult.isEmpty()) {
result = _selectByPks(dbResource, clazz, pks, order);
if (dbResource.isGlobal())
primaryCache.putGlobal(clusterName, tableName, result);
else
primaryCache.put((ShardingDBResource) dbResource, result);
return result;
}
if (hitResult.size() == pks.length) {
return hitResult;
}
try {
// 计算没有命中缓存的主键
List<EntityPK> noHitPkList = Lists.newArrayList();
for (EntityPK pk : pks) {
if (hitResult.get(pk) == null) {
noHitPkList.add(pk);
}
}
EntityPK[] noHitPks = noHitPkList.toArray(new EntityPK[noHitPkList.size()]);
// 从数据库中查询没有命中缓存的数据
Map<EntityPK, T> noHitMap = _selectByPks(dbResource, clazz, noHitPks, order);
if (!noHitMap.isEmpty()) {
if (dbResource.isGlobal())
primaryCache.putGlobal(clusterName, tableName, noHitMap);
else
primaryCache.put((ShardingDBResource) dbResource, noHitMap);
}
// 为了保证pks的顺序
for (EntityPK pk : pks) {
if (hitResult.get(pk) != null) {
result.put(pk, hitResult.get(pk));
} else {
result.put(pk, noHitMap.get(pk));
}
}
} catch (Exception e) {
result = _selectByPks(dbResource, clazz, pks, order);
}
return result;
}
// //////////////////////////////////////////////////////////////////////////////////////
// findBySql相关
// //////////////////////////////////////////////////////////////////////////////////////
protected List<Map<String, Object>> selectBySql(IDBResource dbResource, SQL sql) throws SQLException {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
PreparedStatement ps = null;
ResultSet rs = null;
try {
Connection conn = dbResource.getConnection();
if (dbResource.isGlobal())
ps = SQLBuilder.buildSelectBySqlGlobal(conn, sql);
else
ps = SQLBuilder.buildSelectBySql(conn, sql, ((ShardingDBResource) dbResource).getTableIndex());
long begin = System.currentTimeMillis();
rs = ps.executeQuery();
long constTime = System.currentTimeMillis() - begin;
if (constTime > Const.SLOWQUERY_SQL) {
SlowQueryLogger.write(conn, sql, constTime);
}
result = (List<Map<String, Object>>) SQLBuilder.createResultObject(rs);
} finally {
JdbcUtil.close(ps, rs);
}
return result;
}
// //////////////////////////////////////////////////////////////////////////////////////
// findByQuery相关
// //////////////////////////////////////////////////////////////////////////////////////
protected <T> List<T> selectByQuery(IDBResource dbResource, IQuery<T> query, Class<T> clazz) throws SQLException {
List<T> result = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Connection conn = dbResource.getConnection();
SQL sql = null;
if (dbResource.isGlobal())
sql = SQLBuilder.buildSelectByQuery(clazz, -1, query);
else
sql = SQLBuilder.buildSelectByQuery(clazz, ((ShardingDBResource) dbResource).getTableIndex(), query);
ps = conn.prepareStatement(sql.getSql());
fillParam(ps, sql);
long begin = System.currentTimeMillis();
rs = ps.executeQuery();
long constTime = System.currentTimeMillis() - begin;
if (constTime > Const.SLOWQUERY_QUERY) {
SlowQueryLogger.write(conn, sql, constTime);
}
result = (List<T>) SQLBuilder.createResultObject(clazz, rs);
} finally {
JdbcUtil.close(ps, rs);
}
return result;
}
// //////////////////////////////////////////////////////////////////////////////////////
// getPk相关
// //////////////////////////////////////////////////////////////////////////////////////
protected <T> EntityPK[] selectPksByQuery(IDBResource dbResource, IQuery<T> query, Class<T> clazz)
throws SQLException {
List<EntityPK> result = Lists.newArrayList();
PreparedStatement ps = null;
ResultSet rs = null;
try {
Connection conn = dbResource.getConnection();
SQL sql = null;
if (dbResource.isGlobal())
sql = SQLBuilder.buildSelectPkByQuery(clazz, -1, query);
else
sql = SQLBuilder.buildSelectPkByQuery(clazz, ((ShardingDBResource) dbResource).getTableIndex(), query);
ps = conn.prepareStatement(sql.getSql());
fillParam(ps, sql);
long begin = System.currentTimeMillis();
rs = ps.executeQuery();
long constTime = System.currentTimeMillis() - begin;
if (constTime > Const.SLOWQUERY_PKS) {
SlowQueryLogger.write(conn, sql, constTime);
}
ResultSetMetaData rsmd = rs.getMetaData();
int pkNum = rsmd.getColumnCount();
while (rs.next()) {
try {
PKName[] pkNames = new PKName[pkNum];
PKValue[] pkValues = new PKValue[pkNum];
for (int i = 1; i <= pkNum; i++) {
pkNames[i - 1] = PKName.valueOf(rsmd.getColumnName(i));
pkValues[i - 1] = PKValue.valueOf(rs.getObject(i));
}
result.add(EntityPK.valueOf(pkNames, pkValues));
} catch (Exception e) {
throw new SQLException(e);
}
}
} finally {
JdbcUtil.close(ps, rs);
}
return result.toArray(new EntityPK[result.size()]);
}
}