Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
SequenceManagerHelper |
|
| 4.285714285714286;4.286 |
1 | package org.apache.ojb.broker.util.sequence; | |
2 | ||
3 | /* Copyright 2002-2005 The Apache Software Foundation | |
4 | * | |
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | * you may not use this file except in compliance with the License. | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | import java.sql.ResultSet; | |
19 | import java.sql.Statement; | |
20 | import java.util.Collection; | |
21 | import java.util.Iterator; | |
22 | import java.util.Properties; | |
23 | import java.util.Vector; | |
24 | ||
25 | import org.apache.ojb.broker.PersistenceBroker; | |
26 | import org.apache.ojb.broker.PersistenceBrokerException; | |
27 | import org.apache.ojb.broker.accesslayer.StatementManagerIF; | |
28 | import org.apache.ojb.broker.metadata.ClassDescriptor; | |
29 | import org.apache.ojb.broker.metadata.FieldDescriptor; | |
30 | import org.apache.ojb.broker.query.Query; | |
31 | import org.apache.ojb.broker.util.logging.Logger; | |
32 | import org.apache.ojb.broker.util.logging.LoggerFactory; | |
33 | ||
34 | /** | |
35 | * Helper class for SequenceManager implementations. | |
36 | * | |
37 | * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a> | |
38 | * @version $Id: SequenceManagerHelper.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $ | |
39 | */ | |
40 | public class SequenceManagerHelper | |
41 | { | |
42 | private static Logger log = LoggerFactory.getLogger(SequenceManagerHelper.class); | |
43 | ||
44 | /** | |
45 | * Property name used to configure sequence manager implementations. | |
46 | */ | |
47 | public static final String PROP_SEQ_AS = "seq.as"; | |
48 | /** | |
49 | * Property name used to configure sequence manager implementations. | |
50 | * @deprecated use {@link #PROP_SEQ_START} instead. | |
51 | */ | |
52 | public static final String PROP_SEQ_START_OLD = "sequenceStart"; | |
53 | /** | |
54 | * Property name used to configure sequence manager implementations. | |
55 | */ | |
56 | public static final String PROP_SEQ_START = "seq.start"; | |
57 | /** | |
58 | * Property name used to configure sequence manager implementations. | |
59 | */ | |
60 | public static final String PROP_SEQ_INCREMENT_BY = "seq.incrementBy"; | |
61 | /** | |
62 | * Property name used to configure sequence manager implementations. | |
63 | */ | |
64 | public static final String PROP_SEQ_MAX_VALUE = "seq.maxValue"; | |
65 | /** | |
66 | * Property name used to configure sequence manager implementations. | |
67 | */ | |
68 | public static final String PROP_SEQ_MIN_VALUE = "seq.minValue"; | |
69 | /** | |
70 | * Property name used to configure sequence manager implementations. | |
71 | */ | |
72 | public static final String PROP_SEQ_CYCLE = "seq.cycle"; | |
73 | /** | |
74 | * Property name used to configure sequence manager implementations. | |
75 | */ | |
76 | public static final String PROP_SEQ_CACHE = "seq.cache"; | |
77 | /** | |
78 | * Property name used to configure sequence manager implementations. | |
79 | */ | |
80 | public static final String PROP_SEQ_ORDER = "seq.order"; | |
81 | ||
82 | private static final String SEQ_PREFIX = "SEQ_"; | |
83 | private static final String SEQ_UNASSIGNED = "UNASSIGNED"; | |
84 | private static final String SM_SELECT_MAX = "SELECT MAX("; | |
85 | private static final String SM_FROM = ") FROM "; | |
86 | ||
87 | /** | |
88 | * Prefix for global sequence names. | |
89 | */ | |
90 | ||
91 | /** | |
92 | * Returns a unique sequence name (unique across all extents). | |
93 | * <br/> | |
94 | * If we found a non null value for the 'sequence-name' attribute in | |
95 | * the field descriptor, we use the 'sequence-name' value as sequence name. | |
96 | * <br/> | |
97 | * Else if the top-level class of the target class has extents, | |
98 | * we take the first extent class table name of the extents as | |
99 | * sequence name. | |
100 | * <br/> | |
101 | * Else we take the table name of the target class. | |
102 | * <p> | |
103 | * If the method argument 'autoNaming' is true, the generated | |
104 | * sequence name will be set in the given field descriptor | |
105 | * using {@link org.apache.ojb.broker.metadata.FieldDescriptor#setSequenceName} | |
106 | * to speed up sequence name lookup in future calls. | |
107 | * </p> | |
108 | * @param brokerForClass current used PB instance | |
109 | * @param field target field | |
110 | * @param autoNaming if 'false' no auto sequence name was build and | |
111 | * a exception was throw if none could be found in field. | |
112 | */ | |
113 | public static String buildSequenceName(PersistenceBroker brokerForClass, | |
114 | FieldDescriptor field, boolean autoNaming) | |
115 | throws SequenceManagerException | |
116 | { | |
117 | String seqName = field.getSequenceName(); | |
118 | /* | |
119 | if we found a sequence name bound to the field descriptor | |
120 | via 'sequence-name' attribute we use that name | |
121 | */ | |
122 | if (seqName != null && seqName.trim().length() != 0) | |
123 | { | |
124 | return seqName; | |
125 | } | |
126 | else if (!autoNaming) | |
127 | { | |
128 | /* | |
129 | arminw: | |
130 | we don't find a sequence name and we should not automatic build one, | |
131 | thus we throw an exception | |
132 | */ | |
133 | throw new SequenceManagerException("Could not find sequence-name for field '" + | |
134 | field + "' of class '" + field.getClassDescriptor().getClassNameOfObject() + | |
135 | "', property 'autoNaming' in sequence-manager element in repository was '" + | |
136 | autoNaming + "'. Set autoNaming true in sequence-descriptor or define a " + | |
137 | " sequence-name in field-descriptor."); | |
138 | } | |
139 | ||
140 | ClassDescriptor cldTargetClass = field.getClassDescriptor(); | |
141 | /* | |
142 | check for inheritance on multiple table | |
143 | */ | |
144 | cldTargetClass = findInheritanceRoot(cldTargetClass); | |
145 | Class topLevel = brokerForClass.getTopLevelClass(cldTargetClass.getClassOfObject()); | |
146 | ClassDescriptor cldTopLevel = brokerForClass.getClassDescriptor(topLevel); | |
147 | /** | |
148 | * | |
149 | * MBAIRD | |
150 | * Should not use classname for the sequenceName as we will end up | |
151 | * re-using sequence numbers for classes mapped to the same table. | |
152 | * Instead, make the FullTableName the discriminator since it will | |
153 | * always be unique for that table, and hence that class. | |
154 | * | |
155 | * arminw: | |
156 | * If the found top-level class has extents, we take the first | |
157 | * found extent class table name as sequence name. Else we take | |
158 | * the table name of the 'targetClass'. | |
159 | * | |
160 | */ | |
161 | if (cldTopLevel.isExtent()) | |
162 | { | |
163 | /* | |
164 | arminw: | |
165 | this is a little critical, because we do not know if the extent classes | |
166 | will change by and by and the first found extent class may change, thus the | |
167 | returned table name could change! | |
168 | But I don't know a way to resolve this problem. I put a comment to the | |
169 | sequence manager docs | |
170 | TODO: find better solution | |
171 | */ | |
172 | // seqName = brokerForClass.getClassDescriptor(((Class) cldTopLevel.getExtentClasses(). | |
173 | // get(0))).getFullTableName(); | |
174 | seqName = firstFoundTableName(brokerForClass, cldTopLevel); | |
175 | } | |
176 | else | |
177 | { | |
178 | seqName = cldTargetClass.getFullTableName(); | |
179 | } | |
180 | // log.info("* targetClass: "+targetClass +", toplevel: "+topLevel+ " seqName: "+seqName); | |
181 | if (seqName == null) | |
182 | { | |
183 | seqName = SEQ_UNASSIGNED; | |
184 | log.warn("Too complex structure, can not assign automatic sequence name for field '" + | |
185 | field.getAttributeName() + "' in class '" + | |
186 | field.getClassDescriptor().getClassNameOfObject() + | |
187 | "'. Use a default sequence name instead: " + (SEQ_PREFIX + seqName)); | |
188 | } | |
189 | // System.out.println("* targetClass: " + cldTargetClass.getClassNameOfObject() + ", toplevel: " + topLevel + " seqName: " + seqName); | |
190 | seqName = SEQ_PREFIX + seqName; | |
191 | if (log.isDebugEnabled()) | |
192 | log.debug("Set automatic generated sequence-name for field '" + | |
193 | field.getAttributeName() + "' in class '" + | |
194 | field.getClassDescriptor().getClassNameOfObject() + | |
195 | "'."); | |
196 | field.setSequenceName(seqName); | |
197 | return seqName; | |
198 | } | |
199 | ||
200 | /** | |
201 | * Returns the root {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the inheriatance | |
202 | * hierachy of the given descriptor or the descriptor itself if no inheriatance on multiple table is | |
203 | * used. | |
204 | */ | |
205 | private static ClassDescriptor findInheritanceRoot(ClassDescriptor cld) | |
206 | { | |
207 | ClassDescriptor result = cld; | |
208 | if(cld.getSuperClassDescriptor() != null) | |
209 | { | |
210 | result = findInheritanceRoot(cld.getSuperClassDescriptor()); | |
211 | } | |
212 | return result; | |
213 | } | |
214 | ||
215 | /** | |
216 | * try to find the first none null table name for the given class-descriptor. | |
217 | * If cld has extent classes, all of these cld's searched for the first none null | |
218 | * table name. | |
219 | */ | |
220 | private static String firstFoundTableName(PersistenceBroker brokerForClass, ClassDescriptor cld) | |
221 | { | |
222 | String name = null; | |
223 | if (!cld.isInterface() && cld.getFullTableName() != null) | |
224 | { | |
225 | return cld.getFullTableName(); | |
226 | } | |
227 | if (cld.isExtent()) | |
228 | { | |
229 | Collection extentClasses = cld.getExtentClasses(); | |
230 | for (Iterator iterator = extentClasses.iterator(); iterator.hasNext();) | |
231 | { | |
232 | name = firstFoundTableName(brokerForClass, brokerForClass.getClassDescriptor((Class) iterator.next())); | |
233 | // System.out.println("## " + cld.getClassNameOfObject()+" - name: "+name); | |
234 | if (name != null) break; | |
235 | } | |
236 | } | |
237 | return name; | |
238 | } | |
239 | ||
240 | /** | |
241 | * Lookup all tables associated with given class (search all extent classes) | |
242 | * to find the current maximum value for the given field. | |
243 | * <br><b>Note:</b> Only works for <code>long</code> autoincrement fields. | |
244 | * @param brokerForClass persistence broker instance match the database of the | |
245 | * given field/class | |
246 | * @param field the target field | |
247 | */ | |
248 | public static long getMaxForExtent(PersistenceBroker brokerForClass, FieldDescriptor field) throws PersistenceBrokerException | |
249 | { | |
250 | if (field == null) | |
251 | { | |
252 | log.error("Given FieldDescriptor was null, could not detect max value across all extents"); | |
253 | return 0; | |
254 | // throw new PersistenceBrokerException("Given FieldDescriptor was null"); | |
255 | } | |
256 | // first lookup top-level class | |
257 | Class topLevel = brokerForClass.getTopLevelClass(field.getClassDescriptor().getClassOfObject()); | |
258 | return getMaxId(brokerForClass, topLevel, field); | |
259 | } | |
260 | ||
261 | /** | |
262 | * Search down all extent classes and return max of all found | |
263 | * PK values. | |
264 | */ | |
265 | public static long getMaxId(PersistenceBroker brokerForClass, Class topLevel, FieldDescriptor original) throws PersistenceBrokerException | |
266 | { | |
267 | long max = 0; | |
268 | long tmp; | |
269 | ClassDescriptor cld = brokerForClass.getClassDescriptor(topLevel); | |
270 | ||
271 | // if class is not an interface / not abstract we have to search its directly mapped table | |
272 | if (!cld.isInterface() && !cld.isAbstract()) | |
273 | { | |
274 | tmp = getMaxIdForClass(brokerForClass, cld, original); | |
275 | if (tmp > max) | |
276 | { | |
277 | max = tmp; | |
278 | } | |
279 | } | |
280 | // if class is an extent we have to search through its subclasses | |
281 | if (cld.isExtent()) | |
282 | { | |
283 | Vector extentClasses = cld.getExtentClasses(); | |
284 | for (int i = 0; i < extentClasses.size(); i++) | |
285 | { | |
286 | Class extentClass = (Class) extentClasses.get(i); | |
287 | if (cld.getClassOfObject().equals(extentClass)) | |
288 | { | |
289 | throw new PersistenceBrokerException("Circular extent in " + extentClass + | |
290 | ", please check the repository"); | |
291 | } | |
292 | else | |
293 | { | |
294 | // fix by Mark Rowell | |
295 | // Call recursive | |
296 | tmp = getMaxId(brokerForClass, extentClass, original); | |
297 | } | |
298 | if (tmp > max) | |
299 | { | |
300 | max = tmp; | |
301 | } | |
302 | } | |
303 | } | |
304 | return max; | |
305 | } | |
306 | ||
307 | /** | |
308 | * lookup current maximum value for a single field in | |
309 | * table the given class descriptor was associated. | |
310 | */ | |
311 | public static long getMaxIdForClass( | |
312 | PersistenceBroker brokerForClass, ClassDescriptor cldForOriginalOrExtent, FieldDescriptor original) | |
313 | throws PersistenceBrokerException | |
314 | { | |
315 | FieldDescriptor field = null; | |
316 | if (!original.getClassDescriptor().equals(cldForOriginalOrExtent)) | |
317 | { | |
318 | // check if extent match not the same table | |
319 | if (!original.getClassDescriptor().getFullTableName().equals( | |
320 | cldForOriginalOrExtent.getFullTableName())) | |
321 | { | |
322 | // we have to look for id's in extent class table | |
323 | field = cldForOriginalOrExtent.getFieldDescriptorByName(original.getAttributeName()); | |
324 | } | |
325 | } | |
326 | else | |
327 | { | |
328 | field = original; | |
329 | } | |
330 | if (field == null) | |
331 | { | |
332 | // if null skip this call | |
333 | return 0; | |
334 | } | |
335 | ||
336 | String column = field.getColumnName(); | |
337 | long result = 0; | |
338 | ResultSet rs = null; | |
339 | Statement stmt = null; | |
340 | StatementManagerIF sm = brokerForClass.serviceStatementManager(); | |
341 | String table = cldForOriginalOrExtent.getFullTableName(); | |
342 | // String column = cld.getFieldDescriptorByName(fieldName).getColumnName(); | |
343 | String sql = SM_SELECT_MAX + column + SM_FROM + table; | |
344 | try | |
345 | { | |
346 | //lookup max id for the current class | |
347 | stmt = sm.getGenericStatement(cldForOriginalOrExtent, Query.NOT_SCROLLABLE); | |
348 | rs = stmt.executeQuery(sql); | |
349 | rs.next(); | |
350 | result = rs.getLong(1); | |
351 | } | |
352 | catch (Exception e) | |
353 | { | |
354 | log.warn("Cannot lookup max value from table " + table + " for column " + column + | |
355 | ", PB was " + brokerForClass + ", using jdbc-descriptor " + | |
356 | brokerForClass.serviceConnectionManager().getConnectionDescriptor(), e); | |
357 | } | |
358 | finally | |
359 | { | |
360 | try | |
361 | { | |
362 | sm.closeResources(stmt, rs); | |
363 | } | |
364 | catch (Exception ignore) | |
365 | { | |
366 | // ignore it | |
367 | } | |
368 | } | |
369 | return result; | |
370 | } | |
371 | ||
372 | /** | |
373 | * Database sequence properties helper method. | |
374 | * Return sequence <em>start value</em> or <em>null</em> | |
375 | * if not set. | |
376 | * | |
377 | * @param prop The {@link java.util.Properties} instance to use. | |
378 | * @return The found expression or <em>null</em>. | |
379 | */ | |
380 | public static Long getSeqStart(Properties prop) | |
381 | { | |
382 | String result = prop.getProperty(PROP_SEQ_START, null); | |
383 | if(result == null) | |
384 | { | |
385 | result = prop.getProperty(PROP_SEQ_START_OLD, null); | |
386 | } | |
387 | if(result != null) | |
388 | { | |
389 | return new Long(Long.parseLong(result)); | |
390 | } | |
391 | else | |
392 | { | |
393 | return null; | |
394 | } | |
395 | } | |
396 | ||
397 | /** | |
398 | * Database sequence properties helper method. | |
399 | * Return sequence <em>increment by value</em> or <em>null</em> | |
400 | * if not set. | |
401 | * | |
402 | * @param prop The {@link java.util.Properties} instance to use. | |
403 | * @return The found expression or <em>null</em>. | |
404 | */ | |
405 | public static Long getSeqIncrementBy(Properties prop) | |
406 | { | |
407 | String result = prop.getProperty(PROP_SEQ_INCREMENT_BY, null); | |
408 | if(result != null) | |
409 | { | |
410 | return new Long(Long.parseLong(result)); | |
411 | } | |
412 | else | |
413 | { | |
414 | return null; | |
415 | } | |
416 | } | |
417 | ||
418 | /** | |
419 | * Database sequence properties helper method. | |
420 | * Return sequence <em>max value</em> or <em>null</em> | |
421 | * if not set. | |
422 | * | |
423 | * @param prop The {@link java.util.Properties} instance to use. | |
424 | * @return The found expression or <em>null</em>. | |
425 | */ | |
426 | public static Long getSeqMaxValue(Properties prop) | |
427 | { | |
428 | String result = prop.getProperty(PROP_SEQ_MAX_VALUE, null); | |
429 | if(result != null) | |
430 | { | |
431 | return new Long(Long.parseLong(result)); | |
432 | } | |
433 | else | |
434 | { | |
435 | return null; | |
436 | } | |
437 | } | |
438 | ||
439 | /** | |
440 | * Database sequence properties helper method. | |
441 | * Return sequence <em>min value</em> or <em>null</em> | |
442 | * if not set. | |
443 | * | |
444 | * @param prop The {@link java.util.Properties} instance to use. | |
445 | * @return The found expression or <em>null</em>. | |
446 | */ | |
447 | public static Long getSeqMinValue(Properties prop) | |
448 | { | |
449 | String result = prop.getProperty(PROP_SEQ_MIN_VALUE, null); | |
450 | if(result != null) | |
451 | { | |
452 | return new Long(Long.parseLong(result)); | |
453 | } | |
454 | else | |
455 | { | |
456 | return null; | |
457 | } | |
458 | } | |
459 | ||
460 | /** | |
461 | * Database sequence properties helper method. | |
462 | * Return sequence <em>cache value</em> or <em>null</em> | |
463 | * if not set. | |
464 | * | |
465 | * @param prop The {@link java.util.Properties} instance to use. | |
466 | * @return The found expression or <em>null</em>. | |
467 | */ | |
468 | public static Long getSeqCacheValue(Properties prop) | |
469 | { | |
470 | String result = prop.getProperty(PROP_SEQ_CACHE, null); | |
471 | if(result != null) | |
472 | { | |
473 | return new Long(Long.parseLong(result)); | |
474 | } | |
475 | else | |
476 | { | |
477 | return null; | |
478 | } | |
479 | } | |
480 | ||
481 | /** | |
482 | * Database sequence properties helper method. | |
483 | * Return sequence <em>cycle</em> Booelan or <em>null</em> | |
484 | * if not set. | |
485 | * | |
486 | * @param prop The {@link java.util.Properties} instance to use. | |
487 | * @return The found expression or <em>null</em>. | |
488 | */ | |
489 | public static Boolean getSeqCycleValue(Properties prop) | |
490 | { | |
491 | String result = prop.getProperty(PROP_SEQ_CYCLE, null); | |
492 | if(result != null) | |
493 | { | |
494 | return Boolean.valueOf(result); | |
495 | } | |
496 | else | |
497 | { | |
498 | return null; | |
499 | } | |
500 | } | |
501 | ||
502 | /** | |
503 | * Database sequence properties helper method. | |
504 | * Return sequence <em>order</em> Booelan or <em>null</em> | |
505 | * if not set. | |
506 | * | |
507 | * @param prop The {@link java.util.Properties} instance to use. | |
508 | * @return The found expression or <em>null</em>. | |
509 | */ | |
510 | public static Boolean getSeqOrderValue(Properties prop) | |
511 | { | |
512 | String result = prop.getProperty(PROP_SEQ_ORDER, null); | |
513 | if(result != null) | |
514 | { | |
515 | return Boolean.valueOf(result); | |
516 | } | |
517 | else | |
518 | { | |
519 | return null; | |
520 | } | |
521 | } | |
522 | ||
523 | /** | |
524 | * Database sequence properties helper method. | |
525 | * Return the datatype to set for the sequence or <em>null</em> | |
526 | * if not set. | |
527 | * | |
528 | * @param prop The {@link java.util.Properties} instance to use. | |
529 | * @return The found expression or <em>null</em>. | |
530 | */ | |
531 | public static String getSeqAsValue(Properties prop) | |
532 | { | |
533 | return prop.getProperty(PROP_SEQ_AS, null); | |
534 | } | |
535 | } |