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