/* * 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; } }