web-dev-qa-db-fra.com

Récupérer la ligne entière avec ResultSet

Est-il possible de récupérer une ligne entière sans appeler getInt (..) getString (..) pour chaque colonne?

J'ai plusieurs threads, chaque bande de roulement doit écrire le résultat dans une collection thread-safe.
Je souhaite pouvoir écrire les lignes directement dans cette collection, puis analyser les membres de cette collection et récupérer les valeurs en fonction des types de colonne.

11
yuris

Vous pouvez construire une classe comme celle-ci, qui mappe les types de données SQL avec les types de données Java:

class Row
{
    public Map <Object,Class> row;
    public static Map <String, Class> TYPE;

    static
    {
        TYPE = new HashMap<String, Class>();

        TYPE.put("INTEGER", Integer.class);
        TYPE.put("TINYINT", Byte.class);
        TYPE.put("SMALLINT", Short.class);
        TYPE.put("BIGINT", Long.class);
        TYPE.put("REAL", Float.class);
        TYPE.put("FLOAT", Double.class);
        TYPE.put("DOUBLE", Double.class);
        TYPE.put("DECIMAL", BigDecimal.class);
        TYPE.put("NUMERIC", BigDecimal.class);
        TYPE.put("BOOLEAN", Boolean.class);
        TYPE.put("CHAR", String.class);
        TYPE.put("VARCHAR", String.class);
        TYPE.put("LONGVARCHAR", String.class);
        TYPE.put("DATE", Date.class);
        TYPE.put("TIME", Time.class);
        TYPE.put("TIMESTAMP", Timestamp.class);
        // ...
    }

    public Row ()
    {
        row = new HashMap<Object,Class>();
    }

    public void add<t> (t data)
    {
        row.put(data, data.getClass());
    }

    public void add (Object data, String sqlType)
    {
        add((Row.TYPE.get(sqlType)) data);
    }

    public static void formTable (ResultSet rs, ArrayList<Row> table)
    {
        if (rs == null) return;

        ResultSetMetaData rsmd = rs.getMetaData();

        int NumOfCol = rsmd.getColumnCount();

        while (rs.next())
        {
            row = new Row ();

            for(int i = 1; i <= NumOfCol; i++)
            {
                row.add(rs.getObject(i), rsmd.getColumnTypeName(i));
            }

            table.add(row);
        }
    }
}

Que vous pouvez utiliser comme ça:

List<Row> table = new ArrayList<Row>();

Row row = null;

ResultSet rs = st.executeQuery("SELECT * FROM table_name");

Row.formTable(rs, table);

Ensuite, vous pouvez récupérer des champs et les convertir en leurs types de données respectifs:

for (Row row : table)
{

    for (Object data : row.row.getKeySet())
    {
        System.out.print(" > " + ((row.row.get(data) data));
    }

    System.out.println();

}
4
Khaled.K

Ligne représentée sous forme de liste:

import Java.math.BigDecimal;
import Java.sql.Date;
import Java.sql.ResultSet;
import Java.sql.ResultSetMetaData;
import Java.sql.SQLException;
import Java.sql.Time;
import Java.sql.Timestamp;
import Java.util.AbstractMap;
import Java.util.ArrayList;
import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;
import Java.util.Map.Entry;
import Java.util.logging.Level;
import Java.util.logging.Logger;

/**
 * @author Adam Dziedzic
 * 
 */
public class Row {
    public List<Entry<Object, Class>> row;
    public static Map<String, Class> TYPE;

    static {
        TYPE = new HashMap<String, Class>();

        TYPE.put("INTEGER", Integer.class);
        TYPE.put("TINYINT", Byte.class);
        TYPE.put("SMALLINT", Short.class);
        TYPE.put("BIGINT", Long.class);
        TYPE.put("REAL", Float.class);
        TYPE.put("FLOAT", Double.class);
        TYPE.put("DOUBLE", Double.class);
        TYPE.put("DECIMAL", BigDecimal.class);
        TYPE.put("NUMERIC", BigDecimal.class);
        TYPE.put("BOOLEAN", Boolean.class);
        TYPE.put("CHAR", String.class);
        TYPE.put("VARCHAR", String.class);
        TYPE.put("LONGVARCHAR", String.class);
        TYPE.put("DATE", Date.class);
        TYPE.put("TIME", Time.class);
        TYPE.put("TIMESTAMP", Timestamp.class);
        TYPE.put("SERIAL",Integer.class);
        // ...
    }

    public Row() {
        row = new ArrayList<Entry<Object, Class>>();
    }

    public <T> void add(T data) {
        row.add(new AbstractMap.SimpleImmutableEntry<Object,Class>(data, data.getClass()));
    }

    public void add(Object data, String sqlType) {
        Class castType = Row.TYPE.get(sqlType.toUpperCase());
        try {
            this.add(castType.cast(data));
        } catch (NullPointerException e) {
            e.printStackTrace();
            Logger lgr = Logger.getLogger(Row.class.getName());
            lgr.log(Level.SEVERE, e.getMessage()+" Add the type "+sqlType+" to the TYPE hash map in the Row class.", e);
            throw e;
        }
    }

    public static void formTable(ResultSet rs, List<Row> table)
            throws SQLException {
        if (rs == null)
            return;

        ResultSetMetaData rsmd;
        try {
            rsmd = rs.getMetaData();

            int NumOfCol = rsmd.getColumnCount();

            while (rs.next()) {
                Row current_row = new Row();

                for (int i = 1; i <= NumOfCol; i++) {
                    current_row.add(rs.getObject(i), rsmd.getColumnTypeName(i));
                }

                table.add(current_row);
            }
        } catch (SQLException e) {
            throw e;
        }
    }
}

Usage: 

List<Row> table = new ArrayList<Row>();

ResultSet rs = st.executeQuery("SELECT * FROM table_name");

Row.formTable(rs, table);

for (Row row : table)
{
    for (Entry<Object, Class> col: row.row)
    {
        System.out.print(" > " + ((col.getValue()).cast(col.getKey())));
    }
    System.out.println();
}
3
ady

Voici un exemple utilisant une requête pour déterminer la taille de la requête, puis divisant le traitement en plusieurs threads.

J'utilise MySQL, donc les requêtes sont écrites pour ça. Vous devrez modifier les requêtes pour votre moteur de base de données.

public static void main(String[] args) throws Exception {
    final int count;
    try (final Connection conn = DATA_SOURCE.getConnection()) {
        final String countQuery = "SELECT COUNT(*) FROM my_table";
        try (final PreparedStatement ps = conn.prepareStatement(countQuery);
                final ResultSet resultSet = ps.executeQuery()) {
            resultSet.next();
            count = resultSet.getInt(1);
        }
    }
    final int chunksize = 1000;
    final Queue<SqlResult> results = new ConcurrentLinkedQueue<>();
    final ExecutorService es = Executors.newFixedThreadPool(10);
    for (int end = 0; end < count; end += chunksize) {
        es.execute(new ResultReader(count, end, DATA_SOURCE, results));
    }
}

private static class ResultReader implements Runnable {

    private final int start;
    private final int size;
    private final DataSource dataSource;
    private final Queue<SqlResult> results;

    public ResultReader(int start, int size, DataSource dataSource, Queue<SqlResult> results) {
        this.start = start;
        this.size = size;
        this.dataSource = dataSource;
        this.results = results;
    }

    @Override
    public void run() {
        try (final Connection connection = dataSource.getConnection()) {
            final String query = "SELECT id, something, somethingElse FROM my_table LIMIT ?, ?";
            try (final PreparedStatement ps = connection.prepareStatement(query)) {
                ps.setInt(1, start);
                ps.setInt(2, size);
                try (final ResultSet rs = ps.executeQuery()) {
                    while (rs.next()) {
                        final SqlResult sqlResult = new SqlResult();
                        sqlResult.setId(rs.getInt("id"));
                        sqlResult.setSomething(rs.getString("something"));
                        sqlResult.setSomethingElse(rs.getString("somethingElse"));
                        results.add(sqlResult);
                    }
                }
            }
        } catch (SQLException ex) {
            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

private static class SqlResult {

    private int id;
    private String something;
    private String somethingElse;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getSomething() {
        return something;
    }

    public void setSomething(String something) {
        this.something = something;
    }

    public String getSomethingElse() {
        return somethingElse;
    }

    public void setSomethingElse(String somethingElse) {
        this.somethingElse = somethingElse;
    }
}

Cela suppose que vous ayez déjà un regroupement de connexions, en utilisant une DataSource.

Chacun des opérateurs exécute une requête à l'aide de LIMIT, traite les résultats dans des objets SqlResult et les ajoute à une Queue concurrente.

1
Boris the Spider

J'ai utilisé adam.cajf answer, très utile, cependant, je devais ajouter une ligne de code pour ajouter une fonction, sinon cela jetterait une erreur dans mon ensemble de données particulier.

if (data != null) {
0
Dmitry Buslaev