Coverage Report - org.apache.ojb.broker.util.sequence.SequenceManagerHelper
 
Classes in this File Line Coverage Branch Coverage Complexity
SequenceManagerHelper
N/A
N/A
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  
 }