99import edu .ie3 .util .TimeUtil ;
1010import java .sql .*;
1111import java .util .*;
12+ import java .util .stream .Stream ;
13+ import java .util .stream .StreamSupport ;
1214import org .slf4j .Logger ;
1315import org .slf4j .LoggerFactory ;
1416
@@ -55,6 +57,9 @@ public ResultSet executeQuery(Statement stmt, String query) throws SQLException
5557 return stmt .executeQuery (query );
5658 } catch (SQLException e ) {
5759 throw new SQLException (String .format ("Error at execution of query \" %1.127s\" : " , query ), e );
60+ } finally {
61+ // commits any changes made and unlocks database
62+ getConnection ().commit ();
5863 }
5964 }
6065
@@ -70,6 +75,9 @@ public int executeUpdate(String query) throws SQLException {
7075 } catch (SQLException e ) {
7176 throw new SQLException (
7277 String .format ("Error at execution of query, SQLReason: '%s'" , e .getMessage ()), e );
78+ } finally {
79+ // commits any changes made and unlocks database
80+ getConnection ().commit ();
7381 }
7482 }
7583
@@ -85,7 +93,8 @@ public Connection getConnection() throws SQLException {
8593 }
8694
8795 /**
88- * Establishes and returns a database connection
96+ * Establishes and returns a database connection. The {@link Connection#getAutoCommit()} is set to
97+ * {@code false}.
8998 *
9099 * @param reuseConnection should the connection be used again, if it is still valid? If not, a new
91100 * connection will be established
@@ -98,6 +107,7 @@ public Connection getConnection(boolean reuseConnection) throws SQLException {
98107 if (connection != null ) connection .close ();
99108
100109 connection = DriverManager .getConnection (jdbcUrl , connectionProps );
110+ connection .setAutoCommit (false );
101111 } catch (SQLException e ) {
102112 throw new SQLException ("Could not establish connection: " , e );
103113 }
@@ -115,21 +125,82 @@ public void shutdown() {
115125 }
116126
117127 /**
118- * Extracts all field to value maps from the ResultSet, one for each row
128+ * Method to execute a {@link PreparedStatement} and return its result as a stream.
119129 *
120- * @param rs the ResultSet to use
121- * @return a list of field maps
130+ * @param ps to execute
131+ * @param fetchSize used for {@link PreparedStatement#setFetchSize(int)}
132+ * @return a stream of maps
133+ * @throws SQLException if an exception occurred while executing the query
122134 */
123- public List <Map <String , String >> extractFieldMaps ( ResultSet rs ) {
124- List < Map < String , String >> fieldMaps = new ArrayList <>();
135+ public Stream <Map <String , String >> toStream ( PreparedStatement ps , int fetchSize )
136+ throws SQLException {
125137 try {
126- while (rs .next ()) {
127- fieldMaps .add (extractFieldMap (rs ));
138+ ps .setFetchSize (fetchSize );
139+ ResultSet resultSet = ps .executeQuery ();
140+ Iterator <Map <String , String >> sqlIterator = getSqlIterator (resultSet );
141+
142+ return StreamSupport .stream (
143+ Spliterators .spliteratorUnknownSize (
144+ sqlIterator , Spliterator .NONNULL | Spliterator .IMMUTABLE ),
145+ true )
146+ .onClose (() -> closeResultSet (ps , resultSet ));
147+ } catch (SQLException e ) {
148+ // catches the exception, closes the statement and re-throws the exception
149+ closeResultSet (ps , null );
150+ throw e ;
151+ }
152+ }
153+
154+ /**
155+ * Returns an {@link Iterator} for the given {@link ResultSet}.
156+ *
157+ * @param rs given result set
158+ * @return an iterator
159+ */
160+ public Iterator <Map <String , String >> getSqlIterator (ResultSet rs ) {
161+ return new Iterator <>() {
162+ @ Override
163+ public boolean hasNext () {
164+ try {
165+ return rs .next ();
166+ } catch (SQLException e ) {
167+ log .error ("Exception at extracting next ResultSet: " , e );
168+ closeResultSet (null , rs );
169+ return false ;
170+ }
128171 }
172+
173+ @ Override
174+ public Map <String , String > next () {
175+ try {
176+ boolean isEmpty = !rs .isBeforeFirst () && rs .getRow () == 0 ;
177+
178+ if (isEmpty || rs .isAfterLast ())
179+ throw new NoSuchElementException (
180+ "There is no more element to iterate to in the ResultSet." );
181+
182+ return extractFieldMap (rs );
183+ } catch (SQLException e ) {
184+ log .error ("Exception at extracting ResultSet: " , e );
185+ closeResultSet (null , rs );
186+ return Collections .emptyMap ();
187+ }
188+ }
189+ };
190+ }
191+
192+ /**
193+ * Method for closing a {@link ResultSet}.
194+ *
195+ * @param rs to close
196+ */
197+ private void closeResultSet (PreparedStatement ps , ResultSet rs ) {
198+ try (ps ;
199+ rs ) {
200+ log .debug ("Resources successfully closed." );
129201 } catch (SQLException e ) {
130- log .error ( "Exception at extracting ResultSet: " , e );
202+ log .warn ( "Failed to properly close sources. " , e );
131203 }
132- return fieldMaps ;
133204 }
134205
135206 /**
@@ -138,26 +209,24 @@ public List<Map<String, String>> extractFieldMaps(ResultSet rs) {
138209 * @param rs the ResultSet to use
139210 * @return the field map for the current row
140211 */
141- public Map <String , String > extractFieldMap (ResultSet rs ) {
212+ public Map <String , String > extractFieldMap (ResultSet rs ) throws SQLException {
142213 TreeMap <String , String > insensitiveFieldsToAttributes =
143214 new TreeMap <>(String .CASE_INSENSITIVE_ORDER );
144- try {
145- ResultSetMetaData metaData = rs .getMetaData ();
146- int columnCount = metaData .getColumnCount ();
147- for (int i = 1 ; i <= columnCount ; i ++) {
148- String columnName = StringUtils .snakeCaseToCamelCase (metaData .getColumnName (i ));
149- String value ;
150- Object result = rs .getObject (i );
151- if (result instanceof Timestamp ) {
152- value = TimeUtil .withDefaults .toString (rs .getTimestamp (i ).toInstant ());
153- } else {
154- value = String .valueOf (rs .getObject (i ));
155- }
156- insensitiveFieldsToAttributes .put (columnName , value );
215+
216+ ResultSetMetaData metaData = rs .getMetaData ();
217+ int columnCount = metaData .getColumnCount ();
218+ for (int i = 1 ; i <= columnCount ; i ++) {
219+ String columnName = StringUtils .snakeCaseToCamelCase (metaData .getColumnName (i ));
220+ String value ;
221+ Object result = rs .getObject (i );
222+ if (result instanceof Timestamp ) {
223+ value = TimeUtil .withDefaults .toString (rs .getTimestamp (i ).toInstant ());
224+ } else {
225+ value = String .valueOf (rs .getObject (i ));
157226 }
158- } catch (SQLException e ) {
159- log .error ("Exception at extracting ResultSet: " , e );
227+ insensitiveFieldsToAttributes .put (columnName , value );
160228 }
229+
161230 return insensitiveFieldsToAttributes ;
162231 }
163232}
0 commit comments