265-273 rows
/* * Copyright 2002-2010 the original author or authors. * * 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.springframework.orm.ibatis;import java.sql.Connection;import java.sql.SQLException;import java.util.List;import java.util.Map;import javax.sql.DataSource;import com.ibatis.sqlmap.client.SqlMapClient;import com.ibatis.sqlmap.client.SqlMapExecutor;import com.ibatis.sqlmap.client.SqlMapSession;import com.ibatis.sqlmap.client.event.RowHandler;import org.springframework.dao.DataAccessException;import org.springframework.jdbc.CannotGetJdbcConnectionException;import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;import org.springframework.jdbc.datasource.DataSourceUtils;import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;import org.springframework.jdbc.support.JdbcAccessor;import org.springframework.util.Assert;/** * Helper class that simplifies data access via the iBATIS * {@link com.ibatis.sqlmap.client.SqlMapClient} API, converting checked * SQLExceptions into unchecked DataAccessExceptions, following the * <code>org.springframework.dao</code> exception hierarchy. * Uses the same {@link org.springframework.jdbc.support.SQLExceptionTranslator} * mechanism as {@link org.springframework.jdbc.core.JdbcTemplate}. * * <p>The main method of this class executes a callback that implements a * data access action. Furthermore, this class provides numerous convenience * methods that mirror {@link com.ibatis.sqlmap.client.SqlMapExecutor}'s * execution methods. * * <p>It is generally recommended to use the convenience methods on this template * for plain query/insert/update/delete operations. However, for more complex * operations like batch updates, a custom SqlMapClientCallback must be implemented, * usually as anonymous inner class. For example: * * <pre class="code"> * getSqlMapClientTemplate().execute(new SqlMapClientCallback() { * public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { * executor.startBatch(); * executor.update("insertSomething", "myParamValue"); * executor.update("insertSomethingElse", "myOtherParamValue"); * executor.executeBatch(); * return null; * } * });</pre> * * The template needs a SqlMapClient to work on, passed in via the "sqlMapClient" * property. A Spring context typically uses a {@link SqlMapClientFactoryBean} * to build the SqlMapClient. The template an additionally be configured with a * DataSource for fetching Connections, although this is not necessary if a * DataSource is specified for the SqlMapClient itself (typically through * SqlMapClientFactoryBean's "dataSource" property). * * @author Juergen Hoeller * @since 24.02.2004 * @see #execute * @see #setSqlMapClient * @see #setDataSource * @see #setExceptionTranslator * @see SqlMapClientFactoryBean#setDataSource * @see com.ibatis.sqlmap.client.SqlMapClient#getDataSource * @see com.ibatis.sqlmap.client.SqlMapExecutor */public class SqlMapClientTemplate extends JdbcAccessor implements SqlMapClientOperations {private SqlMapClient sqlMapClient;/** * Create a new SqlMapClientTemplate. */public SqlMapClientTemplate() {}/** * Create a new SqlMapTemplate. * @param sqlMapClient iBATIS SqlMapClient that defines the mapped statements */public SqlMapClientTemplate(SqlMapClient sqlMapClient) {setSqlMapClient(sqlMapClient);afterPropertiesSet();}/** * Create a new SqlMapTemplate. * @param dataSource JDBC DataSource to obtain connections from * @param sqlMapClient iBATIS SqlMapClient that defines the mapped statements */public SqlMapClientTemplate(DataSource dataSource, SqlMapClient sqlMapClient) {setDataSource(dataSource);setSqlMapClient(sqlMapClient);afterPropertiesSet();}/** * Set the iBATIS Database Layer SqlMapClient that defines the mapped statements. */public void setSqlMapClient(SqlMapClient sqlMapClient) {this.sqlMapClient = sqlMapClient;}/** * Return the iBATIS Database Layer SqlMapClient that this template works with. */public SqlMapClient getSqlMapClient() {return this.sqlMapClient;}/** * If no DataSource specified, use SqlMapClient's DataSource. * @see com.ibatis.sqlmap.client.SqlMapClient#getDataSource() */@Overridepublic DataSource getDataSource() {DataSource ds = super.getDataSource();return (ds != null ? ds : this.sqlMapClient.getDataSource());}@Overridepublic void afterPropertiesSet() {if (this.sqlMapClient == null) {throw new IllegalArgumentException("Property 'sqlMapClient' is required");}super.afterPropertiesSet();}/** * Execute the given data access action on a SqlMapExecutor. * @param action callback object that specifies the data access action * @return a result object returned by the action, or <code>null</code> * @throws DataAccessException in case of SQL Maps errors */public <T> T execute(SqlMapClientCallback<T> action) throws DataAccessException {Assert.notNull(action, "Callback object must not be null");Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");// We always need to use a SqlMapSession, as we need to pass a Spring-managed// Connection (potentially transactional) in. This shouldn't be necessary if// we run against a TransactionAwareDataSourceProxy underneath, but unfortunately// we still need it to make iBATIS batch execution work properly: If iBATIS// doesn't recognize an existing transaction, it automatically executes the// batch for every single statement...SqlMapSession session = this.sqlMapClient.openSession();if (logger.isDebugEnabled()) {logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation");}Connection ibatisCon = null;try {Connection springCon = null;DataSource dataSource = getDataSource();boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);// Obtain JDBC Connection to operate on...try {ibatisCon = session.getCurrentConnection();if (ibatisCon == null) {springCon = (transactionAware ?dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));session.setUserConnection(springCon);if (logger.isDebugEnabled()) {logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");}}else {if (logger.isDebugEnabled()) {logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");}}}catch (SQLException ex) {throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);}// Execute given callback...try {return action.doInSqlMapClient(session);}catch (SQLException ex) {throw getExceptionTranslator().translate("SqlMapClient operation", null, ex);}finally {try {if (springCon != null) {if (transactionAware) {springCon.close();}else {DataSourceUtils.doReleaseConnection(springCon, dataSource);}}}catch (Throwable ex) {logger.debug("Could not close JDBC Connection", ex);}}// Processing finished - potentially session still to be closed.}finally {// Only close SqlMapSession if we know we've actually opened it// at the present level.if (ibatisCon == null) {session.close();}}}/** * Execute the given data access action on a SqlMapExecutor, * expecting a List result. * @param action callback object that specifies the data access action * @return the List result * @throws DataAccessException in case of SQL Maps errors * @deprecated as of Spring 3.0 - not really needed anymore with generic * {@link #execute} method */@Deprecatedpublic List executeWithListResult(SqlMapClientCallback<List> action) throws DataAccessException {return execute(action);}/** * Execute the given data access action on a SqlMapExecutor, * expecting a Map result. * @param action callback object that specifies the data access action * @return the Map result * @throws DataAccessException in case of SQL Maps errors * @deprecated as of Spring 3.0 - not really needed anymore with generic * {@link #execute} method */@Deprecatedpublic Map executeWithMapResult(SqlMapClientCallback<Map> action) throws DataAccessException {return execute(action);}public Object queryForObject(String statementName) throws DataAccessException {return queryForObject(statementName, null);}public Object queryForObject(final String statementName, final Object parameterObject)throws DataAccessException {return execute(new SqlMapClientCallback<Object>() {public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {return executor.queryForObject(statementName, parameterObject);}});}public Object queryForObject(final String statementName, final Object parameterObject, final Object resultObject)throws DataAccessException {return execute(new SqlMapClientCallback<Object>() {public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {return executor.queryForObject(statementName, parameterObject, resultObject);}});}public List queryForList(String statementName) throws DataAccessException {return queryForList(statementName, null);}public List queryForList(final String statementName, final Object parameterObject)throws DataAccessException {return execute(new SqlMapClientCallback<List>() {public List doInSqlMapClient(SqlMapExecutor executor) throws SQLException {return executor.queryForList(statementName, parameterObject);}});}public List queryForList(String statementName, int skipResults, int maxResults)throws DataAccessException {return queryForList(statementName, null, skipResults, maxResults);}public List queryForList(final String statementName, final Object parameterObject, final int skipResults, final int maxResults)throws DataAccessException {return execute(new SqlMapClientCallback<List>() {public List doInSqlMapClient(SqlMapExecutor executor) throws SQLException {return executor.queryForList(statementName, parameterObject, skipResults, maxResults);}});}public void queryWithRowHandler(String statementName, RowHandler rowHandler)throws DataAccessException {queryWithRowHandler(statementName, null, rowHandler);}public void queryWithRowHandler(final String statementName, final Object parameterObject, final RowHandler rowHandler)throws DataAccessException {execute(new SqlMapClientCallback<Object>() {public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {executor.queryWithRowHandler(statementName, parameterObject, rowHandler);return null;}});}public Map queryForMap(final String statementName, final Object parameterObject, final String keyProperty)throws DataAccessException {return execute(new SqlMapClientCallback<Map>() {public Map doInSqlMapClient(SqlMapExecutor executor) throws SQLException {return executor.queryForMap(statementName, parameterObject, keyProperty);}});}public Map queryForMap(final String statementName, final Object parameterObject, final String keyProperty, final String valueProperty)throws DataAccessException {return execute(new SqlMapClientCallback<Map>() {public Map doInSqlMapClient(SqlMapExecutor executor) throws SQLException {return executor.queryForMap(statementName, parameterObject, keyProperty, valueProperty);}});}public Object insert(String statementName) throws DataAccessException {return insert(statementName, null);}public Object insert(final String statementName, final Object parameterObject)throws DataAccessException {return execute(new SqlMapClientCallback<Object>() {public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {return executor.insert(statementName, parameterObject);}});}public int update(String statementName) throws DataAccessException {return update(statementName, null);}public int update(final String statementName, final Object parameterObject)throws DataAccessException {return execute(new SqlMapClientCallback<Integer>() {public Integer doInSqlMapClient(SqlMapExecutor executor) throws SQLException {return executor.update(statementName, parameterObject);}});}public void update(String statementName, Object parameterObject, int requiredRowsAffected)throws DataAccessException {int actualRowsAffected = update(statementName, parameterObject);if (actualRowsAffected != requiredRowsAffected) {throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(statementName, requiredRowsAffected, actualRowsAffected);}}public int delete(String statementName) throws DataAccessException {return delete(statementName, null);}public int delete(final String statementName, final Object parameterObject)throws DataAccessException {return execute(new SqlMapClientCallback<Integer>() {public Integer doInSqlMapClient(SqlMapExecutor executor) throws SQLException {return executor.delete(statementName, parameterObject);}});}public void delete(String statementName, Object parameterObject, int requiredRowsAffected)throws DataAccessException {int actualRowsAffected = delete(statementName, parameterObject);if (actualRowsAffected != requiredRowsAffected) {throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(statementName, requiredRowsAffected, actualRowsAffected);}}}
/* * Copyright 2002-2008 the original author or authors. * * 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.springframework.orm.ibatis;import java.sql.SQLException;import com.ibatis.sqlmap.client.SqlMapExecutor;/** * Callback interface for data access code that works with the iBATIS * {@link com.ibatis.sqlmap.client.SqlMapExecutor} interface. To be used * with {@link SqlMapClientTemplate}'s <code>execute</code> method, * assumably often as anonymous classes within a method implementation. * * @author Juergen Hoeller * @since 24.02.2004 * @see SqlMapClientTemplate * @see org.springframework.jdbc.datasource.DataSourceTransactionManager */public interface SqlMapClientCallback<T> {/** * Gets called by <code>SqlMapClientTemplate.execute</code> with an active * <code>SqlMapExecutor</code>. Does not need to care about activating * or closing the <code>SqlMapExecutor</code>, or handling transactions. * * <p>If called without a thread-bound JDBC transaction (initiated by * DataSourceTransactionManager), the code will simply get executed on the * underlying JDBC connection with its transactional semantics. If using * a JTA-aware DataSource, the JDBC connection and thus the callback code * will be transactional if a JTA transaction is active. * * <p>Allows for returning a result object created within the callback, * i.e. a domain object or a collection of domain objects. * A thrown custom RuntimeException is treated as an application exception: * It gets propagated to the caller of the template. * * @param executor an active iBATIS SqlMapSession, passed-in as * SqlMapExecutor interface here to avoid manual lifecycle handling * @return a result object, or <code>null</code> if none * @throws SQLException if thrown by the iBATIS SQL Maps API * @see SqlMapClientTemplate#execute * @see SqlMapClientTemplate#executeWithListResult * @see SqlMapClientTemplate#executeWithMapResult */T doInSqlMapClient(SqlMapExecutor executor) throws SQLException;}