Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » orm » ibatis » [javadoc | source]
    1   /*
    2    * Copyright 2002-2007 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.orm.ibatis;
   18   
   19   import java.sql.Connection;
   20   import java.sql.SQLException;
   21   import java.util.List;
   22   import java.util.Map;
   23   
   24   import javax.sql.DataSource;
   25   
   26   import com.ibatis.common.util.PaginatedList;
   27   import com.ibatis.sqlmap.client.SqlMapClient;
   28   import com.ibatis.sqlmap.client.SqlMapExecutor;
   29   import com.ibatis.sqlmap.client.SqlMapSession;
   30   import com.ibatis.sqlmap.client.event.RowHandler;
   31   import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
   32   
   33   import org.springframework.dao.DataAccessException;
   34   import org.springframework.dao.InvalidDataAccessApiUsageException;
   35   import org.springframework.jdbc.CannotGetJdbcConnectionException;
   36   import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;
   37   import org.springframework.jdbc.datasource.DataSourceUtils;
   38   import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
   39   import org.springframework.jdbc.support.JdbcAccessor;
   40   import org.springframework.util.Assert;
   41   
   42   /**
   43    * Helper class that simplifies data access via the iBATIS
   44    * {@link com.ibatis.sqlmap.client.SqlMapClient} API, converting checked
   45    * SQLExceptions into unchecked DataAccessExceptions, following the
   46    * <code>org.springframework.dao</code> exception hierarchy.
   47    * Uses the same {@link org.springframework.jdbc.support.SQLExceptionTranslator}
   48    * mechanism as {@link org.springframework.jdbc.core.JdbcTemplate}.
   49    *
   50    * <p>The main method of this class executes a callback that implements a
   51    * data access action. Furthermore, this class provides numerous convenience
   52    * methods that mirror {@link com.ibatis.sqlmap.client.SqlMapExecutor}'s
   53    * execution methods.
   54    *
   55    * <p>It is generally recommended to use the convenience methods on this template
   56    * for plain query/insert/update/delete operations. However, for more complex
   57    * operations like batch updates, a custom SqlMapClientCallback must be implemented,
   58    * usually as anonymous inner class. For example:
   59    *
   60    * <pre class="code">
   61    * getSqlMapClientTemplate().execute(new SqlMapClientCallback() {
   62    * 	 public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
   63    * 		 executor.startBatch();
   64    * 		 executor.update("insertSomething", "myParamValue");
   65    * 		 executor.update("insertSomethingElse", "myOtherParamValue");
   66    * 		 executor.executeBatch();
   67    * 		 return null;
   68    * 	 }
   69    * });</pre>
   70    *
   71    * The template needs a SqlMapClient to work on, passed in via the "sqlMapClient"
   72    * property. A Spring context typically uses a {@link SqlMapClientFactoryBean}
   73    * to build the SqlMapClient. The template an additionally be configured with a
   74    * DataSource for fetching Connections, although this is not necessary if a
   75    * DataSource is specified for the SqlMapClient itself (typically through
   76    * SqlMapClientFactoryBean's "dataSource" property).
   77    *
   78    * @author Juergen Hoeller
   79    * @since 24.02.2004
   80    * @see #execute
   81    * @see #setSqlMapClient
   82    * @see #setDataSource
   83    * @see #setExceptionTranslator
   84    * @see SqlMapClientFactoryBean#setDataSource
   85    * @see com.ibatis.sqlmap.client.SqlMapClient#getDataSource
   86    * @see com.ibatis.sqlmap.client.SqlMapExecutor
   87    */
   88   public class SqlMapClientTemplate extends JdbcAccessor implements SqlMapClientOperations {
   89   
   90   	private SqlMapClient sqlMapClient;
   91   
   92   	private boolean lazyLoadingAvailable = true;
   93   
   94   
   95   	/**
   96   	 * Create a new SqlMapClientTemplate.
   97   	 */
   98   	public SqlMapClientTemplate() {
   99   	}
  100   
  101   	/**
  102   	 * Create a new SqlMapTemplate.
  103   	 * @param sqlMapClient iBATIS SqlMapClient that defines the mapped statements
  104   	 */
  105   	public SqlMapClientTemplate(SqlMapClient sqlMapClient) {
  106   		setSqlMapClient(sqlMapClient);
  107   		afterPropertiesSet();
  108   	}
  109   
  110   	/**
  111   	 * Create a new SqlMapTemplate.
  112   	 * @param dataSource JDBC DataSource to obtain connections from
  113   	 * @param sqlMapClient iBATIS SqlMapClient that defines the mapped statements
  114   	 */
  115   	public SqlMapClientTemplate(DataSource dataSource, SqlMapClient sqlMapClient) {
  116   		setDataSource(dataSource);
  117   		setSqlMapClient(sqlMapClient);
  118   		afterPropertiesSet();
  119   	}
  120   
  121   
  122   	/**
  123   	 * Set the iBATIS Database Layer SqlMapClient that defines the mapped statements.
  124   	 */
  125   	public void setSqlMapClient(SqlMapClient sqlMapClient) {
  126   		this.sqlMapClient = sqlMapClient;
  127   	}
  128   
  129   	/**
  130   	 * Return the iBATIS Database Layer SqlMapClient that this template works with.
  131   	 */
  132   	public SqlMapClient getSqlMapClient() {
  133   		return this.sqlMapClient;
  134   	}
  135   
  136   	/**
  137   	 * If no DataSource specified, use SqlMapClient's DataSource.
  138   	 * @see com.ibatis.sqlmap.client.SqlMapClient#getDataSource()
  139   	 */
  140   	public DataSource getDataSource() {
  141   		DataSource ds = super.getDataSource();
  142   		return (ds != null ? ds : this.sqlMapClient.getDataSource());
  143   	}
  144   
  145   	public void afterPropertiesSet() {
  146   		if (this.sqlMapClient == null) {
  147   			throw new IllegalArgumentException("Property 'sqlMapClient' is required");
  148   		}
  149   		if (this.sqlMapClient instanceof ExtendedSqlMapClient) {
  150   			// Check whether iBATIS lazy loading is available, that is,
  151   			// whether a DataSource was specified on the SqlMapClient itself.
  152   			this.lazyLoadingAvailable = (((ExtendedSqlMapClient) this.sqlMapClient).getDelegate().getTxManager() != null);
  153   		}
  154   		super.afterPropertiesSet();
  155   	}
  156   
  157   
  158   	/**
  159   	 * Execute the given data access action on a SqlMapExecutor.
  160   	 * @param action callback object that specifies the data access action
  161   	 * @return a result object returned by the action, or <code>null</code>
  162   	 * @throws DataAccessException in case of SQL Maps errors
  163   	 */
  164   	public Object execute(SqlMapClientCallback action) throws DataAccessException {
  165   		Assert.notNull(action, "Callback object must not be null");
  166   		Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");
  167   
  168   		// We always needs to use a SqlMapSession, as we need to pass a Spring-managed
  169   		// Connection (potentially transactional) in. This shouldn't be necessary if
  170   		// we run against a TransactionAwareDataSourceProxy underneath, but unfortunately
  171   		// we still need it to make iBATIS batch execution work properly: If iBATIS
  172   		// doesn't recognize an existing transaction, it automatically executes the
  173   		// batch for every single statement...
  174   
  175   		SqlMapSession session = this.sqlMapClient.openSession();
  176   		if (logger.isDebugEnabled()) {
  177   			logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation");
  178   		}
  179   		Connection ibatisCon = null;
  180   
  181   		try {
  182   			Connection springCon = null;
  183   			DataSource dataSource = getDataSource();
  184   			boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);
  185   
  186   			// Obtain JDBC Connection to operate on...
  187   			try {
  188   				ibatisCon = session.getCurrentConnection();
  189   				if (ibatisCon == null) {
  190   					springCon = (transactionAware ?
  191   							dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));
  192   					session.setUserConnection(springCon);
  193   					if (logger.isDebugEnabled()) {
  194   						logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");
  195   					}
  196   				}
  197   				else {
  198   					if (logger.isDebugEnabled()) {
  199   						logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");
  200   					}
  201   				}
  202   			}
  203   			catch (SQLException ex) {
  204   				throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
  205   			}
  206   
  207   			// Execute given callback...
  208   			try {
  209   				return action.doInSqlMapClient(session);
  210   			}
  211   			catch (SQLException ex) {
  212   				throw getExceptionTranslator().translate("SqlMapClient operation", null, ex);
  213   			}
  214   			finally {
  215   				try {
  216   					if (springCon != null) {
  217   						if (transactionAware) {
  218   							springCon.close();
  219   						}
  220   						else {
  221   							DataSourceUtils.doReleaseConnection(springCon, dataSource);
  222   						}
  223   					}
  224   				}
  225   				catch (Throwable ex) {
  226   					logger.debug("Could not close JDBC Connection", ex);
  227   				}
  228   			}
  229   
  230   			// Processing finished - potentially session still to be closed.
  231   		}
  232   		finally {
  233   			// Only close SqlMapSession if we know we've actually opened it
  234   			// at the present level.
  235   			if (ibatisCon == null) {
  236   				session.close();
  237   			}
  238   		}
  239   	}
  240   
  241   	/**
  242   	 * Execute the given data access action on a SqlMapExecutor,
  243   	 * expecting a List result.
  244   	 * @param action callback object that specifies the data access action
  245   	 * @return the List result
  246   	 * @throws DataAccessException in case of SQL Maps errors
  247   	 */
  248   	public List executeWithListResult(SqlMapClientCallback action) throws DataAccessException {
  249   		return (List) execute(action);
  250   	}
  251   
  252   	/**
  253   	 * Execute the given data access action on a SqlMapExecutor,
  254   	 * expecting a Map result.
  255   	 * @param action callback object that specifies the data access action
  256   	 * @return the Map result
  257   	 * @throws DataAccessException in case of SQL Maps errors
  258   	 */
  259   	public Map executeWithMapResult(SqlMapClientCallback action) throws DataAccessException {
  260   		return (Map) execute(action);
  261   	}
  262   
  263   
  264   	public Object queryForObject(String statementName) throws DataAccessException {
  265   		return queryForObject(statementName, null);
  266   	}
  267   
  268   	public Object queryForObject(final String statementName, final Object parameterObject)
  269   			throws DataAccessException {
  270   
  271   		return execute(new SqlMapClientCallback() {
  272   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  273   				return executor.queryForObject(statementName, parameterObject);
  274   			}
  275   		});
  276   	}
  277   
  278   	public Object queryForObject(
  279   			final String statementName, final Object parameterObject, final Object resultObject)
  280   			throws DataAccessException {
  281   
  282   		return execute(new SqlMapClientCallback() {
  283   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  284   				return executor.queryForObject(statementName, parameterObject, resultObject);
  285   			}
  286   		});
  287   	}
  288   
  289   	public List queryForList(String statementName) throws DataAccessException {
  290   		return queryForList(statementName, null);
  291   	}
  292   
  293   	public List queryForList(final String statementName, final Object parameterObject)
  294   			throws DataAccessException {
  295   
  296   		return executeWithListResult(new SqlMapClientCallback() {
  297   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  298   				return executor.queryForList(statementName, parameterObject);
  299   			}
  300   		});
  301   	}
  302   
  303   	public List queryForList(String statementName, int skipResults, int maxResults)
  304   			throws DataAccessException {
  305   
  306   		return queryForList(statementName, null, skipResults, maxResults);
  307   	}
  308   
  309   	public List queryForList(
  310   			final String statementName, final Object parameterObject, final int skipResults, final int maxResults)
  311   			throws DataAccessException {
  312   
  313   		return executeWithListResult(new SqlMapClientCallback() {
  314   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  315   				return executor.queryForList(statementName, parameterObject, skipResults, maxResults);
  316   			}
  317   		});
  318   	}
  319   
  320   	public void queryWithRowHandler(String statementName, RowHandler rowHandler)
  321   			throws DataAccessException {
  322   
  323   		queryWithRowHandler(statementName, null, rowHandler);
  324   	}
  325   
  326   	public void queryWithRowHandler(
  327   			final String statementName, final Object parameterObject, final RowHandler rowHandler)
  328   			throws DataAccessException {
  329   
  330   		execute(new SqlMapClientCallback() {
  331   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  332   				executor.queryWithRowHandler(statementName, parameterObject, rowHandler);
  333   				return null;
  334   			}
  335   		});
  336   	}
  337   
  338   	/**
  339   	 * @deprecated as of iBATIS 2.3.0
  340   	 */
  341   	public PaginatedList queryForPaginatedList(String statementName, int pageSize)
  342   			throws DataAccessException {
  343   
  344   		return queryForPaginatedList(statementName, null, pageSize);
  345   	}
  346   
  347   	/**
  348   	 * @deprecated as of iBATIS 2.3.0
  349   	 */
  350   	public PaginatedList queryForPaginatedList(
  351   			final String statementName, final Object parameterObject, final int pageSize)
  352   			throws DataAccessException {
  353   
  354   		// Throw exception if lazy loading will not work.
  355   		if (!this.lazyLoadingAvailable) {
  356   			throw new InvalidDataAccessApiUsageException(
  357   					"SqlMapClient needs to have DataSource to allow for lazy loading" +
  358   					" - specify SqlMapClientFactoryBean's 'dataSource' property");
  359   		}
  360   
  361   		return (PaginatedList) execute(new SqlMapClientCallback() {
  362   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  363   				return executor.queryForPaginatedList(statementName, parameterObject, pageSize);
  364   			}
  365   		});
  366   	}
  367   
  368   	public Map queryForMap(
  369   			final String statementName, final Object parameterObject, final String keyProperty)
  370   			throws DataAccessException {
  371   
  372   		return executeWithMapResult(new SqlMapClientCallback() {
  373   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  374   				return executor.queryForMap(statementName, parameterObject, keyProperty);
  375   			}
  376   		});
  377   	}
  378   
  379   	public Map queryForMap(
  380   			final String statementName, final Object parameterObject, final String keyProperty, final String valueProperty)
  381   			throws DataAccessException {
  382   
  383   		return executeWithMapResult(new SqlMapClientCallback() {
  384   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  385   				return executor.queryForMap(statementName, parameterObject, keyProperty, valueProperty);
  386   			}
  387   		});
  388   	}
  389   
  390   	public Object insert(String statementName) throws DataAccessException {
  391   		return insert(statementName, null);
  392   	}
  393   
  394   	public Object insert(final String statementName, final Object parameterObject)
  395   			throws DataAccessException {
  396   
  397   		return execute(new SqlMapClientCallback() {
  398   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  399   				return executor.insert(statementName, parameterObject);
  400   			}
  401   		});
  402   	}
  403   
  404   	public int update(String statementName) throws DataAccessException {
  405   		return update(statementName, null);
  406   	}
  407   
  408   	public int update(final String statementName, final Object parameterObject)
  409   			throws DataAccessException {
  410   
  411   		Integer result = (Integer) execute(new SqlMapClientCallback() {
  412   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  413   				return new Integer(executor.update(statementName, parameterObject));
  414   			}
  415   		});
  416   		return result.intValue();
  417   	}
  418   
  419   	public void update(String statementName, Object parameterObject, int requiredRowsAffected)
  420   			throws DataAccessException {
  421   
  422   		int actualRowsAffected = update(statementName, parameterObject);
  423   		if (actualRowsAffected != requiredRowsAffected) {
  424   			throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(
  425   					statementName, requiredRowsAffected, actualRowsAffected);
  426   		}
  427   	}
  428   
  429   	public int delete(String statementName) throws DataAccessException {
  430   		return delete(statementName, null);
  431   	}
  432   
  433   	public int delete(final String statementName, final Object parameterObject)
  434   			throws DataAccessException {
  435   
  436   		Integer result = (Integer) execute(new SqlMapClientCallback() {
  437   			public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  438   				return new Integer(executor.delete(statementName, parameterObject));
  439   			}
  440   		});
  441   		return result.intValue();
  442   	}
  443   
  444   	public void delete(String statementName, Object parameterObject, int requiredRowsAffected)
  445   			throws DataAccessException {
  446   
  447   		int actualRowsAffected = delete(statementName, parameterObject);
  448   		if (actualRowsAffected != requiredRowsAffected) {
  449   			throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(
  450   					statementName, requiredRowsAffected, actualRowsAffected);
  451   		}
  452   	}
  453   
  454   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » orm » ibatis » [javadoc | source]