1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.executor;
17
18 import java.sql.SQLException;
19 import java.util.List;
20
21 import org.apache.ibatis.cache.Cache;
22 import org.apache.ibatis.cache.CacheKey;
23 import org.apache.ibatis.cache.TransactionalCacheManager;
24 import org.apache.ibatis.cursor.Cursor;
25 import org.apache.ibatis.mapping.BoundSql;
26 import org.apache.ibatis.mapping.MappedStatement;
27 import org.apache.ibatis.mapping.ParameterMapping;
28 import org.apache.ibatis.mapping.ParameterMode;
29 import org.apache.ibatis.mapping.StatementType;
30 import org.apache.ibatis.reflection.MetaObject;
31 import org.apache.ibatis.session.ResultHandler;
32 import org.apache.ibatis.session.RowBounds;
33 import org.apache.ibatis.transaction.Transaction;
34
35
36
37
38
39 public class CachingExecutor implements Executor {
40
41 private final Executor delegate;
42 private final TransactionalCacheManager tcm = new TransactionalCacheManager();
43
44 public CachingExecutor(Executor delegate) {
45 this.delegate = delegate;
46 delegate.setExecutorWrapper(this);
47 }
48
49 @Override
50 public Transaction getTransaction() {
51 return delegate.getTransaction();
52 }
53
54 @Override
55 public void close(boolean forceRollback) {
56 try {
57
58 if (forceRollback) {
59 tcm.rollback();
60 } else {
61 tcm.commit();
62 }
63 } finally {
64 delegate.close(forceRollback);
65 }
66 }
67
68 @Override
69 public boolean isClosed() {
70 return delegate.isClosed();
71 }
72
73 @Override
74 public int update(MappedStatement ms, Object parameterObject) throws SQLException {
75 flushCacheIfRequired(ms);
76 return delegate.update(ms, parameterObject);
77 }
78
79 @Override
80 public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
81 flushCacheIfRequired(ms);
82 return delegate.queryCursor(ms, parameter, rowBounds);
83 }
84
85 @Override
86 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
87 BoundSql boundSql = ms.getBoundSql(parameterObject);
88 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
89 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
90 }
91
92 @Override
93 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
94 throws SQLException {
95 Cache cache = ms.getCache();
96 if (cache != null) {
97 flushCacheIfRequired(ms);
98 if (ms.isUseCache() && resultHandler == null) {
99 ensureNoOutParams(ms, boundSql);
100 @SuppressWarnings("unchecked")
101 List<E> list = (List<E>) tcm.getObject(cache, key);
102 if (list == null) {
103 list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
104 tcm.putObject(cache, key, list);
105 }
106 return list;
107 }
108 }
109 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
110 }
111
112 @Override
113 public List<BatchResult> flushStatements() throws SQLException {
114 return delegate.flushStatements();
115 }
116
117 @Override
118 public void commit(boolean required) throws SQLException {
119 delegate.commit(required);
120 tcm.commit();
121 }
122
123 @Override
124 public void rollback(boolean required) throws SQLException {
125 try {
126 delegate.rollback(required);
127 } finally {
128 if (required) {
129 tcm.rollback();
130 }
131 }
132 }
133
134 private void ensureNoOutParams(MappedStatement ms, BoundSql boundSql) {
135 if (ms.getStatementType() == StatementType.CALLABLE) {
136 for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
137 if (parameterMapping.getMode() != ParameterMode.IN) {
138 throw new ExecutorException("Caching stored procedures with OUT params is not supported. Please configure useCache=false in " + ms.getId() + " statement.");
139 }
140 }
141 }
142 }
143
144 @Override
145 public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
146 return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
147 }
148
149 @Override
150 public boolean isCached(MappedStatement ms, CacheKey key) {
151 return delegate.isCached(ms, key);
152 }
153
154 @Override
155 public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
156 delegate.deferLoad(ms, resultObject, property, key, targetType);
157 }
158
159 @Override
160 public void clearLocalCache() {
161 delegate.clearLocalCache();
162 }
163
164 private void flushCacheIfRequired(MappedStatement ms) {
165 Cache cache = ms.getCache();
166 if (cache != null && ms.isFlushCacheRequired()) {
167 tcm.clear(cache);
168 }
169 }
170
171 @Override
172 public void setExecutorWrapper(Executor executor) {
173 throw new UnsupportedOperationException("This method should not be called");
174 }
175
176 }