mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
add mapping support
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package com.alibaba.druid.mapping;
|
||||
|
||||
public class DruidMappingException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DruidMappingException(){
|
||||
super();
|
||||
}
|
||||
|
||||
public DruidMappingException(String message, Throwable cause){
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DruidMappingException(String message){
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DruidMappingException(Throwable cause){
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +1,24 @@
|
||||
package com.alibaba.druid.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Entity {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private String tableName;
|
||||
private String name;
|
||||
private String description;
|
||||
private String tableName;
|
||||
|
||||
private List<Property> properties = new ArrayList<Property>();
|
||||
private Map<String, Property> properties = new LinkedHashMap<String, Property>();
|
||||
|
||||
public Entity(){
|
||||
}
|
||||
|
||||
public Entity(String name, String description, String tableName){
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -35,11 +44,30 @@ public class Entity {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
public List<Property> getProperties() {
|
||||
public void addProperty(Property property) {
|
||||
this.properties.put(property.getName(), property);
|
||||
}
|
||||
|
||||
public Property getProperty(String name) {
|
||||
Property prop = this.properties.get(name);
|
||||
|
||||
if (prop == null) {
|
||||
for (Map.Entry<String, Property> entry : properties.entrySet()) {
|
||||
if (entry.getKey().equalsIgnoreCase(name)) {
|
||||
prop = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Map<String, Property> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(List<Property> properties) {
|
||||
public void setProperties(Map<String, Property> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,150 @@
|
||||
package com.alibaba.druid.mapping;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.druid.sql.ast.SQLExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
|
||||
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
|
||||
|
||||
public class EntityMapingMySqlVisitor extends MySqlASTVisitorAdapter {
|
||||
|
||||
private final Entity entity;
|
||||
private final LinkedHashMap<String, Entity> entities;
|
||||
|
||||
public EntityMapingMySqlVisitor(Entity entity){
|
||||
super();
|
||||
this.entity = entity;
|
||||
public EntityMapingMySqlVisitor(LinkedHashMap<String, Entity> entities){
|
||||
this.entities = entities;
|
||||
}
|
||||
|
||||
public Entity getEntity() {
|
||||
public Map<String, Entity> getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
public Entity getFirstEntity() {
|
||||
for (Map.Entry<String, Entity> entry : entities.entrySet()) {
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Entity getEntity(String name) {
|
||||
Entity entity = this.entities.get(name);
|
||||
|
||||
if (entity == null) {
|
||||
for (Map.Entry<String, Entity> entry : entities.entrySet()) {
|
||||
if (entry.getKey().equalsIgnoreCase(name)) {
|
||||
entity = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public boolean visit(SQLSelectItem x) {
|
||||
x.getExpr().setParent(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(MySqlSelectQueryBlock x) {
|
||||
if (x.getSelectList().size() == 0) {
|
||||
fillSelectList(x);
|
||||
}
|
||||
|
||||
if (x.getSelectList().size() == 1) {
|
||||
if (x.getSelectList().get(0).getExpr() instanceof SQLAllColumnExpr) {
|
||||
x.getSelectList().clear();
|
||||
fillSelectList(x);
|
||||
}
|
||||
}
|
||||
|
||||
if (x.getFrom() == null) {
|
||||
Entity firstEntity = this.getFirstEntity();
|
||||
SQLExprTableSource from = new SQLExprTableSource(new SQLIdentifierExpr(firstEntity.getName()));
|
||||
x.setFrom(from);
|
||||
}
|
||||
|
||||
for (SQLSelectItem item : x.getSelectList()) {
|
||||
item.setParent(x);
|
||||
}
|
||||
|
||||
return super.visit(x);
|
||||
}
|
||||
|
||||
public boolean visit(SQLSelectQueryBlock x) {
|
||||
if (x instanceof MySqlSelectQueryBlock) {
|
||||
return visit((MySqlSelectQueryBlock) x);
|
||||
}
|
||||
|
||||
if (x.getSelectList().size() == 0) {
|
||||
fillSelectList(x);
|
||||
}
|
||||
|
||||
for (SQLSelectItem item : x.getSelectList()) {
|
||||
item.setParent(x);
|
||||
}
|
||||
|
||||
return super.visit(x);
|
||||
}
|
||||
|
||||
protected void fillSelectList(SQLSelectQueryBlock x) {
|
||||
Entity entity = getFirstEntity();
|
||||
|
||||
for (Property item : entity.getProperties().values()) {
|
||||
x.getSelectList().add(new SQLSelectItem(new SQLIdentifierExpr(item.getName()), '"' + item.getName() + '"'));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean visit(SQLIdentifierExpr x) {
|
||||
String propertyName = x.getName();
|
||||
|
||||
Property property = null;
|
||||
for (Entity entity : this.entities.values()) {
|
||||
property = entity.getProperty(propertyName);
|
||||
if (property != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (property == null) {
|
||||
throw new DruidMappingException("property not found : " + propertyName);
|
||||
}
|
||||
|
||||
String dbColumName = property.getDbColumnName();
|
||||
x.setName(dbColumName);
|
||||
|
||||
if (x.getParent() instanceof SQLSelectItem) {
|
||||
SQLSelectItem selectItem = (SQLSelectItem) x.getParent();
|
||||
if (selectItem.getAlias() == null) {
|
||||
selectItem.setAlias('"' + property.getName() + '"');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean visit(SQLExprTableSource x) {
|
||||
SQLExpr expr = x.getExpr();
|
||||
|
||||
if (expr instanceof SQLIdentifierExpr) {
|
||||
SQLIdentifierExpr tableExpr = (SQLIdentifierExpr) expr;
|
||||
String entityName = tableExpr.getName();
|
||||
|
||||
Entity entity = this.getEntity(entityName);
|
||||
|
||||
if (entity == null) {
|
||||
throw new DruidMappingException("entity not foudn : " + entityName);
|
||||
}
|
||||
|
||||
tableExpr.setName(entity.getTableName());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.alibaba.druid.mapping;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import com.alibaba.druid.sql.SQLUtils;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLNumberExpr;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit;
|
||||
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlSelectParser;
|
||||
|
||||
public class MappingEngine {
|
||||
|
||||
private LinkedHashMap<String, Entity> entities = new LinkedHashMap<String, Entity>();
|
||||
private Integer maxLimit;
|
||||
|
||||
public Integer getMaxLimit() {
|
||||
return maxLimit;
|
||||
}
|
||||
|
||||
public void setMaxLimit(Integer maxLimit) {
|
||||
this.maxLimit = maxLimit;
|
||||
}
|
||||
|
||||
public LinkedHashMap<String, Entity> getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
public void addEntity(Entity entity) {
|
||||
this.entities.put(entity.getName(), entity);
|
||||
}
|
||||
|
||||
public EntityMapingMySqlVisitor createMappingVisitor() {
|
||||
return new EntityMapingMySqlVisitor(entities);
|
||||
}
|
||||
|
||||
public String explain(String sql) {
|
||||
MySqlSelectQueryBlock query = explainToSQLObject(sql);
|
||||
|
||||
if (this.maxLimit != null) {
|
||||
if (query.getLimit() == null) {
|
||||
Limit limit = new Limit();
|
||||
limit.setRowCount(new SQLNumberExpr(maxLimit));
|
||||
query.setLimit(limit);
|
||||
} else {
|
||||
SQLNumberExpr rowCountExpr = (SQLNumberExpr) query.getLimit().getRowCount();
|
||||
int rowCount = rowCountExpr.getNumber().intValue();
|
||||
if (rowCount > maxLimit.intValue()) {
|
||||
rowCountExpr.setNumber(maxLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query.accept(this.createMappingVisitor());
|
||||
|
||||
return SQLUtils.toMySqlString(query);
|
||||
}
|
||||
|
||||
public MySqlSelectQueryBlock explainToSQLObject(String sql) {
|
||||
MySqlSelectParser selectParser = new MySqlSelectParser(sql);
|
||||
MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) selectParser.query();
|
||||
return query;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,18 @@ package com.alibaba.druid.mapping;
|
||||
public class Property {
|
||||
|
||||
private String name;
|
||||
private String desciption;
|
||||
private String dbColumnName;
|
||||
|
||||
public Property(){
|
||||
}
|
||||
|
||||
public Property(String name, String desciption, String dbColumnName){
|
||||
this.name = name;
|
||||
this.desciption = desciption;
|
||||
this.dbColumnName = dbColumnName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -21,4 +31,12 @@ public class Property {
|
||||
this.dbColumnName = dbColumnName;
|
||||
}
|
||||
|
||||
public String getDesciption() {
|
||||
return desciption;
|
||||
}
|
||||
|
||||
public void setDesciption(String desciption) {
|
||||
this.desciption = desciption;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.alibaba.druid.sql;
|
||||
|
||||
import com.alibaba.druid.sql.ast.SQLObject;
|
||||
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor;
|
||||
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor;
|
||||
import com.alibaba.druid.sql.dialect.postgresql.visitor.PGOutputVisitor;
|
||||
|
||||
public class SQLUtils {
|
||||
|
||||
public static String toMySqlString(SQLObject sqlObject) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
sqlObject.accept(new MySqlOutputVisitor(out));
|
||||
|
||||
String sql = out.toString();
|
||||
return sql;
|
||||
}
|
||||
|
||||
public static String toOracleString(SQLObject sqlObject) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
sqlObject.accept(new OracleOutputVisitor(out));
|
||||
|
||||
String sql = out.toString();
|
||||
return sql;
|
||||
}
|
||||
|
||||
public static String toPGString(SQLObject sqlObject) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
sqlObject.accept(new PGOutputVisitor(out));
|
||||
|
||||
String sql = out.toString();
|
||||
return sql;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,8 +27,10 @@ public class SQLExprTableSource extends SQLTableSourceImpl {
|
||||
public SQLExprTableSource(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SQLExprTableSource(SQLExpr expr){
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
public SQLExpr getExpr() {
|
||||
return this.expr;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.alibaba.druid.sql.dialect.mysql.visitor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.druid.sql.ast.SQLExpr;
|
||||
|
||||
public class MySqlMappingVisitor extends MySqlASTVisitorAdapter {
|
||||
|
||||
private final Map<String, SQLExpr> mapping = new HashMap<String, SQLExpr>();
|
||||
|
||||
public void addMapping(String column, String targetExpr) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -85,14 +85,22 @@ import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
|
||||
|
||||
public class SQLASTOutputVisitor extends SQLASTVisitorAdapter {
|
||||
|
||||
protected Appendable appender;
|
||||
private String indent = "\t";
|
||||
private int indentCount = 0;
|
||||
protected final Appendable appender;
|
||||
private String indent = "\t";
|
||||
private int indentCount = 0;
|
||||
|
||||
public SQLASTOutputVisitor(Appendable appender){
|
||||
this.appender = appender;
|
||||
}
|
||||
|
||||
public int getIndentCount() {
|
||||
return indentCount;
|
||||
}
|
||||
|
||||
public Appendable getAppender() {
|
||||
return appender;
|
||||
}
|
||||
|
||||
public void decrementIndent() {
|
||||
this.indentCount -= 1;
|
||||
}
|
||||
@@ -213,8 +221,8 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter {
|
||||
if (x.getLeft() instanceof SQLBinaryOpExpr) {
|
||||
SQLBinaryOpExpr left = (SQLBinaryOpExpr) x.getLeft();
|
||||
boolean leftRational = left.getOperator() == SQLBinaryOperator.BooleanAnd
|
||||
|| left.getOperator() == SQLBinaryOperator.BooleanOr;
|
||||
|
||||
|| left.getOperator() == SQLBinaryOperator.BooleanOr;
|
||||
|
||||
if (left.getOperator().priority > x.getOperator().priority) {
|
||||
if (leftRational) {
|
||||
incrementIndent();
|
||||
@@ -222,7 +230,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter {
|
||||
print('(');
|
||||
left.accept(this);
|
||||
print(')');
|
||||
|
||||
|
||||
if (leftRational) {
|
||||
decrementIndent();
|
||||
}
|
||||
@@ -257,11 +265,11 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter {
|
||||
if (rightRational) {
|
||||
incrementIndent();
|
||||
}
|
||||
|
||||
|
||||
print('(');
|
||||
right.accept(this);
|
||||
print(')');
|
||||
|
||||
|
||||
if (rightRational) {
|
||||
decrementIndent();
|
||||
}
|
||||
@@ -985,5 +993,5 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ public interface SQLASTVisitor {
|
||||
|
||||
boolean visit(SQLExistsExpr x);
|
||||
|
||||
boolean visit(SQLIdentifierExpr astNode);
|
||||
boolean visit(SQLIdentifierExpr x);
|
||||
|
||||
boolean visit(SQLInListExpr x);
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ public class SQLASTVisitorAdapter implements SQLASTVisitor {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(SQLIdentifierExpr astNode) {
|
||||
public boolean visit(SQLIdentifierExpr x) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,21 +22,28 @@ import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URL;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Date;
|
||||
import java.sql.Driver;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Types;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.alibaba.druid.logging.Log;
|
||||
import com.alibaba.druid.logging.LogFactory;
|
||||
import com.alibaba.druid.mapping.DruidMappingException;
|
||||
|
||||
/**
|
||||
* @author wenshao<szujobs@hotmail.com>
|
||||
@@ -520,6 +527,69 @@ public final class JdbcUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Map<String, Object>> executeQuery(Connection conn, String sql, List<Object> parameters) {
|
||||
List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>();
|
||||
|
||||
PreparedStatement stmt = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
stmt = conn.prepareStatement(sql);
|
||||
|
||||
for (int i = 0, size = parameters.size(); i < size; ++i) {
|
||||
Object val = parameters.get(i);
|
||||
int paramIndex = i + 1;
|
||||
|
||||
if (val == null) {
|
||||
stmt.setNull(paramIndex, Types.VARCHAR);
|
||||
} else if (val instanceof BigDecimal) {
|
||||
stmt.setBigDecimal(paramIndex, (BigDecimal) val);
|
||||
} else if (val instanceof String) {
|
||||
stmt.setString(paramIndex, (String) val);
|
||||
} else if (val instanceof Date) {
|
||||
long time = ((Date) val).getTime();
|
||||
stmt.setTimestamp(paramIndex, new java.sql.Timestamp(time));
|
||||
} else if (val instanceof Byte) {
|
||||
stmt.setByte(paramIndex, (Byte) val);
|
||||
} else if (val instanceof Short) {
|
||||
stmt.setShort(paramIndex, (Short) val);
|
||||
} else if (val instanceof Integer) {
|
||||
stmt.setInt(paramIndex, (Integer) val);
|
||||
} else if (val instanceof Long) {
|
||||
stmt.setLong(paramIndex, (Long) val);
|
||||
} else if (val instanceof Float) {
|
||||
stmt.setFloat(paramIndex, (Float) val);
|
||||
} else if (val instanceof Double) {
|
||||
stmt.setDouble(paramIndex, (Double) val);
|
||||
} else {
|
||||
throw new DruidMappingException("not support parameters : " + val);
|
||||
}
|
||||
}
|
||||
|
||||
rs = stmt.executeQuery();
|
||||
|
||||
ResultSetMetaData rsMeta = rs.getMetaData();
|
||||
|
||||
while (rs.next()) {
|
||||
Map<String, Object> row = new LinkedHashMap<String, Object>();
|
||||
|
||||
for (int i = 0, size = rsMeta.getColumnCount(); i < size; ++i) {
|
||||
String columName = rsMeta.getColumnName(i + 1);
|
||||
Object value = rs.getObject(i + 1);
|
||||
row.put(columName, value);
|
||||
}
|
||||
|
||||
rows.add(row);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DruidMappingException("execute error", e);
|
||||
} finally {
|
||||
JdbcUtils.close(rs);
|
||||
JdbcUtils.close(stmt);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
public static String toString(StackTraceElement[] stackTrace) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (StackTraceElement item : stackTrace) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.alibaba.druid.bvt.mapping;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import com.alibaba.druid.mapping.Entity;
|
||||
import com.alibaba.druid.mapping.MappingEngine;
|
||||
import com.alibaba.druid.mapping.Property;
|
||||
|
||||
public class MappingVisitorTest extends TestCase {
|
||||
|
||||
MappingEngine engine = new MappingEngine();
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
Entity entity = new Entity();
|
||||
entity.setName("设备");
|
||||
entity.setDescription("Device");
|
||||
entity.setTableName("device");
|
||||
|
||||
entity.addProperty(new Property("编号", "", "serviceTag"));
|
||||
entity.addProperty(new Property("IP地址", "", "ip"));
|
||||
|
||||
engine.addEntity(entity);
|
||||
}
|
||||
|
||||
public void test_0() throws Exception {
|
||||
|
||||
Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device", engine.explain("select *"));
|
||||
Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device\nWHERE ip = '127.0.0.1'",
|
||||
engine.explain("select 编号, IP地址 WHERE IP地址 = '127.0.0.1'"));
|
||||
|
||||
}
|
||||
|
||||
public void test_1() throws Exception {
|
||||
engine.setMaxLimit(10);
|
||||
|
||||
Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device\nLIMIT 10", engine.explain("select *"));
|
||||
Assert.assertEquals("SELECT serviceTag AS \"编号\", ip AS \"IP地址\"\nFROM device\nWHERE ip = '127.0.0.1'\nLIMIT 10",
|
||||
engine.explain("select 编号, IP地址 WHERE IP地址 = '127.0.0.1'"));
|
||||
|
||||
engine.setMaxLimit(null);
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user