2

I'm looking to store a list of relations between three objects, where two of the related objects will each only exist in one relation in the list and the third is a count that I can increment. It's not very generalizable to SO at large, but you can see what I'm doing here

    public class Transaction implements AutoCloseable {
    private final Connection conn;
    private final Map<String, Map.Entry<PreparedStatement, Integer>> sts = new HashMap<>();

    Transaction(final Connection conn) throws SQLException {
        conn.setAutoCommit(false);
        this.conn = conn;
    }

    <T> void batch(final String st, final List<T> ts,
            final BatchingInstructions<T> bi) throws SQLException {
        final PreparedStatement stat;
        int counter;

        // if statement has already been created, retrieve statement and batch count
        if (sts.containsKey(st)) {
            final Map.Entry<PreparedStatement, Integer> m = sts.get(st);
            stat = m.getKey();
            counter = m.getValue();
        } else {
            // else create statement and batch count
            stat = conn.prepareStatement(getInsert(st));
            counter = 0;
            sts.put(st, // Can't do a new Map.Entry, really
        }

        for (final T t : ts) {
            bi.batch(stat, t);
            stat.addBatch();
            // increment how many times a batch has been added to the statement.
            // execute batch if necessary.
            if (++counter > 5000) {
                stat.executeBatch();
            }
        }

    }

    @Override
    public void close() throws Exception {
        for (final Map.Entry<PreparedStatement, Integer> m : sts.values()) {
            if (m.getValue() > 0) {
                m.getKey().executeBatch();
            }
            m.getKey().close();
        }
        conn.commit();
    }

Guava's Table doesn't exactly work, and a Map<String, Map<Statement, Integer>> is also not ideal, and it's all complicated by the need to increment one of the values (though for that I'll probably use the mutableInt solution found here Most efficient way to increment a Map value in Java).

I was wondering if anybody knew of any straightforward solutions to this issue, possibly in third party libraries?


Using the accepted answer, I was able to have the new class do a number of other useful things.

class BatchPreparedStatement implements AutoCloseable {
    private final PreparedStatement st;
    private int counter = 0;

    BatchPreparedStatement(final PreparedStatement st) {
        this.st = st;
    }

    <T> void addBatch(final BatchingInstructions<T> bi, final T t)
            throws SQLException {
        bi.batch(st, t);
        st.addBatch();
        executeBatch(10000);
    }

    void executeBatch(final int count) throws SQLException {
        if (counter++ > count) {
            st.executeBatch();
            counter = 0;
        }
    }

    void finished() throws SQLException {
        executeBatch(0);
    }

    @Override
    public void close() throws Exception {
        st.close();
    }
}
Community
  • 1
  • 1
pail
  • 193
  • 4
  • 10

3 Answers3

1

Why dont you you create a Class that holds the Statement, the count and handles all the addBatching plus other miscellaneous activities.

class BatchPreparedStatementHolder {

    private int batchCount;
    private PreparedStatement ps;

    public BatchPreparedStatementHolder(PreparedStatement statement){
        batchCount = 0;
        ps = statment;
    }

    public void addBatch(){
        ps.addBatch();
        // increment how many times a batch has been added to the statement.
        // execute batch if necessary.
        if (++batchCount> 5000) {
            ps.executeBatch();
            batchCount = 0;
        }
    }

    ....

}
BevynQ
  • 8,089
  • 4
  • 25
  • 37
0

The class suggestion is pretty good but a little heavy weight if you just need something simple. I would use something like a HashMap. (other languages call these "dictionaries"). I don't have time to read all your code, but it looks like you may be using Maps?

Anyways, you could make the key the auto-incremented value (as in databases) and the value could be, say a tuple or array of the other two values. See the following link for good advice on HashMaps:

http://java67.blogspot.com/2013/02/10-examples-of-hashmap-in-java-programming-tutorial.html

Or perhaps just make an array where the index refers to the auto-incremented function, and the content is another array?

Not sure exactly what you need but these might work!

Josh T
  • 564
  • 3
  • 12
0

Rather than keeping the Map.Entry in the map you can keep the nested class .. which will be easy to handle.. something like that ..

class Transaction implements AutoCloseable {
            private final Connection conn;

            class MapValue {
                PreparedStatement p;
                int index ;
                public MapValue(){}
                public MapValue(PreparedStatement p , int i)
                {
                    index= i;
                    this.p=p;
                }
            }
            private final Map<String, MapValue> sts = new HashMap<>();

            Transaction(final Connection conn) throws SQLException {
                conn.setAutoCommit(false);
                this.conn = conn;
            }

            <T> void batch(final String st, final List<T> ts,
                    final BatchingInstructions<T> bi) throws SQLException {
                final PreparedStatement stat;
                int counter;

                // if statement has already been created, retrieve statement and batch count
                if (sts.containsKey(st)) {
                     MapValue  m = sts.get(st);
                    stat = m.p;
                    counter = m.index;
                } else {
                    // else create statement and batch count

                    stat = conn.prepareStatement(getInsert(st));
                    MapValue m = new MapValue(stat, 0);
                    sts.put(st, m);

                }

                for (final T t : ts) {
                    bi.batch(stat, t);
                    stat.addBatch();
                    // increment how many times a batch has been added to the statement.
                    // execute batch if necessary.
                    if (++counter > 5000) {
                        stat.executeBatch();
                    }
                }
                @Override
                public void close() throws Exception {
                    for (final Map.Entry<PreparedStatement, Integer> m : sts.values()) {
                        if (m.getValue() > 0) {
                            m.getKey().executeBatch();
                        }
                        m.getKey().close();
                    }
                    conn.commit();
                }


      }
Neha
  • 1,548
  • 2
  • 10
  • 13