2137_20221109a.patch
new/src/com/goldencode/p2j/persist/AbstractQuery.java 2022-11-09 18:36:33 +0000 | ||
---|---|---|
220 | 220 |
** OM 20210917 Code maintenance. |
221 | 221 |
** OM 20220112 Added implementation of SKIP-DELETED-RECORD attribute. |
222 | 222 |
** AL2 20220412 Do proxy checks for values used as query parameters. |
223 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded |
|
224 |
** as 'partial/incomplete'. |
|
223 | 225 |
*/ |
224 | 226 | |
225 | 227 |
/* |
... | ... | |
282 | 284 |
import java.util.logging.*; |
283 | 285 |
import com.goldencode.p2j.persist.event.*; |
284 | 286 |
import com.goldencode.p2j.persist.lock.*; |
287 |
import com.goldencode.p2j.persist.orm.*; |
|
285 | 288 |
import com.goldencode.p2j.util.*; |
286 | 289 |
import com.goldencode.p2j.util.ErrorManager; |
287 | 290 | |
... | ... | |
323 | 326 |
/** Flag to indicate if this query is optimized using indexed reposition */ |
324 | 327 |
protected boolean indexedReposition = false; |
325 | 328 |
|
329 |
/** The included properties for each DMO (FIELDS option). */ |
|
330 |
protected Map<DataModelObject, Property[]> included = null; |
|
331 | ||
332 |
/** The excluded properties for each DMO (EXCEPT option). */ |
|
333 |
protected Map<DataModelObject, Property[]> excluded = null; |
|
334 |
|
|
326 | 335 |
/** Flag indicating fatal error was encountered processing query substitution parameters */ |
327 | 336 |
private boolean fatalError = false; |
328 | 337 |
|
... | ... | |
3173 | 3182 |
*/ |
3174 | 3183 |
public void include(DataModelObject dmo, String ... fields) |
3175 | 3184 |
{ |
3176 |
// UnimplementedFeature.missing("RECORD-PHRASE: FIELDS (...) option"); |
|
3177 |
// TODO: implement |
|
3185 |
RecordBuffer buffer = (RecordBuffer) ((BufferReference) dmo).buffer(); |
|
3186 |
if (buffer.isTemporary()) |
|
3187 |
{ |
|
3188 |
return; |
|
3189 |
} |
|
3190 |
|
|
3191 |
if (included == null) |
|
3192 |
{ |
|
3193 |
included = new IdentityHashMap<>(); |
|
3194 |
} |
|
3195 |
|
|
3196 |
collectFields(included, dmo, fields); |
|
3178 | 3197 |
} |
3179 | 3198 |
|
3180 | 3199 |
/** |
... | ... | |
3188 | 3207 |
*/ |
3189 | 3208 |
public void exclude(DataModelObject dmo, String ... fields) |
3190 | 3209 |
{ |
3191 |
// UnimplementedFeature.missing("RECORD-PHRASE: EXCLUDE (...) option"); |
|
3192 |
// TODO: implement |
|
3210 |
RecordBuffer buffer = (RecordBuffer) ((BufferReference) dmo).buffer(); |
|
3211 |
if (buffer.isTemporary()) |
|
3212 |
{ |
|
3213 |
return; |
|
3214 |
} |
|
3215 |
|
|
3216 |
if (excluded == null) |
|
3217 |
{ |
|
3218 |
excluded = new IdentityHashMap<>(); |
|
3219 |
} |
|
3220 |
|
|
3221 |
collectFields(excluded, dmo, fields); |
|
3193 | 3222 |
} |
3194 | 3223 |
|
3195 | 3224 |
/** |
... | ... | |
3429 | 3458 |
return new logical(false); |
3430 | 3459 |
} |
3431 | 3460 |
} |
3461 |
|
|
3462 |
/** |
|
3463 |
* Populate the map of with the ORM properties specified in the given fields. |
|
3464 |
* |
|
3465 |
* @param target |
|
3466 |
* The target map. |
|
3467 |
* @param dmo |
|
3468 |
* The DMO instance. |
|
3469 |
* @param fields |
|
3470 |
* The fields to resolve. |
|
3471 |
*/ |
|
3472 |
private void collectFields(Map<DataModelObject, Property[]> target, DataModelObject dmo, String[] fields) |
|
3473 |
{ |
|
3474 |
RecordBuffer buffer = (RecordBuffer) ((BufferReference) dmo).buffer(); |
|
3475 |
DmoMeta meta = buffer.getDmoInfo(); |
|
3476 | ||
3477 |
Property[] props = target.computeIfAbsent(dmo, (d) -> |
|
3478 |
{ |
|
3479 |
return new Property[meta.getExistingFields().size()]; |
|
3480 |
}); |
|
3481 |
|
|
3482 |
for (int i = 0; i < fields.length; i++) |
|
3483 |
{ |
|
3484 |
Property p = meta.getFieldInfo(fields[i]); |
|
3485 |
|
|
3486 |
props[p.id - 1] = p; |
|
3487 |
} |
|
3488 |
} |
|
3432 | 3489 |
} |
new/src/com/goldencode/p2j/persist/FastFindCache.java 2022-11-09 18:35:42 +0000 | ||
---|---|---|
14 | 14 |
** negative result (i.e., no record is found) do not have the negative result cached. |
15 | 15 |
** CA 20220104 Node.l3Cache must be a weak reference, otherwise once the direct cache invalidates a key, |
16 | 16 |
** the reverse lookup has no chance of being cleaned up, as this is not done explicitly. |
17 |
** CA 20221109 Partial/Incomplete records (loaded with EXCEPT/FIELDS options) can't be cached. |
|
17 | 18 |
*/ |
18 | 19 | |
19 | 20 |
/* |
... | ... | |
262 | 263 |
*/ |
263 | 264 |
public void put(Key k, Record value) |
264 | 265 |
{ |
265 |
// if no key or record represents a housekeeping (e.g., dirty database) copy, don't store it |
|
266 |
if (k == null || (value != null && value.checkState(DmoState.COPY))) |
|
266 |
// if no key or record represents a housekeeping (e.g., dirty database) copy or an incomplete record |
|
267 |
// (partial fields are populated due to EXCEPT/FIELDS options), don't store it |
|
268 |
if (k == null || |
|
269 |
(value != null && (value.checkState(DmoState.COPY) || value.checkState(DmoState.INCOMPLETE)))) |
|
267 | 270 |
{ |
268 | 271 |
return; // throw some error? |
269 | 272 |
} |
new/src/com/goldencode/p2j/persist/Persistence.java 2022-11-09 18:36:45 +0000 | ||
---|---|---|
577 | 577 |
** allows a lazy registration of scopeables, to avoid the unnecessary overhead of |
578 | 578 |
** processing all the scopeables for each and every block. |
579 | 579 |
** TJD 20220504 Upgrade do Java 11 minor changes |
580 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded |
|
581 |
** as 'partial/incomplete'. |
|
580 | 582 |
*/ |
581 | 583 | |
582 | 584 |
/* |
... | ... | |
634 | 636 | |
635 | 637 |
package com.goldencode.p2j.persist; |
636 | 638 | |
637 |
import java.lang.*; |
|
638 | 639 |
import java.sql.*; |
639 | 640 |
import java.util.*; |
641 |
import java.util.BitSet; |
|
640 | 642 |
import java.util.Date; |
641 | 643 |
import java.util.function.*; |
642 | 644 |
import java.util.logging.*; |
... | ... | |
1756 | 1758 |
// have to take another hit retrieving the data almost immediately after this method |
1757 | 1759 |
// returns, which is the common case for dynamic record retrieval. |
1758 | 1760 |
Class<? extends Record> dmoClass = buffer.getDMOImplementationClass(); |
1759 |
initialResult = result = session.get(dmoClass, id); |
|
1761 |
initialResult = result = session.get(dmoClass, id, null);
|
|
1760 | 1762 |
|
1761 | 1763 |
if (result == null) |
1762 | 1764 |
{ |
... | ... | |
1978 | 1980 |
throws PersistenceException, |
1979 | 1981 |
LockTimeoutException |
1980 | 1982 |
{ |
1983 |
return load(implClass, id, lockType, timeout, updateLock, null); |
|
1984 |
} |
|
1985 |
|
|
1986 |
/** |
|
1987 |
* Load a single record from the session-level cache or, optionally, refresh it from the |
|
1988 |
* database. The record will be locked with the specified lock type upon return from this |
|
1989 |
* method. Note that the lock refers to a runtime-level lock, not a database-level lock. |
|
1990 |
* |
|
1991 |
* @param implClass |
|
1992 |
* The DMO implementation class of record being read. |
|
1993 |
* @param id |
|
1994 |
* Primary key of the record to be loaded into the buffer. May not be {@code null}. |
|
1995 |
* @param lockType |
|
1996 |
* Type of lock the lock manager should register on the resulting record for the |
|
1997 |
* current user context. |
|
1998 |
* @param timeout |
|
1999 |
* Number of milliseconds to wait to acquire the requested lock type. Throws |
|
2000 |
* {@link LockTimeoutException} if this time elapses before the lock is acquired. |
|
2001 |
* Specify 0 to wait indefinitely for the lock. Ignored if {@code lockType} is |
|
2002 |
* {@code NONE}. |
|
2003 |
* @param updateLock |
|
2004 |
* {@code true} to update the current lock state; |
|
2005 |
* {@code false} to leave the current lock state unchanged. |
|
2006 |
* @param partialFields |
|
2007 |
* For an incomplete record, read only these fields. |
|
2008 |
* |
|
2009 |
* @return A single DMO instance loaded from the database, or {@code null} if no result was |
|
2010 |
* found. |
|
2011 |
* |
|
2012 |
* @throws LockTimeoutException |
|
2013 |
* if a record was found, but its lock could not be acquired within the positive |
|
2014 |
* {@code timeout} period specified. |
|
2015 |
* @throws LockUnavailableException |
|
2016 |
* if a record was found and a no-wait lock type was specified, but the lock could |
|
2017 |
* not be acquired immediately. |
|
2018 |
* @throws PersistenceException |
|
2019 |
* if there was an error loading the DMO from the ORM session and/or database. |
|
2020 |
*/ |
|
2021 |
public Record load(Class<? extends Record> implClass, |
|
2022 |
Long id, |
|
2023 |
LockType lockType, |
|
2024 |
long timeout, |
|
2025 |
boolean updateLock, |
|
2026 |
BitSet partialFields) |
|
2027 |
throws PersistenceException, |
|
2028 |
LockTimeoutException |
|
2029 |
{ |
|
1981 | 2030 |
boolean trace = LOG.isLoggable(Level.FINEST); |
1982 | 2031 |
boolean warn = (trace || LOG.isLoggable(Level.WARNING)); |
1983 | 2032 |
boolean trackSlow = warn && loadThreshold > 0L; |
... | ... | |
2025 | 2074 |
} |
2026 | 2075 |
|
2027 | 2076 |
Session session = local.getSession(); |
2028 |
result = session.get(implClass, id); |
|
2077 |
result = session.get(implClass, id, partialFields);
|
|
2029 | 2078 |
|
2030 | 2079 |
if (result == null) |
2031 | 2080 |
{ |
new/src/com/goldencode/p2j/persist/PreselectQuery.java 2022-11-09 18:37:12 +0000 | ||
---|---|---|
587 | 587 |
** CA 20221006 Added JMX instrumentation for query assemble. Refs #6814 |
588 | 588 |
** CA 20221013 In 'repositionByID', if the row number is after the last row in the ResultSet, |
589 | 589 |
** assume an error (query off-end) and do not try to fetch the record. |
590 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded |
|
591 |
** as 'partial/incomplete'. At this time, extent fields are always loaded. |
|
590 | 592 |
*/ |
591 | 593 | |
592 | 594 |
/* |
... | ... | |
4439 | 4441 |
|
4440 | 4442 |
boolean qualified = !isOmitAlias(); |
4441 | 4443 |
|
4444 |
RecordBuffer buffer = comp.getBuffer(); |
|
4445 | ||
4442 | 4446 |
if (qualified) |
4443 | 4447 |
{ |
4444 |
RecordBuffer buffer = comp.getBuffer(); |
|
4445 | 4448 |
buf.append(buffer.getDMOAlias()); |
4446 | 4449 |
} |
4447 | 4450 |
|
... | ... | |
4454 | 4457 |
|
4455 | 4458 |
buf.append(DatabaseManager.PRIMARY_KEY); |
4456 | 4459 |
} |
4460 |
else |
|
4461 |
{ |
|
4462 |
Property[] iprops = included == null ? null : included.get(buffer.getDMOProxy()); |
|
4463 |
Property[] xprops = excluded == null ? null : excluded.get(buffer.getDMOProxy()); |
|
4464 |
if (iprops != null || xprops != null) |
|
4465 |
{ |
|
4466 |
Property[] props = null; |
|
4467 |
|
|
4468 |
// only permanent buffers can use include/exclude |
|
4469 |
if (iprops == null) |
|
4470 |
{ |
|
4471 |
DmoMeta meta = buffer.getDmoInfo(); |
|
4472 |
|
|
4473 |
props = new Property[meta.getExistingFields().size()]; |
|
4474 |
|
|
4475 |
// include all fields |
|
4476 |
Iterator<Property> piter = meta.getFields(false); |
|
4477 |
while (piter.hasNext()) |
|
4478 |
{ |
|
4479 |
Property p = piter.next(); |
|
4480 |
|
|
4481 |
if (p.propId > 0) |
|
4482 |
{ |
|
4483 |
props[p.id - 1] = p; |
|
4484 |
} |
|
4485 |
} |
|
4486 |
} |
|
4487 |
else |
|
4488 |
{ |
|
4489 |
props = new Property[iprops.length]; |
|
4490 |
System.arraycopy(iprops, 0, props, 0, props.length); |
|
4491 |
} |
|
4492 |
|
|
4493 |
if (xprops != null) |
|
4494 |
{ |
|
4495 |
// exclude properties |
|
4496 |
for (int j = 0; j < xprops.length; j++) |
|
4497 |
{ |
|
4498 |
Property xprop = xprops[j]; |
|
4499 |
if (xprop != null) |
|
4500 |
{ |
|
4501 |
props[j] = null; |
|
4502 |
} |
|
4503 |
} |
|
4504 |
} |
|
4505 |
|
|
4506 |
String alias = buffer.getDMOAlias(); |
|
4507 |
buf.append('.').append(DatabaseManager.PRIMARY_KEY); |
|
4508 |
for (int j = 0; j < props.length; j++) |
|
4509 |
{ |
|
4510 |
Property p = props[j]; |
|
4511 |
if (p == null || p.extent != 0) // TODO: denormalized extents |
|
4512 |
{ |
|
4513 |
continue; |
|
4514 |
} |
|
4515 |
|
|
4516 |
buf.append(", ").append(alias).append(".").append(p.name); |
|
4517 |
} |
|
4518 |
} |
|
4519 |
} |
|
4457 | 4520 |
} |
4458 | 4521 |
|
4459 | 4522 |
buf.append(" "); |
... | ... | |
5802 | 5865 |
{ |
5803 | 5866 |
try |
5804 | 5867 |
{ |
5805 |
dmo = p.load(buffer.getDMOImplementationClass(), id, lt, timeout, updateLock); |
|
5868 |
// force the load of the entire record |
|
5869 |
dmo = p.load(buffer.getDMOImplementationClass(), id, lt, timeout, updateLock, null); |
|
5806 | 5870 |
|
5807 | 5871 |
break; |
5808 | 5872 |
} |
... | ... | |
5853 | 5917 |
} |
5854 | 5918 |
else |
5855 | 5919 |
{ |
5856 |
dmo = p.load(buffer.getDMOImplementationClass(), id, lt, 0L, updateLock); |
|
5920 |
dmo = p.load(buffer.getDMOImplementationClass(), |
|
5921 |
id, |
|
5922 |
lt, |
|
5923 |
0L, |
|
5924 |
updateLock, |
|
5925 |
// the entire record is loaded if the lock is not NONE |
|
5926 |
updateLock || dmo == null ? null : dmo._getReadFields()); |
|
5857 | 5927 |
} |
5858 | 5928 |
} |
5859 | 5929 |
|
new/src/com/goldencode/p2j/persist/Record.java 2022-11-09 18:37:40 +0000 | ||
---|---|---|
22 | 22 |
** OM 20220713 Added support for converting to/from POJOs. |
23 | 23 |
** OM 20220718 Fixed looping in getPropertyValues(). Added to the returned list the denormalized props. |
24 | 24 |
** TJD 20220504 Upgrade do Java 11 minor changes |
25 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded as |
|
26 |
** 'partial/incomplete'. |
|
25 | 27 |
*/ |
26 | 28 | |
27 | 29 |
/* |
... | ... | |
143 | 145 |
*/ |
144 | 146 |
public final integer _getInteger(int offset) |
145 | 147 |
{ |
148 |
checkIncomplete(offset); |
|
149 | ||
146 | 150 |
Integer datum = (Integer) data[offset]; |
147 | 151 |
|
148 | 152 |
return datum != null ? new integer(datum) : new integer(); |
... | ... | |
161 | 165 |
*/ |
162 | 166 |
protected final void _setInteger(int offset, NumberType w) |
163 | 167 |
{ |
168 |
checkIncomplete(offset); |
|
169 | ||
164 | 170 |
Integer datum = null; |
165 | 171 |
if (w != null) |
166 | 172 |
{ |
... | ... | |
250 | 256 |
*/ |
251 | 257 |
public final datetimetz _getDatetimetz(int offset) |
252 | 258 |
{ |
259 |
checkIncomplete(offset); |
|
260 | ||
253 | 261 |
OffsetDateTime datum = (OffsetDateTime) data[offset]; |
254 | 262 |
|
255 | 263 |
return datum != null ? new datetimetz(datum) : new datetimetz(); |
... | ... | |
354 | 362 |
*/ |
355 | 363 |
public final datetime _getDatetime(int offset) |
356 | 364 |
{ |
365 |
checkIncomplete(offset); |
|
366 | ||
357 | 367 |
Timestamp datum = (Timestamp) data[offset]; |
358 | 368 |
|
359 | 369 |
return datum != null ? new datetime(datum) : new datetime(); |
... | ... | |
457 | 467 |
*/ |
458 | 468 |
public final date _getDate(int offset) |
459 | 469 |
{ |
470 |
checkIncomplete(offset); |
|
471 | ||
460 | 472 |
Date datum = (Date) data[offset]; |
461 | 473 |
|
462 | 474 |
return datum != null ? new date(datum) : new date(); |
... | ... | |
560 | 572 |
*/ |
561 | 573 |
public final logical _isLogical(int offset) |
562 | 574 |
{ |
575 |
checkIncomplete(offset); |
|
576 | ||
563 | 577 |
Boolean datum = (Boolean) data[offset]; |
564 | 578 |
|
565 | 579 |
return datum != null ? new logical(datum) : new logical(); |
... | ... | |
770 | 784 |
*/ |
771 | 785 |
public final decimal _getDecimal(int offset) |
772 | 786 |
{ |
787 |
checkIncomplete(offset); |
|
788 | ||
773 | 789 |
BigDecimal datum = (BigDecimal) data[offset]; |
774 | 790 |
|
775 | 791 |
return datum != null ? new decimal(datum) : new decimal(); |
... | ... | |
886 | 902 |
*/ |
887 | 903 |
public final character _getCharacter(int offset) |
888 | 904 |
{ |
905 |
checkIncomplete(offset); |
|
906 | ||
889 | 907 |
String datum = (String) data[offset]; |
890 | 908 |
|
891 | 909 |
if (datum != null) |
... | ... | |
996 | 1014 |
*/ |
997 | 1015 |
public final rowid _getRowid(int offset) |
998 | 1016 |
{ |
1017 |
checkIncomplete(offset); |
|
1018 | ||
999 | 1019 |
Long datum = (Long) data[offset]; |
1000 | 1020 |
|
1001 | 1021 |
return datum != null ? new rowid(datum) : new rowid(); |
... | ... | |
1101 | 1121 |
*/ |
1102 | 1122 |
public final handle _getHandle(int offset) |
1103 | 1123 |
{ |
1124 |
checkIncomplete(offset); |
|
1125 | ||
1104 | 1126 |
Long datum = (Long) data[offset]; |
1105 | 1127 |
|
1106 | 1128 |
return datum != null ? handle.fromResourceId(datum, false) : new handle(); |
... | ... | |
1206 | 1228 |
*/ |
1207 | 1229 |
public final recid _getRecid(int offset) |
1208 | 1230 |
{ |
1231 |
checkIncomplete(offset); |
|
1232 | ||
1209 | 1233 |
Integer datum = (Integer) data[offset]; |
1210 | 1234 |
|
1211 | 1235 |
return datum != null ? new recid(datum) : new recid(); |
... | ... | |
1310 | 1334 |
*/ |
1311 | 1335 |
public final blob _getBlob(int offset) |
1312 | 1336 |
{ |
1337 |
checkIncomplete(offset); |
|
1338 | ||
1313 | 1339 |
byte[] datum = (byte[]) data[offset]; |
1314 | 1340 |
|
1315 | 1341 |
return datum != null ? new blob(datum) : new blob(); |
... | ... | |
1415 | 1441 |
*/ |
1416 | 1442 |
public final clob _getClob(int offset) |
1417 | 1443 |
{ |
1444 |
checkIncomplete(offset); |
|
1445 | ||
1418 | 1446 |
return new clob((String) data[offset], getCodePage(offset)); |
1419 | 1447 |
} |
1420 | 1448 |
|
... | ... | |
1516 | 1544 |
*/ |
1517 | 1545 |
public final comhandle _getComhandle(int offset) |
1518 | 1546 |
{ |
1547 |
checkIncomplete(offset); |
|
1548 | ||
1519 | 1549 |
String datum = (String) data[offset]; |
1520 | 1550 |
|
1521 | 1551 |
return datum != null ? comhandle.fromString(datum) : new comhandle(); |
... | ... | |
1619 | 1649 |
*/ |
1620 | 1650 |
public final object<?> _getObject(int offset) |
1621 | 1651 |
{ |
1652 |
checkIncomplete(offset); |
|
1653 | ||
1622 | 1654 |
Long datum = (Long) data[offset]; |
1623 | 1655 |
|
1624 | 1656 |
return datum != null ? object.fromResourceId(datum) : new object<>(); |
... | ... | |
1726 | 1758 |
*/ |
1727 | 1759 |
public final raw _getRaw(int offset) |
1728 | 1760 |
{ |
1761 |
checkIncomplete(offset); |
|
1762 | ||
1729 | 1763 |
byte[] datum = (byte[]) data[offset]; |
1730 | 1764 |
|
1731 | 1765 |
return datum != null ? new raw(datum) : raw.instantiateUnknownRaw(); |
... | ... | |
1893 | 1927 |
{ |
1894 | 1928 |
sb.append("\""); |
1895 | 1929 |
} |
1896 |
sb.append(data[i] == null ? "?" : data[i]); |
|
1930 |
|
|
1931 |
boolean incomplete = readFields != null && !readFields.get(i); |
|
1932 |
|
|
1933 |
sb.append(data[i] == null ? incomplete ? "n/a" : "?" : data[i]); |
|
1897 | 1934 |
if (isChar) |
1898 | 1935 |
{ |
1899 | 1936 |
sb.append("\""); |
new/src/com/goldencode/p2j/persist/orm/BaseRecord.java 2022-11-09 18:38:23 +0000 | ||
---|---|---|
28 | 28 |
** used in a Java REST service, which must not be associated with a session. |
29 | 29 |
** ECF 20221005 Added support for dynamic initial values. |
30 | 30 |
** TJD 20220504 Upgrade do Java 11 minor changes |
31 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded as |
|
32 |
** 'partial/incomplete'. |
|
31 | 33 |
*/ |
32 | 34 | |
33 | 35 |
/* |
... | ... | |
128 | 130 |
/** The data represented by this record, in it's low-level (database friendly) form */ |
129 | 131 |
protected final Object[] data; |
130 | 132 |
|
133 |
/** For an incomplete record, this holds the fields which were read or assigned in this record. */ |
|
134 |
protected BitSet readFields = null; |
|
135 |
|
|
131 | 136 |
/** Current state of the record */ |
132 | 137 |
protected final RecordState state = new RecordState(); |
133 | 138 |
|
... | ... | |
785 | 790 |
} |
786 | 791 |
|
787 | 792 |
/** |
793 |
* Get the {@link #readFields}. |
|
794 |
* |
|
795 |
* @return The {@link #readFields}. |
|
796 |
*/ |
|
797 |
public BitSet _getReadFields() |
|
798 |
{ |
|
799 |
return readFields; |
|
800 |
} |
|
801 | ||
802 |
/** |
|
788 | 803 |
* Set a single datum into the data array, and update the record and property state accordingly. |
789 | 804 |
* |
790 | 805 |
* @param offset |
... | ... | |
794 | 809 |
*/ |
795 | 810 |
protected void setDatum(int offset, Object datum) |
796 | 811 |
{ |
812 |
if (readFields != null) |
|
813 |
{ |
|
814 |
readFields.set(offset); |
|
815 |
} |
|
816 | ||
797 | 817 |
if (activeBuffer != null) |
798 | 818 |
{ |
799 | 819 |
activeOffset = offset; |
... | ... | |
1015 | 1035 |
} |
1016 | 1036 |
|
1017 | 1037 |
/** |
1038 |
* Check if for an incomplete record, the specified field was read or assigned. |
|
1039 |
* |
|
1040 |
* @param index |
|
1041 |
* The field index. |
|
1042 |
*/ |
|
1043 |
protected void checkIncomplete(int index) |
|
1044 |
{ |
|
1045 |
if (checkState(DmoState.INCOMPLETE) && !readFields.get(index)) |
|
1046 |
{ |
|
1047 |
RecordMeta meta = _recordMeta(); |
|
1048 |
String tname = meta.legacyName; |
|
1049 |
String fname = meta.getPropertyMeta(false)[index].getLegacyName(); |
|
1050 |
String msg = "Field " + fname + " from " + tname + " record (recid " + primaryKey() + |
|
1051 |
") was missing from FIELDS phrase"; |
|
1052 |
|
|
1053 |
ErrorManager.recordOrThrowError(8826, msg, false); |
|
1054 |
} |
|
1055 |
} |
|
1056 | ||
1057 |
/** |
|
1058 |
* Mark this record as incomplete. |
|
1059 |
* |
|
1060 |
* @param session |
|
1061 |
* Database session (used for UNDO purposes). |
|
1062 |
*/ |
|
1063 |
void markIncomplete(Session session) |
|
1064 |
{ |
|
1065 |
updateState(session, DmoState.INCOMPLETE, true); |
|
1066 |
readFields = new BitSet(data.length); |
|
1067 |
} |
|
1068 | ||
1069 |
/** |
|
1018 | 1070 |
* An index was updated. |
1019 | 1071 |
* |
1020 | 1072 |
* @param idxUid |
... | ... | |
1225 | 1277 |
final int readProperty(int propOffset, ResultSet rs, int rsOffset) |
1226 | 1278 |
throws PersistenceException |
1227 | 1279 |
{ |
1280 |
return readProperty(propOffset, rs, rsOffset, false); |
|
1281 |
} |
|
1282 |
|
|
1283 |
/** |
|
1284 |
* Read a property directly from a {@code ResultSet}. |
|
1285 |
* |
|
1286 |
* @param propOffset |
|
1287 |
* The index of the property to be read. |
|
1288 |
* @param rs |
|
1289 |
* The result set to read from. |
|
1290 |
* @param rsOffset |
|
1291 |
* The index in {@code ResultSet} to read from. If the property is a multiple column |
|
1292 |
* then more than one column will ve read from {@code ResultSet}. |
|
1293 |
* @param skip |
|
1294 |
* Flag indicating if this property will not be read from the result-set. |
|
1295 |
* |
|
1296 |
* @return The number of columns actually read from the {@code ResultSet}. |
|
1297 |
*/ |
|
1298 |
final int readProperty(int propOffset, ResultSet rs, int rsOffset, boolean skip) |
|
1299 |
throws PersistenceException |
|
1300 |
{ |
|
1228 | 1301 |
PropertyMeta pm = _recordMeta().getPropertyMeta(false)[propOffset]; |
1229 | 1302 |
PropertyReader propertyReader = pm.getDataHandler(); |
1303 |
if (skip) |
|
1304 |
{ |
|
1305 |
return propertyReader.propertySize(); |
|
1306 |
} |
|
1230 | 1307 |
|
1231 | 1308 |
try |
1232 | 1309 |
{ |
1310 |
if (readFields != null) |
|
1311 |
{ |
|
1312 |
readFields.set(propOffset); |
|
1313 |
} |
|
1314 |
|
|
1233 | 1315 |
return propertyReader.readProperty(rs, rsOffset, data, propOffset); |
1234 | 1316 |
} |
1235 | 1317 |
catch (java.sql.SQLException sqle) |
new/src/com/goldencode/p2j/persist/orm/DmoState.java 2022-11-09 18:38:44 +0000 | ||
---|---|---|
2 | 2 |
** Module : DmoState.java |
3 | 3 |
** Abstract : DMO states which govern persistence behavior. |
4 | 4 |
** |
5 |
** Copyright (c) 2020-2021, Golden Code Development Corporation.
|
|
5 |
** Copyright (c) 2020-2022, Golden Code Development Corporation.
|
|
6 | 6 |
** |
7 | 7 |
** -#- -I- --Date-- ---------------------------------------Description--------------------------------------- |
8 | 8 |
** 001 ECF 20200315 First revision. |
9 | 9 |
** 002 ECF 20210519 Added TRACKED state. |
10 | 10 |
** ECF 20210923 Added DELETING state. |
11 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded as |
|
12 |
** 'partial/incomplete'. |
|
11 | 13 |
*/ |
12 | 14 | |
13 | 15 |
/* |
... | ... | |
110 | 112 |
|
111 | 113 |
/** DMO currently is being tracked for possible UNDO processing */ |
112 | 114 |
public static final int TRACKED = 0x0400; |
115 |
|
|
116 |
/** DMO is read incomplete (due to EXCEPT/FIELDS options) and can't be cached. */ |
|
117 |
public static final int INCOMPLETE = 0x0800; |
|
113 | 118 |
} |
new/src/com/goldencode/p2j/persist/orm/Loader.java 2022-11-09 18:39:09 +0000 | ||
---|---|---|
11 | 11 |
** (Temporary?) set the properties to be accessed with old-fashioned accessors. |
12 | 12 |
** CA 20201005 Fixed the sort order when loading a child extent table. |
13 | 13 |
** TJD 20220504 Upgrade do Java 11 minor changes |
14 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded as |
|
15 |
** 'partial/incomplete'. Extent fields are always loaded at this time. |
|
14 | 16 |
*/ |
15 | 17 | |
16 | 18 |
/* |
... | ... | |
323 | 325 |
* DMO implementation class. |
324 | 326 |
* @param id |
325 | 327 |
* Surrogate primary key. |
326 |
* |
|
328 |
*
|
|
327 | 329 |
* @return An instance of {@code dmoClass}, loaded with data from the database, or {@code |
328 | 330 |
* null} if the record was not found. The DMO is instantiated either way, but it will |
329 | 331 |
* be discarded if the record is not found. |
... | ... | |
334 | 336 |
<T extends BaseRecord> T load(Class<T> dmoClass, Long id) |
335 | 337 |
throws PersistenceException |
336 | 338 |
{ |
339 |
return load(dmoClass, id, null); |
|
340 |
} |
|
341 |
|
|
342 |
/** |
|
343 |
* Load a single record from the database, given its DMO class and primary key. The primary |
|
344 |
* key is assumed to represent a valid identifier; if not, {@code null} will be returned. |
|
345 |
* <p> |
|
346 |
* TODO: a SQL statement is prepared and closed for each load statement executed by this |
|
347 |
* method. We rely on statement caching at the connection pool level to make this performant, |
|
348 |
* but perhaps there is some predictive analysis we can do to make this more efficient. |
|
349 |
* |
|
350 |
* @param dmoClass |
|
351 |
* DMO implementation class. |
|
352 |
* @param id |
|
353 |
* Surrogate primary key. |
|
354 |
* @param partialFields |
|
355 |
* For an incomplete record, read only these fields. |
|
356 |
* |
|
357 |
* @return An instance of {@code dmoClass}, loaded with data from the database, or {@code |
|
358 |
* null} if the record was not found. The DMO is instantiated either way, but it will |
|
359 |
* be discarded if the record is not found. |
|
360 |
* |
|
361 |
* @throws PersistenceException |
|
362 |
* if an error occurred accessing the database. |
|
363 |
*/ |
|
364 |
<T extends BaseRecord> T load(Class<T> dmoClass, Long id, BitSet partialFields) |
|
365 |
throws PersistenceException |
|
366 |
{ |
|
337 | 367 |
try |
338 | 368 |
{ |
339 | 369 |
T dmo = dmoClass.getDeclaredConstructor().newInstance(); |
340 | 370 |
dmo.primaryKey(id); |
341 | 371 |
|
342 |
return load(dmo, id); |
|
372 |
return load(dmo, id, partialFields);
|
|
343 | 373 |
} |
344 | 374 |
catch (ReflectiveOperationException exc) |
345 | 375 |
{ |
... | ... | |
360 | 390 |
* DMO implementation class. |
361 | 391 |
* @param id |
362 | 392 |
* Surrogate primary key. |
393 |
* @param partialFields |
|
394 |
* For an incomplete record, read only these fields. |
|
363 | 395 |
* |
364 | 396 |
* @return An instance of {@code dmoClass}, loaded with data from the database, or {@code |
365 | 397 |
* null} if the record was not found. The DMO is instantiated either way, but it will |
... | ... | |
368 | 400 |
* @throws PersistenceException |
369 | 401 |
* if an error occurred accessing the database. |
370 | 402 |
*/ |
371 |
<T extends BaseRecord> T load(T dmo, Long id) |
|
403 |
<T extends BaseRecord> T load(T dmo, Long id, BitSet partialFields)
|
|
372 | 404 |
throws PersistenceException |
373 | 405 |
{ |
374 | 406 |
try |
... | ... | |
380 | 412 |
boolean hasScalarData = props.length > 0 && props[0].getExtent() == 0; |
381 | 413 |
|
382 | 414 |
int start = 0; |
415 |
if (partialFields != null) |
|
416 |
{ |
|
417 |
dmo.markIncomplete(session); |
|
418 |
} |
|
383 | 419 |
|
384 | 420 |
for (String sql : loadSql) |
385 | 421 |
{ |
... | ... | |
404 | 440 |
} |
405 | 441 |
|
406 | 442 |
// the first query is always on the primary table, if the DMO has scalar data |
407 |
start = readScalarData(dmo, rs, 0, props); |
|
443 |
start = readScalarData(dmo, rs, 0, props, partialFields);
|
|
408 | 444 |
} |
409 | 445 |
else |
410 | 446 |
{ |
... | ... | |
448 | 484 |
* use 0. If the record data is prepended by a single column (Ex: primaryKey), pass 1. |
449 | 485 |
* @param props |
450 | 486 |
* Array of property metadata objects. |
487 |
* @param partialFields |
|
488 |
* For an incomplete record, read only these fields. |
|
451 | 489 |
* |
452 | 490 |
* @return Index of next slot in the data array to be filled (if any) after this series of |
453 | 491 |
* scalar data has been read, or -1 to indicate the result set was empty (record |
... | ... | |
459 | 497 |
private static int readScalarData(BaseRecord r, |
460 | 498 |
ResultSet rs, |
461 | 499 |
int rowOffset, |
462 |
PropertyMeta[] props) |
|
500 |
PropertyMeta[] props, |
|
501 |
BitSet partialFields) |
|
463 | 502 |
throws SQLException, PersistenceException |
464 | 503 |
{ |
465 | 504 |
// the current row is expected to be already selected, since the filter is the primary |
... | ... | |
480 | 519 |
break; |
481 | 520 |
} |
482 | 521 |
|
483 |
int colCnt = r.readProperty(pm.offset, rs, pm.columnIndex + rowOffset); |
|
522 |
boolean skip = partialFields != null && !partialFields.get(pm.offset); |
|
523 |
int colCnt = r.readProperty(pm.offset, rs, pm.columnIndex + rowOffset, skip); |
|
484 | 524 |
|
485 | 525 |
if (colCnt != pm.columnCount) |
486 | 526 |
{ |
new/src/com/goldencode/p2j/persist/orm/PropertyReader.java 2022-11-09 18:39:36 +0000 | ||
---|---|---|
3 | 3 |
** Abstract : Functional interface for reading a (possibly multiple column) property from a SQL |
4 | 4 |
** result set. |
5 | 5 |
** |
6 |
** Copyright (c) 2020, Golden Code Development Corporation. |
|
6 |
** Copyright (c) 2020-2022, Golden Code Development Corporation.
|
|
7 | 7 |
** |
8 | 8 |
** -#- -I- --Date-- ---------------------------------Description--------------------------------- |
9 | 9 |
** 001 OM 20200212 Created initial version. |
10 |
** 002 CA 20221109 Added 'propertySize' method which returns the number of columns in the result-set for this |
|
11 |
** property type. |
|
10 | 12 |
*/ |
11 | 13 | |
12 | 14 |
/* |
... | ... | |
93 | 95 |
*/ |
94 | 96 |
public int readProperty(ResultSet rs, int rsOffset, Object[] data, int propIndex) |
95 | 97 |
throws SQLException; |
98 |
|
|
99 |
/** |
|
100 |
* Get the size (number of columns in the result-set) required by this property. |
|
101 |
* |
|
102 |
* @return Defaults to 1. |
|
103 |
*/ |
|
104 |
public default int propertySize() |
|
105 |
{ |
|
106 |
return 1; |
|
107 |
} |
|
96 | 108 |
} |
new/src/com/goldencode/p2j/persist/orm/RecordState.java 2022-11-09 18:39:43 +0000 | ||
---|---|---|
2 | 2 |
** Module : RecordState.java |
3 | 3 |
** Abstract : Current state of a database record in an application. |
4 | 4 |
** |
5 |
** Copyright (c) 2019-2021, Golden Code Development Corporation.
|
|
5 |
** Copyright (c) 2019-2022, Golden Code Development Corporation.
|
|
6 | 6 |
** |
7 | 7 |
** -#- -I- --Date-- ---------------------------------------Description--------------------------------------- |
8 | 8 |
** 001 ECF 20191001 First revision. Object which tracks DMO state as changes are applied to it. |
9 | 9 |
** 002 ECF 20210519 Added TRACKED state to toString. |
10 | 10 |
** ECF 20210924 Added DELETING state to toString. |
11 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded as |
|
12 |
** 'partial/incomplete'. |
|
11 | 13 |
*/ |
12 | 14 | |
13 | 15 |
/* |
... | ... | |
144 | 146 |
} |
145 | 147 |
if ((state & DmoState.TRACKED) == DmoState.TRACKED) |
146 | 148 |
{ |
147 |
sb.append("TRACKED"); |
|
149 |
sb.append("TRACKED "); |
|
150 |
} |
|
151 |
if ((state & DmoState.INCOMPLETE) == DmoState.INCOMPLETE) |
|
152 |
{ |
|
153 |
sb.append("INCOMPLETE"); |
|
148 | 154 |
} |
149 | 155 |
} |
150 | 156 |
sb.append("}"); |
new/src/com/goldencode/p2j/persist/orm/SQLQuery.java 2022-11-09 18:40:44 +0000 | ||
---|---|---|
33 | 33 |
** a wrapper for two separate parameters, changed hydrateRecordImpl logic to skip fields that are not |
34 | 34 |
** queried. |
35 | 35 |
** TJD 20220504 Upgrade do Java 11 minor changes |
36 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded as |
|
37 |
** 'partial/incomplete'. Extent fields are always loaded at this time. |
|
36 | 38 |
*/ |
37 | 39 | |
38 | 40 |
/* |
... | ... | |
755 | 757 |
try |
756 | 758 |
{ |
757 | 759 |
SIMPLE_QUERY_PROFILER.updateCacheMisses(resultSet, 1); |
758 |
r = recordClass.getDeclaredConstructor().newInstance();
|
|
760 |
r = recordClass.newInstance(); |
|
759 | 761 |
|
760 | 762 |
// see FqlToSqlConverter.expandAlias() |
761 | 763 |
Set<Map.Entry<String, Property>> allFieldEntries = dmoInfo.propsByName.entrySet(); |
... | ... | |
771 | 773 |
} |
772 | 774 |
int k = 0; |
773 | 775 |
int recOffset = 0; |
774 |
boolean hasExtents = false; |
|
776 |
// TODO: EXCEPT/FIELDS for extent fields; for now, load all extents |
|
777 |
boolean incomplete = fieldEntries != allFieldEntries; |
|
778 |
boolean hasExtents = incomplete; |
|
779 |
if (incomplete) |
|
780 |
{ |
|
781 |
r.markIncomplete(session); |
|
782 |
} |
|
775 | 783 |
|
776 | 784 |
for (Map.Entry<String, Property> prop : fieldEntries) |
777 | 785 |
{ |
new/src/com/goldencode/p2j/persist/orm/Session.java 2022-11-09 18:40:30 +0000 | ||
---|---|---|
25 | 25 |
** ECF 20211022 A cache expiration is not tracked as an undoable event. |
26 | 26 |
** ECF 20211230 Do not attempt to delete a newly created record that was never flushed to the database. |
27 | 27 |
** OM 20220628 Signature change for createSQLQuery() method. |
28 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded as |
|
29 |
** 'partial/incomplete'. Partial/Incomplete records are being cached, but when they try to |
|
30 |
** be loaded with no 'partial field information', the cached record is fully refreshed from |
|
31 |
** the database, and not just returned. |
|
28 | 32 |
*/ |
29 | 33 | |
30 | 34 |
/* |
... | ... | |
450 | 454 |
public <T extends BaseRecord> T get(Class<T> dmoImplClass, Long id) |
451 | 455 |
throws PersistenceException |
452 | 456 |
{ |
453 |
return getImpl(dmoImplClass, id, new RecordIdentifier<>(dmoImplClass.getName(), id)); |
|
457 |
return getImpl(dmoImplClass, id, new RecordIdentifier<>(dmoImplClass.getName(), id), null); |
|
458 |
} |
|
459 | ||
460 |
/** |
|
461 |
* Gets a specified record of a table. To lookup the record local cache is analyzed first. If |
|
462 |
* the record is not found in cache, the backing database is queried. If the specified record |
|
463 |
* is not found in database, {@code null} is returned. If the record was fetched from the |
|
464 |
* database at this moment, it is kept in cache for further access. |
|
465 |
* |
|
466 |
* @param <T> |
|
467 |
* The type of the record. |
|
468 |
* @param dmoImplClass |
|
469 |
* The class of the entity (class that implements the table's DMO interface). |
|
470 |
* @param id |
|
471 |
* The id/rowid of the record to be returned. |
|
472 |
* @param partialFields |
|
473 |
* For an incomplete record, read only these fields. |
|
474 |
* |
|
475 |
* @return The requested record, if found in cache or database and {@code null} otherwise. |
|
476 |
* |
|
477 |
* @throws PersistenceException |
|
478 |
* In case some error occurs in the process. |
|
479 |
*/ |
|
480 |
public <T extends BaseRecord> T get(Class<T> dmoImplClass, Long id, BitSet partialFields) |
|
481 |
throws PersistenceException |
|
482 |
{ |
|
483 |
return getImpl(dmoImplClass, id, new RecordIdentifier<>(dmoImplClass.getName(), id), partialFields); |
|
454 | 484 |
} |
455 | 485 |
|
456 | 486 |
/** |
... | ... | |
473 | 503 |
public <T extends BaseRecord> T get(RecordIdentifier<String> key) |
474 | 504 |
throws PersistenceException |
475 | 505 |
{ |
476 |
return getImpl(null, key.getRecordID(), key); |
|
506 |
return getImpl(null, key.getRecordID(), key, null);
|
|
477 | 507 |
} |
478 | 508 |
|
479 | 509 |
/** |
... | ... | |
497 | 527 |
public <T extends BaseRecord> T get(String entity, Long id) |
498 | 528 |
throws PersistenceException |
499 | 529 |
{ |
500 |
return getImpl(null, id, new RecordIdentifier<>(entity, id)); |
|
530 |
return getImpl(null, id, new RecordIdentifier<>(entity, id), null);
|
|
501 | 531 |
} |
502 | 532 |
|
503 | 533 |
/** |
... | ... | |
513 | 543 |
* @param key |
514 | 544 |
* The cache key of the entity. If {@code null} the returned value is {@code null}; |
515 | 545 |
* provided. |
546 |
* @param partialFields |
|
547 |
* For an incomplete record, read only these fields. |
|
516 | 548 |
* @param <T> |
517 | 549 |
* The compile type of the record to be returned. |
518 | 550 |
* |
... | ... | |
522 | 554 |
* @throws PersistenceException |
523 | 555 |
* if there is an error getting a JDBC connection from the data source. |
524 | 556 |
*/ |
525 |
private <T extends BaseRecord> T getImpl(Class<T> dmoImplClass, Long id, RecordIdentifier<String> key) |
|
557 |
private <T extends BaseRecord> T getImpl(Class<T> dmoImplClass, |
|
558 |
Long id, |
|
559 |
RecordIdentifier<String> key, |
|
560 |
BitSet partialFields) |
|
526 | 561 |
throws PersistenceException |
527 | 562 |
{ |
528 | 563 |
// quick out |
... | ... | |
544 | 579 |
|
545 | 580 |
T dmo = getCached(key); |
546 | 581 |
|
582 |
boolean refreshIncomplete = false; |
|
547 | 583 |
if (dmo != null) |
548 | 584 |
{ |
549 | 585 |
// assert that we have the right DMO type for this primary key |
... | ... | |
557 | 593 |
throw new PersistenceException(msg); |
558 | 594 |
} |
559 | 595 |
|
560 |
return dmo; |
|
596 |
if (dmo.checkState(DmoState.INCOMPLETE) && partialFields == null) |
|
597 |
{ |
|
598 |
// we have a cached record which is incomplete, and this requires to refresh the incomplete record |
|
599 |
// from the database |
|
600 |
refreshIncomplete = true; |
|
601 |
} |
|
602 |
else |
|
603 |
{ |
|
604 |
return dmo; |
|
605 |
} |
|
561 | 606 |
} |
562 | 607 |
|
563 | 608 |
if (dmoImplClass == null) |
... | ... | |
579 | 624 |
throw new PersistenceException("Invalid entity name: " + dmoImplClass.getName()); |
580 | 625 |
} |
581 | 626 |
|
582 |
dmo = loader.load(dmoImplClass, id); |
|
627 |
T loaded = loader.load(dmoImplClass, id, partialFields); |
|
628 |
if (refreshIncomplete) |
|
629 |
{ |
|
630 |
// evict the incomplete record from the cache, will be recached if one was found |
|
631 |
evict(dmo); |
|
632 |
|
|
633 |
if (loaded == null) |
|
634 |
{ |
|
635 |
dmo = null; |
|
636 |
} |
|
637 |
else |
|
638 |
{ |
|
639 |
System.arraycopy(loaded.data, 0, dmo.data, 0, dmo.data.length); |
|
640 | ||
641 |
// set the incomplete record as complete |
|
642 |
dmo.updateState(this, DmoState.INCOMPLETE, false); |
|
643 |
dmo.readFields = null; |
|
644 |
} |
|
645 |
} |
|
646 |
else |
|
647 |
{ |
|
648 |
dmo = loaded; |
|
649 |
} |
|
583 | 650 |
|
584 | 651 |
if (dmo != null) |
585 | 652 |
{ |
... | ... | |
962 | 1029 |
// ignore the cache and go directly to underlying database |
963 | 1030 |
// use the variant of load which overwrites the DMOs current field values with data |
964 | 1031 |
// fetched from the database |
965 |
T loaded = loader.load(dmo, dmo.id); |
|
1032 |
T loaded = loader.load(dmo, dmo.id, null);
|
|
966 | 1033 |
|
967 | 1034 |
RecordIdentifier<String> key = new RecordIdentifier<>(dmo.getClass().getName(), dmo.id); |
968 | 1035 |
|
new/src/com/goldencode/p2j/persist/orm/types/DatetimetzType.java 2022-11-09 18:41:02 +0000 | ||
---|---|---|
13 | 13 |
** CA 20210304 Fixed datetimetz literal parsing. |
14 | 14 |
** OM 20220218 Fixed timezone when the dtz is rehydrated. |
15 | 15 |
** ECF 20221005 Added support for dynamic initial values. |
16 |
** CA 20221109 Added 'propertySize' method which returns the number of columns in the result-set for this |
|
17 |
** property type. |
|
16 | 18 |
*/ |
17 | 19 | |
18 | 20 |
/* |
... | ... | |
180 | 182 |
} |
181 | 183 |
|
182 | 184 |
/** |
185 |
* Get the number of columns read from the result-set. |
|
186 |
* |
|
187 |
* @return always 2. |
|
188 |
*/ |
|
189 |
@Override |
|
190 |
public int propertySize() |
|
191 |
{ |
|
192 |
return 2; |
|
193 |
} |
|
194 |
|
|
195 |
/** |
|
183 | 196 |
* Instantiate a low-level initial value suitable for storage in a {@link BaseRecord}'s data |
184 | 197 |
* array, parsed from a string representation. |
185 | 198 |
* |