Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/objectstyle/cayenne/dba/oracle/OraclePkGenerator.java


1   /* ====================================================================
2    *
3    * The ObjectStyle Group Software License, Version 1.0
4    *
5    * Copyright (c) 2002-2003 The ObjectStyle Group
6    * and individual authors of the software.  All rights reserved.
7    *
8    * Redistribution and use in source and binary forms, with or without
9    * modification, are permitted provided that the following conditions
10   * are met:
11   *
12   * 1. Redistributions of source code must retain the above copyright
13   *    notice, this list of conditions and the following disclaimer.
14   *
15   * 2. Redistributions in binary form must reproduce the above copyright
16   *    notice, this list of conditions and the following disclaimer in
17   *    the documentation and/or other materials provided with the
18   *    distribution.
19   *
20   * 3. The end-user documentation included with the redistribution, if
21   *    any, must include the following acknowlegement:
22   *       "This product includes software developed by the
23   *        ObjectStyle Group (http://objectstyle.org/)."
24   *    Alternately, this acknowlegement may appear in the software itself,
25   *    if and wherever such third-party acknowlegements normally appear.
26   *
27   * 4. The names "ObjectStyle Group" and "Cayenne"
28   *    must not be used to endorse or promote products derived
29   *    from this software without prior written permission. For written
30   *    permission, please contact andrus@objectstyle.org.
31   *
32   * 5. Products derived from this software may not be called "ObjectStyle"
33   *    nor may "ObjectStyle" appear in their names without prior written
34   *    permission of the ObjectStyle Group.
35   *
36   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39   * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
40   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47   * SUCH DAMAGE.
48   * ====================================================================
49   *
50   * This software consists of voluntary contributions made by many
51   * individuals on behalf of the ObjectStyle Group.  For more
52   * information on the ObjectStyle Group, please see
53   * <http://objectstyle.org/>.
54   *
55   */
56  
57  package org.objectstyle.cayenne.dba.oracle;
58  
59  import java.sql.Connection;
60  import java.sql.ResultSet;
61  import java.sql.SQLException;
62  import java.sql.Statement;
63  import java.util.ArrayList;
64  import java.util.Iterator;
65  import java.util.List;
66  
67  import org.objectstyle.cayenne.CayenneRuntimeException;
68  import org.objectstyle.cayenne.access.DataNode;
69  import org.objectstyle.cayenne.dba.JdbcPkGenerator;
70  import org.objectstyle.cayenne.map.DbEntity;
71  import org.objectstyle.cayenne.map.DbKeyGenerator;
72  
73  /**
74   * Sequence-based primary key generator implementation for Oracle.
75   * Uses Oracle sequences to generate primary key values. This approach is
76   * at least 50% faster when tested with Oracle compared to the lookup table
77   * approach.
78   *
79   * <p>When using Cayenne key caching mechanism, make sure that sequences in
80   * the database have "INCREMENT BY" greater or equal to OraclePkGenerator
81   * "pkCacheSize" property value. If this is not the case, you will need to
82   * adjust PkGenerator value accordingly. For example when sequence is
83   * incremented by 1 each time, use the following code:</p>
84   *
85   * <pre>
86   * dataNode.getAdapter().getPkGenerator().setPkCacheSize(1);
87   * </pre>
88   *
89   * @author Andrei Adamchik
90   */
91  public class OraclePkGenerator extends JdbcPkGenerator {
92      private static final String _SEQUENCE_PREFIX = "pk_";
93  
94      public void createAutoPk(DataNode node, List dbEntities) throws Exception {
95          List sequences = getExistingSequences(node);
96  
97          // create needed sequences
98          Iterator it = dbEntities.iterator();
99          while (it.hasNext()) {
100             DbEntity ent = (DbEntity) it.next();
101             if (!sequences.contains(sequenceName(ent))) {
102                 runUpdate(node, createSequenceString(ent));
103             }
104         }
105     }
106 
107     public List createAutoPkStatements(List dbEntities) {
108         List list = new ArrayList();
109         Iterator it = dbEntities.iterator();
110         while (it.hasNext()) {
111             DbEntity ent = (DbEntity) it.next();
112             list.add(createSequenceString(ent));
113         }
114 
115         return list;
116     }
117 
118     public void dropAutoPk(DataNode node, List dbEntities) throws Exception {
119         List sequences = getExistingSequences(node);
120 
121         // drop obsolete sequences
122         Iterator it = dbEntities.iterator();
123         while (it.hasNext()) {
124             DbEntity ent = (DbEntity) it.next();
125             if (sequences.contains(stripSchemaName(sequenceName(ent)))) {
126                 runUpdate(node, dropSequenceString(ent));
127             }
128         }
129     }
130 
131     public List dropAutoPkStatements(List dbEntities) {
132         List list = new ArrayList();
133         Iterator it = dbEntities.iterator();
134         while (it.hasNext()) {
135             DbEntity ent = (DbEntity) it.next();
136             list.add(dropSequenceString(ent));
137         }
138 
139         return list;
140     }
141 
142     protected String createSequenceString(DbEntity ent) {
143         StringBuffer buf = new StringBuffer();
144         buf
145             .append("CREATE SEQUENCE ")
146             .append(sequenceName(ent))
147             .append(" START WITH 200")
148             .append(" INCREMENT BY ")
149             .append(pkCacheSize(ent));
150         return buf.toString();
151     }
152 
153     /**
154      * Returns a SQL string needed to drop any database objects associated
155      * with automatic primary key generation process for a specific DbEntity.
156      */
157     protected String dropSequenceString(DbEntity ent) {
158         StringBuffer buf = new StringBuffer();
159         buf.append("DROP SEQUENCE ").append(sequenceName(ent));
160         return buf.toString();
161     }
162 
163     /**
164      * Generates primary key by calling Oracle sequence corresponding to the
165      * <code>dbEntity</code>. Executed SQL looks like this:
166      *
167      * <pre>
168      * SELECT pk_table_name.nextval FROM DUAL
169      * </pre>
170      */
171     protected int pkFromDatabase(DataNode node, DbEntity ent) throws Exception {
172 
173         DbKeyGenerator pkGenerator = ent.getPrimaryKeyGenerator();
174         String pkGeneratingSequenceName;
175         if (pkGenerator != null
176             && DbKeyGenerator.ORACLE_TYPE.equals(pkGenerator.getGeneratorType())
177             && pkGenerator.getGeneratorName() != null)
178             pkGeneratingSequenceName = pkGenerator.getGeneratorName();
179         else
180             pkGeneratingSequenceName = sequenceName(ent);
181 
182         Connection con = node.getDataSource().getConnection();
183         try {
184             Statement st = con.createStatement();
185             try {
186                 ResultSet rs =
187                     st.executeQuery(
188                         "SELECT " + pkGeneratingSequenceName + ".nextval FROM DUAL");
189                 try {
190                     //Object pk = null;
191                     if (!rs.next()) {
192                         throw new CayenneRuntimeException(
193                             "Error generating pk for DbEntity " + ent.getName());
194                     }
195                     return rs.getInt(1);
196                 }
197                 finally {
198                     rs.close();
199                 }
200             }
201             finally {
202                 st.close();
203             }
204         }
205         finally {
206             con.close();
207         }
208     }
209 
210     protected int pkCacheSize(DbEntity entity) {
211         // use custom generator if possible
212         DbKeyGenerator keyGenerator = entity.getPrimaryKeyGenerator();
213         if (keyGenerator != null
214             && DbKeyGenerator.ORACLE_TYPE.equals(keyGenerator.getGeneratorType())
215             && keyGenerator.getGeneratorName() != null) {
216 
217             Integer size = keyGenerator.getKeyCacheSize();
218             return (size != null && size.intValue() >= 1)
219                 ? size.intValue()
220                 : super.getPkCacheSize();
221         }
222         else {
223             return super.getPkCacheSize();
224         }
225     }
226 
227     /** Returns expected primary key sequence name for a DbEntity. */
228     protected String sequenceName(DbEntity entity) {
229 
230         // use custom generator if possible
231         DbKeyGenerator keyGenerator = entity.getPrimaryKeyGenerator();
232         if (keyGenerator != null
233             && DbKeyGenerator.ORACLE_TYPE.equals(keyGenerator.getGeneratorType())
234             && keyGenerator.getGeneratorName() != null) {
235 
236             return keyGenerator.getGeneratorName().toLowerCase();
237         }
238         else {
239             String entName = entity.getName();
240             String seqName = _SEQUENCE_PREFIX + entName.toLowerCase();
241 
242             if (entity.getSchema() != null && entity.getSchema().length() > 0) {
243                 seqName = entity.getSchema() + "." + seqName;
244             }
245             return seqName;
246         }
247     }
248 
249     protected String stripSchemaName(String sequenceName) {
250         int ind = sequenceName.indexOf('.');
251         return (ind >= 0) ? sequenceName.substring(ind + 1) : sequenceName;
252     }
253 
254     /**
255      * Fetches a list of existing sequences that might match Cayenne
256      * generated ones.
257      */
258     protected List getExistingSequences(DataNode node) throws SQLException {
259 
260         // check existing sequences
261         Connection con = node.getDataSource().getConnection();
262 
263         try {
264             Statement sel = con.createStatement();
265             try {
266                 ResultSet rs =
267                     sel.executeQuery("SELECT LOWER(SEQUENCE_NAME) FROM ALL_SEQUENCES");
268                 try {
269                     List sequenceList = new ArrayList();
270                     while (rs.next()) {
271                         sequenceList.add(rs.getString(1));
272                     }
273                     return sequenceList;
274                 }
275                 finally {
276                     rs.close();
277                 }
278             }
279             finally {
280                 sel.close();
281             }
282         }
283         finally {
284             con.close();
285         }
286     }
287 }