Общий Java-класс для связи JSON от сервера к клиенту

1

В настоящее время я работаю над переносом проекта на java, сохраняя данные без изменений (база данных). Большинство данных берутся через stored procedures (SP), и в базе данных есть множество SP. Поэтому, выполняя каждый SP, я должен написать класс для этого, который создает огромную кучу классов.

Следовательно, есть ли способ обобщить класс, чтобы я мог преобразовать результаты каждого SP в этот класс, а затем на сторону клиента (как json)?

Следующие сценарии скрыты. N ma qn:

  1. Динамическое число полей.
  2. Динамические имена полей.
  3. Тип может быть строкой (может иметь дело с этим).

Я попытался отправить данные как java.util.List, но это не получается в симпатичном формате. Приходится принимать данные с учетом индексов.

PS: Я искал то же самое, но не смог найти. И жаль, если я прошу слишком много.

Теги:
stored-procedures

1 ответ

2
Лучший ответ

Да, должно быть возможно написать такой общий класс. Вот небольшой примерный класс в качестве отправной точки для вас. Я использую Firebird с примером базы данных employee.fdb потому что уже определены некоторые хранимые процедуры.

Поэтому, чтобы подключиться к серверу Firebird, я использую JDBC-драйвер Jaybird и jaybird-full-2.2.5.jar JAR файл jaybird-full-2.2.5.jar.

Для JAVA существует несколько разных JSON-библиотек. Я использую JSR 353: Java API для обработки JSON - Реализация ссылок здесь в потоковом режиме (например, StaX для XML). Итак, второй внешний JAR здесь javax.json-1.0.4.jar.

Мой пример работает только для хранимых процедур, возвращающих наборы результатов. Для хранимых процедур с выходными параметрами вместо PreparedStatement следует использовать CallableStatement.

Во-первых, общий оператор SQL создается для конкретной хранимой процедуры с ее входными параметрами. Для вызова хранимой процедуры используется PreparedStatemend. Параметры задаются в соответствии с отдельными типами параметров. (Процедуры createSql() и createStatement())

В процедуре convertToJson() метод ResultSet.getMetaData() используется для получения информации о столбце набора результатов (количество столбцов, имя столбца и тип столбца).

executeStoredProcedure() - это общедоступные методы API для вызова.

Метод main() подключается к èmployee.fdb данных èmployee.fdb и вызывает три хранимые процедуры: GET_EMP_PROJ, MAIL_LABEL и ORG_CHART.

package com.genericsptojson;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;

import javax.json.Json;
import javax.json.stream.JsonGenerator;
import javax.json.stream.JsonGeneratorFactory;

public class GenericSpToJson {

  private static final String DB_URL = "jdbc:firebirdsql:localhost/3050:/var/lib/firebird/2.5/data/employee.fdb";
  private static final String DB_USER = "SYSDBA";
  private static final String DB_PWD = "***";

  private Connection con;

  public GenericSpToJson(Connection con) {
    this.con = con;
  }

  /**
   * Creates the SQL to call the stored procedure. 
   * 
   * @param spName
   *          Name of stored procecdure to call
   * @param paramCount
   *          number of input parameters
   * @return SQL with placeholders for input parameters
   */
  private String createSql(String spName, int paramCount) {
    if(paramCount > 0) {
      final StringBuilder params = new StringBuilder();
      boolean isFirst = true;
      for(int i = 0; i < paramCount; i++) {
        if(isFirst) {
          isFirst = false;
        } else {
          params.append(", ");
        }
        params.append('?');
      }
      return String.format("SELECT * FROM %s (%s)", spName, params.toString());
    } else {
      return String.format("SELECT * FROM %s", spName);
    }
  }

  /**
   * Creates a PreparedStatement to call the stored procedure. This works only
   * for stored procedures creating result sets. Stored procedures with OUT
   * parameters should be handled by a CallableStatement instead.
   * 
   * @param spName
   *          The stored procedure name to be called.
   * @param params
   *          The input parameters.
   * @return A prepared statement. All parameters are set.
   * @throws SQLException
   */
  private PreparedStatement createStatement(String spName, Object... params) throws SQLException {
    final PreparedStatement stmt = con.prepareStatement(createSql(spName, params.length));
    for(int i = 0; i < params.length; i++) {
      final Object param = params[i];
      if(param instanceof String) {
        stmt.setString(i + 1, (String) param);
      } else if(param instanceof Integer) {
        stmt.setInt(i + 1, ((Integer) param).intValue());
      } else {
        // Handle other param types ...
      }
    }
    return stmt;
  }

  /**
   * Converts the result set to JSON in streaming mode.
   * 
   * @param spName
   *          The stored procedure name.
   * @param rs
   *          The result set of the stored procedure call.
   * @param out
   *          The output stream to write the JSON into.
   * @throws SQLException
   */
  private void convertToJson(String spName, ResultSet rs, OutputStream out) throws SQLException {
    // Get the result set meta data to obtain column information on the fly. 
    final ResultSetMetaData metaData = rs.getMetaData();

    // Create the JSON generator with pretty printing
    final Map<String, Object> properties = new HashMap<String, Object>(1);
    properties.put(JsonGenerator.PRETTY_PRINTING, true);
    final JsonGeneratorFactory jsonGeneratorFactory = Json.createGeneratorFactory(properties);
    final JsonGenerator generator = jsonGeneratorFactory.createGenerator(out);
    generator.writeStartObject(); // root object

    generator.write("storedProcedureName", spName);
    generator.write("columnCount", metaData.getColumnCount());
    generator.writeStartArray("records"); // records array
    while(rs.next()) {
      generator.writeStartObject(); // record object
      // Each record object contains one field for every column.
      // The field name is the columns name.
      for(int col = 1; col <= metaData.getColumnCount(); col++) {
        final String fieldName = metaData.getColumnName(col);
        switch(metaData.getColumnType(col)) {
          case Types.INTEGER:
            final int intValue = rs.getInt(col);
            if(rs.wasNull()) {
              generator.writeNull(fieldName);
            } else {
              generator.write(fieldName, intValue);
            }
            break;
          case Types.VARCHAR:
          case Types.CHAR:
            String stringValue = rs.getString(col);
            if(rs.wasNull()) {
              generator.writeNull(fieldName);
            } else {
              if(metaData.getColumnType(col) == Types.CHAR) {
                stringValue = stringValue.trim();
              }
              generator.write(fieldName, stringValue);
            }
            break;
          // Handle other types here
          default:
            System.out.println(String.format("Unhandled SQL type: %s", metaData.getColumnTypeName(col)));
        }
      }
      generator.writeEnd(); // record object
    }
    generator.writeEnd(); // records array
    generator.writeEnd(); // root object
    generator.flush();
    generator.close();
  }

  /**
   * Executes the stored procedures with the given input parameters and creates
   * JSON in streaming mode.
   * 
   * @param spName
   *          The name of the stored procedure.
   * @param out
   *          The output stream to write the generated JSON into.
   * @param params
   *          The stored procedure parameters.
   */
  public void executeStoredProcedure(String spName, OutputStream out, Object... params) {
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try {
      stmt = createStatement(spName, params);
      rs = stmt.executeQuery();
      convertToJson(spName, rs, out);
    } catch (SQLException e) {
      e.printStackTrace();
    } finally {
      // Cleaning up ...
      if(stmt != null) {
        try {
          stmt.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if(rs != null) {  
        try {
          rs.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }   
      }
    }
  }

  /**
   * Convenience method to call the stored procedure and create a JSON string.
   * This should only be called for short result sets. For longer result sets
   * use {@link #executeStoredProcedure(String, OutputStream, Object...)} where
   * it is not necessary to hold the entire JSON document in memory.
   * 
   * @param spName
   *          The name of the stored procedure to call.
   * @param params
   *          The stored procedure parameters
   * @return The stored procedure call result as a JSON string.
   * @throws UnsupportedEncodingException
   */
  public String executeStoredProcedure(String spName, Object... params) throws UnsupportedEncodingException {
    final ByteArrayOutputStream out = new ByteArrayOutputStream();
    executeStoredProcedure(spName, out, params);
    return out.toString("UTF-8");
  }


  public static void main(String[] args) {
    Connection con = null;
    try {
      Class.forName("org.firebirdsql.jdbc.FBDriver");
      con = DriverManager.getConnection(DB_URL, DB_USER, DB_PWD);

      final GenericSpToJson converter = new GenericSpToJson(con);

      System.out.println("Executing stored procedure GET_EMP_PROJ (8):\n"
          + converter.executeStoredProcedure("GET_EMP_PROJ", 8));
      System.out.println("\n\nExecuting stored procedure MAIL_LABEL (1015):\n"
          + converter.executeStoredProcedure("MAIL_LABEL", 1015));
      System.out.println("\n\nExecuting stored procedure ORG_CHART:\n"
          + converter.executeStoredProcedure("ORG_CHART"));
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    } catch (SQLException e) {
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    if(con != null) {
      try {
        con.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }    
}

Выход (сокращен):

Executing stored procedure GET_EMP_PROJ (8):

{
    "storedProcedureName":"GET_EMP_PROJ",
    "columnCount":1,
    "records":[
        {
            "PROJ_ID":"VBASE"
        },
        {
            "PROJ_ID":"GUIDE"
        },
        {
            "PROJ_ID":"MKTPR"
        }
    ]
}


Executing stored procedure MAIL_LABEL (1015):

{
    "storedProcedureName":"MAIL_LABEL",
    "columnCount":6,
    "records":[
        {
            "LINE1":"GeoTech Inc.",
            "LINE2":"K.M. Neppelenbroek",
            "LINE3":"P.0.Box 702",
            "LINE4":"",
            "LINE5":null,
            "LINE6":"Netherlands    2514"
        }
    ]
}


Executing stored procedure ORG_CHART:

{
    "storedProcedureName":"ORG_CHART",
    "columnCount":5,
    "records":[
        {
            "HEAD_DEPT":null,
            "DEPARTMENT":"Corporate Headquarters",
            "MNGR_NAME":"Bender, Oliver H.",
            "TITLE":"CEO",
            "EMP_CNT":2
        },
        {
            "HEAD_DEPT":"Corporate Headquarters",
            "DEPARTMENT":"Sales and Marketing",
            "MNGR_NAME":"MacDonald, Mary S.",
            "TITLE":"VP",
            "EMP_CNT":2
        },
        // ... SNIP ...
        {
            "HEAD_DEPT":"Corporate Headquarters",
            "DEPARTMENT":"Finance",
            "MNGR_NAME":"Steadman, Walter",
            "TITLE":"CFO",
            "EMP_CNT":2
        }
    ]
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню