All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cayenne.dba.oracle.OracleAdapter Maven / Gradle / Ivy

The newest version!
/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/

package org.apache.cayenne.dba.oracle;

import java.lang.reflect.Field;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor;
import org.apache.cayenne.access.translator.ParameterBinding;
import org.apache.cayenne.access.translator.ejbql.EJBQLTranslatorFactory;
import org.apache.cayenne.access.types.ByteType;
import org.apache.cayenne.access.types.CharType;
import org.apache.cayenne.access.types.ExtendedType;
import org.apache.cayenne.access.types.ExtendedTypeFactory;
import org.apache.cayenne.access.types.ExtendedTypeMap;
import org.apache.cayenne.access.types.JsonType;
import org.apache.cayenne.access.types.ShortType;
import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
import org.apache.cayenne.configuration.Constants;
import org.apache.cayenne.configuration.RuntimeProperties;
import org.apache.cayenne.dba.JdbcAdapter;
import org.apache.cayenne.dba.PkGenerator;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.query.BatchQuery;
import org.apache.cayenne.query.InsertBatchQuery;
import org.apache.cayenne.query.Query;
import org.apache.cayenne.query.SQLAction;
import org.apache.cayenne.query.UpdateBatchQuery;
import org.apache.cayenne.resource.ResourceLocator;

/**
 * DbAdapter implementation for Oracle RDBMS
 * . Sample connection settings to use with Oracle are shown below:
 *
 * 
 *          test-oracle.jdbc.username = test
 *          test-oracle.jdbc.password = secret
 *          test-oracle.jdbc.url = jdbc:oracle:thin:@//192.168.0.20:1521/ora1
 *          test-oracle.jdbc.driver = oracle.jdbc.driver.OracleDriver
 * 
*/ public class OracleAdapter extends JdbcAdapter { public static final String ORACLE_FLOAT = "FLOAT"; public static final String ORACLE_BLOB = "BLOB"; public static final String ORACLE_CLOB = "CLOB"; public static final String ORACLE_NCLOB = "NCLOB"; public static final String TRIM_FUNCTION = "RTRIM"; public static final String NEW_CLOB_FUNCTION = "EMPTY_CLOB()"; public static final String NEW_BLOB_FUNCTION = "EMPTY_BLOB()"; protected static boolean initDone; protected static int oracleCursorType = Integer.MAX_VALUE; protected static boolean supportsOracleLOB; private List SYSTEM_SCHEMAS = Arrays.asList( "ANONYMOUS", "APPQOSSYS", "AUDSYS", "CTXSYS", "DBSFWUSER", "DBSNMP", "DIP", "DVF", "GGSYS", "DVSYS", "GSMADMIN_INTERNAL", "GSMCATUSER", "GSMUSER", "LBACSYS", "MDDATA", "MDSYS", "OJVMSYS", "OLAPSYS", "ORACLE_OCM", "ORDDATA", "ORDPLUGINS", "ORDSYS", "OUTLN", "REMOTE_SCHEDULER_AGENT", "SYSTEM", "WMSYS", "SI_INFORMTN_SCHEMA", "SYS", "SYSBACKUP", "SYSDG", "SYSKM", "SYSRAC", "SYS$UMF", "XDB", "XS$NULL"); static { // TODO: as CAY-234 shows, having such initialization done in a static // fashion // makes it untestable and any potential problems hard to reproduce. // Make this // an instance method (with all the affected vars) and write unit tests. initDriverInformation(); } protected static void initDriverInformation() { initDone = true; // configure static information try { Class oraTypes = Class.forName("oracle.jdbc.driver.OracleTypes"); Field cursorField = oraTypes.getField("CURSOR"); oracleCursorType = cursorField.getInt(null); supportsOracleLOB = true; } catch (Throwable th) { // ignoring... } } // TODO: rename to something that looks like English ... public static boolean isSupportsOracleLOB() { return supportsOracleLOB; } /** * Utility method that returns true if the query will update at * least one BLOB or CLOB DbAttribute. * * @since 1.2 */ static boolean updatesLOBColumns(BatchQuery query) { boolean isInsert = query instanceof InsertBatchQuery; boolean isUpdate = query instanceof UpdateBatchQuery; if (!isInsert && !isUpdate) { return false; } List updatedAttributes = (isInsert) ? query.getDbAttributes() : ((UpdateBatchQuery) query) .getUpdatedAttributes(); for (DbAttribute attr : updatedAttributes) { int type = attr.getType(); if (type == Types.CLOB || type == Types.BLOB) { return true; } } return false; } /** * Returns an Oracle JDBC extension type defined in * oracle.jdbc.driver.OracleTypes.CURSOR. This value is determined from * Oracle driver classes via reflection in runtime, so that Cayenne code has * no compile dependency on the driver. This means that calling this method * when the driver is not available will result in an exception. */ public static int getOracleCursorType() { if (oracleCursorType == Integer.MAX_VALUE) { throw new CayenneRuntimeException("No information exists about oracle types. " + "Check that Oracle JDBC driver is available to the application."); } return oracleCursorType; } public OracleAdapter(@Inject RuntimeProperties runtimeProperties, @Inject(Constants.DEFAULT_TYPES_LIST) List defaultExtendedTypes, @Inject(Constants.USER_TYPES_LIST) List userExtendedTypes, @Inject(Constants.TYPE_FACTORIES_LIST) List extendedTypeFactories, @Inject(Constants.RESOURCE_LOCATOR) ResourceLocator resourceLocator, @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, resourceLocator, valueObjectTypeRegistry); // enable batch updates by default setSupportsBatchUpdates(true); } /** * @since 4.2 */ @Override public SQLTreeProcessor getSqlTreeProcessor() { return new OracleSQLTreeProcessor(); } /** * @since 3.0 */ @Override protected EJBQLTranslatorFactory createEJBQLTranslatorFactory() { return new OracleEJBQLTranslatorFactory(); } /** * Installs appropriate ExtendedTypes as converters for passing values * between JDBC and Java layers. */ @Override protected void configureExtendedTypes(ExtendedTypeMap map) { super.configureExtendedTypes(map); // create specially configured CharType handler OracleCharType charType = new OracleCharType(); map.registerType(charType); // create specially configured ByteArrayType handler map.registerType(new OracleByteArrayType()); // override date handler with Oracle handler map.registerType(new OracleUtilDateType()); // At least on MacOS X, driver does not handle Short and Byte properly map.registerType(new ShortType(true)); map.registerType(new ByteType(true)); map.registerType(new OracleBooleanType()); map.registerType(new JsonType(charType, true)); } /** * Returns a query string to drop a table corresponding to ent * DbEntity. Changes superclass behavior to drop all related foreign key * constraints. * * @since 3.0 */ @Override public Collection dropTableStatements(DbEntity table) { return Collections.singleton("DROP TABLE " + getQuotingStrategy().quotedFullyQualifiedName(table) + " CASCADE CONSTRAINTS"); } @Override public void bindParameter(PreparedStatement statement, ParameterBinding binding) throws SQLException, Exception { // Oracle doesn't support BOOLEAN even when binding NULL, so have to // intercept // NULL Boolean here, as super doesn't pass it through ExtendedType... if (binding.getValue() == null && binding.getJdbcType() == Types.BOOLEAN) { ExtendedType typeProcessor = getExtendedTypes().getRegisteredType(Boolean.class); typeProcessor.setJdbcObject(statement, binding.getValue(), binding.getStatementPosition(), binding .getJdbcType(),binding.getScale()); } else { super.bindParameter(statement, binding); } } /** * Fixes some reverse engineering problems. Namely if a columns is created * as DECIMAL and has non-positive precision it is converted to INTEGER. */ @Override public DbAttribute buildAttribute(String name, String typeName, int type, int size, int scale, boolean allowNulls) { DbAttribute attr = super.buildAttribute(name, typeName, type, size, scale, allowNulls); if (type == Types.DECIMAL && scale <= 0) { if (size <= 9) { attr.setType(Types.INTEGER); } else if(size <= 19) { attr.setType(Types.BIGINT); } attr.setScale(-1); } else if (type == Types.OTHER) { // in this case we need to guess the attribute type // based on its string value if (ORACLE_FLOAT.equals(typeName)) { attr.setType(Types.FLOAT); } else if (ORACLE_BLOB.equals(typeName)) { attr.setType(Types.BLOB); } else if (ORACLE_CLOB.equals(typeName)) { attr.setType(Types.CLOB); } else if (ORACLE_NCLOB.equals(typeName)) { attr.setType(Types.NCLOB); } } else if (type == Types.DATE) { // Oracle DATE can store JDBC TIMESTAMP if ("DATE".equals(typeName)) { attr.setType(Types.TIMESTAMP); } } return attr; } /** * Uses OracleActionBuilder to create the right action. * * @since 1.2 */ @Override public SQLAction getAction(Query query, DataNode node) { return query.createSQLAction(new OracleActionBuilder(node)); } @Override public List getSystemSchemas() { return SYSTEM_SCHEMAS; } /** * @since 5.0 */ @Override public boolean typeSupportsScale(int type) { return type != Types.TIME && super.typeSupportsScale(type); } /** * @since 3.0 */ final class OracleBooleanType implements ExtendedType { @Override public String getClassName() { return Boolean.class.getName(); } @Override public void setJdbcObject(PreparedStatement st, Boolean val, int pos, int type, int precision) throws Exception { // Oracle does not support Types.BOOLEAN, so we have to override // user mapping // unconditionally if (val == null) { st.setNull(pos, Types.INTEGER); } else { boolean flag = Boolean.TRUE.equals(val); st.setInt(pos, flag ? 1 : 0); } } @Override public Boolean materializeObject(ResultSet rs, int index, int type) throws Exception { // Oracle does not support Types.BOOLEAN, so we have to override // user mapping // unconditionally int i = rs.getInt(index); return rs.wasNull() ? null : i == 0 ? Boolean.FALSE : Boolean.TRUE; } @Override public Boolean materializeObject(CallableStatement st, int index, int type) throws Exception { // Oracle does not support Types.BOOLEAN, so we have to override // user mapping // unconditionally int i = st.getInt(index); return st.wasNull() ? null : i == 0 ? Boolean.FALSE : Boolean.TRUE; } @Override public String toString(Boolean value) { if (value == null) { return "NULL"; } return '\'' + value.toString() + '\''; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy