001: /*
002: * Copyright 2006 Davide Deidda
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: /*
018: * DatabasePersistor.java
019: *
020: * Created on 8 aprile 2005, 19.42
021: */
022: package it.biobytes.ammentos.persistors;
023:
024: import it.biobytes.ammentos.Ammentos;
025: import it.biobytes.ammentos.AutomaticType;
026: import it.biobytes.ammentos.CollectionField;
027: import it.biobytes.ammentos.EntityIterable;
028: import it.biobytes.ammentos.Field;
029: import it.biobytes.ammentos.Metadata;
030: import it.biobytes.ammentos.PersistenceException;
031: import it.biobytes.ammentos.Persistor;
032: import it.biobytes.ammentos.Transaction;
033: import it.biobytes.ammentos.query.Query;
034: import java.sql.Connection;
035: import java.sql.PreparedStatement;
036: import java.sql.ResultSet;
037: import java.sql.SQLException;
038: import java.sql.Statement;
039: import java.util.ArrayList;
040: import java.util.Iterator;
041: import java.util.List;
042: import java.util.logging.Level;
043: import java.util.logging.Logger;
044:
045: /**
046: *
047: * @author davide
048: */
049: public class DatabasePersistor extends Persistor {
050:
051: private Logger m_logger = Logger.getLogger("ammentos");
052:
053: private class ResultSetIterator<T> implements Iterator<T> {
054:
055: private Connection m_conn;
056: private ResultSet m_rs = null;
057: private T m_lastObj;
058: private Class<T> m_class;
059: private int m_offset;
060: private int m_maxResults;
061: private int m_count;
062: PreparedStatement m_stmt = null;
063:
064: public ResultSetIterator(Class<T> c, Query qry, Connection conn)
065: throws SQLException, PersistenceException {
066: m_conn = conn;
067: m_stmt = prepareLoadStatement(c, qry, conn);
068: m_rs = m_stmt.executeQuery();
069: m_offset = qry.getOffset();
070: m_maxResults = qry.getMaxResults();
071: // A value of 0 means no limits
072: if (m_maxResults == 0) {
073: m_maxResults = Integer.MAX_VALUE;
074: }
075: m_class = c;
076:
077: // Scrolling resultset to offset
078: int count = 0;
079: while ((count < m_offset) && m_rs.next()) {
080: count++;
081: }
082: }
083:
084: public boolean hasNext() {
085: boolean res;
086: if (m_lastObj != null) {
087: res = true;
088: } else {
089: m_lastObj = next();
090: return m_lastObj != null;
091: }
092: return res;
093: }
094:
095: public T next() throws RuntimeException {
096: T res = null;
097: if (m_lastObj != null) {
098: res = m_lastObj;
099: m_lastObj = null;
100: } else {
101: try {
102: if (m_count < m_maxResults && m_rs.next()) {
103: m_count++;
104: res = it.biobytes.ammentos.Ammentos
105: .createInstance(m_class);
106: loadFields(m_class, res, m_rs);
107: }
108: } catch (Exception ex) {
109: throw new RuntimeException(ex);
110: }
111: }
112:
113: return res;
114: }
115:
116: public void remove() {
117: throw new UnsupportedOperationException(
118: "Not supported yet.");
119: }
120:
121: public void close() {
122: try {
123: m_rs.close();
124: } catch (Exception e) {
125: }
126: try {
127: m_stmt.close();
128: } catch (Exception e) {
129: }
130: try {
131: m_conn.close();
132: } catch (Exception e) {
133: }
134: }
135: }
136:
137: private class ResultSetIterable<T> extends EntityIterable<T> {
138:
139: private ResultSetIterator<T> m_iterator;
140:
141: public ResultSetIterable(Class<T> c, Query qry, Connection conn)
142: throws SQLException, PersistenceException {
143: super (c);
144: m_iterator = new ResultSetIterator<T>(c, qry, conn);
145: }
146:
147: public Iterator<T> getIterator() {
148: return m_iterator;
149: }
150:
151: public void close() {
152: m_iterator.close();
153: }
154: }
155:
156: /** Creates a new instance of DatabasePersistor */
157: public DatabasePersistor() {
158: //m_logger.setLevel(Level.ALL);
159: }
160:
161: public <T> EntityIterable<T> loadIterable(Class<T> c, Query qry)
162: throws PersistenceException {
163: return loadIterable(c, qry, Ammentos.getDbConnection());
164: }
165:
166: public <T> EntityIterable<T> loadIterable(Class<T> c, Query qry,
167: Transaction trans) throws PersistenceException {
168: return loadIterable(c, qry, trans.getDbConnection());
169: }
170:
171: private <T> EntityIterable<T> loadIterable(Class<T> c, Query qry,
172: Connection conn) throws PersistenceException {
173: try {
174: return new ResultSetIterable<T>(c, qry, conn);
175: } catch (SQLException ex) {
176: throw new PersistenceException(ex);
177: }
178: }
179:
180: private <T> PreparedStatement prepareLoadStatement(Class<T> c,
181: Query qry, Connection conn) throws PersistenceException,
182: SQLException {
183: Metadata metadata = Ammentos.getMetadata(c);
184:
185: // Getting query domain
186: String queryDomain = qry.getDomain();
187: if (queryDomain == null) {
188: queryDomain = metadata.getSourceDomain();
189: }
190:
191: // Building query
192: StringBuffer sql = new StringBuffer("SELECT ");
193: sql.append(getSelectFieldsString(metadata));
194: sql.append(" FROM " + queryDomain);
195:
196: if (!qry.isEmpty()) {
197: sql.append(qry.getSql());
198: }
199: m_logger.info("sql: " + sql);
200:
201: // Filling query parameters
202: PreparedStatement res = conn.prepareStatement(sql.toString());
203: if (!qry.isEmpty()) {
204: qry.setParamValues(res, 1);
205: }
206: return res;
207: }
208:
209: public <T> void save(Class<T> c, T obj) throws PersistenceException {
210: Connection conn = null;
211: try {
212: conn = Ammentos.getDbConnection();
213: save(c, obj, conn);
214: } finally {
215: try {
216: conn.close();
217: } catch (Exception e) {
218: }
219: }
220: }
221:
222: private <T> void save(Class<T> c, T obj, Connection conn)
223: throws PersistenceException {
224: try {
225: if (!isLoaded(c, obj)) {
226: insert(c, obj, conn);
227: } else {
228: update(c, obj, conn);
229: }
230: saveCollectionFields(c, obj);
231: } catch (Exception e) {
232: throw new PersistenceException(e);
233: }
234: }
235:
236: public <T> void save(Class<T> c, T obj, Transaction trans)
237: throws PersistenceException {
238: save(c, obj, trans.getDbConnection());
239: }
240:
241: private <T> void insert(Class<T> c, T obj, Connection conn)
242: throws SQLException, PersistenceException {
243: PreparedStatement pstmt = null;
244: try {
245: Metadata metadata = Ammentos.getMetadata(c);
246: // Framework Automatic values are set here
247: setAutomaticValues(c, obj);
248:
249: StringBuilder sql = new StringBuilder();
250: sql.append("insert into ").append(
251: metadata.getTargetDomain()).append(" (");
252:
253: Field[] insertingFields = metadata.getInsertableFields();
254:
255: for (int i = 0; i < insertingFields.length; i++) {
256: sql.append(insertingFields[i].getName());
257: if (i < (insertingFields.length - 1)) {
258: sql.append(",");
259: }
260: }
261: sql.append(" ) values ( ");
262:
263: for (int i = 0; i < insertingFields.length; i++) {
264: sql.append("?");
265: if (i < (insertingFields.length - 1)) {
266: sql.append(",");
267: }
268: }
269:
270: sql.append(" )");
271:
272: m_logger.info("query: " + sql.toString());
273:
274: if (metadata.usesDbSequences()) {
275: pstmt = conn.prepareStatement(sql.toString(),
276: Statement.RETURN_GENERATED_KEYS);
277: } else {
278: pstmt = conn.prepareStatement(sql.toString());
279: }
280:
281: // NB: the paramIndexes start from 1
282: for (int i = 0; i < insertingFields.length; i++) {
283: Field field = insertingFields[i];
284: field.setParamValue(field.get(obj), pstmt, i + 1);
285: }
286:
287: pstmt.executeUpdate();
288:
289: // Database Automatic values are set here
290: if (metadata.usesDbSequences()) {
291: ResultSet rs = pstmt.getGeneratedKeys();
292: setAutomaticValues(c, obj, rs);
293: rs.close();
294: }
295:
296: // Once saved the object is considered loaded, so next calls will perform
297: // update operations
298: markAsLoaded(c, obj);
299: } finally {
300: try {
301: pstmt.close();
302: } catch (Exception e) {
303: }
304: }
305: }
306:
307: private <T> void update(Class<T> c, T obj, Connection conn)
308: throws SQLException, PersistenceException {
309: PreparedStatement pstmt = null;
310: try {
311: Metadata metadata = Ammentos.getMetadata(c);
312: Field[] primaryKeys = metadata.getPrimaryKeyFields();
313:
314: // Checking for primary key validity
315: if (primaryKeys == null || primaryKeys[0] == null) {
316: throw new SQLException(
317: "Primary key not correctly set. Impossible to update!");
318: }
319:
320: if (metadata.getUpdatableFields().length > 0) {
321: StringBuilder sql = new StringBuilder();
322: sql.append("update ")
323: .append(metadata.getTargetDomain()).append(
324: " set ");
325:
326: Field[] updatingFields = metadata.getUpdatableFields();
327: for (int i = 0; i < updatingFields.length; i++) {
328: sql.append(updatingFields[i].getName());
329: sql.append("= ?");
330: if (i < (updatingFields.length - 1)) {
331: sql.append(",");
332: }
333: }
334:
335: sql.append(" where ");
336:
337: for (int i = 0; i < primaryKeys.length; i++) {
338: if (i > 0) {
339: sql.append(" and ");
340: }
341: Field primaryKey = primaryKeys[i];
342: sql.append(primaryKey.getName()).append(" =? ");
343: }
344:
345: m_logger.info("query: " + sql);
346:
347: pstmt = conn.prepareStatement(sql.toString());
348:
349: // NB: the paramIndexes start from 1
350: for (int i = 0; i < updatingFields.length; i++) {
351: Field field = updatingFields[i];
352: field.setParamValue(field.get(obj), pstmt, i + 1);
353: }
354:
355: // Setting primary key value (is always the last)
356: for (int i = 0; i < primaryKeys.length; i++) {
357: Field primaryKey = primaryKeys[i];
358: primaryKey.setParamValue(primaryKey.get(obj),
359: pstmt, updatingFields.length + i + 1);
360: }
361:
362: pstmt.execute();
363: }
364: } finally {
365: try {
366: pstmt.close();
367: } catch (Exception e) {
368: }
369: }
370: }
371:
372: public <T> boolean load(Class<T> c, T obj, Object primaryKey)
373: throws PersistenceException {
374: Connection conn = null;
375: T res = null;
376: try {
377: // Getting connection
378: conn = Ammentos.getDbConnection();
379: return load(c, obj, primaryKey, conn);
380: } catch (Exception e) {
381: throw new PersistenceException(e.getMessage());
382: } finally {
383: try {
384: conn.close();
385: } catch (Exception e) {
386: }
387: }
388: }
389:
390: private <T> boolean load(Class<T> c, T obj, Object primaryKey,
391: Connection conn) throws PersistenceException, SQLException {
392: PreparedStatement stmt = null;
393: boolean res = false;
394: ResultSet rs = null;
395: try {
396: Metadata metadata = Ammentos.getMetadata(c);
397: Field primaryKeyField = metadata.getPrimaryKeyField();
398: // Checking for primary key validity
399: if (primaryKeyField == null) {
400: throw new PersistenceException(
401: "Primary key not correctly set. Impossible to find!");
402: }
403:
404: // Building the query
405: StringBuffer sql = new StringBuffer("SELECT ");
406: sql.append(getSelectFieldsString(metadata));
407: sql.append(" FROM " + metadata.getSourceDomain());
408: sql.append(" WHERE ").append(
409: metadata.getPrimaryKeyField().getName()).append(
410: "=?");
411:
412: m_logger.info("sql: " + sql);
413:
414: // Filling query parameters
415: stmt = conn.prepareStatement(sql.toString());
416:
417: primaryKeyField.setParamValue(primaryKey, stmt, 1);
418:
419: rs = stmt.executeQuery();
420: if (rs.next()) {
421: loadFields(c, obj, rs);
422: res = true;
423: }
424: } finally {
425: try {
426: rs.close();
427: } catch (Exception e) {
428: }
429: try {
430: stmt.close();
431: } catch (Exception e) {
432: }
433: }
434: return res;
435: }
436:
437: public <T> boolean load(Class<T> c, T obj, Object primaryKey,
438: Transaction trans) throws PersistenceException {
439: try {
440: return load(c, obj, primaryKey, trans.getDbConnection());
441: } catch (SQLException e) {
442: throw new PersistenceException(e);
443: }
444: }
445:
446: public <T> void delete(Class<T> c, T obj)
447: throws PersistenceException {
448: Connection conn = null;
449: try {
450: conn = Ammentos.getDbConnection();
451: delete(c, obj, conn);
452: } catch (SQLException e) {
453: m_logger.warning("Error (here): " + e.getMessage());
454: // Throwing the exception
455: throw new PersistenceException(e);
456: } finally {
457: try {
458: conn.close();
459: } catch (Exception e) {
460: }
461: }
462: }
463:
464: private <T> void delete(Class<T> c, T obj, Connection conn)
465: throws PersistenceException, SQLException {
466: deleteCollectionFields(c, obj);
467: PreparedStatement pstmt = null;
468: try {
469: Field[] primaryKeys = Ammentos.getMetadata(c)
470: .getPrimaryKeyFields();
471: // Checking for primary key validity
472: if (primaryKeys == null || primaryKeys[0] == null) {
473: throw new SQLException(
474: "Primary key not correctly set. Impossible to delete!");
475: }
476:
477: Metadata metadata = Ammentos.getMetadata(c);
478:
479: StringBuilder sql = new StringBuilder();
480: sql.append("delete from ").append(
481: metadata.getTargetDomain()).append(" where ");
482:
483: for (int i = 0; i < primaryKeys.length; i++) {
484: if (i > 0) {
485: sql.append(" and ");
486: }
487: Field primaryKey = primaryKeys[i];
488: sql.append(primaryKey.getName()).append(" =? ");
489: }
490:
491: m_logger.info("sql: " + sql);
492:
493: pstmt = conn.prepareStatement(sql.toString());
494:
495: // Setting primary key values
496: for (int i = 0; i < primaryKeys.length; i++) {
497: Field primaryKey = primaryKeys[i];
498: primaryKey.setParamValue(primaryKey.get(obj), pstmt,
499: i + 1);
500: }
501:
502: pstmt.executeUpdate();
503:
504: // Now throws object is marked as not loaded, so that next calls will
505: // cause a new insert
506: unmarkAsLoaded(c, obj);
507: } finally {
508: try {
509: pstmt.close();
510: } catch (Exception e) {
511: }
512: }
513: }
514:
515: public <T> void delete(Class<T> c, T obj, Transaction trans)
516: throws PersistenceException {
517: try {
518: delete(c, obj, trans.getDbConnection());
519: } catch (SQLException e) {
520: throw new PersistenceException(e);
521: }
522: }
523:
524: /**
525: * Makes the select fields string as a comma separated list
526: */
527: protected String getSelectFieldsString(Metadata metadata) {
528: Field[] fields = metadata.getLoadableFields();
529:
530: StringBuffer res = new StringBuffer();
531: for (int i = 0; i < fields.length; i++) {
532: res.append(fields[i].getName());
533: if (i < (fields.length - 1)) {
534: res.append(',');
535: }
536: }
537:
538: return res.toString();
539: }
540:
541: protected <T> void loadFields(Class<T> c, T obj, ResultSet rs)
542: throws PersistenceException {
543: Metadata metadata = Ammentos.getMetadata(c);
544: Field[] fields = metadata.getFields();
545:
546: for (Field field : fields) {
547: // Dynamic fields are not loaded from the EXTERNAL
548: if (field.isDynamic()) {
549: continue;
550: }
551: Object fieldValue = null;
552:
553: try {
554: fieldValue = field.loadValue(rs);
555: } catch (Exception e) {
556: }
557: field.set(obj, fieldValue);
558: }
559:
560: loadCollectionFields(c, obj);
561: markAsLoaded(c, obj);
562: }
563:
564: /**
565: * Sets the values for the automatic fields.
566: */
567: protected <T> void setAutomaticValues(Class<T> c, T obj)
568: throws PersistenceException {
569: Metadata metadata = Ammentos.getMetadata(c);
570: for (Field field : metadata.getFields()) {
571: if (field.isAutomatic()
572: && field.getAutomaticType() == AutomaticType.FRAMEWORK) {
573: field.set(obj, field.generateValue());
574: }
575: }
576: }
577:
578: /**
579: * Sets the values for the automatic fields from auto generated values. Only fields
580: * marked as automatic=true and automaticType=EXTERNAL will be evaluated in this method.
581: *
582: * @author Mark Bednarczyk
583: */
584: protected <T> void setAutomaticValues(Class<T> c, T obj,
585: ResultSet rs) throws PersistenceException {
586: Metadata metadata = Ammentos.getMetadata(c);
587: for (Field field : metadata.getFields()) {
588: try {
589: if (field.isAutomatic()
590: && field.getAutomaticType() == AutomaticType.EXTERNAL) {
591: if (rs.next() == false) {
592: break;
593: }
594:
595: String stringValue = rs.getString(1);
596: field.set(obj, field.loadValue(stringValue));
597: }
598: } catch (SQLException e) {
599: throw new PersistenceException(
600: "Unable to assign automatic field from EXTERNAL ["
601: + field.getName() + "]");
602: }
603: }
604: }
605:
606: private <T> void saveCollectionFields(Class<T> c, T obj)
607: throws PersistenceException {
608: List<CollectionField> cFields = Ammentos.getMetadata(c)
609: .getCollectionFields();
610: for (CollectionField cf : cFields) {
611: cf.save(obj);
612: }
613: }
614:
615: private <T> void deleteCollectionFields(Class<T> c, T obj)
616: throws PersistenceException {
617: List<CollectionField> cFields = Ammentos.getMetadata(c)
618: .getCollectionFields();
619: for (CollectionField cf : cFields) {
620: cf.delete(obj);
621: }
622: }
623:
624: private <T> void loadCollectionFields(Class<T> c, T obj)
625: throws PersistenceException {
626: m_logger.info("Loading list fields for: " + c.getName());
627: List<CollectionField> cFields = Ammentos.getMetadata(c)
628: .getCollectionFields();
629: for (CollectionField cf : cFields) {
630: m_logger.info("Loading CollectionField " + cf);
631: cf.load(obj);
632: m_logger.info("Loaded CollectionField " + cf);
633: }
634: }
635:
636: public <T> int count(Class<T> c, Query qry, Connection conn)
637: throws PersistenceException, SQLException {
638: Metadata metadata = Ammentos.getMetadata(c);
639: PreparedStatement stmt = null;
640: int res = 0;
641: ResultSet rs = null;
642:
643: // Getting query domain
644: String queryDomain = qry.getDomain();
645: if (queryDomain == null) {
646: queryDomain = metadata.getSourceDomain();
647: }
648:
649: try {
650: // Building the query
651: // Building query
652: StringBuffer sql = new StringBuffer("SELECT count(*)");
653: sql.append(" FROM " + queryDomain);
654:
655: if (!qry.isEmpty()) {
656: sql.append(qry.getSql());
657: }
658: m_logger.info("sql: " + sql);
659:
660: // Filling query parameters
661: stmt = conn.prepareStatement(sql.toString());
662:
663: if (!qry.isEmpty()) {
664: qry.setParamValues(stmt, 1);
665: }
666:
667: rs = stmt.executeQuery();
668: if (rs.next()) {
669: res = rs.getInt(1);
670: }
671: } finally {
672: try {
673: rs.close();
674: } catch (Exception e) {
675: }
676: try {
677: stmt.close();
678: } catch (Exception e) {
679: }
680: }
681: return res;
682: }
683:
684: @Override
685: public <T> int count(Class<T> c, Query qry)
686: throws PersistenceException {
687: int res = 0;
688: try {
689: res = count(c, qry, Ammentos.getDbConnection());
690: } catch (Exception ex) {
691: m_logger.warning("Error: " + ex.getMessage());
692: throw new PersistenceException(ex);
693: }
694: return res;
695: }
696:
697: @Override
698: public <T> int count(Class<T> c, Query qry, Transaction trans)
699: throws PersistenceException {
700: int res = 0;
701: try {
702: res = count(c, qry, trans.getDbConnection());
703: } catch (Exception ex) {
704: m_logger.warning("Error: " + ex.getMessage());
705: throw new PersistenceException(ex);
706: }
707: return res;
708: }
709: }
|