1. /*
  2. * Copyright 2002-2004 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. package org.springframework.orm.ibatis;
  17. import java.sql.Connection;
  18. import java.sql.SQLException;
  19. import java.util.List;
  20. import java.util.Map;
  21. import javax.sql.DataSource;
  22. import com.ibatis.common.util.PaginatedList;
  23. import com.ibatis.sqlmap.client.SqlMapClient;
  24. import com.ibatis.sqlmap.client.SqlMapExecutor;
  25. import com.ibatis.sqlmap.client.SqlMapSession;
  26. import com.ibatis.sqlmap.client.event.RowHandler;
  27. import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
  28. import org.springframework.dao.DataAccessException;
  29. import org.springframework.dao.InvalidDataAccessApiUsageException;
  30. import org.springframework.jdbc.datasource.DataSourceUtils;
  31. import org.springframework.jdbc.support.JdbcAccessor;
  32. /**
  33. * Helper class that simplifies data access via the SqlMapClient API of the
  34. * iBATIS Database Layer, and converts checked SQLExceptions into unchecked
  35. * DataAccessExceptions, compatible to the org.springframework.dao exception
  36. * hierarchy. Uses the same SQLExceptionTranslator mechanism as JdbcTemplate.
  37. *
  38. * <p>The main method of this class executes a callback that implements a
  39. * data access action. Furthermore, this class provides numerous convenience
  40. * methods that mirror SqlMapSession's execution methods. See the SqlMapClient
  41. * javadocs for details on those methods.
  42. *
  43. * <p>Needs a SqlMapClient to work on, passed in via the "sqlMapClient" property.
  44. * Can additionally be configured with a DataSource for fetching Connections,
  45. * although this is not necessary if a DataSource is specified for the SqlMapClient.
  46. *
  47. * <p>NOTE: The SqlMapClient/SqlMapSession API is the API of iBATIS SQL Maps 2.
  48. * With SQL Maps 1.x, the SqlMap/MappedStatement API has to be used.
  49. *
  50. * @author Juergen Hoeller
  51. * @since 24.02.2004
  52. * @see #execute
  53. * @see #setSqlMapClient
  54. * @see #setDataSource
  55. * @see #setExceptionTranslator
  56. * @see SqlMapClientFactoryBean#setDataSource
  57. * @see com.ibatis.sqlmap.client.SqlMapClient#getDataSource
  58. * @see com.ibatis.sqlmap.client.SqlMapExecutor
  59. */
  60. public class SqlMapClientTemplate extends JdbcAccessor implements SqlMapClientOperations {
  61. private SqlMapClient sqlMapClient;
  62. /**
  63. * Create a new SqlMapClientTemplate.
  64. */
  65. public SqlMapClientTemplate() {
  66. }
  67. /**
  68. * Create a new SqlMapTemplate.
  69. * @param dataSource JDBC DataSource to obtain connections from
  70. * @param sqlMapClient iBATIS SqlMapClient that defines the mapped statements
  71. */
  72. public SqlMapClientTemplate(DataSource dataSource, SqlMapClient sqlMapClient) {
  73. setDataSource(dataSource);
  74. this.sqlMapClient = sqlMapClient;
  75. }
  76. /**
  77. * Set the iBATIS Database Layer SqlMapClient that defines the mapped statements.
  78. */
  79. public void setSqlMapClient(SqlMapClient sqlMapClient) {
  80. this.sqlMapClient = sqlMapClient;
  81. }
  82. /**
  83. * Return the iBATIS Database Layer SqlMapClient that this template works with.
  84. */
  85. public SqlMapClient getSqlMapClient() {
  86. return sqlMapClient;
  87. }
  88. public void afterPropertiesSet() {
  89. if (this.sqlMapClient == null) {
  90. throw new IllegalArgumentException("sqlMapClient is required");
  91. }
  92. // if no DataSource specified, use SqlMapClient's DataSource
  93. if (getDataSource() == null) {
  94. setDataSource(this.sqlMapClient.getDataSource());
  95. }
  96. // call this last, to guarantee available DataSource
  97. super.afterPropertiesSet();
  98. }
  99. /**
  100. * Execute the given data access action on a SqlMapSession.
  101. * @param action callback object that specifies the data access action
  102. * @return a result object returned by the action, or null
  103. * @throws DataAccessException in case of SQL Maps errors
  104. */
  105. public Object execute(SqlMapClientCallback action) throws DataAccessException {
  106. SqlMapSession session = this.sqlMapClient.openSession();
  107. try {
  108. Connection con = DataSourceUtils.getConnection(getDataSource());
  109. try {
  110. session.setUserConnection(con);
  111. return action.doInSqlMapClient(session);
  112. }
  113. catch (SQLException ex) {
  114. throw getExceptionTranslator().translate("SqlMapClientTemplate", "(mapped statement)", ex);
  115. }
  116. finally {
  117. DataSourceUtils.closeConnectionIfNecessary(con, getDataSource());
  118. }
  119. }
  120. finally {
  121. session.close();
  122. }
  123. }
  124. /**
  125. * Execute the given data access action on a SqlMapSession,
  126. * expecting a List result.
  127. * @param action callback object that specifies the data access action
  128. * @return the List result
  129. * @throws DataAccessException in case of SQL Maps errors
  130. */
  131. public List executeWithListResult(SqlMapClientCallback action) throws DataAccessException {
  132. return (List) execute(action);
  133. }
  134. /**
  135. * Execute the given data access action on a SqlMapSession,
  136. * expecting a Map result.
  137. * @param action callback object that specifies the data access action
  138. * @return the Map result
  139. * @throws DataAccessException in case of SQL Maps errors
  140. */
  141. public Map executeWithMapResult(SqlMapClientCallback action) throws DataAccessException {
  142. return (Map) execute(action);
  143. }
  144. public Object queryForObject(final String statementName, final Object parameterObject)
  145. throws DataAccessException {
  146. return execute(new SqlMapClientCallback() {
  147. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  148. return executor.queryForObject(statementName, parameterObject);
  149. }
  150. });
  151. }
  152. public Object queryForObject(final String statementName, final Object parameterObject,
  153. final Object resultObject) throws DataAccessException {
  154. return execute(new SqlMapClientCallback() {
  155. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  156. return executor.queryForObject(statementName, parameterObject, resultObject);
  157. }
  158. });
  159. }
  160. public List queryForList(final String statementName, final Object parameterObject)
  161. throws DataAccessException {
  162. return executeWithListResult(new SqlMapClientCallback() {
  163. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  164. return executor.queryForList(statementName, parameterObject);
  165. }
  166. });
  167. }
  168. public List queryForList(final String statementName, final Object parameterObject,
  169. final int skipResults, final int maxResults)
  170. throws DataAccessException {
  171. return executeWithListResult(new SqlMapClientCallback() {
  172. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  173. return executor.queryForList(statementName, parameterObject, skipResults, maxResults);
  174. }
  175. });
  176. }
  177. public void queryWithRowHandler(final String statementName, final Object parameterObject,
  178. final RowHandler rowHandler) {
  179. execute(new SqlMapClientCallback() {
  180. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  181. executor.queryWithRowHandler(statementName, parameterObject, rowHandler);
  182. return null;
  183. }
  184. });
  185. }
  186. public List queryForList(final String statementName, final Object parameterObject,
  187. final RowHandler rowHandler) throws DataAccessException {
  188. return executeWithListResult(new SqlMapClientCallback() {
  189. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  190. return executor.queryForList(statementName, parameterObject, rowHandler);
  191. }
  192. });
  193. }
  194. public PaginatedList queryForPaginatedList(final String statementName, final Object parameterObject,
  195. final int pageSize) throws DataAccessException {
  196. // throw exception if lazy loading will not work
  197. if (this.sqlMapClient instanceof ExtendedSqlMapClient &&
  198. ((ExtendedSqlMapClient) this.sqlMapClient).getDelegate().getTxManager() == null) {
  199. throw new InvalidDataAccessApiUsageException("SqlMapClient needs to have DataSource to allow for lazy loading" +
  200. " - specify SqlMapClientFactoryBean's 'dataSource' property");
  201. }
  202. return (PaginatedList) execute(new SqlMapClientCallback() {
  203. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  204. return executor.queryForPaginatedList(statementName, parameterObject, pageSize);
  205. }
  206. });
  207. }
  208. public Map queryForMap(final String statementName, final Object parameterObject,
  209. final String keyProperty) throws DataAccessException {
  210. return executeWithMapResult(new SqlMapClientCallback() {
  211. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  212. return executor.queryForMap(statementName, parameterObject, keyProperty);
  213. }
  214. });
  215. }
  216. public Map queryForMap(final String statementName, final Object parameterObject,
  217. final String keyProperty, final String valueProperty)
  218. throws DataAccessException {
  219. return executeWithMapResult(new SqlMapClientCallback() {
  220. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  221. return executor.queryForMap(statementName, parameterObject, keyProperty, valueProperty);
  222. }
  223. });
  224. }
  225. public Object insert(final String statementName, final Object parameterObject)
  226. throws DataAccessException {
  227. return execute(new SqlMapClientCallback() {
  228. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  229. return executor.insert(statementName, parameterObject);
  230. }
  231. });
  232. }
  233. public int update(final String statementName, final Object parameterObject)
  234. throws DataAccessException {
  235. Integer result = (Integer) execute(new SqlMapClientCallback() {
  236. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  237. return new Integer(executor.update(statementName, parameterObject));
  238. }
  239. });
  240. return result.intValue();
  241. }
  242. public int delete(final String statementName, final Object parameterObject)
  243. throws DataAccessException {
  244. Integer result = (Integer) execute(new SqlMapClientCallback() {
  245. public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
  246. return new Integer(executor.delete(statementName, parameterObject));
  247. }
  248. });
  249. return result.intValue();
  250. }
  251. }