Source code: org/objectstyle/cayenne/dba/oracle/OracleAdapter.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.lang.reflect.Field;
60 import java.lang.reflect.Method;
61 import java.sql.Connection;
62 import java.sql.SQLException;
63 import java.sql.Types;
64
65 import org.apache.log4j.Logger;
66 import org.objectstyle.cayenne.CayenneRuntimeException;
67 import org.objectstyle.cayenne.access.DataNode;
68 import org.objectstyle.cayenne.access.OperationObserver;
69 import org.objectstyle.cayenne.access.trans.QualifierTranslator;
70 import org.objectstyle.cayenne.access.trans.QueryAssembler;
71 import org.objectstyle.cayenne.access.trans.TrimmingQualifierTranslator;
72 import org.objectstyle.cayenne.access.types.ByteArrayType;
73 import org.objectstyle.cayenne.access.types.CharType;
74 import org.objectstyle.cayenne.access.types.ExtendedTypeMap;
75 import org.objectstyle.cayenne.access.util.BatchQueryUtils;
76 import org.objectstyle.cayenne.dba.JdbcAdapter;
77 import org.objectstyle.cayenne.dba.PkGenerator;
78 import org.objectstyle.cayenne.map.DbAttribute;
79 import org.objectstyle.cayenne.map.DbEntity;
80 import org.objectstyle.cayenne.query.BatchQuery;
81 import org.objectstyle.cayenne.query.Query;
82 import org.objectstyle.cayenne.query.SelectQuery;
83
84 /**
85 * DbAdapter implementation for <a href="http://www.oracle.com">Oracle RDBMS</a>.
86 * Sample <a target="_top" href="../../../../../../../developerguide/unit-tests.html">connection
87 * settings</a> to use with Oracle are shown below:
88 *
89 <pre>
90 test-oracle.cayenne.adapter = org.objectstyle.cayenne.dba.oracle.OracleAdapter
91 test-oracle.jdbc.username = test
92 test-oracle.jdbc.password = secret
93 test-oracle.jdbc.url = jdbc:oracle:thin:@192.168.0.20:1521:ora1
94 test-oracle.jdbc.driver = oracle.jdbc.driver.OracleDriver
95 </pre>
96 *
97 * @author Andrei Adamchik
98 */
99 public class OracleAdapter extends JdbcAdapter {
100 private static Logger logObj = Logger.getLogger(OracleAdapter.class);
101
102 public static final String ORACLE_FLOAT = "FLOAT";
103 public static final String ORACLE_BLOB = "BLOB";
104 public static final String ORACLE_CLOB = "CLOB";
105
106 public static final String TRIM_FUNCTION = "RTRIM";
107 public static final String NEW_CLOB_FUNCTION = "EMPTY_CLOB()";
108 public static final String NEW_BLOB_FUNCTION = "EMPTY_BLOB()";
109
110 protected static boolean initDone;
111 protected static int oracleCursorType = Integer.MAX_VALUE;
112 protected static Method outputStreamFromBlobMethod;
113 protected static Method writerFromClobMethod;
114 protected static boolean supportsOracleLOB;
115
116 protected static void initDriverInformation() {
117 initDone = true;
118
119 // configure static information
120 try {
121 Class oraTypes = Class.forName("oracle.jdbc.driver.OracleTypes");
122 Field cursorField = oraTypes.getField("CURSOR");
123 oracleCursorType = cursorField.getInt(null);
124
125 outputStreamFromBlobMethod =
126 Class.forName("oracle.sql.BLOB").getMethod(
127 "getBinaryOutputStream",
128 new Class[0]);
129
130 writerFromClobMethod =
131 Class.forName("oracle.sql.CLOB").getMethod(
132 "getCharacterOutputStream",
133 new Class[0]);
134 supportsOracleLOB = true;
135
136 } catch (Exception ex) {
137 logObj.info(
138 "Error getting Oracle driver information, ignoring. "
139 + "Note that certain adapter features will be disabled.",
140 ex);
141 }
142 }
143
144 public static Method getOutputStreamFromBlobMethod() {
145 return outputStreamFromBlobMethod;
146 }
147
148 public static boolean isSupportsOracleLOB() {
149 return supportsOracleLOB;
150 }
151
152 public static Method getWriterFromClobMethod() {
153 return writerFromClobMethod;
154 }
155
156 /**
157 * Returns an Oracle JDBC extension type defined in
158 * oracle.jdbc.driver.OracleTypes.CURSOR. This value is determined
159 * from Oracle driver classes via reflection in runtime, so that
160 * Cayenne code has no compile dependency on the driver. This means
161 * that calling this method when the driver is not available will
162 * result in an exception.
163 */
164 public static int getOracleCursorType() {
165 if (!initDone) {
166 initDriverInformation();
167 }
168
169 if (oracleCursorType == Integer.MAX_VALUE) {
170 throw new CayenneRuntimeException(
171 "No information exists about oracle types. "
172 + "Check that Oracle JDBC driver is available to the application.");
173 }
174
175 return oracleCursorType;
176 }
177
178 public OracleAdapter() {
179 // enable batch updates by default
180 setSupportsBatchUpdates(true);
181 }
182
183 /**
184 * Installs appropriate ExtendedTypes as converters for passing values
185 * between JDBC and Java layers.
186 */
187 protected void configureExtendedTypes(ExtendedTypeMap map) {
188 super.configureExtendedTypes(map);
189
190 // create specially configured CharType handler
191 map.registerType(new CharType(true, true));
192
193 // create specially configured ByteArrayType handler
194 map.registerType(new ByteArrayType(true, true));
195
196 // override date handler with Oracle handler
197 map.registerType(new OracleUtilDateType());
198 }
199
200 /**
201 * Creates and returns a primary key generator.
202 * Overrides superclass implementation to return an
203 * instance of OraclePkGenerator.
204 */
205 protected PkGenerator createPkGenerator() {
206 return new OraclePkGenerator();
207 }
208
209 /**
210 * Returns a query string to drop a table corresponding
211 * to <code>ent</code> DbEntity. Changes superclass behavior
212 * to drop all related foreign key constraints.
213 */
214 public String dropTable(DbEntity ent) {
215 return "DROP TABLE "
216 + ent.getFullyQualifiedName()
217 + " CASCADE CONSTRAINTS";
218 }
219
220 /**
221 * Fixes some reverse engineering problems. Namely if a columns
222 * is created as DECIMAL and has non-positive precision it is
223 * converted to INTEGER.
224 */
225 public DbAttribute buildAttribute(
226 String name,
227 String typeName,
228 int type,
229 int size,
230 int precision,
231 boolean allowNulls) {
232
233 DbAttribute attr =
234 super.buildAttribute(
235 name,
236 typeName,
237 type,
238 size,
239 precision,
240 allowNulls);
241
242 if (type == Types.DECIMAL && precision <= 0) {
243 attr.setType(Types.INTEGER);
244 attr.setPrecision(-1);
245 } else if (type == Types.OTHER) {
246 // in this case we need to guess the attribute type
247 // based on its string value
248 if (ORACLE_FLOAT.equals(typeName)) {
249 attr.setType(Types.FLOAT);
250 } else if (ORACLE_BLOB.equals(typeName)) {
251 attr.setType(Types.BLOB);
252 } else if (ORACLE_CLOB.equals(typeName)) {
253 attr.setType(Types.CLOB);
254 }
255 }
256
257 return attr;
258 }
259
260 /** Returns Oracle-specific classes for SELECT queries. */
261 protected Class queryTranslatorClass(Query q) {
262 if (q instanceof SelectQuery) {
263 return OracleSelectTranslator.class;
264 } else {
265 return super.queryTranslatorClass(q);
266 }
267 }
268
269 /**
270 * Returns a trimming translator.
271 */
272 public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
273 return new TrimmingQualifierTranslator(
274 queryAssembler,
275 OracleAdapter.TRIM_FUNCTION);
276 }
277
278 /**
279 * Creates an instance of OracleDataNode.
280 */
281 public DataNode createDataNode(String name) {
282 DataNode node = new OracleDataNode(name);
283 node.setAdapter(this);
284 return node;
285 }
286
287 /**
288 * Implements special LOB handling in batches.
289 */
290 public boolean shouldRunBatchQuery(
291 DataNode node,
292 Connection con,
293 BatchQuery query,
294 OperationObserver delegate)
295 throws SQLException, Exception {
296
297 // special handling for LOB updates
298 if (supportsOracleLOB && BatchQueryUtils.updatesLOBColumns(query)
299 && (node instanceof OracleDataNode)) {
300
301 OracleDataNode oracleNode = (OracleDataNode) node;
302 oracleNode.runBatchUpdateWithLOBColumns(con, query, delegate);
303
304 return false;
305 } else {
306 return super.shouldRunBatchQuery(node, con, query, delegate);
307 }
308 }
309 }