/*
* JEF - Copyright 2009-2010 Jiyi (mr.jiyi@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 jef.database.jdbc.result;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.sql.rowset.CachedRowSet;
import jef.common.log.LogUtil;
import jef.database.Condition;
import jef.database.DbUtils;
import jef.database.ORMConfig;
import jef.database.Session.PopulateStrategy;
import jef.database.dialect.DatabaseDialect;
import jef.database.jdbc.JDBCTarget;
import jef.database.meta.Reference;
import jef.database.routing.sql.InMemoryOperateProvider;
import jef.database.wrapper.clause.InMemoryDistinct;
import jef.database.wrapper.clause.InMemoryGroupByHaving;
import jef.database.wrapper.clause.InMemoryOrderBy;
import jef.database.wrapper.clause.InMemoryPaging;
import jef.database.wrapper.clause.InMemoryProcessor;
import jef.database.wrapper.clause.InMemoryStartWithConnectBy;
import jef.database.wrapper.populator.ColumnMeta;
/**
* 查询时记录的结果集
*
* @author Administrator
*
*/
public final class ResultSetContainer extends AbstractResultSet implements IResultSet {
private int current = -1;
// 重新排序部分
private InMemoryOrderBy inMemoryOrder;
// 重新分页逻辑
private InMemoryPaging inMemoryPage;
// 重新分组处理逻辑
private List<InMemoryProcessor> mustInMemoryProcessor;
// 所有列的元数据记录
private ColumnMeta columns;
protected final List<ResultSetHolder> results = new ArrayList<ResultSetHolder>(5);
// 是否缓存
private boolean cache;
public ResultSetContainer(boolean cache) {
this.cache = cache;
}
public int size() {
return results.size();
}
public ColumnMeta getColumns() {
return columns;
}
// 级联过滤条件
protected Map<Reference, List<Condition>> filters;
public Map<Reference, List<Condition>> getFilters() {
return filters;
}
@Override
public ResultSetMetaData getMetaData() throws SQLException {
return columns.getMeta();
}
public static IResultSet toInMemoryProcessorResultSet(InMemoryOperateProvider context, ResultSetHolder... rs) {
ResultSetContainer mrs = new ResultSetContainer(false);
for (ResultSetHolder rsh : rs) {
mrs.add(rsh);
}
context.parepareInMemoryProcess(null, mrs);
return mrs.toProperResultSet(null);
}
public boolean next() {
try {
boolean n = (current > -1) && results.get(current).rs.next();
if (n == false) {
current++;
if (current < results.size()) {
return next();
} else {
return false;
}
}
return n;
} catch (SQLException e) {
LogUtil.exception(e);
return false;
}
}
public boolean previous() throws SQLException {
boolean b = (current < results.size()) && results.get(current).rs.previous();
if (b == false) {
current--;
if (current > -1) {
return previous();
} else {
return false;
}
}
return b;
}
public void beforeFirst() throws SQLException {
for (ResultSetHolder rs : results) {
rs.rs.beforeFirst();
}
current = -1;
}
public boolean first() throws SQLException {
results.get(0).rs.first();
for (int i = 1; i < results.size(); i++) {
ResultSetHolder rs = results.get(i);
if (!rs.rs.isBeforeFirst()) {
rs.rs.beforeFirst();
}
}
current = 0;
return true;
}
public void afterLast() throws SQLException {
for (ResultSetHolder rs : results) {
rs.rs.afterLast();
}
current = results.size();
}
public void add(ResultSetHolder rsh) {
if (columns == null) {
try {
initMetadata(rsh.rs);
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
synchronized (results) {
results.add(rsh);
}
if (cache) {
try {
rsh.rs = tryCache(rsh.rs, rsh.getProfile());
rsh.close(false);
return;
} catch (SQLException e) {
// 缓存失败
LogUtil.exception(e);
}
}
}
/**
* 添加一个
*
* @param rs
* @param statement
*/
public void add(ResultSet rs, Statement statement, JDBCTarget tx) {
if (columns == null) {
try {
initMetadata(rs);
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
ResultSetHolder rsh = new ResultSetHolder(tx, statement, rs);
synchronized (results) {
results.add(rsh);
}
if (cache) {
try {
rsh.rs = tryCache(rs, tx.getProfile());
rsh.close(false);
return;
} catch (SQLException e) {
// 缓存失败
LogUtil.exception(e);
}
}
// rsh.rs = rs;
}
/**
* 关闭全部连接和结果集
*
* @throws SQLException
*/
public void close() throws SQLException {
List<SQLException> ex = new ArrayList<SQLException>();
for (ResultSetHolder rsx : results) {
rsx.close(true);
}
results.clear();
if (ex.size() > 0) {
throw new SQLException("theres " + ex.size() + " resultSet close error!");
}
}
/**
* 转换为可以用于输出正确的结果集
*
*
* 1、a 有内存任务,使用内存处理并排序。
* b 无内存任务,有多个结果集且有排序任务,使用混合排序
* c 无内存任务,有多个结果集且无排序任务,使用当前对象作为结果集
* d 无内存内务,无多个结果集,退化为简单结果集
* 2、有分页任务,包装为分页结果集
*
*
* @return
*/
@SuppressWarnings("unchecked")
public IResultSet toProperResultSet(Map<Reference, List<Condition>> filters, PopulateStrategy... args) {
if (filters == null) {
filters = Collections.EMPTY_MAP;
}
if (results.isEmpty()) {
return new ResultSetWrapper();
}
//最后将要包装的对象
IResultSet result;
// 除了Order、Page以外的内存处理任务
if (mustInMemoryProcessor != null) {
InMemoryProcessResultSet rw = new InMemoryProcessResultSet(results,columns,filters);
rw.addProcessor(mustInMemoryProcessor);
rw.addProcessor(inMemoryOrder);// 如果需要处理,排序是第一位的.
try {
rw.process();
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
result=rw;
}else if(results.size()==1){
ResultSetWrapper rw = new ResultSetWrapper(results.get(0), columns);
rw.setFilters(filters);
result=rw;
}else if(inMemoryOrder!=null){
ReorderResultSet2 rw = new ReorderResultSet2(results, inMemoryOrder, columns, filters);
result=rw;
}else{
this.filters=filters;
result=this;
}
//分页处理器
if(inMemoryPage!=null){
result=new LimitOffsetResultSet(result, inMemoryPage.getOffset(), inMemoryPage.getLimit());
}
return result;
}
public DatabaseDialect getProfile() {
return results.get(current).getProfile();
}
@Override
protected ResultSet get() {
return results.get(current).rs;
}
public void setInMemoryPage(InMemoryPaging inMemoryPaging) {
this.inMemoryPage = inMemoryPaging;
}
public void setInMemoryOrder(InMemoryOrderBy inMemoryOrder) {
this.inMemoryOrder = inMemoryOrder;
}
public void setInMemoryGroups(InMemoryGroupByHaving inMemoryGroups) {
addToInMemprocessor(inMemoryGroups);
}
public void setInMemoryDistinct(InMemoryDistinct instance) {
addToInMemprocessor(instance);
}
public void setInMemoryConnectBy(InMemoryStartWithConnectBy parseStartWith) {
addToInMemprocessor(parseStartWith);
}
public boolean isClosed() throws SQLException {
return results.isEmpty();
}
@Override
public boolean isFirst() throws SQLException {
throw new UnsupportedOperationException("isFirst");
}
@Override
public boolean isLast() throws SQLException {
throw new UnsupportedOperationException("isLast");
}
@Override
public boolean last() throws SQLException {
throw new UnsupportedOperationException("last");
}
@Override
public boolean isBeforeFirst() throws SQLException {
throw new UnsupportedOperationException("isBeforeFirst");
}
@Override
public boolean isAfterLast() throws SQLException {
throw new UnsupportedOperationException("isAfterLast");
}
// //////////////////////////// 私有方法 ///////////////////////////////////
private void addToInMemprocessor(InMemoryProcessor process) {
if (process != null) {
if (this.mustInMemoryProcessor == null) {
mustInMemoryProcessor = new ArrayList<InMemoryProcessor>(4);
}
mustInMemoryProcessor.add(process);
}
}
private void initMetadata(ResultSet wrapped) throws SQLException {
ResultSetMetaData meta = wrapped.getMetaData();
this.columns = new ColumnMeta(meta);
}
private ResultSet tryCache(ResultSet set, DatabaseDialect profile) throws SQLException {
long start = System.currentTimeMillis();
CachedRowSet rs = profile.newCacheRowSetInstance();
rs.populate(set);
if (ORMConfig.getInstance().isDebugMode()) {
LogUtil.debug("Caching Results from database. Cost {}ms.", System.currentTimeMillis() - start);
}
set.close();
return rs;
}
}