ResultSetJSONSerializer.java

/*
 * Copyright (C) 2018 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package nl.b3p.brmo.verschil.util;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.*;

public class ResultSetJSONSerializer extends JsonSerializer<ResultSet> {

    private static final Log LOG = LogFactory.getLog(ResultSetJSONSerializer.class);
    private long count = -1;

    /**
     * Geeft, na serialisatie, het aantal verwerkte records.
     *
     * @return aatntal verwerkte records, {@code -1} in geval van een fout.
     */
    public long getCount() {
        return this.count;
    }

    @Override
    public Class<ResultSet> handledType() {
        return ResultSet.class;
    }

    @Override
    public void serialize(ResultSet resultSet, JsonGenerator gen, SerializerProvider serializers) throws ResultSetSerializerException {
        long counted = 0;
        try {
            LOG.trace("uitlezen query resultaat metadata");
            ResultSetMetaData metaData = resultSet.getMetaData();
            int numCols = metaData.getColumnCount();
            String[] colNames = new String[numCols];
            int[] colTypes = new int[numCols];
            for (int i = 0; i < numCols; i++) {
                colNames[i] = metaData.getColumnName(i + 1);
                colTypes[i] = metaData.getColumnType(i + 1);
            }
            gen.writeStartArray();
            LOG.trace("uitlezen en uitschrijven query resultaat");
            while (resultSet.next()) {
                boolean b;
                long l;
                double d;
                BigDecimal bd;
                gen.writeStartObject();

                for (int i = 0; i < colNames.length; i++) {
                    LOG.trace("veld naar json: " + colNames[i] + " (" + colTypes[i] + "): " + resultSet.getObject(i + 1));
                    gen.writeFieldName(colNames[i]);

                    switch (colTypes[i]) {
                        case Types.INTEGER:
                        case Types.BIGINT:
                            l = resultSet.getLong(i + 1);
                            if (resultSet.wasNull()) {
                                gen.writeNull();
                            } else {
                                gen.writeNumber(l);
                            }
                            break;
                        case Types.SMALLINT:
                        case Types.TINYINT:
                            l = resultSet.getShort(i + 1);
                            if (resultSet.wasNull()) {
                                gen.writeNull();
                            } else {
                                gen.writeNumber(l);
                            }
                            break;
                        case Types.DECIMAL:
                        case Types.NUMERIC:
                            bd = resultSet.getBigDecimal(i + 1);
                            if (resultSet.wasNull()) {
                                gen.writeNull();
                            } else {
                                gen.writeNumber(bd);
                            }
                            break;
                        case Types.FLOAT:
                        case Types.DOUBLE:
                        case Types.REAL:
                            d = resultSet.getDouble(i + 1);
                            if (resultSet.wasNull()) {
                                gen.writeNull();
                            } else {
                                gen.writeNumber(d);
                            }
                            break;
                        case Types.NVARCHAR:
                        case Types.VARCHAR:
                        case Types.LONGNVARCHAR:
                        case Types.LONGVARCHAR:
                            gen.writeString(resultSet.getString(i + 1));
                            break;
                        case Types.BOOLEAN:
                        case Types.BIT:
                            b = resultSet.getBoolean(i + 1);
                            if (resultSet.wasNull()) {
                                gen.writeNull();
                            } else {
                                gen.writeBoolean(b);
                            }
                            break;
                        case Types.DATE:
                            serializers.defaultSerializeDateValue(resultSet.getDate(i + 1), gen);
                            break;
                        case Types.TIMESTAMP:
                            serializers.defaultSerializeDateValue(resultSet.getTime(i + 1), gen);
                            break;
                        case Types.BINARY:
                        case Types.VARBINARY:
                        case Types.LONGVARBINARY:
                            gen.writeBinary(resultSet.getBytes(i + 1));
                            break;
                        case Types.BLOB:
                            Blob blob = resultSet.getBlob(i + 1);
                            serializers.defaultSerializeValue(blob.getBinaryStream(), gen);
                            blob.free();
                            break;
                        case Types.CLOB:
                            Clob clob = resultSet.getClob(i);
                            serializers.defaultSerializeValue(clob.getCharacterStream(), gen);
                            clob.free();
                            break;
                        // TODO er missen mogelijk nog wat SQL data types
                        case Types.ARRAY:
                        case Types.STRUCT:
                        case Types.DISTINCT:
                        case Types.REF:
                            throw new NotImplementedException("ResultSetJSONSerializer (nog) niet geimplementeerd voor SQL type: " + colTypes[i]);
                        case Types.JAVA_OBJECT:
                        default:
                            serializers.defaultSerializeValue(resultSet.getObject(i + 1), gen);
                    }
                }
                counted++;
                gen.writeEndObject();
            }
            gen.writeEndArray();
            this.count = counted;
        } catch (SQLException | IOException | RuntimeException e) {
            LOG.error(e);
            throw new ResultSetSerializerException(e);
        }
    }
}