KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > util > sequence > SequenceManagerNativeImpl


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 JavaDoc;
19 import java.sql.SQLException JavaDoc;
20 import java.sql.Statement JavaDoc;
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.18.2.4 2005/12/21 22:28:41 tomdz 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 JavaDoc 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 JavaDoc(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 JavaDoc stmt = null;
153         if(field != null)
154         { // an autoinc column exists
155
try
156             {
157                 stmt = getBrokerForClass().serviceConnectionManager().getConnection().createStatement();
158                 ResultSet JavaDoc 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 JavaDoc e)
168             {
169                 throw new SequenceManagerException(e);
170             }
171             finally
172             {
173                 try
174                 {
175                     if(stmt != null) stmt.close();
176                 }
177                 catch(SQLException JavaDoc 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 JavaDoc lastInsertSelect(String JavaDoc tableName)
196     {
197         return getBrokerForClass().serviceConnectionManager().
198                 getSupportedPlatform().getLastInsertIdentityQuery(tableName);
199     }
200
201     private void setFieldValue(Object JavaDoc obj, FieldDescriptor field, Long JavaDoc identifier) throws SequenceManagerException
202     {
203         Object JavaDoc 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 }
223
Popular Tags