Browse Source

缓存实现

mxd 5 years ago
parent
commit
2874567bc1

+ 6 - 2
src/main/java/org/ssssssss/magicapi/cache/DefaultSqlCache.java

@@ -21,9 +21,13 @@ public class DefaultSqlCache extends LinkedHashMap<String, DefaultSqlCache.Expir
     }
 
     @Override
-    public void put(String name, String key, Object value) {
+    public void put(String name, String key, Object value, long ttl) {
+        long expireTime = Long.MAX_VALUE;
+        if (ttl >= 0) {
+            expireTime = System.currentTimeMillis() + (ttl == 0 ? this.expire : ttl);
+        }
         // 封装成过期时间节点
-        put(name + separator + key, new ExpireNode<>(System.currentTimeMillis() + this.expire, value));
+        put(name + separator + key, new ExpireNode<>(expireTime, value));
     }
 
     @Override

+ 5 - 6
src/main/java/org/ssssssss/magicapi/cache/SqlCache.java

@@ -1,6 +1,7 @@
 package org.ssssssss.magicapi.cache;
 
 import org.ssssssss.magicapi.utils.MD5Utils;
+import org.ssssssss.script.functions.DatabaseQuery;
 
 import java.util.Arrays;
 
@@ -11,11 +12,9 @@ public interface SqlCache {
 
     /**
      * 计算key
-     * @param sql   sql
-     * @param parameters sql参数
      */
-    default String buildSqlCacheKey(String sql, Object[] parameters) {
-        return MD5Utils.encrypt(sql + ":" + Arrays.toString(parameters));
+    default String buildSqlCacheKey(DatabaseQuery.BoundSql boundSql) {
+        return MD5Utils.encrypt(boundSql.getSql() + ":" + Arrays.toString(boundSql.getParameters()));
     }
 
     /**
@@ -24,7 +23,7 @@ public interface SqlCache {
      * @param key   key
      * @param value 值
      */
-    void put(String name, String key, Object value);
+    void put(String name, String key, Object value, long ttl);
 
     /**
      * 获取缓存
@@ -32,7 +31,7 @@ public interface SqlCache {
      * @param key   key
      * @return
      */
-    Object get(String name,String key);
+    <T> T get(String name, String key);
 
     /**
      * 删除缓存

+ 4 - 0
src/main/java/org/ssssssss/magicapi/config/DynamicDataSource.java

@@ -28,6 +28,10 @@ public class DynamicDataSource {
         this.dataSourceMap.put(dataSourceName, new JdbcTemplate(dataSource));
     }
 
+    public JdbcTemplate getJdbcTemplate() {
+        return getJdbcTemplate(null);
+    }
+
     public JdbcTemplate getJdbcTemplate(String dataSourceName) {
         if (dataSourceName == null) {
             dataSourceName = "";

+ 9 - 0
src/main/java/org/ssssssss/script/annotation/UnableCall.java

@@ -0,0 +1,9 @@
+package org.ssssssss.script.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface UnableCall {
+}

+ 133 - 16
src/main/java/org/ssssssss/script/functions/DatabaseQuery.java

@@ -3,6 +3,7 @@ package org.ssssssss.script.functions;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.ssssssss.magicapi.cache.SqlCache;
 import org.ssssssss.magicapi.config.DynamicDataSource;
 import org.ssssssss.magicapi.dialect.Dialect;
 import org.ssssssss.magicapi.dialect.DialectUtils;
@@ -11,6 +12,7 @@ import org.ssssssss.magicapi.model.Page;
 import org.ssssssss.magicapi.model.PageResult;
 import org.ssssssss.magicapi.provider.PageProvider;
 import org.ssssssss.script.MagicScriptContext;
+import org.ssssssss.script.annotation.UnableCall;
 import org.ssssssss.script.parsing.GenericTokenParser;
 import org.ssssssss.script.parsing.Parser;
 import org.ssssssss.script.parsing.TokenStream;
@@ -23,41 +25,131 @@ import java.util.concurrent.atomic.AtomicBoolean;
 
 public class DatabaseQuery extends HashMap<String, DatabaseQuery> {
 
+	@UnableCall
 	private DynamicDataSource dataSource;
 
+	@UnableCall
 	private JdbcTemplate template;
 
+	@UnableCall
 	private PageProvider pageProvider;
 
+	@UnableCall
 	private RowMapper<Map<String, Object>> rowMapper;
 
-	public DatabaseQuery(JdbcTemplate template, DynamicDataSource dataSource, PageProvider pageProvider, RowMapper<Map<String, Object>> rowMapper) {
-		this.template = template;
+	@UnableCall
+	private SqlCache sqlCache;
+
+	@UnableCall
+	private String cacheName;
+
+	@UnableCall
+	private long ttl;
+
+	public DatabaseQuery() {
+
+	}
+
+	public DatabaseQuery(DynamicDataSource dataSource) {
 		this.dataSource = dataSource;
+		this.template = dataSource.getJdbcTemplate();
+	}
+
+	@UnableCall
+	public void setPageProvider(PageProvider pageProvider) {
 		this.pageProvider = pageProvider;
+	}
+
+	@UnableCall
+	public void setRowMapper(RowMapper<Map<String, Object>> rowMapper) {
 		this.rowMapper = rowMapper;
 	}
 
-	public DatabaseQuery(DynamicDataSource dataSource, PageProvider pageProvider, RowMapper<Map<String, Object>> rowMapper) {
+	@UnableCall
+	public void setDataSource(DynamicDataSource dataSource) {
 		this.dataSource = dataSource;
-		this.pageProvider = pageProvider;
-		this.rowMapper = rowMapper;
-		this.template = dataSource.getJdbcTemplate(null);
+	}
+
+	@UnableCall
+	public void setSqlCache(SqlCache sqlCache) {
+		this.sqlCache = sqlCache;
+	}
+
+	@UnableCall
+	public void setTemplate(JdbcTemplate template) {
+		this.template = template;
+	}
+
+	@UnableCall
+	public void setCacheName(String cacheName) {
+		this.cacheName = cacheName;
+	}
+
+	@UnableCall
+	public void setTtl(long ttl) {
+		this.ttl = ttl;
+	}
+
+	@UnableCall
+	public DatabaseQuery cloneQuery() {
+		DatabaseQuery query = new DatabaseQuery();
+		query.setDataSource(this.dataSource);
+		query.setTemplate(this.template);
+		query.setPageProvider(this.pageProvider);
+		query.setRowMapper(this.rowMapper);
+		query.setSqlCache(this.sqlCache);
+		query.setTtl(this.ttl);
+		return query;
+	}
 
+	@UnableCall
+	private <T> T putCacheValue(T value, BoundSql boundSql) {
+		if (this.cacheName != null) {
+			this.sqlCache.put(this.cacheName, boundSql.getCacheKey(this.sqlCache), value, this.ttl);
+		}
+		return value;
+	}
+
+
+	public DatabaseQuery cache(String cacheName, long ttl) {
+		if (cacheName == null) {
+			return this;
+		}
+		DatabaseQuery query = cloneQuery();
+		query.setCacheName(cacheName);
+		query.setTtl(ttl);
+		return query;
+	}
+
+	public DatabaseQuery cache(String cacheName) {
+		return cache(cacheName, 0);
 	}
 
 	@Override
 	public DatabaseQuery get(Object key) {
+		DatabaseQuery query = cloneQuery();
 		if (key == null) {
-			return new DatabaseQuery(dataSource.getJdbcTemplate(null), this.dataSource, this.pageProvider, this.rowMapper);
+			query.setTemplate(dataSource.getJdbcTemplate());
+		} else {
+			query.setTemplate(dataSource.getJdbcTemplate(key.toString()));
 		}
-		return new DatabaseQuery(dataSource.getJdbcTemplate(key.toString()), this.dataSource, this.pageProvider, this.rowMapper);
+		return query;
 	}
 
 
 	public Object select(String sql) {
 		BoundSql boundSql = new BoundSql(sql);
-		return template.query(boundSql.getSql(), this.rowMapper, boundSql.getParameters());
+		return boundSql.getCacheValue(this.sqlCache, this.cacheName)
+				.orElseGet(() -> putCacheValue(template.query(boundSql.getSql(), this.rowMapper, boundSql.getParameters()), boundSql));
+	}
+
+	public int update(String sql) {
+		BoundSql boundSql = new BoundSql(sql);
+		int value = template.update(boundSql.getSql(), boundSql.getParameters());
+		if (this.cacheName != null) {
+			this.sqlCache.delete(this.cacheName);
+		}
+		return value;
 	}
 
 	public Object page(String sql) {
@@ -74,7 +166,8 @@ public class DatabaseQuery extends HashMap<String, DatabaseQuery> {
 		try {
 			connection = template.getDataSource().getConnection();
 			dialect = DialectUtils.getDialectFromUrl(connection.getMetaData().getURL());
-			count = template.queryForObject(dialect.getCountSql(boundSql.getSql()), Integer.class, boundSql.getParameters());
+			count = (int) boundSql.getCacheValue(this.sqlCache, this.cacheName)
+					.orElseGet(() -> putCacheValue(template.queryForObject(dialect.getCountSql(boundSql.getSql()), Integer.class, boundSql.getParameters()), boundSql));
 			result.setTotal(count);
 		} catch (SQLException e) {
 			throw new MagicAPIException("自动获取数据库方言失败", e);
@@ -83,25 +176,31 @@ public class DatabaseQuery extends HashMap<String, DatabaseQuery> {
 		}
 		if (count > 0) {
 			String pageSql = dialect.getPageSql(boundSql.getSql(), boundSql, offset, limit);
-			result.setList(template.query(pageSql, this.rowMapper, boundSql.getParameters()));
+			result.setList((List<Map<String, Object>>) boundSql.removeCacheKey().getCacheValue(this.sqlCache, this.cacheName)
+					.orElseGet(() -> putCacheValue(template.query(pageSql, this.rowMapper, boundSql.getParameters()), boundSql)));
 		}
 		return result;
 	}
 
 	public Integer selectInt(String sql) {
 		BoundSql boundSql = new BoundSql(sql);
-		return template.queryForObject(boundSql.getSql(), boundSql.getParameters(), Integer.class);
+		return (Integer) boundSql.getCacheValue(this.sqlCache, this.cacheName)
+				.orElseGet(() -> putCacheValue(template.queryForObject(boundSql.getSql(), boundSql.getParameters(), Integer.class), boundSql));
 	}
 
-	public Map<String, Object> selectOne(String sql) {
+	public Object selectOne(String sql) {
 		BoundSql boundSql = new BoundSql(sql);
-		List<Map<String, Object>> list = template.query(boundSql.getSql(), this.rowMapper, boundSql.getParameters());
-		return list != null && list.size() > 0 ? list.get(0) : null;
+		return boundSql.getCacheValue(this.sqlCache, this.cacheName)
+				.orElseGet(() -> {
+					List<Map<String, Object>> list = template.query(boundSql.getSql(), this.rowMapper, boundSql.getParameters());
+					return list != null && list.size() > 0 ? list.get(0) : null;
+				});
 	}
 
 	public Object selectValue(String sql) {
 		BoundSql boundSql = new BoundSql(sql);
-		return template.queryForObject(boundSql.getSql(), boundSql.getParameters(), Object.class);
+		return boundSql.getCacheValue(this.sqlCache, this.cacheName)
+				.orElseGet(() -> putCacheValue(template.queryForObject(boundSql.getSql(), boundSql.getParameters(), Object.class), boundSql));
 	}
 
 	private static Tokenizer tokenizer = new Tokenizer();
@@ -117,6 +216,8 @@ public class DatabaseQuery extends HashMap<String, DatabaseQuery> {
 	public static class BoundSql {
 		private String sql;
 		private List<Object> parameters = new ArrayList<>();
+		private String cacheKey;
+
 
 		BoundSql(String sql) {
 			MagicScriptContext context = MagicScriptContext.get();
@@ -150,6 +251,22 @@ public class DatabaseQuery extends HashMap<String, DatabaseQuery> {
 		public Object[] getParameters() {
 			return parameters.toArray();
 		}
+
+		public BoundSql removeCacheKey() {
+			this.cacheKey = null;
+			return this;
+		}
+
+		public String getCacheKey(SqlCache sqlCache) {
+			if (cacheKey == null) {
+				cacheKey = sqlCache.buildSqlCacheKey(this);
+			}
+			return cacheKey;
+		}
+
+		public <T> Optional<T> getCacheValue(SqlCache sqlCache, String cacheName) {
+			return Optional.ofNullable(cacheName == null ? null : sqlCache.get(cacheName, getCacheKey(sqlCache)));
+		}
 	}
 
 }

+ 20 - 9
src/main/java/org/ssssssss/script/interpreter/JavaReflection.java

@@ -1,5 +1,6 @@
 package org.ssssssss.script.interpreter;
 
+import org.ssssssss.script.annotation.UnableCall;
 import org.ssssssss.script.functions.StreamExtension;
 
 import java.lang.reflect.*;
@@ -146,10 +147,12 @@ public class JavaReflection extends AbstractReflection {
         List<Method> methodList = new ArrayList<>();
         for (int i = 0, n = methods.length; i < n; i++) {
             Method method = methods[i];
-            // if neither name or parameter list size match, bail on this method
             if (!method.getName().equals(name)) {
                 continue;
             }
+            if (method.getAnnotation(UnableCall.class) != null) {
+                continue;
+            }
             methodList.add(method);
         }
         Method method = findMethod(methodList, parameterTypes);
@@ -249,7 +252,7 @@ public class JavaReflection extends AbstractReflection {
         Class cls = obj instanceof Class ? (Class) obj : obj.getClass();
         Map<String, Field> fields = fieldCache.get(cls);
         if (fields == null) {
-            fields = new ConcurrentHashMap<String, Field>();
+            fields = new ConcurrentHashMap<>();
             fieldCache.put(cls, fields);
         }
 
@@ -257,8 +260,12 @@ public class JavaReflection extends AbstractReflection {
         if (field == null) {
             try {
                 field = cls.getDeclaredField(name);
-                field.setAccessible(true);
-                fields.put(name, field);
+                if (field.getAnnotation(UnableCall.class) != null) {
+                    field = null;
+                } else {
+                    field.setAccessible(true);
+                    fields.put(name, field);
+                }
             } catch (Throwable t) {
                 // fall through, try super classes
             }
@@ -268,8 +275,12 @@ public class JavaReflection extends AbstractReflection {
                 while (parentClass != Object.class && parentClass != null) {
                     try {
                         field = parentClass.getDeclaredField(name);
-                        field.setAccessible(true);
-                        fields.put(name, field);
+                        if (field.getAnnotation(UnableCall.class) != null) {
+                            field = null;
+                        } else {
+                            field.setAccessible(true);
+                            fields.put(name, field);
+                        }
                     } catch (NoSuchFieldException e) {
                         // fall through
                     }
@@ -301,7 +312,7 @@ public class JavaReflection extends AbstractReflection {
                 extensionmethodCache.put(target, cachedMethodMap);
             }
             for (Method method : methods) {
-                if (Modifier.isStatic(method.getModifiers()) && method.getParameterCount() > 0) {
+                if (Modifier.isStatic(method.getModifiers()) && method.getParameterCount() > 0 && method.getAnnotation(UnableCall.class) == null) {
                     List<Method> cachedList = cachedMethodMap.get(method.getName());
                     if (cachedList == null) {
                         cachedList = new ArrayList<>();
@@ -366,7 +377,7 @@ public class JavaReflection extends AbstractReflection {
         Class<?> cls = obj instanceof Class ? (Class<?>) obj : (obj instanceof Function ? Function.class : obj.getClass());
         Map<MethodSignature, Method> methods = methodCache.get(cls);
         if (methods == null) {
-            methods = new ConcurrentHashMap<MethodSignature, Method>();
+            methods = new ConcurrentHashMap<>();
             methodCache.put(cls, methods);
         }
 
@@ -384,7 +395,7 @@ public class JavaReflection extends AbstractReflection {
                     method = findApply(cls);
                 } else {
                     method = findMethod(cls, name, parameterTypes);
-                    if (method == null && parameterTypes != null) {
+                    if (method == null) {
                         method = findMethod(cls, name, new Class<?>[]{Object[].class});
                     }
                 }