net.java.ao.db.OracleDatabaseProvider Maven / Gradle / Ivy
/*
* Copyright 2007 Daniel Spiewak
*
* Licensed 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
*
* http://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 net.java.ao.db;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import net.java.ao.Common;
import net.java.ao.DBParam;
import net.java.ao.DatabaseProvider;
import net.java.ao.DisposableDataSource;
import net.java.ao.EntityManager;
import net.java.ao.Query;
import net.java.ao.RawEntity;
import net.java.ao.schema.Case;
import net.java.ao.schema.IndexNameConverter;
import net.java.ao.schema.NameConverters;
import net.java.ao.schema.TableNameConverter;
import net.java.ao.schema.UniqueNameConverter;
import net.java.ao.schema.ddl.DDLField;
import net.java.ao.schema.ddl.DDLForeignKey;
import net.java.ao.schema.ddl.DDLIndex;
import net.java.ao.schema.ddl.DDLTable;
import net.java.ao.schema.ddl.SQLAction;
import net.java.ao.types.TypeInfo;
import net.java.ao.types.TypeManager;
import net.java.ao.types.TypeQualifiers;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static net.java.ao.sql.SqlUtils.closeQuietly;
/**
* @author Daniel Spiewak
*/
public final class OracleDatabaseProvider extends DatabaseProvider {
private static final int ORA_04080_TRIGGER_DOES_NOT_EXIST = 4080;
private static final int ORA_02289_SEQUENCE_DOES_NOT_EXIST = 2289;
public OracleDatabaseProvider(DisposableDataSource dataSource) {
this(dataSource, null);
}
public OracleDatabaseProvider(DisposableDataSource dataSource, String schema) {
super(dataSource, schema, TypeManager.oracle());
}
@Override
public String renderMetadataQuery(final String tableName) {
return "SELECT * FROM (SELECT * FROM " + withSchema(tableName) + ") WHERE ROWNUM <= 1";
}
@Override
public String getSchema() {
return isSchemaNotEmpty() ? Case.UPPER.apply(super.getSchema()) : null;
}
@Override
public ResultSet getTables(Connection conn) throws SQLException {
final DatabaseMetaData metaData = conn.getMetaData();
final String schemaPattern = isSchemaNotEmpty() ? getSchema() : metaData.getUserName();
return metaData.getTables(null, schemaPattern, "%", new String[]{"TABLE"});
}
@Override
public ResultSet getSequences(Connection conn) throws SQLException {
final DatabaseMetaData metaData = conn.getMetaData();
final String schemaPattern = isSchemaNotEmpty() ? getSchema() : metaData.getUserName();
final String sql = "SELECT NULL AS table_cat, o.owner AS table_schem, o.object_name AS table_name, o.object_type AS table_type, NULL AS remarks FROM all_objects o WHERE o.owner = ? AND o.object_type = 'SEQUENCE'";
PreparedStatement statement = conn.prepareStatement(sql);
statement.setString(1, schemaPattern);
return statement.executeQuery();
}
@Override
public ResultSet getIndexes(Connection conn, String tableName) throws SQLException {
final DatabaseMetaData metaData = conn.getMetaData();
final String schemaPattern = isSchemaNotEmpty() ? getSchema() : metaData.getUserName();
return conn.getMetaData().getIndexInfo(null, schemaPattern, tableName, false, true);
}
@Override
public ResultSet getImportedKeys(Connection connection, String tableName) throws SQLException {
final DatabaseMetaData metaData = connection.getMetaData();
final String schemaPattern = isSchemaNotEmpty() ? getSchema() : metaData.getUserName();
return metaData.getImportedKeys(null, schemaPattern, tableName);
}
@Override
protected String renderQuerySelect(final Query query, final TableNameConverter converter, final boolean count) {
StringBuilder sql = new StringBuilder();
// see http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197.html
if (Query.QueryType.SELECT.equals(query.getType())) {
int offset = query.getOffset();
int limit = query.getLimit();
if (offset > 0) {
sql.append("SELECT * FROM ( SELECT QUERY_INNER.*, ROWNUM ROWNUM_INNER FROM ( ");
} else if (limit >= 0) {
sql.append("SELECT * FROM ( ");
}
}
sql.append(super.renderQuerySelect(query, converter, count));
return sql.toString();
}
@Override
protected String renderQueryLimit(Query query) {
StringBuilder sql = new StringBuilder();
if (Query.QueryType.SELECT.equals(query.getType())) {
int offset = query.getOffset();
int limit = query.getLimit();
if (offset > 0 && limit >= 0) {
sql.append(" ) QUERY_INNER WHERE ROWNUM <= ");
sql.append(offset + limit);
sql.append(" ) WHERE ROWNUM_INNER > ");
sql.append(offset);
} else if (offset > 0) {
sql.append(" ) QUERY_INNER ) WHERE ROWNUM_INNER > ");
sql.append(offset);
} else if (limit >= 0) {
sql.append(" ) WHERE ROWNUM <= ");
sql.append(limit);
}
}
return sql.toString();
}
@Override
protected String renderAutoIncrement() {
return "";
}
@Override
public Object parseValue(int type, String value) {
if (value == null || value.equals("") || value.equals("NULL")) {
return null;
}
switch (type) {
case Types.VARCHAR:
case Types.TIMESTAMP:
Matcher matcher = Pattern.compile("'(.*)'.*").matcher(value);
if (matcher.find()) {
value = matcher.group(1);
}
break;
case Types.INTEGER:
case Types.DOUBLE:
case Types.BIGINT:
case Types.BOOLEAN:
//AO-3463: in case if column definition contains `DEFAULT value NOT NULL` Oracle returns value with an extra space in the end.
//In such case we need to trim the value
value = value.trim();
break;
}
return super.parseValue(type, value);
}
@Override
protected String renderUnique(UniqueNameConverter uniqueNameConverter, DDLTable table, DDLField field) {
return "CONSTRAINT " + uniqueNameConverter.getName(table.getName(), field.getName()) + " UNIQUE";
}
@Override
protected String getDateFormat() {
return "dd-MMM-yy hh:mm:ss.SSS a";
}
@Override
protected SQLAction renderAlterTableAddColumnStatement(NameConverters nameConverters, DDLTable table, DDLField field) {
String addStmt = "ALTER TABLE " + withSchema(table.getName()) + " ADD (" + renderField(nameConverters, table, field, new RenderFieldOptions(true, true, true)) + ")";
return SQLAction.of(addStmt);
}
@Override
protected Iterable renderAlterTableChangeColumn(NameConverters nameConverters, DDLTable table, DDLField oldField, DDLField field) {
final UniqueNameConverter uniqueNameConverter = nameConverters.getUniqueNameConverter();
final ImmutableList.Builder back = ImmutableList.builder();
if (!oldField.getType().equals(field.getType())) {
if (field.getType().getSchemaProperties().getSqlTypeName().equals("CLOB") || oldField.getType().getSchemaProperties().getSqlTypeName().equals("CLOB")) {
if (!TypeQualifiers.areCompatible(oldField.getType().getQualifiers(), field.getType().getQualifiers())) {
final String fieldName = processID(field.getName());
final String tempColName = processID(getTempColumnName(field.getName()));
String tempColType;
if (field.getType().getSchemaProperties().getSqlTypeName().equals("CLOB")) {
tempColType = "CLOB";
} else {
final int stringLength = field.getType().getQualifiers().getStringLength();
tempColType = "VARCHAR(" + stringLength + ")";
}
back.add(SQLAction.of(new StringBuilder().append("ALTER TABLE ").append(withSchema(table.getName())).append(" ADD ").append(tempColName).append(" ").append(tempColType)));
back.add(SQLAction.of(new StringBuilder().append("UPDATE ").append(withSchema(table.getName())).append(" SET ").append(tempColName).append(" = ").append(fieldName)));
back.add(SQLAction.of("SAVEPOINT values_copied"));
back.addAll(renderDropColumnActions(nameConverters, table, field));
back.add(SQLAction.of(new StringBuilder().append("ALTER TABLE ").append(withSchema(table.getName())).append(" RENAME COLUMN ").append(tempColName).append(" TO ").append(fieldName)));
}
} else {
back.add(SQLAction.of(new StringBuilder().append("ALTER TABLE ").append(withSchema(table.getName())).append(" MODIFY (").append(processID(field.getName())).append(" ").append(renderFieldType(field)).append(")")));
}
}
if (oldField.isNotNull() && !field.isNotNull()) {
back.add(SQLAction.of(new StringBuilder().append("ALTER TABLE ").append(withSchema(table.getName())).append(" MODIFY (").append(processID(field.getName())).append(" NULL)")));
}
if (!oldField.isNotNull() && field.isNotNull()) {
back.add(SQLAction.of(new StringBuilder().append("ALTER TABLE ").append(withSchema(table.getName())).append(" MODIFY (").append(processID(field.getName())).append(" NOT NULL)")));
}
if (!Objects.equal(oldField.getDefaultValue(), field.getDefaultValue())) {
back.add(SQLAction.of(new StringBuilder().append("ALTER TABLE ").append(withSchema(table.getName())).append(" MODIFY (").append(processID(field.getName())).append(" DEFAULT ").append(renderValue(field.getDefaultValue())).append(")")));
}
if (oldField.isUnique() && !field.isUnique()) {
back.add(SQLAction.of(new StringBuilder().append("ALTER TABLE ").append(withSchema(table.getName())).append(" DROP CONSTRAINT ").append(uniqueNameConverter.getName(table.getName(), field.getName()))));
}
if (!oldField.isUnique() && field.isUnique()) {
back.add(SQLAction.of(new StringBuilder().append("ALTER TABLE ").append(withSchema(table.getName())).append(" ADD CONSTRAINT ").append(uniqueNameConverter.getName(table.getName(), field.getName())).append(" UNIQUE (").append(processID(field.getName())).append(")")));
}
return back.build();
}
@Override
protected SQLAction renderAlterTableDropKey(DDLForeignKey key) {
StringBuilder back = new StringBuilder("ALTER TABLE ");
back.append(withSchema(key.getDomesticTable())).append(" DROP CONSTRAINT ").append(processID(key.getFKName()));
return SQLAction.of(back);
}
@Override
protected SQLAction renderDropIndex(IndexNameConverter indexNameConverter, DDLIndex index) {
final String name = index.getIndexName();
return SQLAction.of(new StringBuilder().append("DROP INDEX ").append(withSchema(name)));
}
@Override
protected SQLAction renderDropTableStatement(DDLTable table) {
return SQLAction.of("DROP TABLE " + withSchema(table.getName()) + " PURGE");
}
@Override
public void handleUpdateError(String sql, SQLException e) throws SQLException {
if (isDropTrigger(sql, e)
|| isDropSequence(sql, e)) {
logger.debug("Ignoring non-existant trigger for SQL <" + sql + ">", e);
return;
}
super.handleUpdateError(sql, e);
}
private boolean isDropTrigger(String sql, SQLException e) {
return e.getErrorCode() == ORA_04080_TRIGGER_DOES_NOT_EXIST && sql.startsWith("DROP");
}
private boolean isDropSequence(String sql, SQLException e) {
return e.getErrorCode() == ORA_02289_SEQUENCE_DOES_NOT_EXIST && sql.startsWith("DROP");
}
@Override
protected , K> K executeInsertReturningKey(EntityManager manager, Connection conn,
Class entityType, Class pkType,
String pkField, String sql, DBParam... params) throws SQLException {
PreparedStatement stmt = null;
ResultSet res = null;
try {
onSql(sql);
stmt = conn.prepareStatement(sql, new String[]{pkField});
K back = (K) setParameters(manager, stmt, params, pkField);
stmt.executeUpdate();
if (back == null) {
res = stmt.getGeneratedKeys();
if (res.next()) {
back = typeManager.getType(pkType).getLogicalType().pullFromDatabase(null, res, pkType, 1);
}
}
return back;
} finally {
closeQuietly(res);
closeQuietly(stmt);
}
}
private Object setParameters(EntityManager manager, PreparedStatement stmt, DBParam[] params, String pkField) throws SQLException {
Object back = null;
int i = 0;
for (; i < params.length; i++) {
Object value = params[i].getValue();
if (value instanceof RawEntity>) {
value = Common.getPrimaryKeyValue((RawEntity>) value);
}
if (params[i].getField().equalsIgnoreCase(pkField)) {
back = value;
}
if (value == null) {
putNull(stmt, i + 1);
} else {
TypeInfo
© 2015 - 2025 Weber Informatics LLC | Privacy Policy