001: /*
002: * Copyright 2002-2005 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.orm.ibatis.support;
018:
019: import java.io.IOException;
020: import java.sql.CallableStatement;
021: import java.sql.PreparedStatement;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024:
025: import com.ibatis.sqlmap.engine.type.BaseTypeHandler;
026:
027: import org.springframework.jdbc.datasource.DataSourceUtils;
028: import org.springframework.jdbc.support.lob.LobCreator;
029: import org.springframework.jdbc.support.lob.LobHandler;
030: import org.springframework.orm.ibatis.SqlMapClientFactoryBean;
031: import org.springframework.transaction.support.TransactionSynchronizationAdapter;
032: import org.springframework.transaction.support.TransactionSynchronizationManager;
033:
034: /**
035: * Abstract base class for iBATIS TypeHandler implementations that map to LOBs.
036: * Retrieves the LobHandler to use from SqlMapClientFactoryBean at config time.
037: *
038: * <p>For writing LOBs, an active Spring transaction synchronization is required,
039: * to be able to register a synchronization that closes the LobCreator.
040: *
041: * <p>Offers template methods for setting parameters and getting result values,
042: * passing in the LobHandler or LobCreator to use.
043: *
044: * @author Juergen Hoeller
045: * @since 1.1.5
046: * @see org.springframework.jdbc.support.lob.LobHandler
047: * @see org.springframework.jdbc.support.lob.LobCreator
048: * @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#setLobHandler
049: */
050: public abstract class AbstractLobTypeHandler extends BaseTypeHandler {
051:
052: /**
053: * Order value for TransactionSynchronization objects that clean up LobCreators.
054: * Return DataSourceUtils.#CONNECTION_SYNCHRONIZATION_ORDER - 100 to execute
055: * LobCreator cleanup before JDBC Connection cleanup, if any.
056: * @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
057: */
058: public static final int LOB_CREATOR_SYNCHRONIZATION_ORDER = DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 200;
059:
060: private LobHandler lobHandler;
061:
062: /**
063: * Constructor used by iBATIS: fetches config-time LobHandler from
064: * SqlMapClientFactoryBean.
065: * @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#getConfigTimeLobHandler
066: */
067: public AbstractLobTypeHandler() {
068: this (SqlMapClientFactoryBean.getConfigTimeLobHandler());
069: }
070:
071: /**
072: * Constructor used for testing: takes an explicit LobHandler.
073: */
074: protected AbstractLobTypeHandler(LobHandler lobHandler) {
075: if (lobHandler == null) {
076: throw new IllegalStateException(
077: "No LobHandler found for configuration - "
078: + "lobHandler property must be set on SqlMapClientFactoryBean");
079: }
080: this .lobHandler = lobHandler;
081: }
082:
083: /**
084: * This implementation delegates to setParameterInternal,
085: * passing in a transaction-synchronized LobCreator for the
086: * LobHandler of this type.
087: * @see #setParameterInternal
088: */
089: public final void setParameter(PreparedStatement ps, int i,
090: Object parameter, String jdbcType) throws SQLException {
091:
092: if (!TransactionSynchronizationManager
093: .isSynchronizationActive()) {
094: throw new IllegalStateException(
095: "Spring transaction synchronization needs to be active for "
096: + "setting values in iBATIS TypeHandlers that delegate to a Spring LobHandler");
097: }
098: final LobCreator lobCreator = this .lobHandler.getLobCreator();
099: try {
100: setParameterInternal(ps, i, parameter, jdbcType, lobCreator);
101: } catch (IOException ex) {
102: throw new SQLException("I/O errors during LOB access: "
103: + ex.getMessage());
104: }
105:
106: TransactionSynchronizationManager
107: .registerSynchronization(new LobCreatorSynchronization(
108: lobCreator));
109: }
110:
111: /**
112: * This implementation delegates to the getResult version
113: * that takes a column index.
114: * @see #getResult(java.sql.ResultSet, String)
115: * @see java.sql.ResultSet#findColumn
116: */
117: public final Object getResult(ResultSet rs, String columnName)
118: throws SQLException {
119: return getResult(rs, rs.findColumn(columnName));
120: }
121:
122: /**
123: * This implementation delegates to getResultInternal,
124: * passing in the LobHandler of this type.
125: * @see #getResultInternal
126: */
127: public final Object getResult(ResultSet rs, int columnIndex)
128: throws SQLException {
129: try {
130: return getResultInternal(rs, columnIndex, this .lobHandler);
131: } catch (IOException ex) {
132: throw new SQLException("I/O errors during LOB access: "
133: + ex.getClass().getName() + ": " + ex.getMessage());
134: }
135: }
136:
137: /**
138: * This implementation always throws a SQLException:
139: * retrieving LOBs from a CallableStatement is not supported.
140: */
141: public Object getResult(CallableStatement cs, int columnIndex)
142: throws SQLException {
143: throw new SQLException(
144: "Retrieving LOBs from a CallableStatement is not supported");
145: }
146:
147: /**
148: * Template method to set the given value on the given statement.
149: * @param ps the PreparedStatement to set on
150: * @param index the statement parameter index
151: * @param value the parameter value to set
152: * @param jdbcType the JDBC type of the parameter
153: * @param lobCreator the LobCreator to use
154: * @throws SQLException if thrown by JDBC methods
155: * @throws IOException if thrown by streaming methods
156: */
157: protected abstract void setParameterInternal(PreparedStatement ps,
158: int index, Object value, String jdbcType,
159: LobCreator lobCreator) throws SQLException, IOException;
160:
161: /**
162: * Template method to extract a value from the given result set.
163: * @param rs the ResultSet to extract from
164: * @param index the index in the ResultSet
165: * @param lobHandler the LobHandler to use
166: * @return the extracted value
167: * @throws SQLException if thrown by JDBC methods
168: * @throws IOException if thrown by streaming methods
169: */
170: protected abstract Object getResultInternal(ResultSet rs,
171: int index, LobHandler lobHandler) throws SQLException,
172: IOException;
173:
174: /**
175: * Callback for resource cleanup at the end of a Spring transaction.
176: * Invokes LobCreator.close to clean up temporary LOBs that might have been created.
177: * @see org.springframework.jdbc.support.lob.LobCreator#close
178: */
179: private static class LobCreatorSynchronization extends
180: TransactionSynchronizationAdapter {
181:
182: private final LobCreator lobCreator;
183:
184: public LobCreatorSynchronization(LobCreator lobCreator) {
185: this .lobCreator = lobCreator;
186: }
187:
188: public int getOrder() {
189: return LOB_CREATOR_SYNCHRONIZATION_ORDER;
190: }
191:
192: public void beforeCompletion() {
193: this.lobCreator.close();
194: }
195: }
196:
197: }
|