Coverage Report - org.apache.ojb.broker.util.sequence.SequenceManagerNativeImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
SequenceManagerNativeImpl
N/A
N/A
3.125
 
 1  
 package org.apache.ojb.broker.util.sequence;
 2  
 
 3  
 /* Copyright 2003-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.SQLException;
 20  
 import java.sql.Statement;
 21  
 
 22  
 import org.apache.commons.logging.Log;
 23  
 import org.apache.commons.logging.LogFactory;
 24  
 import org.apache.ojb.broker.PersistenceBroker;
 25  
 import org.apache.ojb.broker.accesslayer.JdbcAccess;
 26  
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 27  
 import org.apache.ojb.broker.metadata.FieldDescriptor;
 28  
 import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
 29  
 
 30  
 
 31  
 /**
 32  
  * Sequence manager implementation using native database <tt>Identity columns</tt>
 33  
  * (like MySQL, MSSQL, ...). For proper work some specific metadata settings
 34  
  * needed:
 35  
  * <ul>
 36  
  * <li>field representing the identity column need attribute <code>autoincrement</code> 'true'</li>
 37  
  * <li>field representing the identity column need attribute <code>access</code> set 'readonly'</li>
 38  
  * <li>field representing the identity column need attribute <code>primarykey</code> set 'true'</li>
 39  
  * <li>only possible to declare one identity field per class</li>
 40  
  * </ul>
 41  
  * <p/>
 42  
  * <b>Note:</b>
 43  
  * Make sure that the DB generated identity columns represent values &gt 0, because negative values
 44  
  * intern used by this implementation and 0 could cause problems with primitive FK fields.
 45  
  * </p>
 46  
  * <p/>
 47  
  * Implementation configuration properties:
 48  
  * <table cellspacing="2" cellpadding="2" border="3" frame="box">
 49  
  * <tr>
 50  
  * <td><strong>Property Key</strong></td>
 51  
  * <td><strong>Property Values</strong></td>
 52  
  * </tr>
 53  
  * <tr>
 54  
  * <td>no properties to set</td>
 55  
  * <td>
 56  
  * <p/>
 57  
  * </td>
 58  
  * </tr>
 59  
  * </table>
 60  
  * </p>
 61  
  * <p/>
 62  
  * <p/>
 63  
  * <b>Limitations:</b>
 64  
  * <ul>
 65  
  * <li>Native key generation is not 'extent aware'
 66  
  * when extent classes span several tables! Please
 67  
  * see more in shipped docs 'extents and polymorphism'
 68  
  * or sequence manager docs.
 69  
  * </li>
 70  
  * <li>
 71  
  * Only positive identity values are allowed (see above).
 72  
  * </li>
 73  
  * </ul>
 74  
  * </p>
 75  
  * <br/>
 76  
  * <br/>
 77  
  *
 78  
  * @author <a href="mailto:travis@spaceprogram.com">Travis Reeder</a>
 79  
  * @author <a href="mailto:arminw@apache.org">Armin Waibel</a>
 80  
  * @version $Id: SequenceManagerNativeImpl.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
 81  
  */
 82  
 public class SequenceManagerNativeImpl extends AbstractSequenceManager
 83  
 {
 84  
     private Log log = LogFactory.getLog(SequenceManagerNativeImpl.class);
 85  
 
 86  
     /*
 87  
      TODO:
 88  
      1. Find a better solution (if possible) for this problem
 89  
      We need this dummy field to return a negative long value
 90  
      on getUniqueLong(...) call. If we return always the same
 91  
      value, the resulting Identity object was found on cache.
 92  
 
 93  
      2. Problem is that generated oid (by Identity column)
 94  
      must not begin with 0.
 95  
 
 96  
      Use keyword 'volatile' to make decrement of a long value an
 97  
      atomic operation
 98  
      */
 99  
     private static volatile long tempKey = -1;
 100  
 
 101  
     public SequenceManagerNativeImpl(PersistenceBroker broker)
 102  
     {
 103  
         super(broker);
 104  
     }
 105  
 
 106  
     public void afterStore(JdbcAccess dbAccess, ClassDescriptor cld, Object obj) throws SequenceManagerException
 107  
     {
 108  
         FieldDescriptor identityField = extractIdentityColumnField(cld);
 109  
         if(identityField != null)
 110  
         {
 111  
             ifNotReadOnlyFail(identityField);
 112  
             long newId = getLastInsert(cld, identityField);
 113  
             setFieldValue(obj, identityField, new Long(newId));
 114  
         }
 115  
     }
 116  
 
 117  
     /**
 118  
      * Gets the identity column descriptor for the given class
 119  
      * or return <code>null</code> if none defined.
 120  
      *
 121  
      * @param cld The class descriptor
 122  
      * @return The class's identity column or <code>null</code> if it does not have one
 123  
      */
 124  
     private FieldDescriptor extractIdentityColumnField(ClassDescriptor cld)
 125  
     {
 126  
         FieldDescriptor[] pkFields = cld.getPkFields();
 127  
         for(int i = 0; i < pkFields.length; i++)
 128  
         {
 129  
             // to find the identity column we search for a autoincrement
 130  
             // read-only field
 131  
             if(pkFields[i].isAutoIncrement() && pkFields[i].isAccessReadOnly())
 132  
             {
 133  
                 return pkFields[i];
 134  
             }
 135  
         }
 136  
         return null;
 137  
     }
 138  
 
 139  
     private void ifNotReadOnlyFail(FieldDescriptor field) throws SequenceManagerException
 140  
     {
 141  
         // is field declared as read-only?
 142  
         if(!field.isAccessReadOnly())
 143  
         {
 144  
             throw new SequenceManagerException("Can't find Identity column: Identity columns/fields need to be declared as" +
 145  
                     " 'autoincrement' with 'readonly' access in field-descriptor");
 146  
         }
 147  
     }
 148  
 
 149  
     private long getLastInsert(ClassDescriptor cld, FieldDescriptor field) throws SequenceManagerException
 150  
     {
 151  
         long newId = 0;
 152  
         Statement stmt = null;
 153  
         if(field != null)
 154  
         { // an autoinc column exists
 155  
             try
 156  
             {
 157  
                 stmt = getBrokerForClass().serviceConnectionManager().getConnection().createStatement();
 158  
                 ResultSet rs = stmt.executeQuery(lastInsertSelect(cld.getFullTableName()));
 159  
                 if(!rs.next())
 160  
                 {
 161  
                     throw new SequenceManagerException("Could not find native identifier");
 162  
                 }
 163  
                 newId = rs.getLong(1);
 164  
                 rs.close();
 165  
                 if(log.isDebugEnabled()) log.debug("After store - newid=" + newId);
 166  
             }
 167  
             catch(Exception e)
 168  
             {
 169  
                 throw new SequenceManagerException(e);
 170  
             }
 171  
             finally
 172  
             {
 173  
                 try
 174  
                 {
 175  
                     if(stmt != null) stmt.close();
 176  
                 }
 177  
                 catch(SQLException e)
 178  
                 {
 179  
                     if(log.isDebugEnabled())
 180  
                         log.debug("Threw SQLException while in getLastInsert and closing stmt", e);
 181  
                     // ignore it
 182  
                 }
 183  
             }
 184  
         }
 185  
         else
 186  
         {
 187  
             throw new SequenceManagerException("No autoincrement field declared, please check repository for " + cld);
 188  
         }
 189  
         return newId;
 190  
     }
 191  
 
 192  
     /*
 193  
      * query for the last insert id.
 194  
      */
 195  
     protected String lastInsertSelect(String tableName)
 196  
     {
 197  
         return getBrokerForClass().serviceConnectionManager().
 198  
                 getSupportedPlatform().getLastInsertIdentityQuery(tableName);
 199  
     }
 200  
 
 201  
     private void setFieldValue(Object obj, FieldDescriptor field, Long identifier) throws SequenceManagerException
 202  
     {
 203  
         Object result = field.getJdbcType().sequenceKeyConversion(identifier);
 204  
         result = field.getFieldConversion().sqlToJava(result);
 205  
         PersistentField pf = field.getPersistentField();
 206  
         pf.set(obj, result);
 207  
     }
 208  
 
 209  
     /**
 210  
      * returns a negative value
 211  
      */
 212  
     protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
 213  
     {
 214  
         /*
 215  
         arminw:
 216  
         workaround for locking problems of new objects
 217  
         We need unique 'dummy keys' for new objects before storing.
 218  
         Variable 'tempKey' is declared volatile, thus decrement should be atomic
 219  
         */
 220  
         return --tempKey;
 221  
     }
 222  
 }