
org.apache.cayenne.dba.oracle.OracleAdapter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cayenne Show documentation
Show all versions of cayenne Show documentation
Cayenne Object Persistence Framework
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