/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* 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 org.seasar.extension.jdbc.dialect;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.persistence.GenerationType;
import javax.persistence.TemporalType;
import org.seasar.extension.jdbc.PropertyMeta;
import org.seasar.extension.jdbc.SelectForUpdateType;
import org.seasar.extension.jdbc.ValueType;
import org.seasar.extension.jdbc.types.OracleDateCalendarType;
import org.seasar.extension.jdbc.types.OracleDateType;
import org.seasar.extension.jdbc.types.ValueTypes;
import org.seasar.framework.util.tiger.Pair;
/**
* Oracle用の方言をあつかうクラスです。
*
* @author higa
*
*/
public class OracleDialect extends StandardDialect {
/** {@link Date}型をOracle固有の{@literal DATE}型として扱う{@link ValueType} */
public static final ValueType ORACLE_DATE_TYPE = new OracleDateType();
/** {@link Calendar}型をOracle固有の{@literal DATE}型として扱う{@link ValueType} */
public static final ValueType ORACLE_DATE_CALENDAR_TYPE = new OracleDateCalendarType();
/**
* 一意制約違反を表す例外コード
*/
protected static final int uniqueConstraintViolationCode = 1;
private boolean supportsBooleanToInt = true;
private boolean supportsWaveDashToFullwidthTilde = true;
/** Oracle固有の{@literal DATE}型を使用する場合は{@literal true} */
protected boolean useOracleDate = true;
@Override
public String getName() {
return "oracle";
}
@Override
public boolean supportsLimit() {
return true;
}
/**
* <p>
* booleanからintへの変換をサポートしているかどうかを返します。
* </p>
* <p>
* オラクルのようなbooleanをサポートしていないデータベースでは必要になります。
* </p>
*
* @return booleanからintへの変換をサポートしているかどうか
*/
public boolean supportsBooleanToInt() {
return supportsBooleanToInt;
}
/**
* <p>
* WAVE DASH(U+301C)からFULLWIDTH TILDE(U+FF5E)への変換をサポートしているかどうかを返します。
* </p>
* <p>
* オラクルのようなFULLWIDTH TILDEの変換にバグがあるデータベースでは必要になります。
* </p>
*
* @return WAVE DASH(U+301C)からFULLWIDTH TILDE(U+FF5E)への変換をサポートしているかどうか
*/
public boolean supportsWaveDashToFullwidthTilde() {
return supportsWaveDashToFullwidthTilde;
}
@Override
public boolean needsParameterForResultSet() {
return true;
}
@Override
public String convertLimitSql(String sql, int offset, int limit) {
StringBuilder buf = new StringBuilder(sql.length() + 100);
sql = sql.trim();
String lowerSql = sql.toLowerCase();
boolean isForUpdate = false;
if (lowerSql.endsWith(" for update")) {
sql = sql.substring(0, sql.length() - 11);
isForUpdate = true;
}
buf.append("select * from ( select temp_.*, rownum rownumber_ from ( ");
buf.append(sql);
buf.append(" ) temp_ ) where");
boolean hasOffset = offset > 0;
if (hasOffset) {
buf.append(" rownumber_ > ");
buf.append(offset);
}
if (limit > 0) {
if (hasOffset) {
buf.append(" and");
}
buf.append(" rownumber_ <= ");
buf.append(offset + limit);
}
if (isForUpdate) {
buf.append(" for update");
}
return buf.toString();
}
@Override
public ValueType getValueType(PropertyMeta propertyMeta) {
Class<?> clazz = propertyMeta.getPropertyClass();
if (clazz == String.class && supportsWaveDashToFullwidthTilde()) {
if (propertyMeta.isLob()) {
return ValueTypes.WAVE_DASH_CLOB;
}
return ValueTypes.WAVE_DASH_STRING;
}
if (useOracleDate
&& propertyMeta.getTemporalType() == TemporalType.TIMESTAMP) {
if (clazz == Date.class) {
return ORACLE_DATE_TYPE;
}
if (clazz == Calendar.class) {
return ORACLE_DATE_CALENDAR_TYPE;
}
}
ValueType valueType = getValueTypeInternal(propertyMeta
.getPropertyClass());
if (valueType != null) {
return valueType;
}
return super.getValueType(propertyMeta);
}
@Override
public ValueType getValueType(Class<?> clazz, boolean lob,
TemporalType temporalType) {
if (clazz == String.class && supportsWaveDashToFullwidthTilde()) {
if (lob) {
return ValueTypes.WAVE_DASH_CLOB;
}
return ValueTypes.WAVE_DASH_STRING;
}
if (useOracleDate && temporalType == TemporalType.TIMESTAMP) {
if (clazz == Date.class) {
return ORACLE_DATE_TYPE;
}
if (clazz == Calendar.class) {
return ORACLE_DATE_CALENDAR_TYPE;
}
}
return super.getValueType(clazz, lob, temporalType);
}
@Override
protected ValueType getValueTypeInternal(Class<?> clazz) {
if ((clazz == Boolean.class || clazz == boolean.class)
&& supportsBooleanToInt()) {
return ValueTypes.BOOLEAN_INTEGER;
} else if (List.class.isAssignableFrom(clazz)) {
return ValueTypes.ORACLE_RESULT_SET;
}
return null;
}
/**
* booleanからintへの変換をサポートしているかどうかを設定します。
*
* @param supportsBooleanToInt
* booleanからintへの変換をサポートしているかどうか
*/
public void setSupportsBooleanToInt(boolean supportsBooleanToInt) {
this.supportsBooleanToInt = supportsBooleanToInt;
}
/**
* WAVE DASH(U+301C)からFULLWIDTH TILDE(U+FF5E)への変換をサポートしているかどうかを設定します。
*
* @param supportsWaveDashToFullwidthTilde
* WAVE DASH(U+301C)からFULLWIDTH TILDE(U+FF5E)への変換をサポートしているかどうか
*/
public void setSupportsWaveDashToFullwidthTilde(
boolean supportsWaveDashToFullwidthTilde) {
this.supportsWaveDashToFullwidthTilde = supportsWaveDashToFullwidthTilde;
}
/**
* Oracle固有の{@literal DATE}型を使用する場合は{@literal true}を返します。
*
* @return Oracle固有の{@literal DATE}型を使用する場合は{@literal true}
*/
public boolean isUseOracleDate() {
return useOracleDate;
}
/**
* Oracle固有の{@literal DATE}型を使用する場合は{@literal true}を設定します。
*
* @param useOracleDate
* Oracle固有の{@literal DATE}型を使用する場合は{@literal true}
*/
public void setUseOracleDate(boolean useOracleDate) {
this.useOracleDate = useOracleDate;
}
@Override
public GenerationType getDefaultGenerationType() {
return GenerationType.SEQUENCE;
}
@Override
public boolean supportsSequence() {
return true;
}
@Override
public String getSequenceNextValString(final String sequenceName,
final int allocationSize) {
return "select " + sequenceName + ".nextval from dual";
}
@Override
public boolean supportsBatchUpdateResults() {
return false;
}
@Override
public boolean supportsForUpdate(final SelectForUpdateType type,
boolean withTarget) {
return true;
}
@Override
public String getForUpdateString(final SelectForUpdateType type,
final int waitSeconds, final Pair<String, String>... aliases) {
final StringBuilder buf = new StringBuilder(100).append(" for update");
if (aliases.length > 0) {
buf.append(" of ");
for (final Pair<String, String> alias : aliases) {
buf.append(alias.getFirst()).append('.').append(
alias.getSecond()).append(", ");
}
buf.setLength(buf.length() - 2);
}
switch (type) {
case NORMAL:
break;
case NOWAIT:
buf.append(" nowait");
break;
case WAIT:
buf.append(" wait ").append(waitSeconds);
break;
}
return new String(buf);
}
@Override
public String getHintComment(final String hint) {
return "/*+ " + hint + " */ ";
}
@Override
public boolean isUniqueConstraintViolation(Throwable t) {
final Integer code = getErrorCode(t);
if (code != null) {
return uniqueConstraintViolationCode == code.intValue();
}
return false;
}
}