diff --git a/src/com/wentch/redkale/convert/Factory.java b/src/com/wentch/redkale/convert/Factory.java index 1a5734515..4232bb977 100644 --- a/src/com/wentch/redkale/convert/Factory.java +++ b/src/com/wentch/redkale/convert/Factory.java @@ -40,6 +40,10 @@ public abstract class Factory { private final HashMap columnEntrys = new HashMap(); + private final Set skipIgnores = new HashSet<>(); + + private boolean skipAllIgnore = false; + protected Factory(Factory parent) { this.parent = parent; if (parent == null) { @@ -111,11 +115,38 @@ public abstract class Factory { if (en != null) return en; final ConvertType ct = this.getConvertType(); for (ConvertColumn ref : field.getAnnotationsByType(ConvertColumn.class)) { - if (ref.type().contains(ct)) return new ConvertColumnEntry(ref); + if (ref.type().contains(ct)) { + ConvertColumnEntry entry = new ConvertColumnEntry(ref); + if (skipAllIgnore) { + entry.setIgnore(false); + return entry; + } + if (skipIgnores.isEmpty()) return entry; + if (skipIgnores.contains(((Member) field).getDeclaringClass())) entry.setIgnore(false); + return entry; + } } return null; } + /** + * 使所有类的所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false + *

+ * @param skipIgnore + */ + public final void registerSkipAllIgnore(final boolean skipIgnore) { + this.skipAllIgnore = skipIgnore; + } + + /** + * 使该类所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false + *

+ * @param type + */ + public final void registerSkipIgnore(final Class type) { + skipIgnores.add(type); + } + public final boolean register(final Class type, String column, ConvertColumnEntry entry) { if (type == null || column == null || entry == null) return false; try { diff --git a/src/com/wentch/redkale/convert/json/JsonReader.java b/src/com/wentch/redkale/convert/json/JsonReader.java index 0cbf7eb72..c79a6bfd3 100644 --- a/src/com/wentch/redkale/convert/json/JsonReader.java +++ b/src/com/wentch/redkale/convert/json/JsonReader.java @@ -483,6 +483,16 @@ public final class JsonReader implements Reader { return null; } } + } else { + final int start = currpos; + for (;;) { + char ch = text0[currpos]; + if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break; + currpos++; + } + if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ")"); + this.position = currpos - 1; + return new String(text0, start, currpos - start); } this.position = currpos; throw new ConvertException("expected a ':' after a key but '" + text0[position] + "' (position = " + position + ")"); diff --git a/src/com/wentch/redkale/net/http/BasedHttpServlet.java b/src/com/wentch/redkale/net/http/BasedHttpServlet.java index 25dd0c395..c9e9a9a77 100644 --- a/src/com/wentch/redkale/net/http/BasedHttpServlet.java +++ b/src/com/wentch/redkale/net/http/BasedHttpServlet.java @@ -18,7 +18,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; /** * * @author zhangjx - */ + */ public abstract class BasedHttpServlet extends HttpServlet { private Map.Entry[] actions; @@ -28,7 +28,7 @@ public abstract class BasedHttpServlet extends HttpServlet { for (Map.Entry en : actions) { if (request.getRequestURI().startsWith(en.getKey())) { Entry entry = en.getValue(); - if (entry.ignore || authenticate(request, response)) entry.servlet.execute(request, response); + if (entry.ignore || authenticate(entry.moduleid, entry.actionid, request, response)) entry.servlet.execute(request, response); return; } } @@ -48,7 +48,7 @@ public abstract class BasedHttpServlet extends HttpServlet { } } - public abstract boolean authenticate(HttpRequest request, HttpResponse response) throws IOException; + public abstract boolean authenticate(int module, int actionid, HttpRequest request, HttpResponse response) throws IOException; private HashMap load() { final boolean typeIgnore = this.getClass().getAnnotation(AuthIgnore.class) != null; diff --git a/src/com/wentch/redkale/net/http/HttpRequest.java b/src/com/wentch/redkale/net/http/HttpRequest.java index 458ba1a4a..00d1ad2e5 100644 --- a/src/com/wentch/redkale/net/http/HttpRequest.java +++ b/src/com/wentch/redkale/net/http/HttpRequest.java @@ -5,6 +5,7 @@ */ package com.wentch.redkale.net.http; +import com.wentch.redkale.util.ByteArray; import com.wentch.redkale.convert.json.*; import com.wentch.redkale.net.*; import com.wentch.redkale.util.AnyValue.DefaultAnyValue; diff --git a/src/com/wentch/redkale/net/http/HttpResourceServlet.java b/src/com/wentch/redkale/net/http/HttpResourceServlet.java index dfff67d6c..7bebf838d 100644 --- a/src/com/wentch/redkale/net/http/HttpResourceServlet.java +++ b/src/com/wentch/redkale/net/http/HttpResourceServlet.java @@ -27,6 +27,53 @@ public final class HttpResourceServlet extends HttpServlet { private static final Logger logger = Logger.getLogger(HttpResourceServlet.class.getSimpleName()); + protected class WatchThread extends Thread { + + protected final File root; + + protected final WatchService watcher; + + public WatchThread(File root) throws IOException { + this.root = root; + this.setName("Servlet-ResourceWatch-Thread"); + this.setDaemon(true); + this.watcher = this.root.toPath().getFileSystem().newWatchService(); + } + + @Override + public void run() { + try { + final String rootstr = root.getCanonicalPath(); + while (!this.isInterrupted()) { + final WatchKey key = watcher.take(); + final Path parent = keymaps.get(key); + if (parent == null) { + key.cancel(); + continue; + } + key.pollEvents().stream().forEach((event) -> { + try { + Path path = parent.resolve((Path) event.context()); + final String uri = path.toString().substring(rootstr.length()).replace('\\', '/'); + //logger.log(Level.FINEST, "file(" + uri + ") happen " + event.kind() + " event"); + Thread.sleep(1000L); //等待update file完毕 + if (event.kind() == ENTRY_DELETE) { + files.remove(uri); + } else if (event.kind() == ENTRY_MODIFY) { + FileEntry en = files.get(uri); + if (en != null) en.update(); + } + } catch (Exception ex) { + logger.log(Level.FINE, event.context() + " occur erroneous", ex); + } + }); + key.reset(); + } + } catch (Exception e) { + } + } + } + //缓存总大小, 默认128M protected long cachelimit = 128 * 1024 * 1024L; @@ -39,29 +86,33 @@ public final class HttpResourceServlet extends HttpServlet { protected final ConcurrentHashMap files = new ConcurrentHashMap<>(); - protected WatchService watcher; - protected final ConcurrentHashMap keymaps = new ConcurrentHashMap<>(); protected SimpleEntry[] locationRewrites; - protected Thread watchThread; + protected WatchThread watchThread; + + protected List> resx; protected Predicate ranges; @Override public void init(Context context, AnyValue config) { + String[] rootstrs = null; if (config != null) { - String rootstr = config.getValue("webroot", "root").trim(); - if (rootstr.indexOf(':') < 0 && rootstr.indexOf('/') != 0 && System.getProperty("APP_HOME") != null) { - rootstr = new File(System.getProperty("APP_HOME"), rootstr).getPath(); + rootstrs = config.getValue("webroot", "root").trim().split(","); + for (int i = 0; i < rootstrs.length; i++) { + String rootstr = rootstrs[i]; + if (rootstr.indexOf(':') < 0 && rootstr.indexOf('/') != 0 && System.getProperty("APP_HOME") != null) { + rootstrs[i] = new File(System.getProperty("APP_HOME"), rootstr).getPath(); + } } String rangesValue = config.getValue("ranges"); this.ranges = rangesValue != null ? Pattern.compile(rangesValue).asPredicate() : null; try { - this.root = new File(rootstr).getCanonicalFile(); + this.root = new File(rootstrs[0]).getCanonicalFile(); } catch (IOException ioe) { - this.root = new File(rootstr); + this.root = new File(rootstrs[0]); } AnyValue cacheconf = config.getAnyValue("caches"); if (cacheconf != null) { @@ -83,62 +134,36 @@ public final class HttpResourceServlet extends HttpServlet { if (this.cachelimit < 1) return; if (this.root != null) { try { - this.watcher = this.root.toPath().getFileSystem().newWatchService(); - this.watchThread = new Thread() { - - @Override - public void run() { - try { - final String rootstr = root.getCanonicalPath(); - while (!this.isInterrupted()) { - final WatchKey key = watcher.take(); - final Path parent = keymaps.get(key); - if (parent == null) { - key.cancel(); - continue; - } - key.pollEvents().stream().forEach((event) -> { - try { - Path path = parent.resolve((Path) event.context()); - final String uri = path.toString().substring(rootstr.length()).replace('\\', '/'); - //logger.log(Level.FINEST, "file(" + uri + ") happen " + event.kind() + " event"); - Thread.sleep(1000L); //等待update file完毕 - if (event.kind() == ENTRY_DELETE) { - files.remove(uri); - } else if (event.kind() == ENTRY_MODIFY) { - FileEntry en = files.get(uri); - if (en != null) en.update(); - } - } catch (Exception ex) { - logger.log(Level.FINE, event.context() + " occur erroneous", ex); - } - }); - key.reset(); - } - } catch (Exception e) { - } - } - }; - this.watchThread.setName("Servlet-ResourceWatch-Thread"); - this.watchThread.setDaemon(true); + this.watchThread = new WatchThread(this.root); this.watchThread.start(); } catch (IOException ex) { logger.log(Level.WARNING, HttpResourceServlet.class.getSimpleName() + " start watch-thread error", ex); } + if (rootstrs != null && rootstrs.length > 1) { + resx = new ArrayList<>(rootstrs.length - 1); + for (int i = 1; i < rootstrs.length; i++) { + try { + File f = new File(rootstrs[i]).getCanonicalFile(); + WatchThread t = new WatchThread(f); + t.start(); + resx.add(new SimpleEntry<>(f, t)); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + } } } @Override public void destroy(Context context, AnyValue config) { - if (this.watcher != null) { + if (this.watchThread != null) { try { - this.watcher.close(); + this.watchThread.watcher.close(); } catch (IOException ex) { - ex.printStackTrace(); + logger.log(Level.WARNING, HttpResourceServlet.class.getSimpleName() + " close watch-thread error", ex); } - } - if (this.watchThread != null && this.watchThread.isAlive()) { - this.watchThread.interrupt(); + if (this.watchThread.isAlive()) this.watchThread.interrupt(); } } @@ -168,10 +193,10 @@ public final class HttpResourceServlet extends HttpServlet { } if (uri.length() == 0 || uri.equals("/")) uri = "/index.html"; //System.out.println(request); - FileEntry entry = watcher == null ? createFileEntry(uri) : files.get(uri); + FileEntry entry = watchThread == null ? createFileEntry(uri) : files.get(uri); if (entry == null) { entry = createFileEntry(uri); - if (entry != null && watcher != null) files.put(uri, entry); + if (entry != null && watchThread != null) files.put(uri, entry); } if (entry == null) { response.finish404(); @@ -182,12 +207,30 @@ public final class HttpResourceServlet extends HttpServlet { private FileEntry createFileEntry(String uri) { File file = new File(root, uri); - if (!file.isFile() || !file.canRead()) return null; + if (!file.isFile() || !file.canRead()) { + if (resx != null) { + for (SimpleEntry en : resx) { + File f = new File(en.getKey(), uri); + if (f.isFile() && f.canRead()) { + FileEntry fe = new FileEntry(this, f); + if (watchThread == null) return fe; + try { + Path p = f.getParentFile().toPath(); + keymaps.put(p.register(en.getValue().watcher, ENTRY_MODIFY, ENTRY_DELETE), p); + } catch (IOException e) { + logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " create FileEntry(" + uri + ") erroneous", e); + } + return fe; + } + } + } + return null; + } FileEntry en = new FileEntry(this, file); - if (watcher == null) return en; + if (watchThread == null) return en; try { Path p = file.getParentFile().toPath(); - keymaps.put(p.register(watcher, ENTRY_MODIFY, ENTRY_DELETE), p); + keymaps.put(p.register(watchThread.watcher, ENTRY_MODIFY, ENTRY_DELETE), p); } catch (IOException e) { logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " create FileEntry(" + uri + ") erroneous", e); } diff --git a/src/com/wentch/redkale/net/http/HttpServer.java b/src/com/wentch/redkale/net/http/HttpServer.java index 241395078..9ef0e3a0e 100644 --- a/src/com/wentch/redkale/net/http/HttpServer.java +++ b/src/com/wentch/redkale/net/http/HttpServer.java @@ -49,7 +49,7 @@ public final class HttpServer extends Server { final int port = this.address.getPort(); AtomicLong createBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Buffer.creatCounter"); AtomicLong cycleBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Buffer.cycleCounter"); - int rcapacity = Math.max(this.capacity, 8 * 1024); + int rcapacity = Math.max(this.capacity, 16 * 1024 + 8); //兼容 HTTP 2.0 ObjectPool bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize, (Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> { if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false; diff --git a/src/com/wentch/redkale/net/http/MultiContext.java b/src/com/wentch/redkale/net/http/MultiContext.java index 12436a939..cef906b7b 100644 --- a/src/com/wentch/redkale/net/http/MultiContext.java +++ b/src/com/wentch/redkale/net/http/MultiContext.java @@ -5,6 +5,7 @@ */ package com.wentch.redkale.net.http; +import com.wentch.redkale.util.ByteArray; import java.io.*; import java.nio.charset.*; import java.util.*; diff --git a/src/com/wentch/redkale/source/DataJDBCSource.java b/src/com/wentch/redkale/source/DataJDBCSource.java index a6e548bd3..7049ca433 100644 --- a/src/com/wentch/redkale/source/DataJDBCSource.java +++ b/src/com/wentch/redkale/source/DataJDBCSource.java @@ -5,7 +5,6 @@ */ package com.wentch.redkale.source; -import com.wentch.redkale.source.DataSource; import com.wentch.redkale.source.DistributeGenerator.DistributeTables; import static com.wentch.redkale.source.FilterInfo.formatToString; import com.wentch.redkale.util.*; @@ -351,9 +350,7 @@ public final class DataJDBCSource implements DataSource { EntityInfo info = EntityInfo.load(clazz, this); EntityCache cache = info.getCache(); if (cache == null) return; - cache.clear(); - List all = queryList(clazz, null); - cache.fullLoad(all); + cache.fullLoad(queryList(clazz, null)); } //----------------------insert----------------------------- @@ -565,22 +562,6 @@ public final class DataJDBCSource implements DataSource { public void delete(Class clazz, Serializable... ids) { Connection conn = createWriteSQLConnection(); try { - if (ids != null && ids.length == 1 && ids[0] != null && ids[0].getClass().isArray()) { - Class clz = ids[0].getClass(); - if (clz == long[].class) { - long[] vs = (long[]) ids[0]; - ids = new Serializable[vs.length]; - for (int i = 0; i < vs.length; i++) { - ids[i] = vs[i]; - } - } else if (clz == int[].class) { - int[] vs = (int[]) ids[0]; - ids = new Serializable[vs.length]; - for (int i = 0; i < vs.length; i++) { - ids[i] = vs[i]; - } - } - } delete(conn, clazz, ids); } finally { closeSQLConnection(conn); @@ -641,11 +622,24 @@ public final class DataJDBCSource implements DataSource { try { final EntityXInfo info = EntityXInfo.load(this, clazz); String sql = "DELETE FROM " + info.getTable() + " WHERE " + info.getSQLColumn(column); - if (keys.length == 1) { + if (keys.length == 1 && !keys[0].getClass().isArray()) { sql += " = " + formatToString(keys[0]); } else { sql += " IN ("; boolean flag = false; + if (keys.length == 1 && keys[0].getClass().isArray()) { + Class keytype = keys[0].getClass(); + if (keytype.getComponentType().isPrimitive()) { + Object array = keys[0]; + Serializable[] keys0 = new Serializable[Array.getLength(array)]; + for (int i = 0; i < keys0.length; i++) { + keys0[i] = (Serializable) Array.get(array, i); + } + keys = keys0; + } else { + keys = (Serializable[]) keys[0]; + } + } for (final Serializable value : keys) { if (flag) sql += ","; sql += formatToString(value); @@ -662,7 +656,8 @@ public final class DataJDBCSource implements DataSource { final EntityCache cache = info.inner.getCache(); if (cache == null) return; final Attribute attr = info.getAttribute(column); - Serializable[] ids = cache.delete((T t) -> Arrays.binarySearch(keys, attr.get(t)) >= 0); + final Serializable[] keys2 = keys; + Serializable[] ids = cache.delete((T t) -> Arrays.binarySearch(keys2, attr.get(t)) >= 0); if (cacheListener != null) cacheListener.delete(name, clazz, ids); } catch (SQLException e) { throw new RuntimeException(e); @@ -1323,26 +1318,107 @@ public final class DataJDBCSource implements DataSource { } } - //-----------------------list---------------------------- + //-----------------------list set---------------------------- + @Override + public int[] queryColumnIntSet(String selectedColumn, Class clazz, String column, Serializable key) { + return formatCollectionToIntArray(queryColumnSet(selectedColumn, clazz, column, key)); + } + + @Override + public long[] queryColumnLongSet(String selectedColumn, Class clazz, String column, Serializable key) { + return formatCollectionToLongArray(queryColumnSet(selectedColumn, clazz, column, key)); + } + + @Override + public int[] queryColumnIntList(String selectedColumn, Class clazz, String column, Serializable key) { + return formatCollectionToIntArray(queryColumnList(selectedColumn, clazz, column, key)); + } + + @Override + public long[] queryColumnLongList(String selectedColumn, Class clazz, String column, Serializable key) { + return formatCollectionToLongArray(queryColumnList(selectedColumn, clazz, column, key)); + } + + @Override + public Set queryColumnSet(String selectedColumn, Class clazz, String column, Serializable key) { + return queryColumnSet(selectedColumn, clazz, column, FilterExpress.EQUAL, key); + } + + @Override + public List queryColumnList(String selectedColumn, Class clazz, String column, Serializable key) { + return queryColumnList(selectedColumn, clazz, column, FilterExpress.EQUAL, key); + } + + @Override + public int[] queryColumnIntSet(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + return formatCollectionToIntArray(queryColumnSet(selectedColumn, clazz, column, express, key)); + } + + @Override + public long[] queryColumnLongSet(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + return formatCollectionToLongArray(queryColumnSet(selectedColumn, clazz, column, express, key)); + } + + @Override + public int[] queryColumnIntList(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + return formatCollectionToIntArray(queryColumnList(selectedColumn, clazz, column, express, key)); + } + + @Override + public long[] queryColumnLongList(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + return formatCollectionToLongArray(queryColumnList(selectedColumn, clazz, column, express, key)); + } + + @Override + public Set queryColumnSet(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + return (Set) queryColumnCollection(true, selectedColumn, clazz, column, express, key); + } + + @Override + public List queryColumnList(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + return (List) queryColumnCollection(false, selectedColumn, clazz, column, express, key); + } + + private static int[] formatCollectionToIntArray(Collection collection) { + if (collection == null || collection.isEmpty()) return new int[0]; + int[] rs = new int[collection.size()]; + int i = 0; + for (int v : collection) { + rs[i++] = v; + } + return rs; + } + + private static long[] formatCollectionToLongArray(Collection collection) { + if (collection == null || collection.isEmpty()) return new long[0]; + long[] rs = new long[collection.size()]; + int i = 0; + for (long v : collection) { + rs[i++] = v; + } + return rs; + } + /** * 根据指定字段值查询对象某个字段的集合 * * @param * @param + * @param set * @param selectedColumn * @param clazz * @param column + * @param express * @param key * @return */ - @Override - public List queryColumnList(String selectedColumn, Class clazz, String column, Serializable key) { + protected final Collection queryColumnCollection(final boolean set, String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { final EntityXInfo info = EntityXInfo.load(this, clazz); final EntityCache cache = info.inner.getCache(); if (cache != null) { - final Attribute attr = info.getAttribute(column); - List list = cache.queryList(null, (T t) -> key.equals(attr.get(t)), null); - final List rs = new ArrayList<>(); + Predicate filter = genFilter(info.getAttribute(column), express, key); + List list = cache.queryList(SelectColumn.createIncludes(selectedColumn), filter, null); + final Collection rs = set ? new LinkedHashSet<>() : new ArrayList<>(); if (!list.isEmpty()) { final Attribute selected = (Attribute) info.getAttribute(selectedColumn); for (T t : list) { @@ -1353,18 +1429,17 @@ public final class DataJDBCSource implements DataSource { } final Connection conn = createReadSQLConnection(); try { - final List list = new ArrayList(); - final String sql = "SELECT " + info.getSQLColumn(selectedColumn) + " FROM " + info.getTable() + " WHERE " + info.getSQLColumn(column) + " = ?"; - if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " query sql=" + sql.replaceFirst("\\?", String.valueOf(key))); - final PreparedStatement ps = conn.prepareStatement(sql); - ps.setObject(1, key); - final ResultSet set = ps.executeQuery(); - while (set.next()) { - list.add((V) set.getObject(1)); + final Collection collection = set ? new LinkedHashSet<>() : new ArrayList<>(); + final String sql = genSQL(info.getSQLColumn(selectedColumn), info, column, express, key); + if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " query sql=" + sql); + final Statement ps = conn.createStatement(); + final ResultSet rs = ps.executeQuery(sql); + while (rs.next()) { + collection.add((V) rs.getObject(1)); } - set.close(); + rs.close(); ps.close(); - return list; + return collection; } catch (Exception ex) { throw new RuntimeException(ex); } finally { @@ -1422,55 +1497,7 @@ public final class DataJDBCSource implements DataSource { final EntityXInfo info = EntityXInfo.load(this, clazz); final EntityCache cache = info.inner.getCache(); if (cache != null) { - final Attribute attr = info.getAttribute(column); - Predicate filter = null; - switch (express) { - case EQUAL: - filter = (T t) -> key.equals(attr.get(t)); - break; - case NOTEQUAL: - filter = (T t) -> !key.equals(attr.get(t)); - break; - case GREATERTHAN: - filter = (T t) -> ((Number) attr.get(t)).longValue() > ((Number) key).longValue(); - break; - case LESSTHAN: - filter = (T t) -> ((Number) attr.get(t)).longValue() < ((Number) key).longValue(); - break; - case GREATERTHANOREQUALTO: - filter = (T t) -> ((Number) attr.get(t)).longValue() >= ((Number) key).longValue(); - break; - case LESSTHANOREQUALTO: - filter = (T t) -> ((Number) attr.get(t)).longValue() <= ((Number) key).longValue(); - break; - case LIKE: - filter = (T t) -> { - Object rs = attr.get(t); - return rs != null && rs.toString().contains(key.toString()); - }; - break; - case NOTLIKE: - filter = (T t) -> { - Object rs = attr.get(t); - return rs == null || !rs.toString().contains(key.toString()); - }; - break; - case ISNULL: - filter = (T t) -> attr.get(t) == null; - break; - case ISNOTNULL: - filter = (T t) -> attr.get(t) != null; - break; - case OPAND: - filter = (T t) -> (((Number) attr.get(t)).longValue() & ((Number) key).longValue()) > 0; - break; - case OPOR: - filter = (T t) -> (((Number) attr.get(t)).longValue() | ((Number) key).longValue()) > 0; - break; - case OPANDNO: - filter = (T t) -> (((Number) attr.get(t)).longValue() & ((Number) key).longValue()) == 0; - break; - } + Predicate filter = genFilter(info.getAttribute(column), express, key); List rs = cache.queryList(selects, filter, null); if (!rs.isEmpty() || cache.isFullLoaded()) return rs; } @@ -1478,11 +1505,10 @@ public final class DataJDBCSource implements DataSource { try { final SelectColumn sels = selects; final List list = new ArrayList(); - final String sql = "SELECT * FROM " + info.getTable() + " WHERE " + info.getSQLColumn(column) + " " + express.value() + " ?"; + final String sql = genSQL("*", info, column, express, key); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " query sql=" + sql); - final PreparedStatement ps = conn.prepareStatement(sql); - ps.setObject(1, key); - final ResultSet set = ps.executeQuery(); + final Statement ps = conn.createStatement(); + final ResultSet set = ps.executeQuery(sql); while (set.next()) { final T result = info.createInstance(); for (AttributeX attr : info.query.attributes) { @@ -1500,6 +1526,124 @@ public final class DataJDBCSource implements DataSource { } } + private String genSQL(String queryColumn, EntityXInfo info, String column, FilterExpress express, Serializable key) { + String sql = "SELECT " + queryColumn + " FROM " + info.getTable() + " WHERE " + info.getSQLColumn(column) + " " + express.value(); + if (key instanceof Number) { + sql += " " + key; + } else if (key instanceof Collection) { + StringBuilder sb = new StringBuilder(); + for (Object o : (Collection) key) { + if (sb.length() > 0) sb.append(','); + if (o instanceof Number) { + sb.append('"').append(o).append('"'); + } else { + sb.append('"').append(o.toString().replace("\"", "\\\"")).append('"'); + } + } + sql += " (" + sb + ")"; + } else if (key.getClass().isArray()) { + StringBuilder sb = new StringBuilder(); + int len = Array.getLength(key); + for (int i = 0; i < len; i++) { + Object o = Array.get(key, i); + if (sb.length() > 0) sb.append(','); + if (o instanceof Number) { + sb.append('"').append(o).append('"'); + } else { + sb.append('"').append(o.toString().replace("\"", "\\\"")).append('"'); + } + } + sql += " (" + sb + ")"; + } else { + sql += " \"" + key.toString().replace("\"", "\\\"") + "\""; + } + return sql; + } + + private Predicate genFilter(final Attribute attr, FilterExpress express, Serializable key) { + Predicate filter = null; + switch (express) { + case EQUAL: + filter = (T t) -> key.equals(attr.get(t)); + break; + case NOTEQUAL: + filter = (T t) -> !key.equals(attr.get(t)); + break; + case GREATERTHAN: + filter = (T t) -> ((Number) attr.get(t)).longValue() > ((Number) key).longValue(); + break; + case LESSTHAN: + filter = (T t) -> ((Number) attr.get(t)).longValue() < ((Number) key).longValue(); + break; + case GREATERTHANOREQUALTO: + filter = (T t) -> ((Number) attr.get(t)).longValue() >= ((Number) key).longValue(); + break; + case LESSTHANOREQUALTO: + filter = (T t) -> ((Number) attr.get(t)).longValue() <= ((Number) key).longValue(); + break; + case IN: + case NOTIN: + if (key instanceof Collection) { + filter = (T t) -> { + Object rs = attr.get(t); + return rs != null && ((Collection) key).contains(rs); + }; + } else { + Serializable[] keys; + if (key.getClass().isArray()) { + Class keytype = key.getClass(); + if (keytype.getComponentType().isPrimitive()) { + Object array = key; + Serializable[] keys0 = new Serializable[Array.getLength(array)]; + for (int i = 0; i < keys0.length; i++) { + keys0[i] = (Serializable) Array.get(array, i); + } + keys = keys0; + } else { + keys = (Serializable[]) key; + } + } else { + keys = new Serializable[]{key}; + } + Serializable[] keys0 = keys; + filter = (T t) -> { + Object rs = attr.get(t); + return rs != null && Arrays.binarySearch(keys0, rs) > -1; + }; + } + if (express == FilterExpress.NOTIN) filter = filter.negate(); + break; + case LIKE: + filter = (T t) -> { + Object rs = attr.get(t); + return rs != null && rs.toString().contains(key.toString()); + }; + break; + case NOTLIKE: + filter = (T t) -> { + Object rs = attr.get(t); + return rs == null || !rs.toString().contains(key.toString()); + }; + break; + case ISNULL: + filter = (T t) -> attr.get(t) == null; + break; + case ISNOTNULL: + filter = (T t) -> attr.get(t) != null; + break; + case OPAND: + filter = (T t) -> (((Number) attr.get(t)).longValue() & ((Number) key).longValue()) > 0; + break; + case OPOR: + filter = (T t) -> (((Number) attr.get(t)).longValue() | ((Number) key).longValue()) > 0; + break; + case OPANDNO: + filter = (T t) -> (((Number) attr.get(t)).longValue() & ((Number) key).longValue()) == 0; + break; + } + return filter; + } + /** * 根据过滤对象FilterBean查询对象集合 * @@ -1528,6 +1672,33 @@ public final class DataJDBCSource implements DataSource { } //-----------------------sheet---------------------------- + /** + * 根据指定参数查询对象某个字段的集合 + *

+ * @param + * @param + * @param selectedColumn + * @param clazz + * @param flipper + * @param bean + * @return + */ + @Override + public Sheet queryColumnSheet(String selectedColumn, Class clazz, final Flipper flipper, final FilterBean bean) { + Sheet sheet = querySheet(clazz, SelectColumn.createIncludes(selectedColumn), flipper, bean); + final Sheet rs = new Sheet<>(); + if (sheet.isEmpty()) return rs; + rs.setTotal(sheet.getTotal()); + final EntityXInfo info = EntityXInfo.load(this, clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + final List list = new ArrayList<>(); + for (T t : sheet.getRows()) { + list.add(selected.get(t)); + } + rs.setRows(list); + return rs; + } + /** * 根据过滤对象FilterBean和翻页对象Flipper查询一页的数据 * @@ -1558,18 +1729,14 @@ public final class DataJDBCSource implements DataSource { final EntityCache cache = info.inner.getCache(); if (cache != null) { Predicate filter = null; - Comparator sort = null; boolean valid = true; if (bean != null) { FilterInfo finfo = FilterInfo.load(bean.getClass(), this); valid = finfo.isValidCacheJoin(); - if (valid) { - filter = finfo.getFilterPredicate(info.inner, bean); - sort = finfo.getSortComparator(info.inner, flipper); - } + if (valid) filter = finfo.getFilterPredicate(info.inner, bean); } if (valid) { - Sheet sheet = cache.querySheet(selects, filter, flipper, sort); + Sheet sheet = cache.querySheet(selects, filter, flipper, FilterInfo.getSortComparator(info.inner, flipper)); if (!sheet.isEmpty() || cache.isFullLoaded()) return sheet; } } @@ -2084,7 +2251,7 @@ public final class DataJDBCSource implements DataSource { } public void createPrimaryValue(T src) { - long v = primaryValue.incrementAndGet() * allocationSize + nodeid; + long v = allocationSize > 1 ? (primaryValue.incrementAndGet() * allocationSize + nodeid) : primaryValue.incrementAndGet(); Class p = inner.getPrimaryType(); if (p == int.class || p == Integer.class) { getPrimary().set(src, (Integer) ((Long) v).intValue()); diff --git a/src/com/wentch/redkale/source/DataJPASource.java b/src/com/wentch/redkale/source/DataJPASource.java index d7ac29d71..a85cbf288 100644 --- a/src/com/wentch/redkale/source/DataJPASource.java +++ b/src/com/wentch/redkale/source/DataJPASource.java @@ -59,6 +59,66 @@ final class DataJPASource implements DataSource { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } + @Override + public Sheet queryColumnSheet(String selectedColumn, Class clazz, Flipper flipper, FilterBean bean) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Set queryColumnSet(String selectedColumn, Class clazz, String column, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Set queryColumnSet(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public List queryColumnList(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int[] queryColumnIntSet(String selectedColumn, Class clazz, String column, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public long[] queryColumnLongSet(String selectedColumn, Class clazz, String column, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int[] queryColumnIntList(String selectedColumn, Class clazz, String column, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public long[] queryColumnLongList(String selectedColumn, Class clazz, String column, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int[] queryColumnIntSet(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public long[] queryColumnLongSet(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int[] queryColumnIntList(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public long[] queryColumnLongList(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + private static class DataJPAConnection extends DataConnection { private final EntityManager manager; diff --git a/src/com/wentch/redkale/source/DataSource.java b/src/com/wentch/redkale/source/DataSource.java index ced5c3f4d..125376816 100644 --- a/src/com/wentch/redkale/source/DataSource.java +++ b/src/com/wentch/redkale/source/DataSource.java @@ -330,7 +330,28 @@ public interface DataSource { */ public T find(final Class clazz, final FilterBean bean); - //-----------------------list---------------------------- + //-----------------------list set---------------------------- + public int[] queryColumnIntSet(String selectedColumn, Class clazz, String column, Serializable key); + + public long[] queryColumnLongSet(String selectedColumn, Class clazz, String column, Serializable key); + + public int[] queryColumnIntList(String selectedColumn, Class clazz, String column, Serializable key); + + public long[] queryColumnLongList(String selectedColumn, Class clazz, String column, Serializable key); + + /** + * 根据指定字段值查询对象某个字段的集合 + * + * @param + * @param + * @param selectedColumn + * @param clazz + * @param column + * @param key + * @return + */ + public Set queryColumnSet(String selectedColumn, Class clazz, String column, Serializable key); + /** * 根据指定字段值查询对象某个字段的集合 * @@ -344,6 +365,42 @@ public interface DataSource { */ public List queryColumnList(String selectedColumn, Class clazz, String column, Serializable key); + public int[] queryColumnIntSet(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key); + + public long[] queryColumnLongSet(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key); + + public int[] queryColumnIntList(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key); + + public long[] queryColumnLongList(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key); + + /** + * 根据指定字段值查询对象某个字段的集合 + * + * @param + * @param + * @param selectedColumn + * @param clazz + * @param column + * @param express + * @param key + * @return + */ + public Set queryColumnSet(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key); + + /** + * 根据指定字段值查询对象某个字段的集合 + * + * @param + * @param + * @param selectedColumn + * @param clazz + * @param column + * @param express + * @param key + * @return + */ + public List queryColumnList(String selectedColumn, Class clazz, String column, FilterExpress express, Serializable key); + /** * 根据指定字段值查询对象集合 * @@ -415,6 +472,19 @@ public interface DataSource { public List queryList(final Class clazz, final SelectColumn selects, final FilterBean bean); //-----------------------sheet---------------------------- + /** + * 根据指定参数查询对象某个字段的集合 + *

+ * @param + * @param + * @param selectedColumn + * @param clazz + * @param flipper + * @param bean + * @return + */ + public Sheet queryColumnSheet(String selectedColumn, Class clazz, final Flipper flipper, final FilterBean bean); + /** * 根据过滤对象FilterBean和翻页对象Flipper查询一页的数据 * diff --git a/src/com/wentch/redkale/source/DistributeGenerator.java b/src/com/wentch/redkale/source/DistributeGenerator.java index 1b5338e42..6c5532aed 100644 --- a/src/com/wentch/redkale/source/DistributeGenerator.java +++ b/src/com/wentch/redkale/source/DistributeGenerator.java @@ -35,5 +35,10 @@ public @interface DistributeGenerator { int initialValue() default 1; + /** + * 如果allocationSize的值小于或等于1,则主键不会加上nodeid + *

+ * @return + */ int allocationSize() default 1000; } diff --git a/src/com/wentch/redkale/source/EntityCache.java b/src/com/wentch/redkale/source/EntityCache.java index aee292301..06cc82d03 100644 --- a/src/com/wentch/redkale/source/EntityCache.java +++ b/src/com/wentch/redkale/source/EntityCache.java @@ -96,9 +96,9 @@ final class EntityCache { return rs.isPresent() ? (needcopy ? reproduce.copy(this.creator.create(), rs.get()) : rs.get()) : null; } - public List queryList(final SelectColumn selects, final Predicate filter, final Comparator sort) { + public Collection queryCollection(final boolean set, final SelectColumn selects, final Predicate filter, final Comparator sort) { boolean parallel = isParallel(); - final List rs = parallel ? new CopyOnWriteArrayList<>() : new ArrayList<>(); + final Collection rs = parallel ? (set ? new CopyOnWriteArraySet<>() : new CopyOnWriteArrayList<>()) : (set ? new LinkedHashSet<>() : new ArrayList<>()); Stream stream = listStream(); if (filter != null) stream = stream.filter(filter); if (sort != null) stream = stream.sorted(sort); @@ -115,7 +115,15 @@ final class EntityCache { rs.add(item); }); } - return parallel ? new ArrayList<>(rs) : rs; + return parallel ? (set ? new LinkedHashSet<>(rs) : new ArrayList<>(rs)) : rs; + } + + public Set querySet(final SelectColumn selects, final Predicate filter, final Comparator sort) { + return (Set) queryCollection(true, selects, filter, sort); + } + + public List queryList(final SelectColumn selects, final Predicate filter, final Comparator sort) { + return (List) queryCollection(false, selects, filter, sort); } public Sheet querySheet(final SelectColumn selects, final Predicate filter, final Flipper flipper, final Comparator sort) { diff --git a/src/com/wentch/redkale/source/FilterInfo.java b/src/com/wentch/redkale/source/FilterInfo.java index 98c2708be..d62c9344d 100644 --- a/src/com/wentch/redkale/source/FilterInfo.java +++ b/src/com/wentch/redkale/source/FilterInfo.java @@ -13,6 +13,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.logging.Logger; +import javax.persistence.*; /** * @@ -60,6 +61,7 @@ final class FilterInfo { do { for (Field field : cltmp.getDeclaredFields()) { if (field.getAnnotation(Ignore.class) != null) continue; + if (field.getAnnotation(Transient.class) != null) continue; if (Modifier.isStatic(field.getModifiers())) continue; if (fields.contains(field.getName())) continue; char[] chars = field.getName().toCharArray(); @@ -155,7 +157,7 @@ final class FilterInfo { return rootNode.getFilterPredicate(info, bean); } - public Comparator getSortComparator(EntityInfo info, Flipper flipper) { + public static Comparator getSortComparator(EntityInfo info, Flipper flipper) { if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty()) return null; Comparator comparator = null; for (String item : flipper.getSort().split(",")) { diff --git a/src/com/wentch/redkale/net/http/ByteArray.java b/src/com/wentch/redkale/util/ByteArray.java similarity index 82% rename from src/com/wentch/redkale/net/http/ByteArray.java rename to src/com/wentch/redkale/util/ByteArray.java index eac9cbe38..4dc85bbbf 100644 --- a/src/com/wentch/redkale/net/http/ByteArray.java +++ b/src/com/wentch/redkale/util/ByteArray.java @@ -3,9 +3,8 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package com.wentch.redkale.net.http; +package com.wentch.redkale.util; -import com.wentch.redkale.util.*; import java.nio.*; import java.nio.charset.*; @@ -55,6 +54,10 @@ public final class ByteArray { System.arraycopy(this.content, 0, buf, 0, count); } + public byte[] directBytes() { + return content; + } + public int find(int offset, char value) { return find(offset, (byte) value); } @@ -80,6 +83,10 @@ public final class ByteArray { if (count > 0) count--; } + public void addInt(int value) { + add((byte) (value >> 24 & 0xFF), (byte) (value >> 16 & 0xFF), (byte) (value >> 8 & 0xFF), (byte) (value & 0xFF)); + } + public void add(byte value) { if (count >= content.length - 1) { byte[] ns = new byte[content.length + 8]; @@ -89,6 +96,16 @@ public final class ByteArray { content[count++] = value; } + public void add(byte... values) { + if (count >= content.length - values.length) { + byte[] ns = new byte[content.length + values.length]; + System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + System.arraycopy(content, count, values, 0, values.length); + count += values.length; + } + public void add(ByteBuffer buffer, int len) { if (len < 1) return; if (count >= content.length - len) { diff --git a/src/com/wentch/redkale/util/Utility.java b/src/com/wentch/redkale/util/Utility.java index 8e11ffab0..8accf4fd5 100644 --- a/src/com/wentch/redkale/util/Utility.java +++ b/src/com/wentch/redkale/util/Utility.java @@ -78,10 +78,18 @@ public final class Utility { return today.getYear() * 10000 + today.getMonthValue() * 100 + today.getDayOfMonth(); } + public static String binToHexString(byte[] bytes) { + return new String(binToHex(bytes)); + } + public static char[] binToHex(byte[] bytes) { return binToHex(bytes, 0, bytes.length); } + public static String binToHexString(byte[] bytes, int offset, int len) { + return new String(binToHex(bytes, offset, len)); + } + public static char[] binToHex(byte[] bytes, int offset, int len) { final char[] sb = new char[len * 2]; final int end = offset + len;