Source Code Cross Referenced for SRecordMeta.java in  » Database-ORM » SimpleORM » simpleorm » core » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » SimpleORM » simpleorm.core 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package simpleorm.core;
002:
003:        import simpleorm.properties.*;
004:
005:        import java.util.HashMap;
006:        import java.sql.*;
007:        import java.io.*;
008:
009:        /*
010:         * Copyright (c) 2002 Southern Cross Software Queensland (SCSQ).  All rights
011:         * reserved.  See COPYRIGHT.txt included in this distribution.
012:         */
013:
014:        /** Defines the meta data for a {@link SRecordInstance} such as
015:         the table name.  Details about each field are stored in
016:         {@link SFieldMeta} refered to from this object.<p>
017:        
018:         Thus Instance Variables of this class only describe the
019:         definition of a Record, not instances or connections.<p>
020:        
021:         This class also contains routines to create new {@link SRecordInstance}es
022:         such as {@link #findOrCreate} and {@link #select}.
023:         (This packaging makes the calls shorter than using a static
024:         method on SRecordInstance.)<p>
025:        
026:         */
027:
028:        public class SRecordMeta extends SPropertyMap implements  Serializable {
029:            /** instances_ is a hash map of SRecordMetas.  key is userClassName,
030:             * and value is SRecordMeta. Static map of all SRecordMeta
031:             * instances.  used for de-serialization
032:             */
033:            private static HashMap instances_ = new HashMap();
034:
035:            /** Name of underlying java class. Used for de-serialization */
036:            private String userClassName;
037:
038:            /** The underlying java class for this object. */
039:            transient protected Class userClass;
040:
041:            /** The SFieldMetas within this record. <p> This contains both the
042:             foreign keys and then the sFieldReference, ie. it is totally
043:             flattened.*/
044:            transient SArrayList sFieldMetas = new SArrayList(20); // of SFieldMeta.
045:
046:            /** The names of fields store redundantly.  
047:             Only to be used within the debugger.
048:             */
049:            transient public SArrayList sFieldNames = new SArrayList(20); // of String
050:
051:            /** All the field names.  use getField to look up the actual field. 
052:             See also SFIELD_MAP property etc.*/
053:            public SArrayList getFieldNames() {
054:                return sFieldNames;
055:            }
056:
057:            /** Exactly the same elements as SFieldMetas, but keyed by field
058:             names which default to column names or reference table names or
059:             prefixes. */
060:            transient HashMap fieldMap = new HashMap(20);
061:
062:            /** The Primary Key fields. <p> Contains both foreign keys
063:             (recursively) and sFieldReference.*/
064:            transient SArrayList keySFieldMetas = new SArrayList(2); // primary keys
065:
066:            /** Create a new table/record definition.<p>
067:             
068:             @param userClass When a (@link #SRecordInstant} is created by
069:             {@link #findOrCreate}, it is cast to this class which must extend
070:             SRecordInstant.  It is normally unique for each SRecordMeta, but
071:             that is not strictly required.
072:             
073:             @param tableName The name of the SQL table that will be
074:             associated with this record.
075:             
076:             @param pvalues Arbitrary properties that can be associated with
077:             this record.  Rarely used.
078:             */
079:            public SRecordMeta(Object userClass, String tableName,
080:                    SPropertyValue[] pvalues) {
081:                Class clas = SJSharp.castToClass(userClass);
082:                if (!SRecordInstance.class.isAssignableFrom(clas))
083:                    throw new SException.Error(
084:                            "Class must be an SRecordInstance " + clas);
085:                this .userClass = clas;
086:                putProperty(SCon.STABLE_NAME, tableName);
087:
088:                if (pvalues != null)
089:                    setPropertyValues(pvalues);
090:
091:                userClassName = clas.getName();
092:                instances_.put(userClassName, this );
093:            }
094:
095:            public SRecordMeta(Object userClass, String tableName) {
096:                this (userClass, tableName, null);
097:            }
098:
099:            /** Returns the field by SFIELD_NAME, or null if not found. */
100:            public SFieldMeta getField(String fieldName) {
101:                return (SFieldMeta) fieldMap.get(fieldName);
102:            }
103:
104:            /** Returns a <code>CREATE TABLE...</code> for this table.  Delegates
105:             to the database driver.*/
106:            public String createTableSQL() {
107:                return SConnection.getDriver().createTableSQL(this );
108:            }
109:
110:            /** Searches first the cache and then the database for a record with
111:             the primary key == <code>keys</code>.  If one is found returns
112:             it, otherwise creates a new SRecordInstance prepopulated with
113:             the primary key.<p>
114:             
115:             For multi valued concurrency databases such as Oracle and
116:             PostgreSQL, the row is usually selected <code>FOR UPDATE</code> unless
117:             {@link #SQY_READ_ONLY}.<p>
118:             
119:             In either case {@link #findOrCreate} never schedules the
120:             record to be updated or inserted.  That happens when one makes
121:             it dirty by setting a field value or perhaps
122:             SRecordInstance.setDirty().<P>
123:             
124:             The selectList is usually defaulted based on the sqy_bitSet.<p>
125:             
126:             If the record has identifying foreign keys the <code>keys</code>
127:             must contain referenced records, not the actual foreign keys.<p>
128:             
129:             The APIs have been carefully designed so that SQL queries can be
130:             made lazily.  Ie. just schedule the query to be performed the
131:             first time a field value is referenced.  This enables efficient
132:             batching techniques.  However, this implementation performs the
133:             queries eagerly, ie. the SQL query is issued by this method.<p>
134:             
135:             This always finds or creates a record within the context of a
136:             connection.  {@link #createDetached} can be used to create a new
137:             record while not attached.
138:             
139:             See also the SQY_* constants.<p>
140:             
141:             @see #find
142:             @see #create
143:             */
144:            public SRecordInstance findOrCreate(Object keys, long sqy_bitSet,
145:                    SFieldMeta[] selectList) {
146:
147:                return SRecordFinder.findOrCreate(this , keys, sqy_bitSet,
148:                        selectList);
149:            } // findOrCreate
150:
151:            public SRecordInstance findOrCreate(Object keys) {
152:                return findOrCreate(keys, 0);
153:            }
154:
155:            public SRecordInstance findOrCreate(Object keys, long sqy_bitSet) {
156:                return findOrCreate(keys, sqy_bitSet, fieldList(sqy_bitSet
157:                        | SCon.SQY_NO_REFERENCES));
158:                // Could also suppress Primary Keys from select list as we already
159:                // have them.  But keys are normally small so retrieving them is
160:                // cheap and we get a good consistency check that they agree with
161:                // the query.
162:            }
163:
164:            /**
165:             * Same as findOrCreate(key) but returns null if not found
166:             * instead of createing a new record.
167:             * @see #mustFind(Object)
168:             * @see #findOrCreate(Object)
169:             */
170:            public SRecordInstance find(Object keys) {
171:                SRecordInstance found = findOrCreate(keys);
172:                if (found.isNewRow())
173:                    found = null;
174:                return found;
175:            }
176:
177:            /**
178:             * Same as find(key) but throws an exception if the record is not found.
179:             * @see #find
180:             */
181:            public SRecordInstance mustFind(Object keys) {
182:                SRecordInstance found = findOrCreate(keys);
183:                if (found.isNewRow())
184:                    throw new SException.Data("Record not found " + found);
185:                return found;
186:            }
187:
188:            /**
189:             * Same as findOrCreate(key) but asserts must be a new row.
190:             * Also sets the row Dirty so that it will actually be inserted even if no 
191:             * other non-key fields are set.<p>
192:             * 
193:             * It is assumed that the record is not in the database, you will
194:             * get an SQL exception if it already exists.  This saves a SELECT statement.
195:             * But if there is any doubt use findOrCreate instead and then check .isNewRow.<p>
196:             * 
197:             * A check is also made that the row is not already in the cache, ie. two calls to 
198:             * create with the same key will produce an exception.<p>
199:             * 
200:             * @see #findOrCreate(Object, long, SFieldMeta[])
201:             */
202:            public SRecordInstance create(Object keys) {
203:                SRecordInstance found = findOrCreate(keys,
204:                        SCon.SQY_ASSUME_CREATE);
205:                found.assertNewRow(); // always true as 
206:                if (found.wasInCache())
207:                    throw new SException.Error(
208:                            "Attempt to create row that was already in the cache "
209:                                    + found);
210:                found.setDirty();
211:                return found;
212:            }
213:
214:            /** Returns SFieldMetas to select given bitSet.
215:             ### This needs a careful review. */
216:            SFieldMeta[] fieldList(long sqy_bitSet) {
217:                // ## This is a little expensive, results could be cached,
218:                // along with the queries that are actually generated.
219:                SFieldMeta[] result = null;
220:                int resultSize = 0;
221:                // Two passes, first to work out result size, second to build it up.
222:                // This will be faster than another SArrayList.
223:                for (int pass = 0; pass < 2; pass++) {
224:                    for (int fx = 0; fx < sFieldMetas.size(); fx++) {
225:                        SFieldMeta field = (SFieldMeta) sFieldMetas.get(fx);
226:                        //System.out.println("selectList " + pass + field + sqy_bitSet +
227:                        //  SUte.inBitSet(sqy_bitSet, SQY_NO_FOREIGN_KEYS, SQY_) +
228:                        //  SUte.inBitSet(sqy_bitSet, SQY_NO_REFERENCES, SQY_) );
229:
230:                        if (SUte.inBitSet(sqy_bitSet, SCon.SQY_PRIMARY_KEY,
231:                                SCon.SQY_)
232:                                && !field.isPrimaryKey)
233:                            continue; // ie.ONLY primary keys.
234:                        //  ## Change to run down keySFieldMetas if primary.  Or maybe
235:                        //  just get rid of keySFieldMetas.
236:
237:                        // ## Problems if SDESCRIPTIVE etc. is a Reference
238:                        if (SUte.inBitSet(sqy_bitSet, SCon.SQY_DESCRIPTIVE,
239:                                SCon.SQY_)
240:                                && !field.getBoolean(SCon.SDESCRIPTIVE)
241:                                && !field.isPrimaryKey)
242:                            continue;
243:                        if (!SUte.inBitSet(sqy_bitSet, SCon.SQY_UNQUERIED,
244:                                SCon.SQY_)
245:                                && field.getBoolean(SCon.SUNQUERIED))
246:                            continue;
247:                        if (SUte.inBitSet(sqy_bitSet, SCon.SQY_NO_FOREIGN_KEYS,
248:                                SCon.SQY_)
249:                                && field.sFieldReference != null) // DataLoader
250:                            continue;
251:                        if (SUte.inBitSet(sqy_bitSet, SCon.SQY_NO_REFERENCES,
252:                                SCon.SQY_)
253:                                && field instanceof  SFieldReference)
254:                            continue;
255:
256:                        if (SUte.inBitSet(sqy_bitSet,
257:                                SCon.SQY_NO_GENERATED_KEYS, SCon.SQY_)
258:                                && field.getProperty(SCon.SGENERATED_KEY) != null)
259:                            continue;
260:
261:                        // Not rejected so include this field.
262:                        if (pass == 1)
263:                            result[resultSize] = field;
264:                        resultSize++;
265:                    }
266:                    if (pass == 0)
267:                        result = new SFieldMeta[resultSize];
268:                    resultSize = 0;
269:                }
270:                return result;
271:            }
272:
273:            /** Given a list of fields, add primary key fields and then replace
274:             any reference fields with their foreign keys.  Normally one
275:             works with references, but when queries are generated one needs
276:             the ground columns. */
277:            SFieldMeta[] expandSelectList(SFieldMeta[] fields) {
278:                SArrayList xfields = new SArrayList(fields.length * 2);
279:
280:                // Add primary keys if missing
281:                mpxl: for (int mpx = 0; mpx < keySFieldMetas.size(); mpx++) {
282:                    SFieldMeta mp = (SFieldMeta) keySFieldMetas.get(mpx);
283:                    if (!(mp instanceof  SFieldReference)) {
284:                        for (int qpx = 0; qpx < xfields.size(); qpx++)
285:                            if (xfields.get(qpx) == mp)
286:                                continue mpxl;
287:                        // Key not in query, add it.  (Should be rare) Where are Java's array functions?
288:                        if (!xfields.contains(mp))
289:                            xfields.add(mp);
290:                    }
291:                }
292:
293:                // Expand References.
294:                expandSelectListRecur(xfields, fields);
295:                return (SFieldMeta[]) xfields.toArray(new SFieldMeta[0]);
296:            }
297:
298:            private void expandSelectListRecur(SArrayList xfields,
299:                    SFieldMeta[] fields) {
300:                for (int fx = 0; fx < fields.length; fx++) {
301:                    SFieldMeta field = fields[fx];
302:                    if (field instanceof  SFieldReference)
303:                        expandSelectListRecur(xfields,
304:                                ((SFieldReference) field).foreignKeyFields);
305:                    else if (!xfields.contains(field))
306:                        xfields.add(field);
307:                }
308:            }
309:
310:            /** Produce an SQL Query, prepare it, and return an
311:             {@link SPreparedStatement}.  See that class for an example of its use.
312:             <code>where</code> and <code>orderBy</code> are SFieldMetas that
313:             arecopied literally into the query.  The SQY_* BitSets are defined
314:             in SConstants.<p>
315:             
316:             (This delegates the actual query creation to the Driver class.)  */
317:            public SPreparedStatement select(String where, String orderBy,
318:                    long sqy_bitSet, SFieldMeta[] selectList) {
319:                return select(where, orderBy, sqy_bitSet, selectList,
320:                        new SPreparedStatement());
321:            }
322:
323:            /** Use this variant to add extra parameters between ps creation and
324:             * sql generation.  Also use it to subclass ps*/
325:            public SPreparedStatement select(String where, String orderBy,
326:                    long sqy_bitSet, SFieldMeta[] selectList,
327:                    SPreparedStatement ps) {
328:                ps.prepareStatement(this , where, orderBy, sqy_bitSet,
329:                        selectList == null ? fieldList(sqy_bitSet
330:                                | SCon.SQY_NO_REFERENCES)
331:                                : expandSelectList(selectList));
332:                return ps;
333:            }
334:
335:            public SPreparedStatement select(String where, String orderBy,
336:                    long sqy_bitSet) {
337:                return select(where, orderBy, sqy_bitSet, null);
338:            }
339:
340:            public SPreparedStatement select(String where, String orderBy) {
341:                return select(where, orderBy, 0);
342:            }
343:
344:            /** Start a query using the query builder instead of raw SQL.
345:             @see SQuery
346:             @see #select
347:             */
348:            public SQuery newQuery(long sqy_bitSet, SFieldMeta[] selectList) {
349:                return new SQuery(this , sqy_bitSet, selectList);
350:            }
351:
352:            public SQuery newQuery(long sqy_bitSet) {
353:                return newQuery(sqy_bitSet, null);
354:            }
355:
356:            public SQuery newQuery() {
357:                return newQuery(0);
358:            }
359:
360:            public String toString() {
361:                return "[SRecordMeta " + SUte.cleanClass(userClass) + "]";
362:            }
363:
364:            /** Returns a list of all the fields for debugging. */
365:            public String allFieldsString() {
366:                StringBuffer res = new StringBuffer(this  + ":-\n");
367:                for (int fx = 0; fx < sFieldMetas.size(); fx++) {
368:                    res.append("    "
369:                            + ((SFieldMeta) sFieldMetas.get(fx))
370:                                    .toLongerString() + "\n");
371:                }
372:                return res.toString();
373:            }
374:
375:            /**
376:             * Returns the (one) generator for the one key of this SRecord.
377:             */
378:            public SGenerator getSGenerator() {
379:                /// Get and check primary key details
380:                if (keySFieldMetas.size() != 1)
381:                    throw new SException.Error(
382:                            "Must only have one key field if generated " + this );
383:                SFieldMeta keyfld = (SFieldMeta) keySFieldMetas.get(0);
384:                SGenerator gkey = (SGenerator) keyfld
385:                        .getProperty(SCon.SGENERATED_KEY);
386:                if (gkey == null)
387:                    throw new SException.Error(keyfld
388:                            + " is not SGENERATED_KEY");
389:
390:                return gkey;
391:            }
392:
393:            /** Creates a new object with a newly generated key.  Note that the
394:             SELECT MAX method is used unless a database dependent
395:             alternative is available (currently only for PostgreSQL). {@link
396:             SGENERATED_KEY}.
397:             
398:             Always creates a new empty record. 
399:             
400:             ## This code is partially copied into SRecordInstance.attach() for
401:             records created while detached.
402:             */
403:            public SRecordInstance createWithGeneratedKey() {
404:                SFieldMeta keyfld = (SFieldMeta) keySFieldMetas.get(0);
405:                SRecordInstance newRec = getSGenerator()
406:                        .createWithGeneratedKey(this , keyfld);
407:                return newRec;
408:            }
409:
410:            /** Flushes and Purges all record instances for this table.
411:             <code>SConnection.flushAndPurge()</code> may be more useful in
412:             practice.
413:             
414:             @see SRecordInstance#flushAndPurge
415:             @see SConnection#flushAndPurge
416:             */
417:            public void flushAndPurge() {
418:                SConnection scon = SConnection.getBegunConnection();
419:                java.util.Iterator ci = scon.transactionCache.values()
420:                        .iterator();
421:                while (ci.hasNext()) {
422:                    SRecordInstance ri = (SRecordInstance) ci.next();
423:                    if (ri.getMeta() == this ) {
424:                        ri.flush();
425:                        ci.remove(); // #### Is this correct?  Deleting cursor?
426:                        ri.incompleteDestroy();
427:                    }
428:                }
429:            }
430:
431:            /**
432:             * Create an SRecordInstance that will be initially detached. All
433:             * key fields must be populated before attaching unless they are
434:             * generated keys. Objects created this way
435:             * <i>must not</i> not have existed in the database previously, or a unique
436:             * key violation exception is thrown by JDBC at flush() time.
437:             * <p>
438:             * A typical use of this method is by a thick-client application that
439:             * creates a record, and then passes it to the application server which, in turn,
440:             * will insert it into the database.<p>
441:             *
442:             * The primary key can be set either using the key parameter,
443:             * explicitly after this method using set*, just before it
444:             * is reattached and the database is thus available, or if it is
445:             * generated then it can be automatically created by the attach()
446:             * method.
447:             *
448:             * @return SRecordInstance A newly created 'blank' record
449:             * @see simpleorm.core.SRecordInstance#attach
450:             */
451:            public SRecordInstance createDetached() {
452:                try {
453:                    SRecordInstance instance = (SRecordInstance) userClass
454:                            .newInstance();
455:                    instance.readOnly = false;
456:                    instance.newRow = true;
457:
458:                    // Set all nullable fields as if they contained valid data. Without this, objects
459:                    // created this way become hyper-sensitive, in that even when the data
460:                    // could legitimately be null, they throw exceptions when you try
461:                    // to access those fields
462:                    for (int ii = 0; ii < sFieldMetas.size(); ii++) {
463:                        //if ( !((SFieldMeta)sFieldMetas.get( ii )).getBoolean( SMANDATORY ) )
464:                        {
465:                            instance.bitSets[ii] = SCon.INS_VALID;
466:                        }
467:                    }
468:
469:                    return instance;
470:                } catch (Exception ie) {
471:                    throw new SException.Data(ie);
472:                }
473:            }
474:
475:            /**
476:             createDetached() and then sets the key field(s).
477:             */
478:            public SRecordInstance createDetached(Object key) {
479:
480:                SRecordInstance inst = createDetached();
481:
482:                SRecordFinder.setPrimaryKeys(inst, key);
483:
484:                return inst;
485:            }
486:
487:            /**
488:             * SRecordMeta is like a singleton, in that only one instance of SRecordMeta must
489:             * exist in the VM for a specific table. This is a special method used during
490:             * de-serialization to determine if the object de-serialized should be substituted.
491:             * This method is implemented to return the SRecordMeta object for the appropriate
492:             * user Class that already exists.
493:             */
494:            protected Object readResolve() throws ObjectStreamException {
495:                try {
496:                    Class.forName(userClassName); // This forces class to load and, thus, its SRecordMeta to be instantiated
497:
498:                    Object substituted = instances_.get(userClassName);
499:                    if (substituted == null) {
500:                        throw new NullPointerException();
501:                    }
502:                    return substituted;
503:                } catch (Exception e) {
504:                    throw new SException.InternalError(
505:                            "Error de-serializing SRecordMeta for "
506:                                    + userClassName);
507:                }
508:            }
509:
510:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.