From 19150e9f9ce88ce4e4ffe1fbf51cd6d27fc84426 Mon Sep 17 00:00:00 2001 From: Redkale <22250530@qq.com> Date: Mon, 31 Oct 2016 13:32:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ColumnValue=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../redkale/service/DataSourceService.java | 10 ++ src/org/redkale/source/ColumnExpress.java | 21 ++++ src/org/redkale/source/ColumnValue.java | 67 ++++++++++ src/org/redkale/source/DataDefaultSource.java | 118 +++++++++++++++++ src/org/redkale/source/DataSource.java | 4 + src/org/redkale/source/EntityCache.java | 119 ++++++++++++++---- src/org/redkale/source/FilterNode.java | 2 +- 7 files changed, 315 insertions(+), 26 deletions(-) create mode 100644 src/org/redkale/source/ColumnExpress.java create mode 100644 src/org/redkale/source/ColumnValue.java diff --git a/src/org/redkale/service/DataSourceService.java b/src/org/redkale/service/DataSourceService.java index 883c5ae9b..24b6ceaad 100644 --- a/src/org/redkale/service/DataSourceService.java +++ b/src/org/redkale/service/DataSourceService.java @@ -63,6 +63,16 @@ public class DataSourceService implements DataSource, Service, AutoCloseable { source.updateColumn(clazz, column, value, node); } + @Override + public void updateColumn(final Class clazz, final Serializable id, final ColumnValue... values) { + source.updateColumn(clazz, id, values); + } + + @Override + public void updateColumn(final Class clazz, final FilterNode node, final ColumnValue... values) { + source.updateColumn(clazz, node, values); + } + @Override public void updateColumnIncrement(final Class clazz, final Serializable id, final String column, long incvalue) { source.updateColumnIncrement(clazz, id, column, incvalue); diff --git a/src/org/redkale/source/ColumnExpress.java b/src/org/redkale/source/ColumnExpress.java new file mode 100644 index 000000000..72e93fc09 --- /dev/null +++ b/src/org/redkale/source/ColumnExpress.java @@ -0,0 +1,21 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +/** + * 函数表达式, 均与SQL定义中的表达式相同 + * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + */ +public enum ColumnExpress { + MOV, //直接赋值 col = val + INCR, //追加值 col = col + val + AND, //与值 col = col & val + OR; //或值 col = col | val +} diff --git a/src/org/redkale/source/ColumnValue.java b/src/org/redkale/source/ColumnValue.java new file mode 100644 index 000000000..8e0162249 --- /dev/null +++ b/src/org/redkale/source/ColumnValue.java @@ -0,0 +1,67 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; + +/** + * ColumnValue主要用于多个字段更新的表达式。 + * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class ColumnValue { + + private String column; + + private ColumnExpress express; + + private Serializable value; + + public ColumnValue() { + } + + public ColumnValue(String column, Serializable value) { + this(column, ColumnExpress.MOV, value); + } + + public ColumnValue(String column, ColumnExpress express, Serializable value) { + this.column = column; + this.express = express == null ? ColumnExpress.MOV : express; + this.value = value; + } + + public String getColumn() { + return column; + } + + public void setColumn(String column) { + this.column = column; + } + + public ColumnExpress getExpress() { + return express; + } + + public void setExpress(ColumnExpress express) { + this.express = express; + } + + public Serializable getValue() { + return value; + } + + public void setValue(Serializable value) { + this.value = value; + } + + @Override + public String toString() { + return "{\"column\":\"" + column + "\", \"express\":" + express + ", \"value\":" + ((value instanceof CharSequence) ? ("\"" + value + "\"") : value) + "}"; + } +} diff --git a/src/org/redkale/source/DataDefaultSource.java b/src/org/redkale/source/DataDefaultSource.java index ba0063e59..88c295d2e 100644 --- a/src/org/redkale/source/DataDefaultSource.java +++ b/src/org/redkale/source/DataDefaultSource.java @@ -731,6 +731,124 @@ public final class DataDefaultSource implements DataSource, Function Entity类的泛型 + * @param clazz Entity类 + * @param id 主键值 + * @param values 字段值 + */ + @Override + public void updateColumn(final Class clazz, final Serializable id, final ColumnValue... values) { + final EntityInfo info = loadEntityInfo(clazz); + if (info.isVirtualEntity()) { + updateColumn(null, info, id, values); + return; + } + Connection conn = createWriteSQLConnection(); + try { + updateColumn(conn, info, id, values); + } finally { + closeSQLConnection(conn); + } + } + + private void updateColumn(final Connection conn, final EntityInfo info, final Serializable id, final ColumnValue... values) { + if (values == null || values.length < 1) return; + try { + StringBuilder setsql = new StringBuilder(); + final List> attrs = new ArrayList<>(); + final List cols = new ArrayList<>(); + final boolean virtual = info.isVirtualEntity(); + for (ColumnValue col : values) { + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) continue; + attrs.add(attr); + cols.add(col); + if (!virtual) { + if (setsql.length() > 0) setsql.append(", "); + setsql.append(info.getSQLColumn(null, col.getColumn())).append(" = ").append(info.formatToString(col.getValue())); + } + } + if (!virtual) { + String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id); + if (debug.get()) logger.finest(info.getType().getSimpleName() + ": " + sql); + final Statement stmt = conn.createStatement(); + stmt.execute(sql); + stmt.close(); + } + //--------------------------------------------------- + final EntityCache cache = info.getCache(); + if (cache == null) return; + T rs = cache.updateColumn(id, attrs, cols); + if (cacheListener != null) cacheListener.updateCache(info.getType(), rs); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + /** + * 根据主键值更新对象的多个column对应的值, 必须是Entity Class + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * @param values 字段值 + */ + @Override + public void updateColumn(final Class clazz, final FilterNode node, final ColumnValue... values) { + final EntityInfo info = loadEntityInfo(clazz); + if (info.isVirtualEntity()) { + updateColumn(null, info, node, values); + return; + } + Connection conn = createWriteSQLConnection(); + try { + updateColumn(conn, info, node, values); + } finally { + closeSQLConnection(conn); + } + } + + private void updateColumn(final Connection conn, final EntityInfo info, final FilterNode node, final ColumnValue... values) { + if (values == null || values.length < 1) return; + try { + StringBuilder setsql = new StringBuilder(); + final List> attrs = new ArrayList<>(); + final List cols = new ArrayList<>(); + final boolean virtual = info.isVirtualEntity(); + for (ColumnValue col : values) { + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) continue; + attrs.add(attr); + cols.add(col); + if (!virtual) { + if (setsql.length() > 0) setsql.append(", "); + setsql.append(info.getSQLColumn("a", col.getColumn())).append(" = ").append(info.formatToString(col.getValue())); + } + } + if (!virtual) { + Map joinTabalis = node.getJoinTabalis(); + CharSequence join = node.createSQLJoin(this, joinTabalis, info); + CharSequence where = node.createSQLExpress(info, joinTabalis); + + String sql = "UPDATE " + info.getTable(node) + " a SET " + setsql + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + if (debug.get()) logger.finest(info.getType().getSimpleName() + " update sql=" + sql); + final Statement stmt = conn.createStatement(); + stmt.execute(sql); + stmt.close(); + } + //--------------------------------------------------- + final EntityCache cache = info.getCache(); + if (cache == null) return; + T[] rs = cache.updateColumn(node, attrs, cols); + if (cacheListener != null) cacheListener.updateCache(info.getType(), rs); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + /** * 根据主键值给对象的column对应的值+incvalue, 必须是Entity Class * 等价SQL: UPDATE {clazz} SET {column} = {column} + {incvalue} WHERE {primary} = {id} diff --git a/src/org/redkale/source/DataSource.java b/src/org/redkale/source/DataSource.java index f59b19d83..560cd9373 100644 --- a/src/org/redkale/source/DataSource.java +++ b/src/org/redkale/source/DataSource.java @@ -66,6 +66,10 @@ public interface DataSource { public void updateColumn(final Class clazz, final String column, final Serializable value, final FilterNode node); + public void updateColumn(final Class clazz, final Serializable id, final ColumnValue... values); + + public void updateColumn(final Class clazz, final FilterNode node, final ColumnValue... values); + public void updateColumnIncrement(final Class clazz, final Serializable id, final String column, long incvalue); public void updateColumnAnd(final Class clazz, final Serializable id, final String column, long incvalue); diff --git a/src/org/redkale/source/EntityCache.java b/src/org/redkale/source/EntityCache.java index ba7ebee72..cc9fbf146 100644 --- a/src/org/redkale/source/EntityCache.java +++ b/src/org/redkale/source/EntityCache.java @@ -387,15 +387,19 @@ public final class EntityCache { if (value == null) return; T rs = this.map.get(this.primary.get(value)); if (rs == null) return; - this.chgReproduce.apply(rs, value); + synchronized (rs) { + this.chgReproduce.apply(rs, value); + } } public T update(final T value, Collection> attrs) { if (value == null) return value; T rs = this.map.get(this.primary.get(value)); if (rs == null) return rs; - for (Attribute attr : attrs) { - attr.set(rs, attr.get(value)); + synchronized (rs) { + for (Attribute attr : attrs) { + attr.set(rs, attr.get(value)); + } } return rs; } @@ -404,8 +408,10 @@ public final class EntityCache { if (value == null || node == null) return (T[]) Array.newInstance(type, 0); T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len)); for (T rs : rms) { - for (Attribute attr : attrs) { - attr.set(rs, attr.get(value)); + synchronized (rs) { + for (Attribute attr : attrs) { + attr.set(rs, attr.get(value)); + } } } return rms; @@ -427,46 +433,109 @@ public final class EntityCache { return rms; } + public T updateColumn(final Serializable id, List> attrs, final List values) { + if (id == null || attrs == null || attrs.isEmpty()) return null; + T rs = this.map.get(id); + if (rs == null) return rs; + synchronized (rs) { + for (int i = 0; i < attrs.size(); i++) { + ColumnValue cv = values.get(i); + updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue()); + } + } + return rs; + } + + public T[] updateColumn(final FilterNode node, List> attrs, final List values) { + if (attrs == null || attrs.isEmpty() || node == null) return (T[]) Array.newInstance(type, 0); + T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len)); + for (T rs : rms) { + synchronized (rs) { + for (int i = 0; i < attrs.size(); i++) { + ColumnValue cv = values.get(i); + updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue()); + } + } + } + return rms; + } + public T updateColumnOr(final Serializable id, Attribute attr, final long orvalue) { if (id == null) return null; T rs = this.map.get(id); if (rs == null) return rs; - Number numb = (Number) attr.get(rs); - return updateColumnIncrAndOr(attr, rs, (numb == null) ? orvalue : (numb.longValue() | orvalue)); + synchronized (rs) { + return updateColumn(attr, rs, ColumnExpress.OR, orvalue); + } } public T updateColumnAnd(final Serializable id, Attribute attr, final long andvalue) { if (id == null) return null; T rs = this.map.get(id); if (rs == null) return rs; - Number numb = (Number) attr.get(rs); - return updateColumnIncrAndOr(attr, rs, (numb == null) ? 0 : (numb.longValue() & andvalue)); + synchronized (rs) { + return updateColumn(attr, rs, ColumnExpress.AND, andvalue); + } } public T updateColumnIncrement(final Serializable id, Attribute attr, final long incvalue) { if (id == null) return null; T rs = this.map.get(id); if (rs == null) return rs; - Number numb = (Number) attr.get(rs); - return updateColumnIncrAndOr(attr, rs, (numb == null) ? incvalue : (numb.longValue() + incvalue)); + synchronized (rs) { + return updateColumn(attr, rs, ColumnExpress.INCR, incvalue); + } } - private T updateColumnIncrAndOr(Attribute attr, final T rs, Number numb) { + private T updateColumn(Attribute attr, final T rs, final ColumnExpress express, Serializable val) { final Class ft = attr.type(); - if (ft == int.class || ft == Integer.class) { - numb = numb.intValue(); - } else if (ft == long.class || ft == Long.class) { - numb = numb.longValue(); - } else if (ft == short.class || ft == Short.class) { - numb = numb.shortValue(); - } else if (ft == float.class || ft == Float.class) { - numb = numb.floatValue(); - } else if (ft == double.class || ft == Double.class) { - numb = numb.doubleValue(); - } else if (ft == byte.class || ft == Byte.class) { - numb = numb.byteValue(); + Number numb = null; + Serializable newval = null; + switch (express) { + case INCR: + numb = (Number) attr.get(rs); + if (numb == null) { + numb = (Number) val; + } else { + numb = numb.longValue() + ((Number) val).longValue(); + } + break; + case AND: + numb = (Number) attr.get(rs); + if (numb == null) { + numb = 0; + } else { + numb = numb.longValue() & ((Number) val).longValue(); + } + break; + case OR: + numb = (Number) attr.get(rs); + if (numb == null) { + numb = 0; + } else { + numb = numb.longValue() | ((Number) val).longValue(); + } + break; + case MOV: + newval = val; + break; } - attr.set(rs, (V) numb); + if (numb != null) { + if (ft == int.class || ft == Integer.class) { + newval = numb.intValue(); + } else if (ft == long.class || ft == Long.class) { + newval = numb.longValue(); + } else if (ft == short.class || ft == Short.class) { + newval = numb.shortValue(); + } else if (ft == float.class || ft == Float.class) { + newval = numb.floatValue(); + } else if (ft == double.class || ft == Double.class) { + newval = numb.doubleValue(); + } else if (ft == byte.class || ft == Byte.class) { + newval = numb.byteValue(); + } + } + attr.set(rs, (V) newval); return rs; } diff --git a/src/org/redkale/source/FilterNode.java b/src/org/redkale/source/FilterNode.java index 24177e7f0..35047aa81 100644 --- a/src/org/redkale/source/FilterNode.java +++ b/src/org/redkale/source/FilterNode.java @@ -22,7 +22,7 @@ import org.redkale.util.Attribute; * * @author zhangjx */ -public class FilterNode { +public class FilterNode { //FilterNode 不能实现Serializable接口, 否则DataSource很多重载接口会出现冲突 protected String column;