Source code: org/vrspace/server/db/SQLDB.java
1 package org.vrspace.server.db;
2
3 import org.vrspace.server.DB;
4 import org.vrspace.util.Logger;
5 import org.vrspace.server.*;
6 import org.vrspace.*;
7
8 import java.io.*;
9 import java.lang.reflect.Field;
10 import java.lang.reflect.Method;
11 import java.lang.reflect.Array;
12 import java.lang.reflect.Modifier;
13 import java.net.URL;
14 import java.util.ArrayList;
15 import java.util.Date;
16 import java.util.HashMap;
17 import java.util.StringTokenizer;
18 import java.sql.*;
19
20 /**
21 DB store objects by storing all public fields:
22 * all primitive types and primitive arrays (java.lang.String,
23 java.net.URL and java.util.Date are considered as primitive)
24 * all objects and object arrays that are contained inside object (recursive)
25
26 Requirements and restrictions:
27 * every object that will be stored in DB must have defined
28 "public long db_id" field (or must inherit from class that have db_id field)
29 * objects that have encapsulated data - private fields and get/set methods
30 for retrieving/storing that fields can't be stored correctly!
31 This is common for JDK classes - that classes can be stored by
32 manually coding (it's already done for String, Date, URL and Float)
33
34 Description of DB structure:
35 DB has "repository" tables and "object" tables. "repository" tables stores
36 information about classes, objects and packages; "object" tables store objects.
37
38 "repository" tables:
39
40 mysql> describe classes;
41 +--------------+--------------+------+-----+---------+-------+
42 | Field | Type | Null | Key | Default | Extra |
43 +--------------+--------------+------+-----+---------+-------+
44 | classID | bigint(20) | | PRI | 0 | |
45 | superClassID | bigint(20) | YES | | NULL | |
46 | packageID | bigint(20) | YES | | NULL | |
47 | className | varchar(255) | YES | | NULL | |
48 +--------------+--------------+------+-----+---------+-------+
49
50 mysql> describe packages;
51 +-------------+--------------+------+-----+---------+-------+
52 | Field | Type | Null | Key | Default | Extra |
53 +-------------+--------------+------+-----+---------+-------+
54 | packageID | bigint(20) | | PRI | 0 | |
55 | packageName | varchar(255) | YES | | NULL | |
56 +-------------+--------------+------+-----+---------+-------+
57
58 mysql> describe objects;
59 +----------+------------+------+-----+---------+-------+
60 | Field | Type | Null | Key | Default | Extra |
61 +----------+------------+------+-----+---------+-------+
62 | objectID | bigint(20) | | PRI | 0 | |
63 | classID | bigint(20) | YES | | NULL | |
64 +----------+------------+------+-----+---------+-------+
65
66 details about "repository" tables:
67 * relations in repository tables
68 (these are just logical relations - not explicitly created):
69 - superClassID in one classes record must exist as classID in another
70 classes record (except for java.lang.Object when superClassID is null)
71 - classID in objects table must exist in classes table
72 - packageID in classes table must exist in packages table
73 * className is full class name with dots replaced by optional char
74 example: when optional char is '_', org.vrspace.server.File is stored
75 as org_vrspace_server_File. This is done because some db's can't have
76 points in table names
77
78 details about "object" tables:
79 - names are full class names as stored in "classes" repository table
80 - names of columns in tables are names of object fields
81 - every table has "db_id" column that contains object ID stored in
82 "objects" repository table
83 - tables are divided into five groups:
84 * object:
85 - for storing "main" object (the one that is referenced in put/get method)
86 - if object consist of several inherited classes, every class is stored
87 in separated table
88 - primary key is "db_id"
89 - object fields are stored as follows:
90 * if field is primitive, its value is stored. The type of a column
91 is a type of that primitive mapped to matching database type
92 * if field is a primitive array, object or object array, than
93 that column has integer type and stores a unique reference inside object
94 for that primitive array, object or object array; primitive array,
95 object or object array itself are stored in another table (described below),
96 if they are null, column has null value and there's no recods in another table
97 - if class has "db_id" field it isn't explicitly stored because it's
98 already stored as primary key
99 * object_array:
100 - for storing "main" object array (the one that is referenced in put/get method)
101 - table name has suffix "_arr"
102 - has column "arr_id" that stores index of object in array
103 - primary key is "db_id" + "arr_id"
104 - has column "isNull" which is true if object is null
105 - fields are stored according to rules from object tables
106 * object_contained:
107 - for storing objects that are contained inside "main" object
108 - table name has suffix "_cnt"
109 - has column "cnt_id" that stores reference described in object table
110 - primary key is "db_id" + "cnt_id"
111 - fields are stored according to rules from object tables
112 * object_array_contained:
113 - for storing object arrays that are contained inside "main" object
114 - table name has suffix "_arr_cnt"
115 - has column "cnt_id" that stores reference described in object table
116 - has column "arr_id" that stores index of object in array
117 - primary key is "db_id" + "cnt_id" + "arr_id"
118 - has column "isNull" which is true if object is null
119 - fields are stored according to rules from object tables
120 * primitive_array_contained:
121 - for storing primitive arrays that are contained inside "main" object
122 - table name is primitive name + "_arr"
123 - has column "cnt_id" that stores reference described in object table
124 - has column "arr_id" that stores index of object in array
125 - primary key is "db_id" + "cnt_id" + "arr_id"
126 - in column "value" is stored value of that primitive
127
128 How objects are stored:
129 - "repository" tables are created automatically first time when db starts
130
131 - when storing object of class that is inherited from other class(es),
132 every field is stored in table of class that declare that field.
133
134 - if some class in hierarchy has no public fields, table for that class will
135 not be created, but class itself will be added to classes table.
136 example class is java.lang.Object
137
138 - if object contains another object(s), than contained object(s) is stored
139 in object_contained table(s) with above rules
140
141 - if object contains object array(s) than contained array(s) is stored in
142 object_array table(s) with above rules
143
144 - if object has primitive array(s) than contained array(s) is stored in
145 primitive array table
146
147 ******************************************************************************
148
149 Example - storing object of class D:
150
151 // A.java
152 package org.vrspace;
153 public class A {
154 public long db_id;
155 public int a1 = 6;
156 public char a2 = 'a';
157 }
158
159 // B.java
160 package org.vrspace;
161 public class B extends A {
162 public String b1 = "Hawkwind";
163 }
164
165 // C.java
166 package org.vrspace;
167 public class C extends B {
168 }
169
170 // D.java
171 package org.vrspace;
172 import java.net.URL;
173 import org.vrspace.test.*;
174 public class D extends C {
175 public long d1 = 666;
176 public float d2 = 6.66f;
177 public URL d3 = new URL("http://vrspace.org");
178 public E d4 = new E();
179 public int[] d5 = new int[10];
180 }
181
182 // E.java
183 package org.vrspace.test;
184 public class E {
185 public String e1 = "Ozrics";
186 public int e2 = 6;
187 }
188
189 contens of "repository":
190 ------------------------
191
192 mysql> select * from classes;
193 +---------+--------------+-----------+--------------------+
194 | classID | superClassID | packageID | className |
195 +---------+--------------+-----------+--------------------+
196 | 1 | 0 | 1 | java_lang_Object |
197 | 2 | 1 | 2 | org_vrspace_A |
198 | 3 | 2 | 2 | org_vrspace_B |
199 | 4 | 3 | 2 | org_vrspace_C |
200 | 5 | 4 | 2 | org_vrspace_D |
201 | 6 | 1 | 3 | org_vrspace_test_E |
202 +---------+--------------+-----------+--------------------+
203
204 mysql> select * from packages;
205 +-----------+------------------+
206 | packageID | packageName |
207 +-----------+------------------+
208 | 1 | java_lang |
209 | 2 | org_vrspace |
210 | 3 | org_vrspace_test |
211 +-----------+------------------+
212
213 mysql> select * from objects;
214 +----------+---------+
215 | objectID | classID |
216 +----------+---------+
217 | 1 | 5 |
218 +----------+---------+
219
220 created "object" tables:
221 ------------------------
222
223 mysql> describe org_vrspace_a;
224 +-------+--------------+------+-----+---------+-------+
225 | Field | Type | Null | Key | Default | Extra |
226 +-------+--------------+------+-----+---------+-------+
227 | db_id | bigint(20) | | PRI | 0 | |
228 | a1 | mediumint(9) | YES | | NULL | |
229 | a2 | char(1) | YES | | NULL | |
230 +-------+--------------+------+-----+---------+-------+
231
232 mysql> describe org_vrspace_b;
233 +-------+--------------+------+-----+---------+-------+
234 | Field | Type | Null | Key | Default | Extra |
235 +-------+--------------+------+-----+---------+-------+
236 | db_id | bigint(20) | | PRI | 0 | |
237 | b1 | varchar(255) | YES | | NULL | |
238 +-------+--------------+------+-----+---------+-------+
239
240 mysql> describe org_vrspace_d;
241 +-------+--------------+------+-----+---------+-------+
242 | Field | Type | Null | Key | Default | Extra |
243 +-------+--------------+------+-----+---------+-------+
244 | db_id | bigint(20) | | PRI | 0 | |
245 | d1 | bigint(20) | YES | | NULL | |
246 | d2 | float(10,2) | YES | | NULL | |
247 | d3 | varchar(255) | YES | | NULL | |
248 | d4 | bigint(20) | YES | | NULL | |
249 | d5 | bigint(20) | YES | | NULL | |
250 +-------+--------------+------+-----+---------+-------+
251
252 mysql> describe int_array;
253 +--------+--------------+------+-----+---------+-------+
254 | Field | Type | Null | Key | Default | Extra |
255 +--------+--------------+------+-----+---------+-------+
256 | db_id | bigint(20) | | PRI | 0 | |
257 | arr_id | bigint(20) | | PRI | 0 | |
258 | nr_id | bigint(20) | | PRI | 0 | |
259 | value | mediumint(9) | YES | | NULL | |
260 +--------+--------------+------+-----+---------+-------+
261
262 mysql> describe org_vrspace_test_e_array;
263 +--------+--------------+------+-----+---------+-------+
264 | Field | Type | Null | Key | Default | Extra |
265 +--------+--------------+------+-----+---------+-------+
266 | db_id | bigint(20) | | PRI | 0 | |
267 | arr_id | bigint(20) | | PRI | 0 | |
268 | nr_id | bigint(20) | | PRI | 0 | |
269 | e1 | varchar(255) | YES | | NULL | |
270 | e2 | mediumint(9) | YES | | NULL | |
271 +--------+--------------+------+-----+---------+-------+
272
273 contens of "object" tables:
274 ---------------------------
275
276 mysql> select * from org_vrspace_a;
277 +-------+------+------+
278 | db_id | a1 | a2 |
279 +-------+------+------+
280 | 1 | 6 | a |
281 +-------+------+------+
282
283 mysql> select * from org_vrspace_b;
284 +-------+----------+
285 | db_id | b1 |
286 +-------+----------+
287 | 1 | Hawkwind |
288 +-------+----------+
289
290 mysql> select * from org_vrspace_d;
291 +-------+------+------+--------------------+------+------+
292 | db_id | d1 | d2 | d3 | d4 | d5 |
293 +-------+------+------+--------------------+------+------+
294 | 1 | 666 | 6.66 | http://vrspace.org | 0 | 1 |
295 +-------+------+------+--------------------+------+------+
296
297 mysql> select * from org_vrspace_test_e_array;
298 +-------+--------+-------+--------+------+
299 | db_id | arr_id | nr_id | e1 | e2 |
300 +-------+--------+-------+--------+------+
301 | 1 | 0 | 0 | Ozrics | 6 |
302 +-------+--------+-------+--------+------+
303
304 mysql> select * from int_array;
305 +-------+--------+-------+-------+
306 | db_id | arr_id | nr_id | value |
307 +-------+--------+-------+-------+
308 | 1 | 0 | 1 | 0 |
309 | 1 | 1 | 1 | 0 |
310 | 1 | 2 | 1 | 0 |
311 | 1 | 3 | 1 | 0 |
312 | 1 | 4 | 1 | 0 |
313 | 1 | 5 | 1 | 0 |
314 | 1 | 6 | 1 | 0 |
315 | 1 | 7 | 1 | 0 |
316 | 1 | 8 | 1 | 0 |
317 | 1 | 9 | 1 | 0 |
318 +-------+--------+-------+-------+
319 @author eddie@vrspace.org
320 */
321
322 public abstract class SQLDB extends DB {
323
324 // object field types, needed for storing field info (name/type) in repository
325 // and later for faster retrieving/storing values in fields
326 protected static final int BOOLEAN = 1;
327 protected static final int BYTE = 2;
328 protected static final int SHORT = 3;
329 protected static final int INT = 4;
330 protected static final int LONG = 5;
331 protected static final int FLOAT = 6;
332 protected static final int DOUBLE = 7;
333 protected static final int CHAR = 8;
334 protected static final int FLOAT_OBJ = 9;
335 protected static final int STRING_OBJ = 10;
336 protected static final int DATE_OBJ = 11;
337 protected static final int URL_OBJ = 12;
338 protected static final int OBJECT = 13;
339 protected static final int BOOLEAN_ARR = 14;
340 protected static final int BYTE_ARR = 15;
341 protected static final int SHORT_ARR = 16;
342 protected static final int INT_ARR = 17;
343 protected static final int LONG_ARR = 18;
344 protected static final int FLOAT_ARR = 19;
345 protected static final int DOUBLE_ARR = 20;
346 protected static final int CHAR_ARR = 21;
347 protected static final int FLOAT_OBJ_ARR = 22;
348 protected static final int STRING_OBJ_ARR = 23;
349 protected static final int DATE_OBJ_ARR = 24;
350 protected static final int URL_OBJ_ARR = 25;
351 protected static final int OBJECT_ARR = 26;
352
353 // field types as text for forming SQL text, DB dependent,
354 // value must be provided in actual DB implementation (subclass of this class)
355 protected String BYTE_TYPE;
356 protected String SHORT_TYPE;
357 protected String INT_TYPE;
358 protected String LONG_TYPE;
359 protected String FLOAT_TYPE;
360 protected String DOUBLE_TYPE;
361 protected String CHAR_TYPE;
362 protected String STRING_TYPE;
363 protected String DATE_TYPE;
364
365 // SQL text of various queries, DB dependent,
366 // value must be provided in actual DB implementation (subclass of this class)
367 protected String SQL_SHOW_TABLES;
368 protected String SQL_CREATE_OBJECTS_TABLE;
369 protected String SQL_CREATE_CLASSES_TABLE;
370 protected String SQL_CREATE_PACKAGES_TABLE;
371 protected String SQL_COUNT_OBJECTS;
372 protected String SQL_COUNT_CLASSES;
373 protected String SQL_COUNT_PACKAGES;
374 protected String SQL_GET_CLASSES;
375 protected String SQL_CHECK_CLASS;
376 protected String SQL_CHECK_PACKAGE;
377 protected String SQL_INSERT_CLASS;
378 protected String SQL_INSERT_PACKAGE;
379 protected String SQL_INSERT_OBJECT;
380 protected String SQL_DELETE_OBJECT;
381 protected String SQL_DEL_OBJECT;
382 protected String SQL_GET_OBJECT_1;
383 protected String SQL_GET_OBJECT_2;
384 protected String SQL_GET_OBJECT_3;
385 protected String SQL_GET_OBJECT_4;
386 protected String SQL_CREATE_OBJ_TABLE_1;
387 protected String SQL_CREATE_OBJ_TABLE_2;
388 protected String SQL_INSERT_PRIM_1;
389 protected String SQL_INSERT_PRIM_2;
390 protected String SQL_CREATE_PRIM_TABLE_1;
391 protected String SQL_CREATE_PRIM_TABLE_2;
392 protected String SQL_CREATE_PRIM_TABLE_3;
393 protected String SQL_GET_ARR_1;
394 protected String SQL_GET_ARR_2;
395 protected String SQL_GET_ARR_3;
396 protected String SQL_GET_ARR_4;
397 protected String SQL_GET_PRIM_ARR_1;
398 protected String SQL_GET_PRIM_ARR_2;
399 protected String SQL_GET_PRIM_ARR_3;
400 protected String SQL_GET_OBJ_ARR_1;
401 protected String SQL_GET_OBJ_ARR_2;
402 protected String SQL_GET_OBJ_ARR_3;
403 protected String SQL_GET_RANGE_1;
404 protected String SQL_GET_RANGE_2;
405 protected String SQL_GET_RANGE_3;
406 protected String SQL_GET_ALL;
407
408 PreparedStatement stmtCheckClass;
409 PreparedStatement stmtCheckPackage;
410 PreparedStatement stmtInsertClass;
411 PreparedStatement stmtInsertPackage;
412 PreparedStatement stmtInsertObject;
413 PreparedStatement stmtDeleteObject;
414 PreparedStatement stmtDelObject;
415 PreparedStatement stmtGetObject;
416 PreparedStatement stmtInsertPrim;
417
418 Connection conn;
419 Statement stmt;
420 ResultSet rs;
421
422 static long objID = 0;
423 static long pkgID = 0;
424 static long clsID = 0;
425
426 /**
427 in-memory repository that contains information about classes
428 key: class name
429 value: SQLClass object
430 */
431 static HashMap clsRepository;
432
433 Logger logger = new Logger();
434
435
436 /** Override this in actual implementations. Called from connect( String ) */
437 protected abstract boolean loadDriver();
438
439 public abstract String create( String name );
440
441 /**
442 Connect to the database
443 @param name database URL
444 */
445 public void connect( String name ) throws Exception {
446
447 loadDriver();
448 conn = DriverManager.getConnection( name );
449 stmt = conn.createStatement();
450 stmtCheckClass = conn.prepareStatement( SQL_CHECK_CLASS );
451 stmtCheckPackage = conn.prepareStatement( SQL_CHECK_PACKAGE );
452 stmtInsertClass = conn.prepareStatement( SQL_INSERT_CLASS );
453 stmtInsertPackage = conn.prepareStatement( SQL_INSERT_PACKAGE );
454 stmtInsertObject = conn.prepareStatement( SQL_INSERT_OBJECT );
455 stmtDeleteObject = conn.prepareStatement( SQL_DELETE_OBJECT );
456 stmtDelObject = conn.prepareStatement( SQL_DEL_OBJECT );
457 //stmtGetObject = conn.prepareStatement( SQL_GET_OBJECT );
458 //stmtCreatePrimTable = conn.prepareStatement( SQL_CREATE_PRIM_TABLE );
459 //stmtInsertPrim = conn.prepareStatement( SQL_INSERT_PRIM );
460
461 // first time create repository tables
462 rs = query( SQL_SHOW_TABLES );
463 if ( ! rs.next() ) {
464 update( SQL_CREATE_OBJECTS_TABLE );
465 update( SQL_CREATE_CLASSES_TABLE );
466 update( SQL_CREATE_PACKAGES_TABLE );
467 }
468
469 // find ID's
470 // - objects can be deleted so count() is not valid for ID detection
471 // - classes and packages can't be deleted, count() is OK
472 synchronized( this ) {
473 rs = query( SQL_COUNT_OBJECTS );
474 rs.next();
475 objID = rs.getLong(1);
476 Logger.logDebug("max ID = " + objID );
477
478 rs = query( SQL_COUNT_CLASSES );
479 rs.next();
480 clsID = rs.getLong(1);
481
482 rs = query( SQL_COUNT_PACKAGES );
483 rs.next();
484 pkgID = rs.getLong(1);
485 }
486
487 // create and load in-memory repository
488 clsRepository = new HashMap( (int)clsID );
489 rs = query( SQL_GET_CLASSES );
490 while ( rs.next() ) {
491 SQLClass sqlcls = new SQLClass();
492 sqlcls.classID = rs.getLong(1);
493 sqlcls.superClassID = rs.getLong(2);
494 sqlcls.packageID = rs.getLong(3);
495 sqlcls.className = rs.getString(4);
496 sqlcls.stmt = conn.prepareStatement( rs.getString(5) );
497 StringTokenizer st = new StringTokenizer( rs.getString(6), ",;" );
498 int nrOfFields = Integer.parseInt( st.nextToken() );
499 if ( nrOfFields == 0 ) {
500 continue;
501 }
502 sqlcls.fieldNames = new String[nrOfFields];
503 sqlcls.fieldTypes = new int[nrOfFields];
504 sqlcls.fieldCntIDs = new long[nrOfFields];
505 sqlcls.fields = new Field[nrOfFields];
506 for ( int i=0; i<nrOfFields; i++ ) {
507 sqlcls.fieldNames[i] = st.nextToken();
508 sqlcls.fieldTypes[i] = Integer.parseInt( st.nextToken() );
509 sqlcls.fieldCntIDs[i] = Long.parseLong( st.nextToken() );
510 sqlcls.fields[i] = Class.forName(sqlcls.className.replace('_','.')).getField( sqlcls.fieldNames[i] );
511 }
512 synchronized( this ) {
513 clsRepository.put( sqlcls.className, sqlcls );
514 }
515 }
516
517 }
518
519
520 /**
521 Stores <b>obj</b> to the database.
522 */
523 public void put( Object obj ) throws Exception {
524 Class objClass = obj.getClass();
525
526 if ( obj == null ){
527 Logger.logWarning( "SQLDB: object of class " + objClass.getName() + " is null!" );
528 return;
529 }
530
531 SQLClass sqlcls = analizeClass( objClass );
532
533 // create and assign new unique id to this object
534 long objectID = getObjectID( sqlcls.classID );
535 try {
536 objClass.getField( "db_id" ).setLong( obj, objectID );
537 } catch ( NoSuchFieldException e1 ) {
538 Logger.logError( "SQLDB: db_id field in " + sqlcls.className + " doesn't exist!");
539 return;
540 } catch ( SecurityException e2 ) {
541 Logger.logError( "SQLDB: db_id field in " + sqlcls.className + " can't be modified!");
542 return;
543 }
544
545 do {
546 insertObject( obj, objectID, getClass(objClass), 0, 0 );
547 objClass = objClass.getSuperclass();
548 } while ( objClass != null );
549
550 }
551
552
553 /**
554 Stores object array to the database.
555 */
556 public void put( Object[] obj ) throws Exception {
557 Class objClass = obj.getClass().getComponentType();
558
559 if ( obj == null ){
560 Logger.logWarning( "SQLDB: object of class " + objClass.getName() + " is null!" );
561 return;
562 }
563
564 SQLClass sqlcls = analizeClass( objClass );
565
566 // create and assign new unique id to this object
567 long objectID = getObjectID( sqlcls.classID );
568 try {
569 objClass.getField( "db_id" ).setLong( obj, objectID );
570 } catch ( NoSuchFieldException e1 ) {
571 Logger.logError( "SQLDB: db_id field in " + sqlcls.className + " doesn't exist!");
572 return;
573 } catch ( SecurityException e2 ) {
574 Logger.logError( "SQLDB: db_id field in " + sqlcls.className + " can't be modified!");
575 return;
576 }
577
578 for ( int i=0; i<obj.length; i++ ) {
579 do {
580 insertObject( obj, objectID, getClass(objClass), 0, i );
581 objClass = objClass.getSuperclass();
582 } while ( objClass != null );
583 }
584
585 }
586
587
588 /**
589 Internal, called from put(Object obj) and put(Object[] obj)
590 */
591 protected void insertObject( Object obj, long objectID, SQLClass cls, long cntID, long arrID )
592 throws Exception {
593 if ( cls == null || cls.fieldNames.length == 0) {
594 return;
595 }
596
597 PreparedStatement stmt = cls.stmt;
598 int fldNr = 1;
599 // set object ID
600 stmt.setLong( fldNr++, objectID );
601 // check if object is contained inside another object
602 stmt.setLong( fldNr++, cntID );
603 // check if object is member of array
604 stmt.setLong( fldNr++, arrID );
605 if ( obj == null ) {
606 stmt.setByte( fldNr++, (byte)1 );
607 stmt.executeUpdate();
608 return;
609 } else {
610 stmt.setByte( fldNr++, (byte)0 );
611 }
612
613 // set object's fields
614 int size = cls.fieldNames.length;
615 Class objClass = obj.getClass();
616 for ( int i=0; i<size; i++ ) {
617 Field field = objClass.getField( cls.fieldNames[i] );
618 int type = cls.fieldTypes[i];
619 switch( type ) {
620 // primitive
621 case BOOLEAN: boolean bool = field.getBoolean(obj);
622 if ( bool ) {
623 stmt.setByte( fldNr, (byte)1 );
624 } else {
625 stmt.setByte( fldNr, (byte)0 );
626 }
627 break;
628 case BYTE: stmt.setByte( fldNr, field.getByte(obj) );
629 break;
630 case SHORT: stmt.setShort( fldNr, field.getShort(obj) );
631 break;
632 case INT: stmt.setInt( fldNr, field.getInt(obj) );
633 break;
634 case LONG: stmt.setLong( fldNr, field.getLong(obj) );
635 break;
636 case FLOAT: stmt.setFloat( fldNr, field.getFloat(obj) );
637 break;
638 case DOUBLE: stmt.setDouble( fldNr, field.getDouble(obj) );
639 break;
640 case CHAR: stmt.setString( fldNr, new Character(field.getChar(obj)).toString() );
641 break;
642 // object (mapped to primitive)
643 case STRING_OBJ: String s = (String)field.get(obj);
644 if ( s == null ) {
645 stmt.setNull( fldNr, Types.VARCHAR );
646 } else {
647 stmt.setString( fldNr, s );
648 }
649 break;
650 case FLOAT_OBJ: Float f = (Float)field.get(obj);
651 if ( f == null ) {
652 stmt.setNull( fldNr, Types.FLOAT );
653 } else {
654 stmt.setFloat( fldNr, f.floatValue() );
655 }
656 break;
657 case DATE_OBJ: Date date = (Date)field.get(obj);
658 if ( date == null ) {
659 stmt.setNull( fldNr, Types.DATE );
660 } else {
661 stmt.setDate( fldNr, new java.sql.Date(date.getTime()) );
662 }
663 break;
664 case URL_OBJ: URL url = (URL)field.get(obj);
665 if ( url == null ) {
666 stmt.setNull( fldNr, Types.VARCHAR );
667 } else {
668 stmt.setString( fldNr, url.toString() );
669 }
670 break;
671 // object (general)
672 case OBJECT: Object o = field.get(obj);
673 if ( o == null ) {
674 stmt.setNull( fldNr, Types.BIGINT );
675 } else {
676 // object must be put in another table - recursive call to this method
677 long cnt_id = cls.fieldCntIDs[i];
678 stmt.setLong( fldNr, cnt_id );
679 insertObject( o, objectID, getClass(o.getClass()), cnt_id, 0 );
680 }
681 break;
682 // primitive array and object(mapped to primitive) array
683 case BOOLEAN_ARR:
684 case BYTE_ARR:
685 case SHORT_ARR:
686 case INT_ARR:
687 case LONG_ARR:
688 case FLOAT_ARR:
689 case DOUBLE_ARR:
690 case CHAR_ARR:
691 case FLOAT_OBJ_ARR:
692 case STRING_OBJ_ARR:
693 case DATE_OBJ_ARR:
694 case URL_OBJ_ARR:
695 Object ob = field.get(obj);
696 if ( ob == null ) {
697 stmt.setNull( fldNr, Types.BIGINT );
698 continue;
699 } else {
700 long cnt_id = cls.fieldCntIDs[i];
701 stmt.setLong( fldNr, cnt_id );
702 putPrimitiveArray( ob, objectID, cnt_id );
703 }
704 break;
705 // object(general) array
706 case OBJECT_ARR: Object[] obj_arr = (Object[])field.get(obj);
707 if ( obj_arr == null ) {
708 stmt.setNull( fldNr, Types.BIGINT );
709 continue;
710 } else {
711 long cnt_id = cls.fieldCntIDs[i];
712 stmt.setLong( fldNr, cnt_id );
713 Class obj_arrClass = obj_arr.getClass().getComponentType();
714 do {
715 SQLClass sqlcls = getClass(obj_arrClass);
716 for ( int j=0; j<obj_arr.length; j++ ) {
717 insertObject( obj_arr[j], objectID, sqlcls, cnt_id, j );
718 obj_arrClass = obj_arrClass.getSuperclass();
719 }
720 } while ( obj_arrClass != null );
721 }
722 break;
723 }
724 fldNr++;
725 }
726 stmt.executeUpdate();
727
728 }
729
730
731 /**
732 Internal
733 */
734 protected void putPrimitiveArray( Object obj, long objectID, long cntID )
735 throws Exception {
736 String type = obj.getClass().getComponentType().getName();
737
738 stmtInsertPrim = conn.prepareStatement( SQL_INSERT_PRIM_1 + replacePoints(type) + SQL_INSERT_PRIM_2 );
739 stmtInsertPrim.setLong( 1, objectID );
740 stmtInsertPrim.setLong( 2, cntID );
741
742 int length = ((Object[])obj).length;
743 for (int ind = 0; ind < length; ind++) {
744 stmtInsertPrim.setLong( 3, ind );
745 if ( type.equals("boolean") ) {
746 Boolean bool = new Boolean( Array.getBoolean(obj, ind) );
747 if ( bool.toString().equals("true") ) {
748 stmtInsertPrim.setByte( 4, (byte)1 );
749 } else {
750 stmtInsertPrim.setByte( 4, (byte)0 );
751 }
752 } else if ( type.equals("byte") ) {
753 stmtInsertPrim.setByte( 4, Array.getByte(obj, ind) );
754 } else if ( type.equals("short") ) {
755 stmtInsertPrim.setShort( 4, Array.getShort(obj, ind) );
756 } else if ( type.equals("int") ) {
757 stmtInsertPrim.setInt( 4, Array.getInt(obj, ind) );
758 } else if ( type.equals("long") ) {
759 stmtInsertPrim.setLong( 4, Array.getLong(obj, ind) );
760 } else if ( type.equals("float") ) {
761 stmtInsertPrim.setFloat( 4, Array.getFloat(obj, ind) );
762 } else if ( type.equals("double") ) {
763 stmtInsertPrim.setDouble( 4, Array.getDouble(obj, ind) );
764 } else if ( type.equals("char") ) {
765 stmtInsertPrim.setString( 4, String.valueOf(Array.getChar(obj, ind)) );
766 } else if ( type.equals("java.lang.Float") ) {
767 Float f = (Float)Array.get(obj, ind);
768 if ( f == null ) {
769 stmtInsertPrim.setNull( 4, Types.VARCHAR );
770 } else {
771 stmtInsertPrim.setFloat( 4, f.floatValue() );
772 }
773 } else if ( type.equals("java.lang.String") ) {
774 String s = (String)Array.get(obj, ind);
775 if ( s == null ) {
776 stmtInsertPrim.setNull( 4, Types.VARCHAR );
777 } else {
778 stmtInsertPrim.setString( 4, s );
779 }
780 } else if ( type.equals("java.net.URL") ) {
781 URL url = (URL)Array.get(obj, ind);
782 if ( url == null ) {
783 stmtInsertPrim.setNull( 4, Types.VARCHAR );
784 } else {
785 stmtInsertPrim.setString( 4, url.toString() );
786 }
787 } else if ( type.equals("java.util.Date") ) {
788 Date date = (Date)Array.get(obj, ind);
789 if ( date == null ) {
790 stmtInsertPrim.setNull( 4, Types.BIGINT );
791 } else {
792 stmtInsertPrim.setLong( 4, date.getTime() );
793 }
794 }
795 stmtInsertPrim.executeUpdate();
796 }
797
798 }
799
800
801 /**
802 Retreives an object from the database
803 */
804 public Object get( Object obj ) throws Exception {
805 String className = obj.getClass().getName();
806 long db_id = obj.getClass().getField("db_id").getLong( obj );
807 return getObject( className, db_id, 0, 0 );
808 }
809
810
811 /**
812 Retreives an object from the database
813 */
814 public Object get( String className, long id ) throws Exception {
815 Class objClass = Class.forName( className );
816 Object obj = objClass.newInstance();
817 objClass.getField("db_id").setLong( obj, id );
818 return getObject( obj, id, 0, 0 );
819 }
820
821
822 /**
823 Retreives an object from the database, internal
824 */
825 protected Object getObject( Object obj, long id, long cntID, long arrID ) throws Exception {
826 Statement getStmt = conn.createStatement();
827
828 Class objClass = obj.getClass();
829 boolean firstTime = true;
830
831 while ( true ) {
832 if ( firstTime ) {
833 firstTime = false;
834 } else {
835 objClass = objClass.getSuperclass();
836 if ( objClass.getName().equals("java.lang.Object") ) {
837 break;
838 }
839 }
840 SQLClass sqlcls = getClass(objClass);
841 if ( sqlcls == null || sqlcls.fields.length == 0 ) {
842 continue;
843 }
844
845 StringBuffer sqlText = new StringBuffer(255);
846 sqlText.append( SQL_GET_OBJECT_1 );
847 sqlText.append( replacePoints(sqlcls.className) );
848 sqlText.append( SQL_GET_OBJECT_2 );
849 sqlText.append( id );
850 sqlText.append( SQL_GET_OBJECT_3 );
851 sqlText.append( cntID );
852 sqlText.append( SQL_GET_OBJECT_4 );
853 sqlText.append( arrID );
854 ResultSet getRs = getStmt.executeQuery( sqlText.toString() );
855 if ( !getRs.next() ) {
856 continue;
857 }
858 if ( getRs.getByte("isNull") == 1) {
859 return null;
860 }
861
862 for ( int i=0; i<sqlcls.fields.length; i++ ) {
863 int type = sqlcls.fieldTypes[i];
864 switch (type) {
865 case BOOLEAN: if ( getRs.getByte(sqlcls.fieldNames[i]) == 1 ) {
866 sqlcls.fields[i].setBoolean( obj, true );
867 } else {
868 sqlcls.fields[i].setBoolean( obj, false );
869 }
870 break;
871 case BYTE: sqlcls.fields[i].setByte( obj, getRs.getByte(sqlcls.fieldNames[i]) );
872 break;
873 case SHORT: sqlcls.fields[i].setShort( obj, getRs.getShort(sqlcls.fieldNames[i]) );
874 break;
875 case INT: sqlcls.fields[i].setInt( obj, getRs.getInt(sqlcls.fieldNames[i]) );
876 break;
877 case LONG: sqlcls.fields[i].setLong( obj, getRs.getLong(sqlcls.fieldNames[i]) );
878 break;
879 case FLOAT: sqlcls.fields[i].setFloat( obj, getRs.getFloat(sqlcls.fieldNames[i]) );
880 break;
881 case DOUBLE: sqlcls.fields[i].setDouble( obj, getRs.getDouble(sqlcls.fieldNames[i]) );
882 break;
883 case CHAR: sqlcls.fields[i].setChar( obj, getRs.getString(sqlcls.fieldNames[i]).charAt(0) );
884 break;
885 case STRING_OBJ: sqlcls.fields[i].set( obj, getRs.getString(sqlcls.fieldNames[i]) );
886 break;
887 case FLOAT_OBJ: float f = getRs.getFloat( sqlcls.fieldNames[i] );
888 if ( rs.wasNull() ) {
889 sqlcls.fields[i].set( obj, null );
890 } else {
891 sqlcls.fields[i].set( obj, new Float(f) );
892 }
893 break;
894 case DATE_OBJ: sqlcls.fields[i].set( obj, getRs.getDate(sqlcls.fieldNames[i]) );
895 break;
896 case URL_OBJ: URL url = new URL( getRs.getString(sqlcls.fieldNames[i]) );
897 if ( rs.wasNull() ) {
898 sqlcls.fields[i].set( obj, null );
899 } else {
900 sqlcls.fields[i].set( obj, url );
901 }
902 break;
903 case OBJECT: long obj_cntID = getRs.getLong( sqlcls.fieldNames[i] );
904 if ( rs.wasNull() ) {
905 sqlcls.fields[i].set( obj, null );
906 } else {
907 String clsName = sqlcls.fields[i].getType().getName();
908 Object o = getObject( clsName, id, obj_cntID, 0 );
909 sqlcls.fields[i].set( obj, o );
910 }
911 break;
912 case BOOLEAN_ARR:
913 case BYTE_ARR:
914 case SHORT_ARR:
915 case INT_ARR:
916 case LONG_ARR:
917 case FLOAT_ARR:
918 case DOUBLE_ARR:
919 case CHAR_ARR:
920 case FLOAT_OBJ_ARR:
921 case STRING_OBJ_ARR:
922 case DATE_OBJ_ARR:
923 case URL_OBJ_ARR: long prim_arr_cntID = getRs.getLong( sqlcls.fieldNames[i] );
924 if ( rs.wasNull() ) {
925 sqlcls.fields[i].set( obj, null );
926 } else {
927 String clsPrimName = sqlcls.fields[i].getType().getComponentType().getName();
928 sqlcls.fields[i].set( obj, getPrimitiveArray(clsPrimName, type, id, prim_arr_cntID) );
929 }
930 break;
931 case OBJECT_ARR: long obj_arr_cntID = getRs.getLong( sqlcls.fieldNames[i] );
932 if ( rs.wasNull() ) {
933 sqlcls.fields[i].set( obj, null );
934 } else {
935 String clsArrName = sqlcls.fields[i].getType().getComponentType().getName();
936 sqlcls.fields[i].set( obj, getObjectArray(clsArrName, id, obj_arr_cntID) );
937 }
938 break;
939 }
940 }
941 }
942
943 return obj;
944 }
945
946
947 /**
948 Internal
949 */
950 protected Object getPrimitiveArray( String className, int type, long id, long cntID ) throws Exception {
951 StringBuffer sqlText = new StringBuffer(256);
952 sqlText.append( SQL_GET_ARR_1 );
953 sqlText.append( replacePoints(className) );
954 sqlText.append( SQL_GET_ARR_2 );
955 sqlText.append( id );
956 sqlText.append( SQL_GET_ARR_3 );
957 sqlText.append( cntID );
958 sqlText.append( SQL_GET_ARR_4 );
959 rs = query( sqlText.toString() );
960 rs.next();
961 Object obj = Array.newInstance( Class.forName(className), rs.getInt(1) );
962
963 sqlText = new StringBuffer(256);
964 sqlText.append( SQL_GET_PRIM_ARR_1 );
965 sqlText.append( replacePoints(className) );
966 sqlText.append( SQL_GET_PRIM_ARR_2 );
967 sqlText.append( id );
968 sqlText.append( SQL_GET_PRIM_ARR_3 );
969 sqlText.append( cntID );
970 rs = query( sqlText.toString() );
971
972 int nr = 0;
973 while ( rs.next() ) {
974 switch( type ) {
975 case BOOLEAN_ARR: if ( rs.getByte(1) == 1 ) {
976 Array.setBoolean( obj, nr, true );
977 } else {
978 Array.setBoolean( obj, nr, false );
979 }
980 break;
981 case BYTE_ARR: Array.setByte( obj, nr, rs.getByte(1) );
982 break;
983 case SHORT_ARR: Array.setShort( obj, nr, rs.getShort(1) );
984 break;
985 case INT_ARR: Array.setInt( obj, nr, rs.getInt(1) );
986 break;
987 case LONG_ARR: Array.setLong( obj, nr, rs.getLong(1) );
988 break;
989 case FLOAT_ARR: Array.setFloat( obj, nr, rs.getFloat(1) );
990 break;
991 case DOUBLE_ARR: Array.setDouble( obj, nr, rs.getDouble(1) );
992 break;
993 case CHAR_ARR: Array.setChar( obj, nr, rs.getString(1).charAt(0) );
994 break;
995 case STRING_OBJ_ARR: Array.set( obj, nr, rs.getString(1) );
996 break;
997 case FLOAT_OBJ_ARR: float f = rs.getFloat(1);
998 if ( rs.wasNull() ) {
999 Array.set( obj, nr, null );
1000 } else {
1001 Array.set( obj, nr, new Float(f) );
1002 }
1003 break;
1004 case DATE_OBJ_ARR: Array.set( obj, nr, rs.getDate(1) );
1005 break;
1006 case URL_OBJ_ARR: URL url = new URL( rs.getString(1) );
1007 if ( rs.wasNull() ) {
1008 Array.set( obj, nr, null );
1009 } else {
1010 Array.set( obj, nr, url );
1011 }
1012 break;
1013 }
1014 nr++;
1015 }
1016
1017 return obj;
1018 }
1019
1020
1021 /**
1022 Internal
1023 */
1024 protected Object getObjectArray( String className, long id, long cntID ) throws Exception {
1025 StringBuffer sqlText = new StringBuffer(256);
1026 sqlText.append( SQL_GET_ARR_1 );
1027 sqlText.append( replacePoints(className) );
1028 sqlText.append( SQL_GET_ARR_2 );
1029 sqlText.append( id );
1030 sqlText.append( SQL_GET_ARR_3 );
1031 sqlText.append( cntID );
1032 sqlText.append( SQL_GET_ARR_4 );
1033 rs = query( sqlText.toString() );
1034 rs.next();
1035 int nr = rs.getInt(1);
1036
1037 Class objClass = Class.forName(className);
1038 Object obj = Array.newInstance( objClass, nr );
1039 for ( int i=0; i<nr; i++ ) {
1040 Array.set( obj, i, getObject(objClass.newInstance(), id, cntID, i) );
1041 }
1042
1043 return obj;
1044 }
1045
1046
1047 /**
1048 Get first object of class <B>className</B>, whose <B>field</B> field have value <B>value</B>
1049 */
1050 public Object get( String className, String field, Object value ) throws Exception {
1051 rs = getRangeDb_id( className, field, value );
1052 if ( rs == null ) {
1053 return null;
1054 }
1055 if ( rs.next() ) {
1056 return get(className, rs.getLong("db_id"));
1057 }
1058 return null;
1059 }
1060
1061
1062 /**
1063 Get all objects of class <B>className</B>, whose <B>field</B> field have value <B>value</B>
1064 */
1065 public Object[] getRange( String className, String field, Object value ) throws Exception {
1066 SQLClass sqlcls = (SQLClass)clsRepository.get( replacePoints(className) );
1067 int type = 0;
1068 for ( int i=0; i<sqlcls.fieldNames.length; i++ ) {
1069 if ( sqlcls.fieldNames[i].equals(field) ) {
1070 type = sqlcls.fieldTypes[i];
1071 break;
1072 }
1073 }
1074
1075 switch (type) {
1076 case BOOLEAN:
1077 case BYTE:
1078 case SHORT:
1079 case INT:
1080 case LONG:
1081 case FLOAT:
1082 case DOUBLE:
1083 case CHAR:
1084 case STRING_OBJ:
1085 case FLOAT_OBJ:
1086 case DATE_OBJ:
1087 case URL_OBJ: StringBuffer sqlText = new StringBuffer(256);
1088 sqlText.append( SQL_GET_RANGE_1 );
1089 sqlText.append( replacePoints(className) );
1090 sqlText.append( SQL_GET_RANGE_2 );
1091 sqlText.append( field );
1092 sqlText.append( " = " );
1093 if ( value instanceof String ) {
1094 sqlText.append( " '" );
1095 sqlText.append( value );
1096 sqlText.append( "' " );
1097 } else {
1098 sqlText.append( value );
1099 }
1100 sqlText.append( SQL_GET_RANGE_3 );
1101 rs = query( sqlText.toString() );
1102 ArrayList arr = new ArrayList();
1103 while ( rs.next() ) {
1104 arr.add( get(className, rs.getLong("db_id")) );
1105 }
1106 return arr.toArray();
1107
1108 case OBJECT:
1109
1110 case BOOLEAN_ARR:
1111 case BYTE_ARR:
1112 case SHORT_ARR:
1113 case INT_ARR:
1114 case LONG_ARR:
1115 case FLOAT_ARR:
1116 case DOUBLE_ARR:
1117 case CHAR_ARR:
1118 case STRING_OBJ_ARR:
1119 case FLOAT_OBJ_ARR:
1120 case DATE_OBJ_ARR:
1121 case URL_OBJ_ARR:
1122
1123 case OBJECT_ARR:
1124 }
1125
1126 return null;
1127 }
1128
1129
1130 /** unfinished, work only for primitive fields not objects
1131 Internal
1132 */
1133 protected ResultSet getRangeDb_id( String className, String field, Object value ) {
1134 SQLClass sqlcls = (SQLClass)clsRepository.get( replacePoints(className) );
1135 int type = 0;
1136 for ( int i=0; i<sqlcls.fieldNames.length; i++ ) {
1137 if ( sqlcls.fieldNames[i].equals(field) ) {
1138 type = sqlcls.fieldTypes[i];
1139 break;
1140 }
1141 }
1142
1143 switch (type) {
1144 case BOOLEAN:
1145 case BYTE:
1146 case SHORT:
1147 case INT:
1148 case LONG:
1149 case FLOAT:
1150 case DOUBLE:
1151 case CHAR:
1152 case STRING_OBJ:
1153 case FLOAT_OBJ:
1154 case DATE_OBJ:
1155 case URL_OBJ: StringBuffer sqlText = new StringBuffer(256);
1156 sqlText.append( SQL_GET_RANGE_1 );
1157 sqlText.append( replacePoints(className) );
1158 sqlText.append( SQL_GET_RANGE_2 );
1159 sqlText.append( field );
1160 sqlText.append( " = " );
1161 if ( value instanceof String ) {
1162 sqlText.append( " '" );
1163 sqlText.append( value );
1164 sqlText.append( "' " );
1165 } else {
1166 sqlText.append( value );
1167 }
1168 sqlText.append( SQL_GET_RANGE_3 );
1169 return query( sqlText.toString() );
1170
1171 case OBJECT:
1172 }
1173
1174 return null;
1175 }
1176
1177
1178 /**
1179 Get all objects that are "between" object <B>o1</B> and <B>o2</B>.
1180 Object <B>o1</B> and <B>o2</B> must be of same class, and must have
1181 implement method compareTo(obj) (interface Comparable), else null is returned.
1182 */
1183 public Object[] getRange( Object o1, Object o2 ) throws Exception {
1184 String className = o1.getClass().getName();
1185 if ( !className.equals(o2.getClass().getName()) ) {
1186 return null;
1187 }
1188 Method compareTo = null;
1189 try {
1190 Class[] cls = new Class[1];
1191 cls[0] = Class.forName("java.lang.Object");
1192 compareTo = o1.getClass().getMethod("compareTo", cls);
1193 } catch (NoSuchMethodException e1) {
1194 return null;
1195 } catch (Exception e2) {
1196 return null;
1197 }
1198
1199 Object[] obj = getAll(className);
1200 Object[] o1_arr = new Object[1];
1201 o1_arr[0] = o1;
1202 Object[] o2_arr = new Object[1];
1203 o2_arr[0] = o2;
1204 ArrayList arr = new ArrayList();
1205 for ( int i=0; i<obj.length; i++ ) {
1206 int comp1 = ((Integer)compareTo.invoke(obj[i], o1_arr)).intValue();
1207 int comp2 = ((Integer)compareTo.invoke(obj[i], o2_arr)).intValue();
1208 if ( comp1 > 0 && comp2 < 0 ) {
1209 arr.add( obj[i] );
1210 }
1211 }
1212 return arr.toArray();
1213 }
1214
1215
1216 /**
1217 Get all objects of <B>className</B> from db
1218 */
1219 public Object[] getAll( String className ) throws Exception {
1220 StringBuffer sqlText = new StringBuffer(256);
1221 sqlText.append( SQL_GET_ALL );
1222 sqlText.append( replacePoints(className) );
1223 Statement getAllStmt = conn.createStatement();
1224 ResultSet getAllRs = getAllStmt.executeQuery( sqlText.toString() );
1225
1226 ArrayList arr = new ArrayList();
1227 while ( getAllRs.next() ) {
1228 arr.add( get(className, getAllRs.getLong("db_id")) );
1229 }
1230 return arr.toArray();
1231 }
1232
1233
1234 /**
1235 Update field in object. Field and object are encapsulated inside request object.
1236 */
1237 public void update( Request r ) {
1238 StringBuffer sqlText = new StringBuffer(256);
1239 sqlText.append( " UPDATE " );
1240 sqlText.append( replacePoints(r.getClassName()) );
1241 sqlText.append( " SET " );
1242 sqlText.append( r.getEventName() );
1243 sqlText.append( " = " );
1244 sqlText.append( r.getEventValue() );
1245 sqlText.append( " WHERE db_id = " );
1246 sqlText.append( r.object.db_id );
1247 sqlText.append( " AND cnt_id = 0 AND arr_id = 0" );
1248 try {
1249 stmt.executeUpdate( sqlText.toString() );
1250 } catch ( SQLException e ) {
1251 Logger.logError( "SQLDB: error while updating " + r.getEventName() + " in " +
1252 r.getClassName() + " class!" );
1253 }
1254 }
1255
1256
1257 /**
1258 Delete object from database.
1259 This implementation works but is just temporary, needs complete rewrite
1260 */
1261 public void delete( Object obj ) throws Exception {
1262 long db_id = obj.getClass().getField("db_id").getLong( obj );
1263 int count = 0;
1264
1265 rs = query( SQL_SHOW_TABLES );
1266 while ( rs.next() ) {
1267 String table = rs.getString(1);
1268 if ( table.equals("classes") || table.equals("packages") ) {
1269 continue;
1270 }
1271 if ( table.equals("objects") ) {
1272 stmtDeleteObject.setLong( 1, db_id );
1273 count = stmtDeleteObject.executeUpdate();
1274 } else {
1275 stmtDelObject.setString( 1, table );
1276 stmtDelObject.setLong( 2, db_id );
1277 count = stmtDelObject.executeUpdate();
1278 }
1279 if ( count > 0 ) {
1280 Logger.logDebug("SQLDB: deleted from " + table + ": " + count + " records" );
1281 }
1282 }
1283
1284 }
1285
1286
1287 /**
1288 Not implemented
1289 */
1290 public void commit() {
1291 }
1292
1293
1294 /**
1295 Close connection to the database
1296 */
129