/**
* 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.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.transaction.Transaction;
import javax.transaction.xa.XAResource;
import org.pinus4j.api.SQL;
import org.pinus4j.api.query.IQuery;
import org.pinus4j.api.query.impl.DefaultQueryImpl;
import org.pinus4j.api.query.impl.DefaultQueryImpl.OrderBy;
import org.pinus4j.cluster.enums.EnumDBMasterSlave;
import org.pinus4j.cluster.resources.IDBResource;
import org.pinus4j.datalayer.query.IGlobalQuery;
import org.pinus4j.entity.meta.EntityPK;
import org.pinus4j.exceptions.DBClusterException;
import org.pinus4j.exceptions.DBOperationException;
import org.pinus4j.utils.BeansUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
/**
* global query implements.
*
* @author duanbn.
* @since 1.1.1
*/
public class GlobalJdbcQueryImpl extends AbstractJdbcQuery implements IGlobalQuery {
public static final Logger LOG = LoggerFactory.getLogger(GlobalJdbcQueryImpl.class);
@Override
public Number getCount(Class<?> clazz, boolean useCache, EnumDBMasterSlave masterSlave) {
String clusterName = entityMetaManager.getClusterName(clazz);
String tableName = entityMetaManager.getTableName(clazz);
Transaction tx = null;
IDBResource dbResource = null;
try {
tx = txManager.getTransaction();
boolean isFromSlave = false;
// select db resource.
if (EnumDBMasterSlave.MASTER == masterSlave || !this.dbCluster.isGlobalSlaveExist(clusterName)) {
dbResource = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
} else {
dbResource = this.dbCluster.getSlaveGlobalDBResource(clusterName, tableName, masterSlave);
isFromSlave = true;
}
if (tx != null) {
tx.enlistResource((XAResource) dbResource);
}
long count = selectCountWithCache(dbResource, clazz, useCache).longValue();
if (count == 0 && isFromSlave) {
dbResource.close();
dbResource = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
if (tx != null) {
tx.enlistResource((XAResource) dbResource);
}
count = selectCountWithCache(dbResource, clazz, useCache).longValue();
}
return count;
} catch (Exception e) {
if (tx != null) {
try {
tx.rollback();
} catch (Exception e1) {
throw new DBOperationException(e1);
}
}
throw new DBOperationException(e);
} finally {
if (tx == null && dbResource != null) {
dbResource.close();
}
}
}
@Override
public <T> Number getCountByQuery(IQuery<T> query, Class<T> clazz, boolean useCache, EnumDBMasterSlave masterSlave) {
String clusterName = entityMetaManager.getClusterName(clazz);
String tableName = entityMetaManager.getTableName(clazz);
Transaction tx = null;
IDBResource dbResource = null;
try {
tx = txManager.getTransaction();
boolean isFromSlave = false;
if (EnumDBMasterSlave.MASTER == masterSlave || !this.dbCluster.isGlobalSlaveExist(clusterName)) {
dbResource = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
} else {
dbResource = this.dbCluster.getSlaveGlobalDBResource(clusterName, tableName, masterSlave);
isFromSlave = true;
}
if (tx != null) {
tx.enlistResource((XAResource) dbResource);
}
long count = selectCountByQuery(query, dbResource, clazz).longValue();
if (count == 0 && isFromSlave) {
dbResource.close();
dbResource = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
if (tx != null) {
tx.enlistResource((XAResource) dbResource);
}
count = selectCountByQuery(query, dbResource, clazz).longValue();
}
return count;
} catch (Exception e) {
if (tx != null) {
try {
tx.rollback();
} catch (Exception e1) {
throw new DBOperationException(e1);
}
}
throw new DBOperationException(e);
} finally {
if (tx == null && dbResource != null) {
dbResource.close();
}
}
}
@Override
public <T> T findByPk(EntityPK pk, Class<T> clazz, boolean useCache, EnumDBMasterSlave masterSlave) {
List<T> result = findByPkList(Lists.newArrayList(pk), clazz, null, useCache, masterSlave);
if (!result.isEmpty()) {
return result.get(0);
}
return null;
}
@Override
public <T> List<T> findByPkList(List<EntityPK> pkList, Class<T> clazz, List<OrderBy> order, boolean useCache,
EnumDBMasterSlave masterSlave) {
List<T> result = Lists.newArrayList();
String clusterName = entityMetaManager.getClusterName(clazz);
String tableName = entityMetaManager.getTableName(clazz);
Transaction tx = null;
IDBResource dbResource = null;
try {
tx = txManager.getTransaction();
boolean isFromSlave = false;
if (EnumDBMasterSlave.MASTER == masterSlave || !this.dbCluster.isGlobalSlaveExist(clusterName)) {
dbResource = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
} else {
dbResource = this.dbCluster.getSlaveGlobalDBResource(clusterName, tableName, masterSlave);
isFromSlave = true;
}
if (tx != null) {
tx.enlistResource((XAResource) dbResource);
}
EntityPK[] entityPks = pkList.toArray(new EntityPK[pkList.size()]);
Map<EntityPK, T> data = selectByPksWithCache(dbResource, clazz, entityPks, order, useCache);
if (data.isEmpty() && isFromSlave) {
dbResource.close();
dbResource = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
if (tx != null) {
tx.enlistResource((XAResource) dbResource);
}
data = selectByPksWithCache(dbResource, clazz, entityPks, order, useCache);
}
result.addAll(data.values());
return result;
} catch (Exception e) {
if (tx != null) {
try {
tx.rollback();
} catch (Exception e1) {
throw new DBOperationException(e1);
}
}
throw new DBOperationException(e);
} finally {
if (tx == null && dbResource != null) {
dbResource.close();
}
}
}
@SuppressWarnings("unchecked")
@Override
public <T> List<T> findByQuery(IQuery<T> query, Class<T> clazz, boolean useCache, EnumDBMasterSlave masterSlave) {
List<T> result = Lists.newArrayList();
if (query == null) {
query = new DefaultQueryImpl<T>();
}
String clusterName = entityMetaManager.getClusterName(clazz);
String tableName = entityMetaManager.getTableName(clazz);
if (isSecondCacheAvailable(clazz, useCache)) {
String sCacheKey = ((DefaultQueryImpl<T>) query).getWhereSql().getSecondCacheKey();
List<T> sCacheData = secondCache.getGlobal(sCacheKey, clusterName, tableName);
if (sCacheData != null) {
return sCacheData;
}
}
Transaction tx = null;
IDBResource dbResource = null;
try {
tx = txManager.getTransaction();
boolean isFromSlave = false;
if (EnumDBMasterSlave.MASTER == masterSlave || !this.dbCluster.isGlobalSlaveExist(clusterName)) {
dbResource = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
} else {
dbResource = this.dbCluster.getSlaveGlobalDBResource(clusterName, tableName, masterSlave);
isFromSlave = true;
}
if (tx != null) {
tx.enlistResource((XAResource) dbResource);
}
if (isCacheAvailable(clazz, useCache)) {
EntityPK[] entityPks = selectPksByQuery(dbResource, query, clazz);
Map<EntityPK, T> datas = selectByPksWithCache(dbResource, clazz, entityPks,
((DefaultQueryImpl<T>) query).getOrderList(), useCache);
if ((datas == null || datas.isEmpty()) && isFromSlave) {
dbResource.close();
dbResource = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
if (tx != null) {
tx.enlistResource((XAResource) dbResource);
}
datas = selectByPksWithCache(dbResource, clazz, entityPks,
((DefaultQueryImpl<T>) query).getOrderList(), useCache);
}
result.addAll(datas.values());
} else {
result = selectByQuery(dbResource, query, clazz);
if ((result == null || result.isEmpty()) && isFromSlave) {
dbResource.close();
dbResource = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
if (tx != null) {
tx.enlistResource((XAResource) dbResource);
}
result = selectByQuery(dbResource, query, clazz);
}
}
if (isSecondCacheAvailable(clazz, useCache)) {
String sCacheKey = ((DefaultQueryImpl<T>) query).getWhereSql().getSecondCacheKey();
secondCache.putGlobal(sCacheKey, clusterName, tableName, result);
}
// 过滤从缓存结果, 将没有指定的字段设置为默认值.
List<T> filteResult = new ArrayList<T>(result.size());
if (((DefaultQueryImpl<T>) query).hasQueryFields()) {
for (T obj : result) {
filteResult.add((T) BeansUtil.cloneWithGivenField(obj, ((DefaultQueryImpl<T>) query).getFields()));
}
result = filteResult;
}
return result;
} catch (Exception e) {
if (tx != null) {
try {
tx.rollback();
} catch (Exception e1) {
throw new DBOperationException(e1);
}
}
throw new DBOperationException(e);
} finally {
if (tx == null && dbResource != null) {
dbResource.close();
}
}
}
@Override
public List<Map<String, Object>> findBySql(SQL sql, String clusterName, EnumDBMasterSlave masterSlave) {
IDBResource next = null;
for (String tableName : sql.getTableNames()) {
IDBResource cur = null;
try {
if (EnumDBMasterSlave.MASTER == masterSlave || !this.dbCluster.isGlobalSlaveExist(clusterName)) {
cur = this.dbCluster.getMasterGlobalDBResource(clusterName, tableName);
} else {
cur = this.dbCluster.getSlaveGlobalDBResource(clusterName, tableName, masterSlave);
}
} catch (DBClusterException e) {
throw new DBOperationException(e);
}
if (next != null && (cur != next)) {
throw new DBOperationException("the tables in sql maybe not at the same database");
}
next = cur;
}
Transaction tx = null;
try {
tx = txManager.getTransaction();
if (tx != null) {
tx.enlistResource((XAResource) next);
}
List<Map<String, Object>> result = selectBySql(next, sql);
return result;
} catch (Exception e) {
if (tx != null) {
try {
tx.rollback();
} catch (Exception e1) {
throw new DBOperationException(e1);
}
}
throw new DBOperationException(e);
} finally {
if (tx == null && next != null) {
next.close();
}
}
}
}