All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ttzero.excel.entity.ResultSetSheet Maven / Gradle / Ivy

Go to download

A fast and lower memory excel write/read tool 一个非POI底层支持流式处理的高效且超低内存的Excel读写工具

The newest version!
/*
 * Copyright (c) 2017, [email protected] All Rights Reserved.
 *
 * 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.ttzero.excel.entity;

import org.ttzero.excel.manager.Const;
import org.ttzero.excel.processor.StyleProcessor;
import org.ttzero.excel.reader.Cell;
import org.ttzero.excel.util.StringUtil;

import java.io.IOException;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;

import static java.sql.Types.BIGINT;
import static java.sql.Types.BIT;
import static java.sql.Types.CHAR;
import static java.sql.Types.DATE;
import static java.sql.Types.DECIMAL;
import static java.sql.Types.DOUBLE;
import static java.sql.Types.FLOAT;
import static java.sql.Types.INTEGER;
import static java.sql.Types.LONGVARCHAR;
import static java.sql.Types.NULL;
import static java.sql.Types.NUMERIC;
import static java.sql.Types.REAL;
import static java.sql.Types.SMALLINT;
import static java.sql.Types.TIME;
import static java.sql.Types.TIMESTAMP;
import static java.sql.Types.TINYINT;
import static java.sql.Types.VARCHAR;

/**
 * {@code ResultSetSheet}的数据源为{@link ResultSet}一般情况下它用于存储过程,
 * {@code ResultSetSheet}可以将存储过程的查询结果直接转为工作表的数据,省掉将查结果转为
 * Java对象再转为工作表输出协议的数据结构。
 *
 * 

如果未指定{@code Columns}表头时将从{@link ResultSetMetaData}源数据中获取, * 优先使用别名做为表头,列顺序与query字段一致

* *

这是一个比较小众的工作表,最好只在比较简单的场景下使用,比如一次性导出的场景。 * 因为{@code StatementSheet}并不支持数据切片,所以当查询结果较大时可能出现OOM。 * 如果不确认数据量时最好使用{@link ListSheet}分片获取数据

* * @author guanquan.wang on 2017/9/27. * @see StatementSheet */ public class ResultSetSheet extends Sheet { /** * 数据源ResultSet */ protected ResultSet rs; /** * 行级动态样式处理器 */ private StyleProcessor styleProcessor; /** * 实例化工作表,未指定工作表名称时默认以{@code 'Sheet'+id}命名 */ public ResultSetSheet() { super(); } /** * 实例化工作表并指定工作表名称 * * @param name 工作表名称 */ public ResultSetSheet(String name) { super(name); } /** * 实例化工作表并指定表头信息 * * @param columns 表头信息 */ public ResultSetSheet(final Column... columns) { super(columns); } /** * 实例化工作表并指定工作表名称和表头信息 * * @param name 工作表名称 * @param columns 表头信息 */ public ResultSetSheet(String name, final Column... columns) { super(name, columns); } /** * 实例化工作表并指定工作表名称,水印和表头信息 * * @param name 工作表名称 * @param waterMark 水印 * @param columns 表头信息 */ public ResultSetSheet(String name, WaterMark waterMark, final Column... columns) { super(name, waterMark, columns); } /** * 实例化工作表并指定数据源{@code ResultSet} * * @param rs 数据源{@code ResultSet} */ public ResultSetSheet(ResultSet rs) { this(null, rs); } /** * 实例化工作表并指定工作表名和数据源{@code ResultSet} * * @param name 工作表名 * @param rs 数据源{@code ResultSet} */ public ResultSetSheet(String name, ResultSet rs) { super(name); this.rs = rs; } /** * 实例化工作表并指定数据源{@code ResultSet}和表头信息 * * @param rs 数据源{@code ResultSet} * @param columns 表头信息 */ public ResultSetSheet(ResultSet rs, final Column... columns) { this(null, rs, null, columns); } /** * 实例化工作表并指定工作表名、数据源{@code ResultSet}和表头信息 * * @param name 工作表名 * @param rs 数据源{@code ResultSet} * @param columns 表头信息 */ public ResultSetSheet(String name, ResultSet rs, final Column... columns) { this(name, rs, null, columns); } /** * 实例化工作表并指定数据源{@code ResultSet}、水印和表头信息 * * @param rs 数据源{@code ResultSet} * @param waterMark 水印 * @param columns 表头信息 */ public ResultSetSheet(ResultSet rs, WaterMark waterMark, final Column... columns) { this(null, rs, waterMark, columns); } /** * 实例化工作表并指定工作表名、数据源{@code ResultSet}、水印和表头信息 * * @param name 工作表名 * @param rs 数据源{@code ResultSet} * @param waterMark 水印 * @param columns 表头信息 */ public ResultSetSheet(String name, ResultSet rs, WaterMark waterMark, final Column... columns) { super(name, waterMark, columns); this.rs = rs; } /** * 设置数据源{@code ResultSet} * * @param resultSet 数据源{@code ResultSet} * @return 当前工作表 */ public ResultSetSheet setResultSet(ResultSet resultSet) { this.rs = resultSet; return this; } /** * 设置行级动态样式处理器,作用于整行优先级高于单元格动态样式处理器 * * @param styleProcessor 行级动态样式处理器 * @return 当前工作表 */ public Sheet setStyleProcessor(StyleProcessor styleProcessor) { this.styleProcessor = styleProcessor; putExtProp(Const.ExtendPropertyKey.STYLE_DESIGN, styleProcessor); return this; } /** * 获取当前工作表的行级动态样式处理器,如果未设置则从扩展参数中查找 * * @return 行级动态样式处理器 */ public StyleProcessor getStyleProcessor() { if (styleProcessor != null) return styleProcessor; @SuppressWarnings("unchecked") StyleProcessor fromExtProp = (StyleProcessor) getExtPropValue(Const.ExtendPropertyKey.STYLE_DESIGN); return this.styleProcessor = fromExtProp; } /** * 关闭数据源并关闭{@code ResultSet} * * @throws IOException if I/O error occur */ @Override public void close() throws IOException { if (shouldClose && rs != null) { try { rs.close(); } catch (SQLException e) { LOGGER.warn("Close ResultSet error.", e); } } super.close(); } /** * 重置{@code RowBlock}行块数据 */ @Override protected void resetBlockData() { int len = columns.length, n = 0, limit = getRowLimit(); boolean hasGlobalStyleProcessor = (extPropMark & 2) == 2, hasNext = true; try { for (int rbs = rowBlock.capacity(); n++ < rbs && rows < limit && (hasNext = rs.next()); rows++) { Row row = rowBlock.next(); row.index = rows; row.height = getRowHeight(); Cell[] cells = row.realloc(len); for (int i = 1; i <= len; i++) { SQLColumn hc = (SQLColumn) columns[i - 1]; // clear cells Cell cell = cells[i - 1]; cell.clear(); Object e; if (hc.ri > 0) { switch (hc.sqlType) { case VARCHAR: case CHAR: case LONGVARCHAR: case NULL: e = rs.getString(hc.ri); break; case INTEGER: e = rs.getInt(hc.ri); break; case TINYINT: case SMALLINT: e = rs.getShort(hc.ri); break; case DATE: e = rs.getDate(hc.ri); break; case TIMESTAMP: e = rs.getTimestamp(hc.ri); break; case NUMERIC: case DECIMAL: e = rs.getBigDecimal(hc.ri); break; case BIGINT: e = rs.getLong(hc.ri); break; case REAL: case FLOAT: case DOUBLE: e = rs.getDouble(hc.ri); break; case BIT: e = rs.getBoolean(hc.ri); break; case TIME: e = rs.getTime(hc.ri); break; default: e = rs.getObject(hc.ri); break; } // Clear value if NULL if (rs.wasNull()) e = null; } else e = null; cellValueAndStyle.reset(row, cell, e, hc); if (hasGlobalStyleProcessor) { cellValueAndStyle.setStyleDesign(rs, cell, hc, getStyleProcessor()); } } } } catch (SQLException e) { throw new ExcelWriteException(e); } // Paging if (rows >= limit) { shouldClose = false; rowBlock.markEOF(); ResultSetSheet copy = getClass().cast(clone()); copy.shouldClose = true; workbook.insertSheet(id, copy); } else if (!hasNext) rowBlock.markEOF(); } /** * 获取表头,未指定表头时从{@link ResultSetMetaData}源数据中获取, * 优先使用别名做为表头,列顺序与query字段一致 * * @return 表头信息 */ @Override protected Column[] getHeaderColumns() { if (headerReady) return columns; if (rs == null) { throw new ExcelWriteException("Constructor worksheet error.\nMiss the parameter ResultSet"); } int i = 0; try { ResultSetMetaData metaData = rs.getMetaData(); int count = metaData.getColumnCount(); if (hasHeaderColumns()) { Column[] newColumns = new SQLColumn[columns.length]; for (; i < columns.length; i++) { SQLColumn column = SQLColumn.of(columns[i]); newColumns[i] = column; if (column.tail != null) column = (SQLColumn) column.tail; if (i + 1 > count) { LOGGER.warn("Column [{}] cannot be mapped.", columns[i].getName()); continue; } if (StringUtil.isEmpty(column.getName())) column.setName(metaData.getColumnLabel(i + 1)); column.ri = StringUtil.isNotEmpty(column.key) ? findByKey(metaData, column.key) : i + 1; if (column.ri < 0) { LOGGER.warn("Column [{}] cannot be mapped.", columns[i].getName()); continue; } column.sqlType = metaData.getColumnType(i + 1); Class metaClazz = columnTypeToClass(column.sqlType); if (column.clazz != metaClazz) { LOGGER.warn("The specified type {} is different from metadata column type {}", column.clazz, metaClazz); // column.clazz = metaClazz; } } columns = newColumns; } else { columns = new Column[count]; while (++i <= count) { SQLColumn column = new SQLColumn(metaData.getColumnLabel(i), metaData.getColumnType(i) , columnTypeToClass(metaData.getColumnType(i))); column.ri = StringUtil.isNotEmpty(column.key) ? findByKey(metaData, column.key) : i; columns[i - 1] = column; } } } catch (SQLException e) { LOGGER.warn("Get meta data occur error.", e); } if (hasHeaderColumns()) { for (i = 0; i < columns.length; i++) { if (StringUtil.isEmpty(columns[i].getName())) { columns[i].setName(String.valueOf(i)); } } } return columns; } protected int findByKey(ResultSetMetaData metaData, String key) throws SQLException { for (int i = 1, len = metaData.getColumnCount(); i <= len; i++) { if (key.equals(metaData.getColumnLabel(i))) { return i; } } return -1; } /** * 将SQL类型{@link java.sql.Types}转换为Java类型 * * @param type SQL类型{@code java.sql.Types} * @return Java类型 */ protected Class columnTypeToClass(int type) { Class clazz; switch (type) { case VARCHAR: case CHAR: case LONGVARCHAR: case NULL: clazz = String.class; break; case INTEGER: clazz = Integer.class; break; case TINYINT: case SMALLINT: clazz = Short.class; break; case DATE: clazz = java.sql.Date.class; break; case TIMESTAMP: clazz = Timestamp.class; break; case NUMERIC: case DECIMAL: clazz = BigDecimal.class; break; case BIGINT: clazz = Long.class; break; case REAL: case FLOAT: case DOUBLE: clazz = Double.class; break; case BIT: clazz = Boolean.class; break; case TIME: clazz = java.sql.Time.class; break; default: clazz = Object.class; } return clazz; } /** * {@code ResultSetSheet}独有的列对象,除了{@link Column}包含的信息外,它还保存当列对应的SQL类型和 * {@code ResultSet}下标,有了下标后续列取值可直接根据{@code ri}直接取值 */ public static class SQLColumn extends Column { /** * SQL类型,等同于{@link java.sql.Types}中的静态类型 */ public int sqlType; /** * ResultSet下标 */ public int ri; public SQLColumn(String name, int sqlType, Class clazz) { super(name, clazz); this.sqlType = sqlType; } public SQLColumn(Column other) { super.from(other); if (other instanceof SQLColumn) { SQLColumn o = (SQLColumn) other; this.sqlType = o.sqlType; this.ri = o.ri; } if (other.next != null) { addSubColumn(new SQLColumn(other.next)); } } public static SQLColumn of(Column other) { return new SQLColumn(other); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy