1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.waarp.openr66.dao.database;
22
23 import org.waarp.common.database.exception.WaarpDatabaseSqlException;
24 import org.waarp.common.logging.WaarpLogger;
25 import org.waarp.common.logging.WaarpLoggerFactory;
26 import org.waarp.common.lru.SynchronizedLruCache;
27 import org.waarp.openr66.dao.AbstractDAO;
28 import org.waarp.openr66.dao.DAOFactory;
29 import org.waarp.openr66.dao.Filter;
30 import org.waarp.openr66.dao.database.DBDAOFactory.FakeConnection;
31 import org.waarp.openr66.dao.exception.DAOConnectionException;
32 import org.waarp.openr66.dao.exception.DAONoDataException;
33
34 import java.sql.Connection;
35 import java.sql.PreparedStatement;
36 import java.sql.ResultSet;
37 import java.sql.SQLException;
38 import java.sql.Statement;
39 import java.util.ArrayList;
40 import java.util.List;
41
42 public abstract class StatementExecutor<E> implements AbstractDAO<E> {
43
44 private static final WaarpLogger logger =
45 WaarpLoggerFactory.getLogger(StatementExecutor.class);
46 protected static final String WHERE = " WHERE ";
47 protected static final String PARAMETER = " = ?";
48 protected static final String PARAMETER_COMMA = " = ?, ";
49 protected static final String SQL_COUNT_ALL_PREFIX =
50 "SELECT COUNT(*) AS total FROM ";
51 protected Connection connection;
52
53 protected abstract boolean isDbTransfer();
54
55 public abstract E getFromResultSet(ResultSet set)
56 throws SQLException, DAOConnectionException;
57
58 protected abstract boolean isCachedEnable();
59
60 protected SynchronizedLruCache<String, E> getCache() {
61 return null;
62 }
63
64 protected final void clearCache() {
65 if (isCachedEnable()) {
66 getCache().clear();
67 }
68 }
69
70 protected final void addToCache(final String key, final E elt) {
71 if (isCachedEnable()) {
72 getCache().put(key, elt);
73 }
74 }
75
76 protected final E getFromCache(final String key) {
77 if (isCachedEnable()) {
78 final E value = getCache().get(key);
79 if (value != null) {
80 getCache().updateTtl(key);
81 }
82 return value;
83 }
84 return null;
85 }
86
87 protected final void removeFromCache(final String key) {
88 if (isCachedEnable()) {
89 getCache().remove(key);
90 }
91 }
92
93 protected final boolean isInCache(final String key) {
94 if (isCachedEnable()) {
95 return getCache().contains(key);
96 }
97 return false;
98 }
99
100 protected StatementExecutor(final Connection con) {
101 connection = con;
102 }
103
104 public Connection getConnection() {
105 return connection;
106 }
107
108 protected final void setParameters(final PreparedStatement stm,
109 final Object... values)
110 throws SQLException {
111 if (values == null) {
112 return;
113 }
114 for (int i = 0; i < values.length; i++) {
115 stm.setObject(i + 1, values[i]);
116 }
117 }
118
119 protected final void executeUpdate(final PreparedStatement stm)
120 throws SQLException {
121 final int res;
122 res = stm.executeUpdate();
123 if (res < 1) {
124 logger.error("Update failed, no record updated.");
125
126
127 } else {
128 logger.debug("{} records updated.", res);
129 }
130 }
131
132 protected final int executeAction(final PreparedStatement stm)
133 throws SQLException {
134 return stm.executeUpdate();
135 }
136
137 protected final ResultSet executeQuery(final PreparedStatement stm)
138 throws SQLException {
139 return stm.executeQuery();
140 }
141
142 protected final void closeStatement(final Statement stm) {
143 if (stm == null) {
144 return;
145 }
146 try {
147 stm.close();
148 } catch (final SQLException e) {
149 logger.warn("An error occurs while closing the statement." + " : {}",
150 e.getMessage());
151 }
152 }
153
154 protected final void closeResultSet(final ResultSet rs) {
155 if (rs == null) {
156 return;
157 }
158 try {
159 rs.close();
160 } catch (final SQLException e) {
161 logger.warn("An error occurs while closing the resultSet." + " : {}",
162 e.getMessage());
163 }
164 }
165
166 public final void close() {
167 try {
168 connection.close();
169 } catch (final SQLException e) {
170 logger.warn("Cannot properly close the database connection" + " : {}",
171 e.getMessage());
172 }
173 }
174
175 protected abstract String getId(E e1);
176
177 protected abstract String getTable();
178
179 protected abstract String getSelectRequest();
180
181 protected abstract String getCountRequest();
182
183 protected abstract String getGetAllRequest();
184
185 protected abstract String getExistRequest();
186
187 protected abstract Object[] getInsertValues(E e1)
188 throws WaarpDatabaseSqlException;
189
190 protected abstract String getInsertRequest();
191
192 protected abstract Object[] getUpdateValues(E e1)
193 throws WaarpDatabaseSqlException;
194
195 protected abstract String getUpdateRequest();
196
197 protected abstract String getDeleteRequest();
198
199 protected abstract String getDeleteAllRequest();
200
201 @Override
202 public void delete(final E e1)
203 throws DAOConnectionException, DAONoDataException {
204 if (isCachedEnable()) {
205
206 removeFromCache(getId(e1));
207 }
208 PreparedStatement stm = null;
209 try {
210 stm = connection.prepareStatement(getDeleteRequest());
211 setParameters(stm, getId(e1));
212 try {
213 executeAction(stm);
214 } catch (final SQLException e2) {
215 throw new DAONoDataException(e2);
216 }
217 } catch (final SQLException e) {
218 throw new DAOConnectionException(e);
219 } finally {
220 closeStatement(stm);
221 }
222 }
223
224 @Override
225 public final void deleteAll() throws DAOConnectionException {
226 clearCache();
227 PreparedStatement stm = null;
228 try {
229 stm = connection.prepareStatement(getDeleteAllRequest());
230 executeAction(stm);
231 } catch (final SQLException e) {
232 throw new DAOConnectionException(e);
233 } finally {
234 closeStatement(stm);
235 }
236 }
237
238 protected final String prepareDeleteQuery(final List<Filter> filters,
239 final Object[] params) {
240 final StringBuilder query = new StringBuilder(getDeleteAllRequest());
241 return getFullRequest(filters, params, query);
242 }
243
244 private final String getFullRequest(final List<Filter> filters,
245 final Object[] params,
246 final StringBuilder query) {
247 if (filters.isEmpty()) {
248 return query.toString();
249 }
250 query.append(WHERE);
251 String prefix = "";
252 int i = 0;
253 for (final Filter filter : filters) {
254 query.append(prefix);
255 if (filter.nbAdditionnalParams() > 1) {
256 final Object[] objects = (Object[]) filter.append(query);
257 for (final Object o : objects) {
258 params[i++] = o;
259 }
260 } else if (filter.nbAdditionnalParams() == 1) {
261 params[i] = filter.append(query);
262 i++;
263 } else {
264 filter.append(query);
265 }
266 prefix = " AND ";
267 }
268 return query.toString();
269 }
270
271
272
273
274
275
276
277
278
279
280 public final int delete(final List<Filter> filters)
281 throws DAOConnectionException {
282 final ArrayList<E> es = new ArrayList<E>();
283
284 final Object[] params = prepareFindParams(filters);
285 final String query = prepareDeleteQuery(filters, params);
286
287 PreparedStatement stm = null;
288 try {
289 stm = connection.prepareStatement(query);
290 setParameters(stm, params);
291 return executeAction(stm);
292 } catch (final SQLException e) {
293 throw new DAOConnectionException(e);
294 } finally {
295 closeStatement(stm);
296 }
297 }
298
299 @Override
300 public final List<E> getAll() throws DAOConnectionException {
301 final ArrayList<E> es = new ArrayList<E>();
302 PreparedStatement stm = null;
303 ResultSet res = null;
304 try {
305 stm = connection.prepareStatement(getGetAllRequest());
306 res = executeQuery(stm);
307 while (res.next()) {
308 es.add(getFromResultSet(res));
309 }
310 } catch (final SQLException e) {
311 throw new DAOConnectionException(e);
312 } finally {
313 closeResultSet(res);
314 closeStatement(stm);
315 }
316 return es;
317 }
318
319 protected final String prepareFindQuery(final List<Filter> filters,
320 final Object[] params) {
321 final StringBuilder query = new StringBuilder(getGetAllRequest());
322 return getFullRequest(filters, params, query);
323 }
324
325 protected final Object[] prepareFindParams(final List<Filter> filters) {
326 Object[] params = new Object[0];
327 if (filters != null) {
328 int len = filters.size();
329 for (final Filter filter : filters) {
330
331 len += filter.nbAdditionnalParams() - 1;
332 }
333 params = new Object[len];
334 }
335 return params;
336 }
337
338 @Override
339 public final List<E> find(final List<Filter> filters)
340 throws DAOConnectionException {
341 return find(filters, null, false, -1);
342 }
343
344 @Override
345 public final List<E> find(final List<Filter> filters, final int limit)
346 throws DAOConnectionException {
347 return find(filters, null, false, limit);
348 }
349
350 @Override
351 public final List<E> find(final List<Filter> filters, final String field,
352 final boolean asc) throws DAOConnectionException {
353 return find(filters, field, asc, -1);
354 }
355
356 @Override
357 public final List<E> find(final List<Filter> filters, final String field,
358 final boolean asc, final int limit)
359 throws DAOConnectionException {
360 return find(filters, field, asc, limit, -1);
361 }
362
363 @Override
364 public final List<E> find(final List<Filter> filters, final String field,
365 final boolean asc, final int limit,
366 final int offset) throws DAOConnectionException {
367 final ArrayList<E> es = new ArrayList<E>();
368
369 final Object[] params = prepareFindParams(filters);
370 final StringBuilder query =
371 new StringBuilder(prepareFindQuery(filters, params));
372
373 PreparedStatement stm = null;
374 ResultSet res = null;
375 try {
376 if (field != null) {
377 query.append(" ORDER BY ").append(field);
378 if (!asc) {
379 query.append(" DESC ");
380 }
381 }
382 String squery = query.toString();
383 if (limit > 0 || offset > 0) {
384 squery =
385 DAOFactory.getInstance().getLimitRequest(squery, limit, offset);
386 }
387 logger.debug("DEBUG {} {} {}", squery,
388 params != null && params.length > 0? params[0] : "No first",
389 params.length >= 2? params[1] : " No 2nd");
390 stm = connection.prepareStatement(squery);
391 setParameters(stm, params);
392 res = executeQuery(stm);
393 while (res.next()) {
394 es.add(getFromResultSet(res));
395 }
396 } catch (final SQLException e) {
397 throw new DAOConnectionException(e);
398 } finally {
399 closeResultSet(res);
400 closeStatement(stm);
401 }
402 return es;
403 }
404
405 @Override
406 public final void update(final List<Filter> filters, final String fieldToSet)
407 throws DAOConnectionException {
408 final ArrayList<E> es = new ArrayList<E>();
409
410 final Object[] params = prepareFindParams(filters);
411 if (isCachedEnable()) {
412 final String query = prepareFindQuery(filters, params);
413
414 PreparedStatement stm = null;
415 ResultSet res = null;
416 try {
417 stm = connection.prepareStatement(query);
418 setParameters(stm, params);
419 res = executeQuery(stm);
420 while (res.next()) {
421 final E e = getFromResultSet(res);
422 removeFromCache(getId(e));
423 }
424 } catch (final SQLException e) {
425 throw new DAOConnectionException(e);
426 } finally {
427 closeResultSet(res);
428 closeStatement(stm);
429 }
430 }
431 final StringBuilder query = new StringBuilder("UPDATE " + getTable());
432 query.append(" SET ").append(fieldToSet);
433 final String squery = getFullRequest(filters, params, query);
434 PreparedStatement stm = null;
435 try {
436 stm = connection.prepareStatement(squery);
437 setParameters(stm, params);
438 executeUpdate(stm);
439 } catch (final SQLException e) {
440 throw new DAOConnectionException(e);
441 } finally {
442 closeStatement(stm);
443 }
444 }
445
446 protected final String prepareCountQuery(final List<Filter> filters,
447 final Object[] params) {
448 final StringBuilder query = new StringBuilder(getCountRequest());
449 return getFullRequest(filters, params, query);
450 }
451
452 @Override
453 public final long count(final List<Filter> filters)
454 throws DAOConnectionException {
455
456 final Object[] params = prepareFindParams(filters);
457 final String query = prepareCountQuery(filters, params);
458
459 PreparedStatement stm = null;
460 ResultSet res = null;
461 long total = -1;
462 try {
463 stm = connection.prepareStatement(query);
464 setParameters(stm, params);
465 res = executeQuery(stm);
466 if (res.next()) {
467 total = res.getLong("total");
468 }
469 } catch (final SQLException e) {
470 throw new DAOConnectionException(e);
471 } finally {
472 closeResultSet(res);
473 closeStatement(stm);
474 }
475 if (total < 0) {
476 throw new DAOConnectionException("Count cannot be retrieved");
477 }
478 return total;
479 }
480
481 @Override
482 public final boolean exist(final String id) throws DAOConnectionException {
483 if (isDbTransfer()) {
484 throw new UnsupportedOperationException();
485 }
486 if (isInCache(id)) {
487 return true;
488 }
489 if (connection instanceof FakeConnection) {
490 try {
491 connection = ((FakeConnection) connection).getRealConnection();
492 } catch (final SQLException throwables) {
493 throw new DAOConnectionException(throwables);
494 }
495 }
496 PreparedStatement stm = null;
497 ResultSet res = null;
498 try {
499 stm = connection.prepareStatement(getExistRequest());
500 setParameters(stm, id);
501 res = executeQuery(stm);
502 return res.next();
503 } catch (final SQLException e) {
504 throw new DAOConnectionException(e);
505 } finally {
506 closeResultSet(res);
507 closeStatement(stm);
508 }
509 }
510
511 @Override
512 public final E select(final String id)
513 throws DAOConnectionException, DAONoDataException {
514 if (isDbTransfer()) {
515 throw new UnsupportedOperationException();
516 }
517 if (isCachedEnable()) {
518 final E found = getFromCache(id);
519 if (found != null) {
520 return found;
521 }
522 }
523 if (connection instanceof FakeConnection) {
524 try {
525 connection = ((FakeConnection) connection).getRealConnection();
526 } catch (final SQLException throwables) {
527 throw new DAOConnectionException(throwables);
528 }
529 }
530 PreparedStatement stm = null;
531 ResultSet res = null;
532 try {
533 stm = connection.prepareStatement(getSelectRequest());
534 setParameters(stm, id);
535 res = executeQuery(stm);
536 if (res.next()) {
537 final E found = getFromResultSet(res);
538 addToCache(id, found);
539 return found;
540 } else {
541 throw new DAONoDataException("No " + getClass().getName() + " found");
542 }
543 } catch (final SQLException e) {
544 throw new DAOConnectionException(e);
545 } finally {
546 closeResultSet(res);
547 closeStatement(stm);
548 }
549 }
550
551 @Override
552 public void insert(final E e1) throws DAOConnectionException {
553 if (isCachedEnable()) {
554
555 addToCache(getId(e1), e1);
556 }
557 final Object[] params;
558 try {
559 params = getInsertValues(e1);
560 } catch (final WaarpDatabaseSqlException e) {
561 throw new DAOConnectionException(e);
562 }
563
564 PreparedStatement stm = null;
565 try {
566 stm = connection.prepareStatement(getInsertRequest());
567 setParameters(stm, params);
568 executeUpdate(stm);
569 } catch (final SQLException e) {
570 throw new DAOConnectionException(e);
571 } finally {
572 closeStatement(stm);
573 }
574 }
575
576 @Override
577 public final void update(final E e1)
578 throws DAOConnectionException, DAONoDataException {
579 if (isCachedEnable()) {
580
581 addToCache(getId(e1), e1);
582 }
583 final Object[] params;
584 try {
585 params = getUpdateValues(e1);
586 } catch (final WaarpDatabaseSqlException e) {
587 throw new DAOConnectionException(e);
588 }
589
590 PreparedStatement stm = null;
591 try {
592 stm = connection.prepareStatement(getUpdateRequest());
593 setParameters(stm, params);
594 try {
595 executeUpdate(stm);
596 } catch (final SQLException e2) {
597 throw new DAONoDataException(e2);
598 }
599 } catch (final SQLException e) {
600 throw new DAOConnectionException(e);
601 } finally {
602 closeStatement(stm);
603 }
604 }
605
606 }