001 package org.apache.ojb.broker.util.sequence;
002
003 /* Copyright 2002-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import java.sql.ResultSet;
019 import java.sql.Statement;
020 import java.sql.PreparedStatement;
021
022 import org.apache.commons.lang.SystemUtils;
023 import org.apache.ojb.broker.PersistenceBroker;
024 import org.apache.ojb.broker.accesslayer.StatementManagerIF;
025 import org.apache.ojb.broker.metadata.ClassDescriptor;
026 import org.apache.ojb.broker.metadata.FieldDescriptor;
027 import org.apache.ojb.broker.query.Query;
028 import org.apache.ojb.broker.util.logging.Logger;
029 import org.apache.ojb.broker.util.logging.LoggerFactory;
030
031 /**
032 * This SequenceManager implementation uses database
033 * sequence key generation (e.g supported by
034 * Oracle, SAP DB, PostgreSQL, ...).
035 * This class is responsible for creating new unique ID's.
036 * <br/>
037 * It is possible to define a <code>sequence-name</code>
038 * field-descriptor attribute in the repository file. If
039 * such an attribute was not found, the implementation build
040 * an extent aware sequence name by its own.
041 * <br/>
042 * Keep in mind when define a sequence name, that you are responsible
043 * to be aware of extents, that is: if you ask for an uid for an
044 * interface with several
045 * implementor classes, or a baseclass with several subclasses the returned
046 * uid have to be unique accross all tables representing objects of the
047 * extent in question. Thus you have to use the same <code>sequence-name</code>
048 * for all extents.
049 *
050 * <p>
051 * Implementation configuration properties:
052 * </p>
053 *
054 * <table cellspacing="2" cellpadding="2" border="3" frame="box">
055 * <tr>
056 * <td><strong>Property Key</strong></td>
057 * <td><strong>Property Values</strong></td>
058 * </tr>
059 * <tr>
060 * <td>autoNaming</td>
061 * <td>
062 * Default was 'true'. If set 'true' OJB try to build a
063 * sequence name automatic if none found in field-descriptor
064 * and set this generated name as <code>sequence-name</code>
065 * in field-descriptor.
066 * <br/>
067 * If set 'false' OJB throws an exception
068 * if none sequence name was found in field-descriptor, ditto
069 * OJB does NOT try to create a database sequence entry when
070 * for given sequence name no database sequence could be found.
071 * </td>
072 * </tr>
073 * </table>
074 *
075 *
076 * <br/>
077 * <p>
078 * <b>Limitations:</b>
079 * <ul>
080 * <li>none</li>
081 * </ul>
082 * </p>
083 * <br/>
084 * <br/>
085 *
086 * @author Edson Carlos Ericksson Richter
087 * @author Rajeev Kaul
088 * @author Thomas Mahler
089 * @author Armin Waibel
090 * @version $Id: SequenceManagerNextValImpl.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
091 */
092 public class SequenceManagerNextValImpl extends AbstractSequenceManager
093 {
094 private Logger log = LoggerFactory.getLogger(SequenceManagerNextValImpl.class);
095
096 /**
097 *
098 */
099 public SequenceManagerNextValImpl(PersistenceBroker broker)
100 {
101 super(broker);
102 }
103
104 /**
105 * returns a unique int value for class clazz and field fieldName.
106 * the returned number is unique accross all tables in the extent of clazz.
107 */
108 protected int getUniqueId(FieldDescriptor field) throws SequenceManagerException
109 {
110 return (int) getUniqueLong(field);
111 }
112
113
114 /**
115 * returns a unique long value for class clazz and field fieldName.
116 * the returned number is unique accross all tables in the extent of clazz.
117 */
118 protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
119 {
120 long result;
121 // lookup sequence name
122 String sequenceName = calculateSequenceName(field);
123 try
124 {
125 result = buildNextSequence(field.getClassDescriptor(), sequenceName);
126 }
127 catch (Throwable e)
128 {
129 // maybe the sequence was not created
130 try
131 {
132 log.info("Create DB sequence key '"+sequenceName+"'");
133 createSequence(field.getClassDescriptor(), sequenceName);
134 }
135 catch (Exception e1)
136 {
137 throw new SequenceManagerException(
138 SystemUtils.LINE_SEPARATOR +
139 "Could not grab next id, failed with " + SystemUtils.LINE_SEPARATOR +
140 e.getMessage() + SystemUtils.LINE_SEPARATOR +
141 "Creation of new sequence failed with " +
142 SystemUtils.LINE_SEPARATOR + e1.getMessage() + SystemUtils.LINE_SEPARATOR
143 , e1);
144 }
145 try
146 {
147 result = buildNextSequence(field.getClassDescriptor(), sequenceName);
148 }
149 catch (Throwable e1)
150 {
151 throw new SequenceManagerException("Could not grab next id, sequence seems to exist", e);
152 }
153 }
154 return result;
155 }
156
157 protected long buildNextSequence(ClassDescriptor cld, String sequenceName) throws Exception
158 {
159 ResultSet rs = null;
160 PreparedStatement stmt = null;
161 long result = -1;
162 StatementManagerIF stmtMan = getBrokerForClass().serviceStatementManager();
163 try
164 {
165 stmt = stmtMan.getPreparedStatement(cld, getPlatform().nextSequenceQuery(sequenceName) ,Query.NOT_SCROLLABLE, 1, false);
166 rs = stmt.executeQuery();
167 rs.next();
168 result = rs.getLong(1);
169 }
170 finally
171 {
172 stmtMan.closeResources(stmt, rs);
173 }
174 return result;
175 }
176
177 protected void createSequence(ClassDescriptor cld, String sequenceName) throws Exception
178 {
179 Statement stmt = null;
180 StatementManagerIF stmtMan = getBrokerForClass().serviceStatementManager();
181 // arminw: never try to remove existing sequences, because this may lead in unexpected behaviour
182 // if the reason for the create call isn't a missing sequence (e.g. network problems)
183 // try
184 // {
185 // stmt = stmtMan.getGenericStatement(cld, Query.NOT_SCROLLABLE);
186 // stmt.execute(getPlatform().dropSequenceQuery(sequenceName));
187 // }
188 // catch (Exception ignore)
189 // {
190 // // ignore it
191 // }
192 // finally
193 // {
194 // try
195 // {
196 // stmtMan.closeResources(stmt, null);
197 // }
198 // catch (Exception ignore)
199 // {
200 // // ignore it
201 // }
202 // }
203
204 try
205 {
206 stmt = stmtMan.getGenericStatement(cld, Query.NOT_SCROLLABLE);
207 stmt.execute(getPlatform().createSequenceQuery(sequenceName, getConfigurationProperties()));
208 }
209 finally
210 {
211 try
212 {
213 stmtMan.closeResources(stmt, null);
214 }
215 catch (Exception e)
216 {
217 e.printStackTrace();
218 }
219 }
220 }
221 }