Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d891c6c8dc | ||
|
|
0a4a88ed5a | ||
|
|
3c02219da0 | ||
|
|
65efc3372e | ||
|
|
3acea66788 | ||
|
|
b448514e40 | ||
|
|
024147344b | ||
|
|
52a34d3871 | ||
|
|
1ada26e4dd | ||
|
|
94d1b61f81 | ||
|
|
29299edb90 | ||
|
|
a2e2c5e178 | ||
|
|
8ae39df2e8 | ||
|
|
508b269a82 | ||
|
|
a8627b6105 | ||
|
|
8fee6b2c68 | ||
|
|
dd58571ffd | ||
|
|
8c25683cc5 | ||
|
|
a96f003b8c | ||
|
|
ff01443246 | ||
|
|
e915a253f8 | ||
|
|
b463389733 | ||
|
|
41c97b92c7 | ||
|
|
1142f81e9c | ||
|
|
5bc9f77b7b | ||
|
|
c5d0582807 | ||
|
|
525e65d152 | ||
|
|
d948c7af47 | ||
|
|
11a29b4ed6 | ||
|
|
e31c4a3041 | ||
|
|
2928d5fc93 | ||
|
|
12fc6f7f10 | ||
|
|
4bd8c207b4 | ||
|
|
be030a3640 | ||
|
|
ebaa250f7b | ||
|
|
826a2d7ee6 | ||
|
|
e476cf8176 | ||
|
|
03115694f9 | ||
|
|
26ffb04834 | ||
|
|
2979fcc33d | ||
|
|
9a29a11e22 | ||
|
|
d73a27be71 | ||
|
|
d77f424504 | ||
|
|
3142ad6041 | ||
|
|
6044f014c7 | ||
|
|
d1cfdfa14f | ||
|
|
8f9bfc3f28 | ||
|
|
9ae847d392 | ||
|
|
178226b730 | ||
|
|
801e45abce | ||
|
|
cd54a7040a | ||
|
|
3bd880b061 | ||
|
|
bf355cce28 | ||
|
|
58d08c5787 | ||
|
|
a778af73d8 | ||
|
|
7ca95c3549 | ||
|
|
54933ac3ac | ||
|
|
255048bf5b | ||
|
|
206fa19f3e | ||
|
|
f0e9047f8c | ||
|
|
698966d551 | ||
|
|
fc6b5cb458 | ||
|
|
9eee3bfa58 | ||
|
|
0adc0845fd | ||
|
|
92f98eff5f | ||
|
|
d97f8acf23 | ||
|
|
47e14bf2ec | ||
|
|
8eed4083bc | ||
|
|
9ef7641cd1 | ||
|
|
bafb6065c8 | ||
|
|
8b2460b8ab | ||
|
|
827172e743 | ||
|
|
fa9bd30de5 | ||
|
|
78e66ff74b | ||
|
|
7e8d1c3567 | ||
|
|
c3a7603674 | ||
|
|
4b8cfbba00 | ||
|
|
30771e5366 | ||
|
|
313c7f4ba1 | ||
|
|
ece4215a8a |
140
pom.xml
Normal file
140
pom.xml
Normal file
@@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.redkale</groupId>
|
||||
<artifactId>redkale</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<url>http://redkale.org</url>
|
||||
<description>redkale -- java framework</description>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2</name>
|
||||
<url>http://www.apache.org/licenses/</url>
|
||||
<distribution>repo</distribution>
|
||||
<comments>Apache License</comments>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>Redkale</id>
|
||||
<name>redkale</name>
|
||||
<email>redkale@qq.com</email>
|
||||
<url>http://redkale.org</url>
|
||||
<roles>
|
||||
<role>Project Manager</role>
|
||||
<role>Architect</role>
|
||||
</roles>
|
||||
<organization>redkale</organization>
|
||||
<organizationUrl>http://redkale.org</organizationUrl>
|
||||
<properties>
|
||||
<dept>No</dept>
|
||||
</properties>
|
||||
<timezone>8</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
<name>Redkale</name>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
<scm>
|
||||
<url>https://github.com/redkale/redkale</url>
|
||||
<connection>scm:git:git@github.com/redkale/redkale.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:redkale/redkale.git</developerConnection>
|
||||
</scm>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<compilerArguments>
|
||||
<verbose />
|
||||
<bootclasspath>${java.home}/lib/rt.jar</bootclasspath>
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.redkale.boot.Application</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-compiler-javac</artifactId>
|
||||
<version>2.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-utils</artifactId>
|
||||
<version>3.0.22</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -42,8 +42,8 @@
|
||||
一个组包含多个NODE, 同一Service服务可以由多个进程提供,这些进程称为一个GROUP,且同一GROUP内的进程必须在同一机房或局域网内
|
||||
一个group节点对应一个 Transport 对象。
|
||||
name: 服务组ID,长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
|
||||
protocol:值只能是UDP TCP, 默认TCP
|
||||
kind: 与SNCP服务连接时的数据传输类型;可选值有:rest(不区分大小写);值为空或空字符串表示按SNCP协议传输; 为rest表示按REST传输。默认值为空
|
||||
protocol:值范围:UDP TCP, 默认TCP
|
||||
subprotocol: 子协议,预留字段。默认值为空
|
||||
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息, 就必须有group节点信息。
|
||||
-->
|
||||
<group name="" protocol="TCP">
|
||||
|
||||
@@ -16,6 +16,8 @@ java.util.logging.FileHandler.limit = 10485760
|
||||
java.util.logging.FileHandler.count = 100
|
||||
java.util.logging.FileHandler.encoding = UTF-8
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%u.log
|
||||
#java.util.logging.FileHandler.unusual \u5c5e\u6027\u8868\u793a\u5c06 WARNING\u3001SEVERE \u7ea7\u522b\u7684\u65e5\u5fd7\u590d\u5236\u5199\u5165\u5355\u72ec\u7684\u6587\u4ef6\u4e2d
|
||||
#java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-error-%u.log
|
||||
java.util.logging.FileHandler.append = true
|
||||
|
||||
#java.util.logging.ConsoleHandler.level = FINE
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource
|
||||
com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
|
||||
因此 com.mysql.jdbc.Driver 会被自动转换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
|
||||
并且如果JDBC驱动是以上几个版本,javax.persistence.jdbc.driver属性都可以省略,Redkale会根据javax.persistence.jdbc.url的值来识别驱动
|
||||
-->
|
||||
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*******************************************************************************
|
||||
/** *****************************************************************************
|
||||
* Copyright (c) 2008 - 2013 Oracle Corporation. All rights reserved.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
@@ -12,7 +12,7 @@
|
||||
* Linda DeMichiel - Java Persistence 2.1
|
||||
* Linda DeMichiel - Java Persistence 2.0
|
||||
*
|
||||
******************************************************************************/
|
||||
***************************************************************************** */
|
||||
package javax.persistence;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
@@ -27,19 +27,28 @@ import java.lang.annotation.Target;
|
||||
* The value of the <code>Cacheable</code> annotation is inherited by
|
||||
* subclasses; it can be overridden by specifying
|
||||
* <code>Cacheable</code> on a subclass.
|
||||
*
|
||||
* <p> <code>Cacheable(false)</code> means that the entity and its state must
|
||||
*
|
||||
* <p>
|
||||
* <code>Cacheable(false)</code> means that the entity and its state must
|
||||
* not be cached by the provider.
|
||||
*
|
||||
*
|
||||
* @since Java Persistence 2.0
|
||||
*/
|
||||
@Target( { TYPE })
|
||||
@Target({TYPE})
|
||||
@Retention(RUNTIME)
|
||||
public @interface Cacheable {
|
||||
|
||||
/**
|
||||
* (Optional) Whether or not the entity should be cached.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean value() default true;
|
||||
|
||||
/**
|
||||
* (Optional) 定时自动更新缓存的周期秒数,为0表示不做定时更新, 大于0表示每经过interval秒后会自动从数据库中拉取数据更新Cache
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int interval() default 0;
|
||||
}
|
||||
|
||||
@@ -15,15 +15,17 @@ import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* API接口文档生成类,作用:生成Application实例中所有HttpServer的可用HttpServlet的API接口方法 <br>
|
||||
* 继承 HttpBaseServlet 是为了获取 WebAction 信息
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class ApiDocs extends HttpBaseServlet {
|
||||
|
||||
private final Application app;
|
||||
private final Application app; //Application全局对象
|
||||
|
||||
public ApiDocs(Application app) {
|
||||
this.app = app;
|
||||
|
||||
@@ -30,9 +30,17 @@ import org.redkale.watch.WatchFactory;
|
||||
import org.w3c.dom.*;
|
||||
|
||||
/**
|
||||
* 编译时需要加入: -XDignore.symbol.file=true
|
||||
*
|
||||
* 进程启动类,全局对象。 <br>
|
||||
* <pre>
|
||||
* 程序启动执行步骤:
|
||||
* 1、读取application.xml
|
||||
* 2、进行classpath扫描动态加载Service与Servlet
|
||||
* 3、优先加载所有SNCP协议的服务,再加载其他协议服务
|
||||
* 4、最后进行Service、Servlet与其他资源之间的依赖注入
|
||||
* </pre>
|
||||
* <p>
|
||||
* 进程启动类,程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet 优先加载所有SNCP协议的服务, 再加载其他协议服务, 最后进行Service、Servlet与其他资源之间的依赖注入。
|
||||
* 编译时需要加入: -XDignore.symbol.file=true
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -40,65 +48,99 @@ import org.w3c.dom.*;
|
||||
*/
|
||||
public final class Application {
|
||||
|
||||
//当前进程启动的时间, 类型: long
|
||||
/**
|
||||
* 当前进程启动的时间, 类型: long
|
||||
*/
|
||||
public static final String RESNAME_APP_TIME = "APP_TIME";
|
||||
|
||||
//当前进程的根目录, 类型:String、File、Path
|
||||
/**
|
||||
* 当前进程的根目录, 类型:String、File、Path
|
||||
*/
|
||||
public static final String RESNAME_APP_HOME = "APP_HOME";
|
||||
|
||||
//application.xml 文件中resources节点的内容, 类型: AnyValue
|
||||
/**
|
||||
* application.xml 文件中resources节点的内容, 类型: AnyValue
|
||||
*/
|
||||
public static final String RESNAME_APP_GRES = "APP_GRES";
|
||||
|
||||
//当前进程节点的name, 类型:String
|
||||
/**
|
||||
* 当前进程节点的name, 类型:String
|
||||
*/
|
||||
public static final String RESNAME_APP_NODE = "APP_NODE";
|
||||
|
||||
//当前进程节点的IP地址, 类型:InetAddress、String
|
||||
/**
|
||||
* 当前进程节点的IP地址, 类型:InetAddress、String
|
||||
*/
|
||||
public static final String RESNAME_APP_ADDR = "APP_ADDR";
|
||||
|
||||
//当前Service的IP地址+端口 类型: SocketAddress、InetSocketAddress、String
|
||||
/**
|
||||
* 当前Service的IP地址+端口 类型: SocketAddress、InetSocketAddress、String
|
||||
*/
|
||||
public static final String RESNAME_SERVER_ADDR = "SERVER_ADDR";
|
||||
|
||||
//当前SNCP Server所属的组 类型: String
|
||||
/**
|
||||
* 当前SNCP Server所属的组 类型: String
|
||||
*/
|
||||
public static final String RESNAME_SERVER_GROUP = "SERVER_GROUP";
|
||||
|
||||
//当前Server的ROOT目录 类型:String、File、Path
|
||||
/**
|
||||
* 当前Server的ROOT目录 类型:String、File、Path
|
||||
*/
|
||||
public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT;
|
||||
|
||||
//每个地址对应的Group名
|
||||
final Map<InetSocketAddress, String> globalNodes = new HashMap<>();
|
||||
|
||||
//协议地址的Group集合
|
||||
final Map<String, GroupInfo> globalGroups = new HashMap<>();
|
||||
|
||||
//本地IP地址
|
||||
final InetAddress localAddress;
|
||||
|
||||
//CacheSource 资源
|
||||
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
|
||||
|
||||
//DataSource 资源
|
||||
final List<DataSource> dataSources = new CopyOnWriteArrayList<>();
|
||||
|
||||
//NodeServer 资源
|
||||
final List<NodeServer> servers = new CopyOnWriteArrayList<>();
|
||||
|
||||
//传输端的ByteBuffer对象池
|
||||
final ObjectPool<ByteBuffer> transportBufferPool;
|
||||
|
||||
//传输端的线程池
|
||||
final ExecutorService transportExecutor;
|
||||
|
||||
//传输端的ChannelGroup
|
||||
final AsynchronousChannelGroup transportChannelGroup;
|
||||
|
||||
//全局根ResourceFactory
|
||||
final ResourceFactory resourceFactory = ResourceFactory.root();
|
||||
|
||||
//临时计数器
|
||||
CountDownLatch servicecdl; //会出现两次赋值
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
//是否用于main方法运行
|
||||
private final boolean singletonrun;
|
||||
|
||||
//根WatchFactory
|
||||
private final WatchFactory watchFactory = WatchFactory.root();
|
||||
|
||||
//进程根目录
|
||||
private final File home;
|
||||
|
||||
//日志
|
||||
private final Logger logger;
|
||||
|
||||
//服务配置项
|
||||
private final AnyValue config;
|
||||
|
||||
//服务启动时间
|
||||
private final long startTime = System.currentTimeMillis();
|
||||
|
||||
//Server启动的计数器,用于确保所有Server都启动完后再进行下一步处理
|
||||
private final CountDownLatch serversLatch;
|
||||
|
||||
private Application(final AnyValue config) {
|
||||
@@ -164,7 +206,8 @@ public final class Application {
|
||||
Properties prop = new Properties();
|
||||
final String handlers = properties.getProperty("handlers");
|
||||
if (handlers != null && handlers.contains("java.util.logging.FileHandler")) {
|
||||
prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", fileHandlerClass));
|
||||
//singletonrun模式下不输出文件日志
|
||||
prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", singletonrun ? "" : fileHandlerClass));
|
||||
}
|
||||
if (!prop.isEmpty()) {
|
||||
String prefix = fileHandlerClass + ".";
|
||||
@@ -189,7 +232,7 @@ public final class Application {
|
||||
}
|
||||
this.logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
|
||||
logger.log(Level.INFO, "------------------------------- Redkale ------------------------------");
|
||||
logger.log(Level.INFO, "------------------------------- Redkale -------------------------------");
|
||||
//------------------配置 <transport> 节点 ------------------
|
||||
ObjectPool<ByteBuffer> transportPool = null;
|
||||
ExecutorService transportExec = null;
|
||||
@@ -333,7 +376,7 @@ public final class Application {
|
||||
}
|
||||
GroupInfo ginfo = globalGroups.get(group);
|
||||
if (ginfo == null) {
|
||||
ginfo = new GroupInfo(group, protocol, conf.getValue("kind", ""), new LinkedHashSet<>());
|
||||
ginfo = new GroupInfo(group, protocol, conf.getValue("subprotocol", ""), new LinkedHashSet<>());
|
||||
globalGroups.put(group, ginfo);
|
||||
}
|
||||
for (AnyValue node : conf.getAnyValues("node")) {
|
||||
@@ -423,13 +466,27 @@ public final class Application {
|
||||
channel.write(buffer);
|
||||
buffer.clear();
|
||||
channel.configureBlocking(false);
|
||||
channel.read(buffer);
|
||||
buffer.flip();
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
buffer.get(bytes);
|
||||
channel.close();
|
||||
logger.info(new String(bytes));
|
||||
Thread.sleep(500);
|
||||
try {
|
||||
channel.read(buffer);
|
||||
buffer.flip();
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
buffer.get(bytes);
|
||||
channel.close();
|
||||
logger.info(new String(bytes));
|
||||
Thread.sleep(500);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof PortUnreachableException) {
|
||||
if ("APIDOC".equalsIgnoreCase(command)) {
|
||||
final Application application = Application.create(true);
|
||||
application.init();
|
||||
application.start();
|
||||
new ApiDocs(application).run();
|
||||
logger.info("APIDOC OK");
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void start() throws Exception {
|
||||
@@ -463,7 +520,7 @@ public final class Application {
|
||||
for (final AnyValue serconf : serconfs) {
|
||||
Thread thread = new Thread() {
|
||||
{
|
||||
String host = serconf.getValue("host", "").replace("0.0.0.0", "[0]");
|
||||
String host = serconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "*");
|
||||
setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class ClassFilter<T> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ClassFilter.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(ClassFilter.class.getName()); //日志对象
|
||||
|
||||
private static final boolean finer = logger.isLoggable(Level.FINER);
|
||||
private static final boolean finer = logger.isLoggable(Level.FINER); //日志级别
|
||||
|
||||
private final Set<FilterEntry<T>> entrys = new HashSet<>(); //符合条件的结果
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.net.InetSocketAddress;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 协议地址组合对象, 对应application.xml 中 resources->group 节点信息
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -17,21 +18,21 @@ import java.util.*;
|
||||
*/
|
||||
public class GroupInfo {
|
||||
|
||||
protected String name;
|
||||
protected String name; //地址
|
||||
|
||||
protected String protocol;
|
||||
protected String protocol; //协议 取值范围: TCP、UDP
|
||||
|
||||
protected String kind;
|
||||
protected String subprotocol; //子协议,预留使用
|
||||
|
||||
protected Set<InetSocketAddress> addrs;
|
||||
protected Set<InetSocketAddress> addrs; //地址列表, 对应 resources->group->node节点信息
|
||||
|
||||
public GroupInfo() {
|
||||
}
|
||||
|
||||
public GroupInfo(String name, String protocol, String kind, Set<InetSocketAddress> addrs) {
|
||||
public GroupInfo(String name, String protocol, String subprotocol, Set<InetSocketAddress> addrs) {
|
||||
this.name = name;
|
||||
this.protocol = protocol;
|
||||
this.kind = kind;
|
||||
this.subprotocol = subprotocol;
|
||||
this.addrs = addrs;
|
||||
}
|
||||
|
||||
@@ -51,12 +52,12 @@ public class GroupInfo {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public String getKind() {
|
||||
return kind;
|
||||
public String getSubprotocol() {
|
||||
return subprotocol;
|
||||
}
|
||||
|
||||
public void setKind(String kind) {
|
||||
this.kind = kind;
|
||||
public void setSubprotocol(String subprotocol) {
|
||||
this.subprotocol = subprotocol;
|
||||
}
|
||||
|
||||
public Set<InetSocketAddress> getAddrs() {
|
||||
|
||||
@@ -70,12 +70,12 @@ public class LogFileHandler extends Handler {
|
||||
throwable = sw.toString();
|
||||
}
|
||||
return String.format(format,
|
||||
System.currentTimeMillis(),
|
||||
source,
|
||||
record.getLoggerName(),
|
||||
record.getLevel().getName(),
|
||||
message,
|
||||
throwable);
|
||||
System.currentTimeMillis(),
|
||||
source,
|
||||
record.getLoggerName(),
|
||||
record.getLevel().getName(),
|
||||
message,
|
||||
throwable);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -84,9 +84,13 @@ public class LogFileHandler extends Handler {
|
||||
|
||||
private String pattern;
|
||||
|
||||
private String unusual; //不为null表示将 WARNING、SEVERE 级别的日志写入单独的文件中
|
||||
|
||||
private int limit; //文件大小限制
|
||||
|
||||
private final AtomicInteger index = new AtomicInteger();
|
||||
private final AtomicInteger logindex = new AtomicInteger();
|
||||
|
||||
private final AtomicInteger logunusualindex = new AtomicInteger();
|
||||
|
||||
private int count = 1; //文件限制
|
||||
|
||||
@@ -94,11 +98,17 @@ public class LogFileHandler extends Handler {
|
||||
|
||||
private boolean append;
|
||||
|
||||
private final AtomicLong length = new AtomicLong();
|
||||
private final AtomicLong loglength = new AtomicLong();
|
||||
|
||||
private final AtomicLong logunusuallength = new AtomicLong();
|
||||
|
||||
private File logfile;
|
||||
|
||||
private OutputStream stream;
|
||||
private File logunusualfile;
|
||||
|
||||
private OutputStream logstream;
|
||||
|
||||
private OutputStream logunusualstream;
|
||||
|
||||
public LogFileHandler() {
|
||||
updateTomorrow();
|
||||
@@ -114,7 +124,7 @@ public class LogFileHandler extends Handler {
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
cal.add(Calendar.DAY_OF_YEAR, 1);
|
||||
long t = cal.getTimeInMillis();
|
||||
if (this.tomorrow != t) index.set(0);
|
||||
if (this.tomorrow != t) logindex.set(0);
|
||||
this.tomorrow = t;
|
||||
}
|
||||
|
||||
@@ -131,35 +141,59 @@ public class LogFileHandler extends Handler {
|
||||
while (true) {
|
||||
try {
|
||||
LogRecord record = records.take();
|
||||
final boolean bigger = (limit > 0 && limit <= length.get());
|
||||
if (bigger || tomorrow <= record.getMillis()) {
|
||||
final boolean bigger = (limit > 0 && limit <= loglength.get());
|
||||
final boolean changeday = tomorrow <= record.getMillis();
|
||||
if (bigger || changeday) {
|
||||
updateTomorrow();
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
if (logstream != null) {
|
||||
logstream.close();
|
||||
if (bigger) {
|
||||
for (int i = Math.min(count - 2, index.get() - 1); i > 0; i--) {
|
||||
for (int i = Math.min(count - 2, logindex.get() - 1); i > 0; i--) {
|
||||
File greater = new File(logfile.getPath() + "." + i);
|
||||
if (greater.exists()) Files.move(greater.toPath(), new File(logfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
||||
}
|
||||
Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
||||
}
|
||||
stream = null;
|
||||
logstream = null;
|
||||
}
|
||||
}
|
||||
if (stream == null) {
|
||||
index.incrementAndGet();
|
||||
if (unusual != null && changeday && logunusualstream != null) {
|
||||
logunusualstream.close();
|
||||
if (limit > 0 && limit <= logunusuallength.get()) {
|
||||
for (int i = Math.min(count - 2, logunusualindex.get() - 1); i > 0; i--) {
|
||||
File greater = new File(logunusualfile.getPath() + "." + i);
|
||||
if (greater.exists()) Files.move(greater.toPath(), new File(logunusualfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
||||
}
|
||||
Files.move(logunusualfile.toPath(), new File(logunusualfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
||||
}
|
||||
logunusualstream = null;
|
||||
}
|
||||
if (logstream == null) {
|
||||
logindex.incrementAndGet();
|
||||
java.time.LocalDate date = LocalDate.now();
|
||||
logfile = new File(pattern.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
|
||||
logfile.getParentFile().mkdirs();
|
||||
length.set(logfile.length());
|
||||
stream = new FileOutputStream(logfile, append);
|
||||
loglength.set(logfile.length());
|
||||
logstream = new FileOutputStream(logfile, append);
|
||||
}
|
||||
if (unusual != null && logunusualstream == null) {
|
||||
logunusualindex.incrementAndGet();
|
||||
java.time.LocalDate date = LocalDate.now();
|
||||
logunusualfile = new File(unusual.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
|
||||
logunusualfile.getParentFile().mkdirs();
|
||||
logunusuallength.set(logunusualfile.length());
|
||||
logunusualstream = new FileOutputStream(logunusualfile, append);
|
||||
}
|
||||
//----------------------写日志-------------------------
|
||||
String message = getFormatter().format(record);
|
||||
String encoding = getEncoding();
|
||||
byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding);
|
||||
stream.write(bytes);
|
||||
length.addAndGet(bytes.length);
|
||||
logstream.write(bytes);
|
||||
loglength.addAndGet(bytes.length);
|
||||
if (unusual != null && (record.getLevel() == Level.WARNING || record.getLevel() == Level.SEVERE)) {
|
||||
logunusualstream.write(bytes);
|
||||
logunusuallength.addAndGet(bytes.length);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ErrorManager err = getErrorManager();
|
||||
if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE);
|
||||
@@ -177,30 +211,39 @@ public class LogFileHandler extends Handler {
|
||||
private void configure() {
|
||||
LogManager manager = LogManager.getLogManager();
|
||||
String cname = LogFileHandler.class.getName();
|
||||
pattern = manager.getProperty(cname + ".pattern");
|
||||
if (pattern == null) {
|
||||
pattern = "logs-%m/" + getPrefix() + "log-%d.log";
|
||||
this.pattern = manager.getProperty(cname + ".pattern");
|
||||
if (this.pattern == null) {
|
||||
this.pattern = "logs-%m/" + getPrefix() + "log-%d.log";
|
||||
} else {
|
||||
int pos = pattern.lastIndexOf('/');
|
||||
int pos = this.pattern.lastIndexOf('/');
|
||||
if (pos > 0) {
|
||||
pattern = pattern.substring(0, pos + 1) + getPrefix() + pattern.substring(pos + 1);
|
||||
this.pattern = this.pattern.substring(0, pos + 1) + getPrefix() + this.pattern.substring(pos + 1);
|
||||
} else {
|
||||
pattern = getPrefix() + pattern;
|
||||
this.pattern = getPrefix() + this.pattern;
|
||||
}
|
||||
}
|
||||
String unusualstr = manager.getProperty(cname + ".unusual");
|
||||
if (unusualstr != null) {
|
||||
int pos = unusualstr.lastIndexOf('/');
|
||||
if (pos > 0) {
|
||||
this.unusual = unusualstr.substring(0, pos + 1) + getPrefix() + unusualstr.substring(pos + 1);
|
||||
} else {
|
||||
this.unusual = getPrefix() + unusualstr;
|
||||
}
|
||||
}
|
||||
String limitstr = manager.getProperty(cname + ".limit");
|
||||
try {
|
||||
if (limitstr != null) limit = Math.abs(Integer.decode(limitstr));
|
||||
if (limitstr != null) this.limit = Math.abs(Integer.decode(limitstr));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
String countstr = manager.getProperty(cname + ".count");
|
||||
try {
|
||||
if (countstr != null) count = Math.max(1, Math.abs(Integer.decode(countstr)));
|
||||
if (countstr != null) this.count = Math.max(1, Math.abs(Integer.decode(countstr)));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
String appendstr = manager.getProperty(cname + ".append");
|
||||
try {
|
||||
if (appendstr != null) append = "true".equalsIgnoreCase(appendstr) || "1".equals(appendstr);
|
||||
if (appendstr != null) this.append = "true".equalsIgnoreCase(appendstr) || "1".equals(appendstr);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
String levelstr = manager.getProperty(cname + ".level");
|
||||
@@ -256,7 +299,7 @@ public class LogFileHandler extends Handler {
|
||||
@Override
|
||||
public void flush() {
|
||||
try {
|
||||
if (stream != null) stream.flush();
|
||||
if (logstream != null) logstream.flush();
|
||||
} catch (Exception e) {
|
||||
ErrorManager err = getErrorManager();
|
||||
if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE);
|
||||
@@ -266,7 +309,7 @@ public class LogFileHandler extends Handler {
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
try {
|
||||
if (stream != null) stream.close();
|
||||
if (logstream != null) logstream.close();
|
||||
} catch (Exception e) {
|
||||
ErrorManager err = getErrorManager();
|
||||
if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE);
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.redkale.util.*;
|
||||
@NodeProtocol({"HTTP"})
|
||||
public class NodeHttpServer extends NodeServer {
|
||||
|
||||
protected final boolean rest;
|
||||
protected final boolean rest; //是否加载REST服务, 为true加载rest节点信息并将所有可REST化的Service生成RestHttpServlet
|
||||
|
||||
protected final HttpServer httpServer;
|
||||
|
||||
@@ -131,7 +131,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
if (as.getKey().length() > max) max = as.getKey().length();
|
||||
}
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
sb.append(threadName).append(" Loaded ").append(as.getKey());
|
||||
sb.append(threadName).append(" Load ").append(as.getKey());
|
||||
for (int i = 0; i < max - as.getKey().length(); i++) {
|
||||
sb.append(' ');
|
||||
}
|
||||
@@ -202,7 +202,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
if (as.getKey().length() > max) max = as.getKey().length();
|
||||
}
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
sb.append(threadName).append(" Loaded ").append(as.getKey());
|
||||
sb.append(threadName).append(" Load ").append(as.getKey());
|
||||
for (int i = 0; i < max - as.getKey().length(); i++) {
|
||||
sb.append(' ');
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.Objects;
|
||||
import org.redkale.service.Service;
|
||||
|
||||
/**
|
||||
* NodeServer的拦截类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -17,10 +18,22 @@ import org.redkale.service.Service;
|
||||
*/
|
||||
public class NodeInterceptor {
|
||||
|
||||
/** *
|
||||
* Server.start之前调用 <br>
|
||||
* NodeServer.start的部署是先执行NodeInterceptor.preStart,再执行 Server.start 方法
|
||||
*
|
||||
* @param server NodeServer
|
||||
*/
|
||||
public void preStart(NodeServer server) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Server.shutdown之前调用 <br>
|
||||
* NodeServer.shutdown的部署是先执行NodeInterceptor.preShutdown,再执行 Server.sshutdown 方法
|
||||
*
|
||||
* @param server NodeServer
|
||||
*/
|
||||
public void preShutdown(NodeServer server) {
|
||||
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ public abstract class NodeServer {
|
||||
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
|
||||
String interceptorClass = this.serverConf.getValue("interceptor", "");
|
||||
if (!interceptorClass.isEmpty()) {
|
||||
Class clazz = Class.forName(interceptorClass);
|
||||
Class clazz = Class.forName(interceptorClass);
|
||||
this.interceptor = (NodeInterceptor) clazz.newInstance();
|
||||
}
|
||||
|
||||
@@ -276,8 +276,8 @@ public abstract class NodeServer {
|
||||
if (WebSocketNode.class.isAssignableFrom(type)) continue;
|
||||
}
|
||||
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
|
||||
if (resourceFactory.find(entry.getName(), type) != null) { //Server加载Service时需要判断是否已经加载过了。
|
||||
Service oldother = resourceFactory.find(entry.getName(), type);
|
||||
Service oldother = resourceFactory.find(entry.getName(), type);
|
||||
if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。
|
||||
interceptorServiceWrappers.add(new NodeInterceptor.InterceptorServiceWrapper(entry.getName(), type, oldother));
|
||||
continue;
|
||||
}
|
||||
@@ -347,7 +347,7 @@ public abstract class NodeServer {
|
||||
});
|
||||
if (sb != null) {
|
||||
remoteServiceWrappers.forEach(y -> {
|
||||
sb.append(threadName).append(y.toSimpleString()).append(" loaded and injected").append(LINE_SEPARATOR);
|
||||
sb.append(threadName).append(y.toSimpleString()).append(" load and inject").append(LINE_SEPARATOR);
|
||||
});
|
||||
}
|
||||
//----------------- init -----------------
|
||||
@@ -362,7 +362,7 @@ public abstract class NodeServer {
|
||||
long s = System.currentTimeMillis();
|
||||
y.getService().init(y.getConf());
|
||||
long e = System.currentTimeMillis() - s;
|
||||
if (slist != null) slist.add(new StringBuilder().append(threadName).append(y.toSimpleString()).append(" loaded and inited ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
|
||||
if (slist != null) slist.add(new StringBuilder().append(threadName).append(y.toSimpleString()).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
|
||||
} finally {
|
||||
clds.countDown();
|
||||
}
|
||||
@@ -403,7 +403,7 @@ public abstract class NodeServer {
|
||||
Transport first = transports.get(0);
|
||||
GroupInfo ginfo = application.findGroupInfo(first.getName());
|
||||
Transport newTransport = new Transport(groupid, ginfo.getProtocol(), application.getWatchFactory(),
|
||||
ginfo.getKind(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
|
||||
ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
|
||||
synchronized (application.resourceFactory) {
|
||||
transport = application.resourceFactory.find(groupid, Transport.class);
|
||||
if (transport == null) {
|
||||
@@ -429,7 +429,7 @@ public abstract class NodeServer {
|
||||
Set<InetSocketAddress> addrs = ginfo.copyAddrs();
|
||||
if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> ");
|
||||
transport = new Transport(group, ginfo.getProtocol(), application.getWatchFactory(),
|
||||
ginfo.getKind(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
|
||||
ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
|
||||
application.resourceFactory.register(group, transport);
|
||||
}
|
||||
return transport;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class NodeSncpServer extends NodeServer {
|
||||
List<SncpServlet> servlets = sncpServer.getSncpServlets();
|
||||
Collections.sort(servlets);
|
||||
for (SncpServlet en : servlets) {
|
||||
if (sb != null) sb.append(threadName).append(" Loaded ").append(en).append(LINE_SEPARATOR);
|
||||
if (sb != null) sb.append(threadName).append(" Load ").append(en).append(LINE_SEPARATOR);
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 对象数组的序列化,不包含int[]、long[]这样的primitive class数组.
|
||||
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有采用int存储。
|
||||
* 支持一定程度的泛型。
|
||||
* 数组的反序列化操作类 <br>
|
||||
* 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组。 <br>
|
||||
* 支持一定程度的泛型。 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -8,9 +8,9 @@ package org.redkale.convert;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
/**
|
||||
* 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组.
|
||||
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有必要采用int存储。
|
||||
* 支持一定程度的泛型。
|
||||
* 数组的序列化操作类 <br>
|
||||
* 对象数组的序列化,不包含int[]、long[]这样的primitive class数组。 <br>
|
||||
* 支持一定程度的泛型。 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -11,9 +11,8 @@ import java.lang.reflect.Type;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 对象集合的反序列化.
|
||||
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
|
||||
* 支持一定程度的泛型。
|
||||
* Collection的反序列化操作类 <br>
|
||||
* 支持一定程度的泛型。 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -9,9 +9,8 @@ import java.lang.reflect.*;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 对象集合的序列化.
|
||||
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
|
||||
* 支持一定程度的泛型。
|
||||
* Collection的序列化操作类 <br>
|
||||
* 支持一定程度的泛型。 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package org.redkale.convert;
|
||||
|
||||
/**
|
||||
* 序列化操作类
|
||||
* 序列化/反序列化操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -10,11 +10,12 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 用于类名的别名, 类似javax.persistence.Table
|
||||
* 该值必须是全局唯一
|
||||
* 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.writeClassName(String value) 。
|
||||
* 用于类名的别名, 该值必须是全局唯一 <br>
|
||||
* 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.writeClassName(String value) 。
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Inherited
|
||||
@@ -23,5 +24,10 @@ import java.lang.annotation.*;
|
||||
@Retention(RUNTIME)
|
||||
public @interface ConvertEntity {
|
||||
|
||||
/**
|
||||
* 别名值
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String value();
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
package org.redkale.convert;
|
||||
|
||||
/**
|
||||
* 序列化自定义异常类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class ConvertException extends RuntimeException {
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.redkale.convert.ext.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 序列化模块的工厂类,用于注册自定义的序列化类型,获取Convert
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -104,6 +105,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
this.register(double[].class, DoubleArraySimpledCoder.instance);
|
||||
this.register(String[].class, StringArraySimpledCoder.instance);
|
||||
//---------------------------------------------------------
|
||||
this.register(AnyValue.class, Creator.create(AnyValue.DefaultAnyValue.class));
|
||||
this.register(HttpCookie.class, new Creator<HttpCookie>() {
|
||||
@Override
|
||||
@Creator.ConstructorParameters({"name", "value"})
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
package org.redkale.convert;
|
||||
|
||||
/**
|
||||
* 序列化类型枚举,结合@ConvertColumn使用
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* @author zhangjx
|
||||
*/
|
||||
public enum ConvertType {
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.lang.reflect.*;
|
||||
import org.redkale.util.Attribute;
|
||||
|
||||
/**
|
||||
* 字段的反序列化操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.convert;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 反序列化操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.lang.reflect.*;
|
||||
import org.redkale.util.Attribute;
|
||||
|
||||
/**
|
||||
* 字段的序列化操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.convert;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 序列化操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Map的反序列化操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Map的序列化操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.Set;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 自定义对象的反序列化操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -85,6 +86,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
if (method.isSynthetic()) continue;
|
||||
if (method.getName().length() < 4) continue;
|
||||
if (!method.getName().startsWith("set")) continue;
|
||||
if (method.getAnnotation(java.beans.Transient.class) != null) continue;
|
||||
if (method.getParameterTypes().length != 1) continue;
|
||||
if (method.getReturnType() != void.class) continue;
|
||||
if (reversible && (cps == null || !ObjectEncoder.contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 自定义对象的序列化操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -77,6 +78,7 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
||||
if (method.getName().length() < 3) continue;
|
||||
if (method.getName().equals("getClass")) continue;
|
||||
if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue;
|
||||
if (method.getAnnotation(java.beans.Transient.class) != null) continue;
|
||||
if (method.getParameterTypes().length != 0) continue;
|
||||
if (method.getReturnType() == void.class) continue;
|
||||
if (reversible && (cps == null || !contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.redkale.convert;
|
||||
|
||||
/**
|
||||
* 反序列化的数据读取流
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -42,6 +43,7 @@ public abstract class Reader {
|
||||
* 读取对象的类名, 返回 null 表示对象为null, 返回空字符串表示当前class与返回的class一致,返回非空字符串表示class是当前class的子类。
|
||||
*
|
||||
* @param clazz 类名
|
||||
*
|
||||
* @return 返回字段数
|
||||
*/
|
||||
public String readObjectB(final Class clazz) {
|
||||
@@ -86,6 +88,7 @@ public abstract class Reader {
|
||||
* 根据字段读取字段对应的DeMember
|
||||
*
|
||||
* @param members DeMember的全量集合
|
||||
*
|
||||
* @return 匹配的DeMember
|
||||
*/
|
||||
public abstract DeMember readFieldName(final DeMember[] members);
|
||||
|
||||
@@ -9,6 +9,8 @@ import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 简易类的序列化和反序列化操作类
|
||||
* 能序列化为Boolean、Number或者字符串的类视为简易类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.convert;
|
||||
import org.redkale.util.Attribute;
|
||||
|
||||
/**
|
||||
* 序列化的数据输出流
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -37,7 +38,7 @@ public abstract class Writer {
|
||||
* @return boolean
|
||||
*/
|
||||
public abstract boolean needWriteClassName();
|
||||
|
||||
|
||||
/**
|
||||
* 写入类名
|
||||
*
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.convert.bson;
|
||||
|
||||
import java.nio.*;
|
||||
import java.util.function.*;
|
||||
import org.redkale.util.Utility;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -78,20 +79,14 @@ public class BsonByteBufferWriter extends BsonWriter {
|
||||
if (!buffer.hasRemaining()) {
|
||||
buffer.flip();
|
||||
buffer = supplier.get();
|
||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
||||
bufs[this.buffers.length] = buffer;
|
||||
this.buffers = bufs;
|
||||
this.buffers = Utility.append(this.buffers, buffer);
|
||||
this.index++;
|
||||
}
|
||||
int len = buffer.remaining();
|
||||
int size = 0;
|
||||
while (len < byteLength) {
|
||||
buffer = supplier.get();
|
||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
||||
bufs[this.buffers.length] = buffer;
|
||||
this.buffers = bufs;
|
||||
this.buffers = Utility.append(this.buffers, buffer);
|
||||
len += buffer.remaining();
|
||||
size++;
|
||||
}
|
||||
|
||||
@@ -15,21 +15,21 @@ import org.redkale.util.*;
|
||||
/**
|
||||
* <blockquote><pre>
|
||||
* BSON协议格式:
|
||||
* 1). 基本数据类型: 直接转换成byte[]
|
||||
* 2). SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。
|
||||
* 3). String: length(4 bytes) + byte[](utf8);
|
||||
* 4). 数组: length(4 bytes) + byte[]...
|
||||
* 5). Object:
|
||||
* 1. realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名)
|
||||
* 2. 空字符串(SmallString)
|
||||
* 3. SIGN_OBJECTB 标记位,值固定为0xBB (short)
|
||||
* 4. 循环字段值:
|
||||
* 1) 基本数据类型: 直接转换成byte[]
|
||||
* 2) SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。
|
||||
* 3) String: length(4 bytes) + byte[](utf8);
|
||||
* 4) 数组: length(4 bytes) + byte[]...
|
||||
* 5) Object:
|
||||
* 1、 realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名)
|
||||
* 2、 空字符串(SmallString)
|
||||
* 3、 SIGN_OBJECTB 标记位,值固定为0xBB (short)
|
||||
* 4、 循环字段值:
|
||||
* 4.1 SIGN_HASNEXT 标记位,值固定为1 (byte)
|
||||
* 4.2 字段类型; 1-9为基本类型和字符串; 101-109为基本类型和字符串的数组; 127为Object
|
||||
* 4.3 字段名 (SmallString)
|
||||
* 4.4 字段的值Object
|
||||
* 5. SIGN_NONEXT 标记位,值固定为0 (byte)
|
||||
* 6. SIGN_OBJECTE 标记位,值固定为0xEE (short)
|
||||
* 5、 SIGN_NONEXT 标记位,值固定为0 (byte)
|
||||
* 6、 SIGN_OBJECTE 标记位,值固定为0xEE (short)
|
||||
*
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.convert.bson;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -27,6 +28,9 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
|
||||
static {
|
||||
instance.register(Serializable.class, objectDecoder);
|
||||
instance.register(Serializable.class, objectEncoder);
|
||||
|
||||
instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class));
|
||||
instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class));
|
||||
}
|
||||
|
||||
private BsonFactory(BsonFactory parent, boolean tiny) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
package org.redkale.convert.bson;
|
||||
|
||||
import java.util.function.*;
|
||||
import org.redkale.convert.*;
|
||||
import static org.redkale.convert.Reader.SIGN_NULL;
|
||||
import org.redkale.convert.ext.*;
|
||||
@@ -42,19 +41,7 @@ public class BsonReader extends Reader {
|
||||
}
|
||||
|
||||
public static ObjectPool<BsonReader> createPool(int max) {
|
||||
return new ObjectPool<BsonReader>(max, new Creator<BsonReader>() {
|
||||
|
||||
@Override
|
||||
public BsonReader create(Object... params) {
|
||||
return new BsonReader();
|
||||
}
|
||||
}, null, new Predicate<BsonReader>() {
|
||||
|
||||
@Override
|
||||
public boolean test(BsonReader t) {
|
||||
return t.recycle();
|
||||
}
|
||||
});
|
||||
return new ObjectPool<>(max, (Object... params) -> new BsonReader(), null, (t) -> t.recycle());
|
||||
}
|
||||
|
||||
public BsonReader(byte[] bytes) {
|
||||
@@ -164,7 +151,7 @@ public class BsonReader extends Reader {
|
||||
if (bt == Reader.SIGN_NULL) return null;
|
||||
if (bt != SIGN_OBJECTB) {
|
||||
throw new ConvertException("a bson object must begin with " + (SIGN_OBJECTB)
|
||||
+ " (position = " + position + ") but '" + currentByte() + "'");
|
||||
+ " (position = " + position + ") but '" + currentByte() + "'");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -173,7 +160,7 @@ public class BsonReader extends Reader {
|
||||
public final void readObjectE(final Class clazz) {
|
||||
if (readShort() != SIGN_OBJECTE) {
|
||||
throw new ConvertException("a bson object must end with " + (SIGN_OBJECTE)
|
||||
+ " (position = " + position + ") but '" + currentByte() + "'");
|
||||
+ " (position = " + position + ") but '" + currentByte() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +210,7 @@ public class BsonReader extends Reader {
|
||||
byte b = readByte();
|
||||
if (b == SIGN_HASNEXT) return true;
|
||||
if (b != SIGN_NONEXT) throw new ConvertException("hasNext option must be (" + (SIGN_HASNEXT)
|
||||
+ " or " + (SIGN_NONEXT) + ") but '" + b + "' at position(" + this.position + ")");
|
||||
+ " or " + (SIGN_NONEXT) + ") but '" + b + "' at position(" + this.position + ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -272,19 +259,19 @@ public class BsonReader extends Reader {
|
||||
@Override
|
||||
public int readInt() {
|
||||
return ((content[++this.position] & 0xff) << 24) | ((content[++this.position] & 0xff) << 16)
|
||||
| ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff);
|
||||
| ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() {
|
||||
return ((((long) content[++this.position] & 0xff) << 56)
|
||||
| (((long) content[++this.position] & 0xff) << 48)
|
||||
| (((long) content[++this.position] & 0xff) << 40)
|
||||
| (((long) content[++this.position] & 0xff) << 32)
|
||||
| (((long) content[++this.position] & 0xff) << 24)
|
||||
| (((long) content[++this.position] & 0xff) << 16)
|
||||
| (((long) content[++this.position] & 0xff) << 8)
|
||||
| (((long) content[++this.position] & 0xff)));
|
||||
| (((long) content[++this.position] & 0xff) << 48)
|
||||
| (((long) content[++this.position] & 0xff) << 40)
|
||||
| (((long) content[++this.position] & 0xff) << 32)
|
||||
| (((long) content[++this.position] & 0xff) << 24)
|
||||
| (((long) content[++this.position] & 0xff) << 16)
|
||||
| (((long) content[++this.position] & 0xff) << 8)
|
||||
| (((long) content[++this.position] & 0xff)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.redkale.convert.bson;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Predicate;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -28,19 +27,7 @@ public class BsonWriter extends Writer {
|
||||
protected boolean tiny;
|
||||
|
||||
public static ObjectPool<BsonWriter> createPool(int max) {
|
||||
return new ObjectPool<BsonWriter>(max, new Creator<BsonWriter>() {
|
||||
|
||||
@Override
|
||||
public BsonWriter create(Object... params) {
|
||||
return new BsonWriter();
|
||||
}
|
||||
}, null, new Predicate<BsonWriter>() {
|
||||
|
||||
@Override
|
||||
public boolean test(BsonWriter t) {
|
||||
return t.recycle();
|
||||
}
|
||||
});
|
||||
return new ObjectPool<>(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle());
|
||||
}
|
||||
|
||||
public byte[] toArray() {
|
||||
@@ -82,6 +69,7 @@ public class BsonWriter extends Writer {
|
||||
* 扩充指定长度的缓冲区
|
||||
*
|
||||
* @param len 扩容长度
|
||||
*
|
||||
* @return 固定0
|
||||
*/
|
||||
protected int expand(int len) {
|
||||
|
||||
@@ -84,20 +84,14 @@ public class JsonByteBufferWriter extends JsonWriter {
|
||||
if (!buffer.hasRemaining()) {
|
||||
buffer.flip();
|
||||
buffer = supplier.get();
|
||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
||||
bufs[this.buffers.length] = buffer;
|
||||
this.buffers = bufs;
|
||||
this.buffers = Utility.append(this.buffers, buffer);
|
||||
this.index++;
|
||||
}
|
||||
int len = buffer.remaining();
|
||||
int size = 0;
|
||||
while (len < byteLength) {
|
||||
buffer = supplier.get();
|
||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
||||
bufs[this.buffers.length] = buffer;
|
||||
this.buffers = bufs;
|
||||
this.buffers = Utility.append(this.buffers, buffer);
|
||||
len += buffer.remaining();
|
||||
size++;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.math.BigInteger;
|
||||
import java.net.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.ext.*;
|
||||
import org.redkale.util.DLong;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -29,6 +29,9 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
|
||||
instance.register(DLong.class, DLongSimpledCoder.DLongJsonSimpledCoder.instance);
|
||||
instance.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance);
|
||||
instance.register(Serializable.class, instance.loadEncoder(Object.class));
|
||||
|
||||
instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class));
|
||||
instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class));
|
||||
}
|
||||
|
||||
private JsonFactory(JsonFactory parent, boolean tiny) {
|
||||
|
||||
@@ -17,47 +17,65 @@ import org.redkale.util.*;
|
||||
import org.redkale.watch.*;
|
||||
|
||||
/**
|
||||
* 服务器上下文对象
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class Context {
|
||||
|
||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
//服务启动时间
|
||||
protected final long serverStartTime;
|
||||
|
||||
//Server的线程池
|
||||
protected final ExecutorService executor;
|
||||
|
||||
//ByteBuffer的容量,默认8K
|
||||
protected final int bufferCapacity;
|
||||
|
||||
//ByteBuffer对象池
|
||||
protected final ObjectPool<ByteBuffer> bufferPool;
|
||||
|
||||
//Response对象池
|
||||
protected final ObjectPool<Response> responsePool;
|
||||
|
||||
//服务的根Servlet
|
||||
protected final PrepareServlet prepare;
|
||||
|
||||
//服务的监听地址
|
||||
private final InetSocketAddress address;
|
||||
|
||||
//字符集
|
||||
protected final Charset charset;
|
||||
|
||||
//请求内容的大小上限, 默认64K
|
||||
protected final int maxbody;
|
||||
|
||||
//IO读取的超时时间
|
||||
protected final int readTimeoutSecond;
|
||||
|
||||
//IO写入的超时时间
|
||||
protected final int writeTimeoutSecond;
|
||||
|
||||
//日志Logger
|
||||
protected final Logger logger;
|
||||
|
||||
//BSON操作工厂
|
||||
protected final BsonFactory bsonFactory;
|
||||
|
||||
//JSON操作工厂
|
||||
protected final JsonFactory jsonFactory;
|
||||
|
||||
//监控对象
|
||||
protected final WatchFactory watch;
|
||||
|
||||
public Context(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
|
||||
final int maxbody, Charset charset, InetSocketAddress address, final PrepareServlet prepare, final WatchFactory watch,
|
||||
final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
final int maxbody, Charset charset, InetSocketAddress address, final PrepareServlet prepare, final WatchFactory watch,
|
||||
final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
this.serverStartTime = serverStartTime;
|
||||
this.logger = logger;
|
||||
this.executor = executor;
|
||||
|
||||
@@ -11,8 +11,11 @@ import java.util.logging.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 根Servlet的处理逻辑类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -14,6 +14,9 @@ import java.util.logging.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 根Servlet, 一个Server只能存在一个根Servlet
|
||||
*
|
||||
* 用于分发Request请求
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* 协议底层Server
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* @author zhangjx
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.redkale.convert.bson.BsonConvert;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
|
||||
/**
|
||||
* 协议请求对象
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.nio.channels.CompletionHandler;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* 协议响应对象
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -29,6 +30,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
|
||||
private boolean inited = true;
|
||||
|
||||
protected Object output; //输出的结果对象
|
||||
|
||||
protected BiConsumer<R, Response<C, R>> recycleListener;
|
||||
|
||||
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
|
||||
@@ -54,29 +57,26 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() {
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer[] attachments) {
|
||||
public void completed(final Integer result, final ByteBuffer[] attachments) {
|
||||
int index = -1;
|
||||
for (int i = 0; i < attachments.length; i++) {
|
||||
if (attachments[i].hasRemaining()) {
|
||||
index = i;
|
||||
break;
|
||||
} else {
|
||||
context.offerBuffer(attachments[i]);
|
||||
}
|
||||
}
|
||||
if (index == 0) {
|
||||
channel.write(attachments, attachments, this);
|
||||
} else if (index > 0) {
|
||||
ByteBuffer[] newattachs = new ByteBuffer[attachments.length - index];
|
||||
System.arraycopy(attachments, index, newattachs, 0, newattachs.length);
|
||||
channel.write(newattachs, newattachs, this);
|
||||
if (index >= 0) {
|
||||
channel.write(attachments, index, attachments.length - index, attachments, this);
|
||||
} else {
|
||||
for (ByteBuffer attachment : attachments) {
|
||||
context.offerBuffer(attachment);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer[] attachments) {
|
||||
public void failed(Throwable exc, final ByteBuffer[] attachments) {
|
||||
for (ByteBuffer attachment : attachments) {
|
||||
context.offerBuffer(attachment);
|
||||
}
|
||||
@@ -113,6 +113,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
}
|
||||
recycleListener = null;
|
||||
}
|
||||
this.output = null;
|
||||
request.recycle();
|
||||
if (channel != null) {
|
||||
if (keepAlive) {
|
||||
@@ -143,6 +144,19 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
this.recycleListener = recycleListener;
|
||||
}
|
||||
|
||||
public Object getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已关闭
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return !this.inited;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
this.finish(false);
|
||||
}
|
||||
@@ -153,6 +167,17 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
this.context.responsePool.offer(this);
|
||||
}
|
||||
|
||||
public void finish(final byte[] bs) {
|
||||
if (this.context.bufferCapacity == bs.length) {
|
||||
ByteBuffer buffer = this.context.pollBuffer();
|
||||
buffer.put(bs);
|
||||
buffer.flip();
|
||||
this.finish(buffer);
|
||||
} else {
|
||||
this.finish(ByteBuffer.wrap(bs));
|
||||
}
|
||||
}
|
||||
|
||||
public void finish(ByteBuffer buffer) {
|
||||
this.channel.write(buffer, buffer, finishHandler);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.redkale.util.AnyValue;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 协议请求处理类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class Transport {
|
||||
|
||||
protected final String name; //即<group>的name属性
|
||||
|
||||
protected final String kind; //即<group>的kind属性
|
||||
protected final String subprotocol; //即<group>的subprotocol属性
|
||||
|
||||
protected final boolean tcp;
|
||||
|
||||
@@ -62,16 +62,16 @@ public final class Transport {
|
||||
|
||||
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>();
|
||||
|
||||
public Transport(String name, WatchFactory watch, String kind, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
public Transport(String name, WatchFactory watch, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
||||
this(name, DEFAULT_PROTOCOL, watch, kind, transportBufferPool, transportChannelGroup, clientAddress, addresses);
|
||||
this(name, DEFAULT_PROTOCOL, watch, subprotocol, transportBufferPool, transportChannelGroup, clientAddress, addresses);
|
||||
}
|
||||
|
||||
public Transport(String name, String protocol, WatchFactory watch, String kind, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
public Transport(String name, String protocol, WatchFactory watch, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
||||
this.name = name;
|
||||
this.watch = watch;
|
||||
this.kind = kind == null ? "" : kind.trim();
|
||||
this.subprotocol = subprotocol == null ? "" : subprotocol.trim();
|
||||
this.protocol = protocol;
|
||||
this.tcp = "TCP".equalsIgnoreCase(protocol);
|
||||
this.group = transportChannelGroup;
|
||||
@@ -83,14 +83,17 @@ public final class Transport {
|
||||
public Transport(final Collection<Transport> transports) {
|
||||
Transport first = null;
|
||||
List<String> tmpgroup = new ArrayList<>();
|
||||
for (Transport t : transports) {
|
||||
if (first == null) first = t;
|
||||
tmpgroup.add(t.name);
|
||||
if (transports != null) {
|
||||
for (Transport t : transports) {
|
||||
if (first == null) first = t;
|
||||
tmpgroup.add(t.name);
|
||||
}
|
||||
}
|
||||
if (first == null) throw new NullPointerException("Collection<Transport> is null or empty");
|
||||
//必须按字母排列顺序确保,相同内容的transport列表组合的name相同,而不会因为list的顺序不同产生不同的name
|
||||
this.name = tmpgroup.stream().sorted().collect(Collectors.joining(";"));
|
||||
this.watch = first.watch;
|
||||
this.kind = first.kind;
|
||||
this.subprotocol = first.subprotocol;
|
||||
this.protocol = first.protocol;
|
||||
this.tcp = "TCP".equalsIgnoreCase(first.protocol);
|
||||
this.group = first.group;
|
||||
@@ -118,8 +121,8 @@ public final class Transport {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getKind() {
|
||||
return kind;
|
||||
public String getSubprotocol() {
|
||||
return subprotocol;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
|
||||
@@ -8,8 +8,11 @@ package org.redkale.net;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* 协议处理的自定义线程类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class WorkThread extends Thread {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* 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.net.http;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @deprecated 使用 org.redkale.net.http.HttpBaseServlet 代替
|
||||
* @see org.redkale.net.http.HttpBaseServlet
|
||||
* @author zhangjx
|
||||
*/
|
||||
public abstract class BasedHttpServlet extends HttpBaseServlet {
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import org.redkale.util.*;
|
||||
import org.redkale.watch.*;
|
||||
|
||||
/**
|
||||
* HTTP服务的上下文对象
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -27,10 +28,10 @@ public class HttpContext extends Context {
|
||||
protected final SecureRandom random = new SecureRandom();
|
||||
|
||||
public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
|
||||
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
|
||||
WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
|
||||
WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
super(serverStartTime, logger, executor, bufferCapacity, bufferPool, responsePool, maxbody, charset,
|
||||
address, prepare, watch, readTimeoutSecond, writeTimeoutSecond);
|
||||
address, prepare, watch, readTimeoutSecond, writeTimeoutSecond);
|
||||
|
||||
random.setSeed(Math.abs(System.nanoTime()));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import org.redkale.util.*;
|
||||
import org.redkale.watch.*;
|
||||
|
||||
/**
|
||||
* HTTP Servlet的总入口,请求在HttpPrepareServlet中进行分流。 <br>
|
||||
* 一个HttpServer只有一个HttpPrepareServlet, 用于管理所有HttpServlet。 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -89,6 +91,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
}
|
||||
}
|
||||
}
|
||||
//找不到匹配的HttpServlet则使用静态资源HttpResourceServlet
|
||||
if (servlet == null) servlet = this.resourceHttpServlet;
|
||||
servlet.execute(request, response);
|
||||
} catch (Exception e) {
|
||||
@@ -97,6 +100,14 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加HttpServlet
|
||||
*
|
||||
* @param servlet HttpServlet
|
||||
* @param prefix url前缀
|
||||
* @param conf 配置信息
|
||||
* @param mappings 匹配规则
|
||||
*/
|
||||
@Override
|
||||
public void addServlet(HttpServlet servlet, Object prefix, AnyValue conf, String... mappings) {
|
||||
if (prefix == null) prefix = "";
|
||||
@@ -107,12 +118,12 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix
|
||||
}
|
||||
}
|
||||
synchronized (allMapStrings) {
|
||||
synchronized (allMapStrings) { //需要整段锁住
|
||||
for (String mapping : mappings) {
|
||||
if (mapping == null) continue;
|
||||
if (!prefix.toString().isEmpty()) mapping = prefix + mapping;
|
||||
|
||||
if (contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
||||
if (Utility.contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
||||
if (mapping.charAt(0) != '^') mapping = '^' + mapping;
|
||||
if (mapping.endsWith("/*")) {
|
||||
mapping = mapping.substring(0, mapping.length() - 1) + ".*";
|
||||
@@ -141,22 +152,22 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean contains(String string, char... values) {
|
||||
if (string == null) return false;
|
||||
for (char ch : Utility.charArray(string)) {
|
||||
for (char ch2 : values) {
|
||||
if (ch == ch2) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置静态资源HttpServlet
|
||||
*
|
||||
* @param servlet HttpServlet
|
||||
*/
|
||||
public void setResourceServlet(HttpServlet servlet) {
|
||||
if (servlet != null) {
|
||||
this.resourceHttpServlet = servlet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取静态资源HttpServlet
|
||||
*
|
||||
* @return HttpServlet
|
||||
*/
|
||||
public HttpServlet getResourceServlet() {
|
||||
return this.resourceHttpServlet;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.redkale.util.ByteArray;
|
||||
* 例如简单的翻页查询 <br>
|
||||
* /pipes/record/query/offset:0/limit:20 <br>
|
||||
* 获取页号: int offset = request.getRequstURIPath("offset:", 0); <br>
|
||||
* 获取行数: int limit = request.getRequstURIPath("limit:", 10);
|
||||
* 获取行数: int limit = request.getRequstURIPath("limit:", 10); <br>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -236,7 +236,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* @return 地址
|
||||
*/
|
||||
public SocketAddress getRemoteAddress() {
|
||||
return this.channel.getRemoteAddress();
|
||||
return this.channel == null || !this.channel.isOpen() ? null : this.channel.getRemoteAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,7 +352,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
public String getSessionid(boolean create) {
|
||||
String sessionid = getCookie(SESSIONID_NAME, null);
|
||||
if (create && (sessionid == null || sessionid.isEmpty())) {
|
||||
sessionid = ((HttpContext) context).createSessionid();
|
||||
sessionid = context.createSessionid();
|
||||
this.newsessionid = sessionid;
|
||||
}
|
||||
return sessionid;
|
||||
@@ -368,6 +368,18 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
return newsessionid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定值更新sessionid
|
||||
*
|
||||
* @param newsessionid 新sessionid值
|
||||
*
|
||||
* @return 新的sessionid值
|
||||
*/
|
||||
public String changeSessionid(String newsessionid) {
|
||||
this.newsessionid = newsessionid == null ? context.createSessionid() : newsessionid.trim();
|
||||
return newsessionid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使sessionid失效
|
||||
*/
|
||||
@@ -1109,17 +1121,18 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* @return Flipper翻页对象
|
||||
*/
|
||||
public org.redkale.source.Flipper getFlipper(String name, boolean needcreate, int maxLimit) {
|
||||
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
|
||||
org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name);
|
||||
if (flipper == null) {
|
||||
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
|
||||
int limit = getRequstURIPath("limit:", maxLimit);
|
||||
int offset = getRequstURIPath("offset:", 0);
|
||||
String sort = getRequstURIPath("sort:", "");
|
||||
if (limit > 0) flipper = new org.redkale.source.Flipper(limit, offset, sort);
|
||||
} else if (flipper.getLimit() < 1 || flipper.getLimit() > maxLimit) {
|
||||
} else if (flipper.getLimit() < 1 || (maxLimit > 0 && flipper.getLimit() > maxLimit)) {
|
||||
flipper.setLimit(maxLimit);
|
||||
}
|
||||
if (flipper != null || !needcreate) return flipper;
|
||||
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
|
||||
return new org.redkale.source.Flipper(maxLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.util.regex.*;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* 静态资源HttpServlet
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -268,7 +269,7 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
|
||||
public void update() {
|
||||
if (this.file == null) return;
|
||||
if (this.content != null) {
|
||||
if (this.content != null) {
|
||||
this.servlet.cachedLength.add(0L - this.content.remaining());
|
||||
this.content = null;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ import org.redkale.util.AnyValue.Entry;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。
|
||||
* 同时提供发送json的系列接口: public void finishJson(Type type, Object obj)
|
||||
* Redkale提倡http+json的接口风格, 所以主要输出的数据格式为json, 同时提供异步接口。
|
||||
* Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。 <br>
|
||||
* 同时提供发送json的系列接口: public void finishJson(Type type, Object obj) <br>
|
||||
* Redkale提倡http+json的接口风格, 所以主要输出的数据格式为json, 同时提供异步接口。 <br>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -184,14 +184,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @return HttpResponse
|
||||
*/
|
||||
public HttpResponse addCookie(HttpCookie... cookies) {
|
||||
if (this.cookies == null) {
|
||||
this.cookies = cookies;
|
||||
} else {
|
||||
HttpCookie[] news = new HttpCookie[this.cookies.length + cookies.length];
|
||||
System.arraycopy(this.cookies, 0, news, 0, this.cookies.length);
|
||||
System.arraycopy(cookies, 0, news, this.cookies.length, cookies.length);
|
||||
this.cookies = news;
|
||||
}
|
||||
this.cookies = Utility.append(this.cookies, cookies);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -203,18 +196,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @return HttpResponse
|
||||
*/
|
||||
public HttpResponse addCookie(Collection<HttpCookie> cookies) {
|
||||
if (cookies == null || cookies.isEmpty()) return this;
|
||||
if (this.cookies == null) {
|
||||
this.cookies = cookies.toArray(new HttpCookie[cookies.size()]);
|
||||
} else {
|
||||
HttpCookie[] news = new HttpCookie[this.cookies.length + cookies.size()];
|
||||
System.arraycopy(this.cookies, 0, news, 0, this.cookies.length);
|
||||
int i = this.cookies.length;
|
||||
for (HttpCookie cookie : cookies) {
|
||||
news[i++] = cookie;
|
||||
}
|
||||
this.cookies = news;
|
||||
}
|
||||
this.cookies = Utility.append(this.cookies, cookies);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -225,6 +207,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
public void finishJson(final Object obj) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
if (this.recycleListener != null) this.output = obj;
|
||||
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), obj));
|
||||
}
|
||||
|
||||
@@ -236,6 +219,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
public void finishJson(final JsonConvert convert, final Object obj) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
if (this.recycleListener != null) this.output = obj;
|
||||
finish(convert.convertTo(context.getBufferSupplier(), obj));
|
||||
}
|
||||
|
||||
@@ -247,6 +231,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
public void finishJson(final Type type, final Object obj) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
this.output = obj;
|
||||
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), type, obj));
|
||||
}
|
||||
|
||||
@@ -259,6 +244,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
public void finishJson(final JsonConvert convert, final Type type, final Object obj) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
if (this.recycleListener != null) this.output = obj;
|
||||
finish(convert.convertTo(context.getBufferSupplier(), type, obj));
|
||||
}
|
||||
|
||||
@@ -269,6 +255,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
public void finishJson(final Object... objs) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
if (this.recycleListener != null) this.output = objs;
|
||||
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), objs));
|
||||
}
|
||||
|
||||
@@ -279,6 +266,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
public void finishJson(final org.redkale.service.RetResult ret) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
if (this.recycleListener != null) this.output = ret;
|
||||
if (ret != null && !ret.isSuccess()) {
|
||||
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
||||
this.header.addValue("retinfo", ret.getRetinfo());
|
||||
@@ -294,6 +282,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
if (this.recycleListener != null) this.output = ret;
|
||||
if (ret != null && !ret.isSuccess()) {
|
||||
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
||||
this.header.addValue("retinfo", ret.getRetinfo());
|
||||
@@ -301,35 +290,13 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
finish(convert.convertTo(context.getBufferSupplier(), ret));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象以JavaScript格式输出
|
||||
*
|
||||
* @param var js变量名
|
||||
* @param result 输出对象
|
||||
*/
|
||||
public void finishJsResult(String var, Object result) {
|
||||
this.contentType = "application/javascript; charset=utf-8";
|
||||
finish("var " + var + " = " + request.getJsonConvert().convertTo(result) + ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象以JavaScript格式输出
|
||||
*
|
||||
* @param jsonConvert 指定的JsonConvert
|
||||
* @param var js变量名
|
||||
* @param result 输出对象
|
||||
*/
|
||||
public void finishJsResult(JsonConvert jsonConvert, String var, Object result) {
|
||||
this.contentType = "application/javascript; charset=utf-8";
|
||||
finish("var " + var + " = " + jsonConvert.convertTo(result) + ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定字符串以响应结果输出
|
||||
*
|
||||
* @param obj 输出内容
|
||||
*/
|
||||
public void finish(String obj) {
|
||||
if (this.recycleListener != null) this.output = obj;
|
||||
if (obj == null || obj.isEmpty()) {
|
||||
final ByteBuffer headbuf = createHeader();
|
||||
headbuf.flip();
|
||||
@@ -389,6 +356,23 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
super.finish(buffer404.duplicate());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定byte[]按响应结果输出
|
||||
*
|
||||
* @param bs 输出内容
|
||||
*/
|
||||
@Override
|
||||
public void finish(final byte[] bs) {
|
||||
if (this.context.getBufferCapacity() == bs.length) {
|
||||
ByteBuffer buffer = this.context.pollBuffer();
|
||||
buffer.put(bs);
|
||||
buffer.flip();
|
||||
this.finish(false, buffer);
|
||||
} else {
|
||||
this.finish(false, ByteBuffer.wrap(bs));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定ByteBuffer按响应结果输出
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.redkale.util.*;
|
||||
import org.redkale.watch.WatchFactory;
|
||||
|
||||
/**
|
||||
* Http服务器
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -37,24 +38,88 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
|
||||
super.init(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取静态资源HttpServlet
|
||||
*
|
||||
* @return HttpServlet
|
||||
*/
|
||||
public HttpServlet getResourceServlet() {
|
||||
return ((HttpPrepareServlet) this.prepare).resourceHttpServlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加HttpServlet
|
||||
*
|
||||
* @param prefix url前缀
|
||||
* @param servlet HttpServlet
|
||||
* @param mappings 匹配规则
|
||||
*
|
||||
* @return HttpServer
|
||||
*/
|
||||
public HttpServer addHttpServlet(String prefix, HttpServlet servlet, String... mappings) {
|
||||
this.prepare.addServlet(servlet, prefix, null, mappings);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加HttpServlet
|
||||
*
|
||||
* @param servlet HttpServlet
|
||||
* @param mappings 匹配规则
|
||||
*
|
||||
* @return HttpServer
|
||||
*/
|
||||
public HttpServer addHttpServlet(HttpServlet servlet, String... mappings) {
|
||||
this.prepare.addServlet(servlet, null, null, mappings);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void addHttpServlet(HttpServlet servlet, final String prefix, AnyValue conf, String... mappings) {
|
||||
/**
|
||||
* 添加HttpServlet
|
||||
*
|
||||
* @param prefix url前缀
|
||||
* @param servlet HttpServlet
|
||||
* @param conf 配置信息
|
||||
* @param mappings 匹配规则
|
||||
*
|
||||
* @return HttpServer
|
||||
*/
|
||||
public HttpServer addHttpServlet(HttpServlet servlet, final String prefix, AnyValue conf, String... mappings) {
|
||||
this.prepare.addServlet(servlet, prefix, conf, mappings);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加RestHttpServlet
|
||||
*
|
||||
* @param <S> Service
|
||||
* @param <T> RestHttpServlet
|
||||
* @param name Service的资源名
|
||||
* @param serviceType Service的类型
|
||||
* @param service Service对象
|
||||
* @param baseServletClass RestHttpServlet基类
|
||||
* @param prefix url前缀
|
||||
*
|
||||
* @return RestHttpServlet
|
||||
*/
|
||||
public <S extends Service, T extends RestHttpServlet> RestHttpServlet addRestServlet(String name, Class<S> serviceType, S service, Class<T> baseServletClass, String prefix) {
|
||||
return addRestServlet(name, serviceType, service, baseServletClass, prefix, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加RestHttpServlet
|
||||
*
|
||||
* @param <S> Service
|
||||
* @param <T> RestHttpServlet
|
||||
* @param name Service的资源名
|
||||
* @param serviceType Service的类型
|
||||
* @param service Service对象
|
||||
* @param baseServletClass RestHttpServlet基类
|
||||
* @param prefix url前缀
|
||||
* @param conf 配置信息
|
||||
*
|
||||
* @return RestHttpServlet
|
||||
*/
|
||||
public <S extends Service, T extends RestHttpServlet> RestHttpServlet addRestServlet(
|
||||
final String name, Class<S> serviceType, final S service, final Class<T> baseServletClass, final String prefix, AnyValue conf) {
|
||||
RestHttpServlet servlet = null;
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.net.http;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* MimeType
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -100,6 +101,7 @@ public class MimeType {
|
||||
contentTypes.put("oda", "application/oda");
|
||||
contentTypes.put("ogg", "application/ogg");
|
||||
contentTypes.put("out", "text/plain");
|
||||
contentTypes.put("pac", "application/x-javascript-config");
|
||||
contentTypes.put("pbm", "image/x-portable-bitmap");
|
||||
contentTypes.put("pct", "image/pict");
|
||||
contentTypes.put("pdf", "application/pdf");
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.regex.*;
|
||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
|
||||
/**
|
||||
* HTTP的文件上传请求的上下文对象
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -77,10 +78,21 @@ public final class MultiContext {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断请求是否包含上传文件
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isMultipart() {
|
||||
return this.boundary != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传文件信息列表
|
||||
*
|
||||
* @return Iterable
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
public Iterable<MultiPart> parts() throws IOException {
|
||||
if (!isMultipart()) return emptyIterable;
|
||||
final String boundarystr = "--" + this.boundary;
|
||||
|
||||
@@ -254,7 +254,6 @@ public final class Rest {
|
||||
final int maxStack = 3 + params.length;
|
||||
List<int[]> varInsns = new ArrayList<>();
|
||||
int maxLocals = 4;
|
||||
final String jsvar = entry.jsvar.isEmpty() ? null : entry.jsvar;
|
||||
int argIndex = 0;
|
||||
|
||||
List<Object[]> paramlist = new ArrayList<>();
|
||||
@@ -731,214 +730,110 @@ public final class Rest {
|
||||
mv.visitInsn(RETURN);
|
||||
} else if (returnType == boolean.class) {
|
||||
mv.visitVarInsn(ISTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (returnType == byte.class) {
|
||||
mv.visitVarInsn(ISTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (returnType == short.class) {
|
||||
mv.visitVarInsn(ISTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (returnType == char.class) {
|
||||
mv.visitVarInsn(ISTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (returnType == int.class) {
|
||||
mv.visitVarInsn(ISTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ILOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (returnType == float.class) {
|
||||
mv.visitVarInsn(FSTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(FLOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(FLOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(FLOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (returnType == long.class) {
|
||||
mv.visitVarInsn(LSTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(LLOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(LLOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(LLOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals += 2;
|
||||
} else if (returnType == double.class) {
|
||||
mv.visitVarInsn(DSTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(DLOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(DLOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(DLOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals += 2;
|
||||
} else if (returnType == String.class) {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (returnType == File.class) {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false);
|
||||
} else {
|
||||
throw new RuntimeException(method + " cannot set return Type (java.io.File) to jsvar");
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (RetResult.class.isAssignableFrom(returnType)) {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (RestOutput.class.isAssignableFrom(returnType)) {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "finishJson", "(" + respDesc + restoutputDesc + ")V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "finishJsResult", "(" + respDesc + "Ljava/lang/String;" + restoutputDesc + ")V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "finishJson", "(" + respDesc + restoutputDesc + ")V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
if (jsvar == null) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(jsvar);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
}
|
||||
@@ -1020,7 +915,6 @@ public final class Rest {
|
||||
this.actionid = mapping.actionid();
|
||||
this.cacheseconds = mapping.cacheseconds();
|
||||
this.comment = mapping.comment();
|
||||
this.jsvar = mapping.jsvar();
|
||||
}
|
||||
|
||||
public final Method mappingMethod;
|
||||
@@ -1039,8 +933,6 @@ public final class Rest {
|
||||
|
||||
public final int cacheseconds;
|
||||
|
||||
public final String jsvar;
|
||||
|
||||
@RestMapping()
|
||||
void mapping() { //用于获取Mapping 默认值
|
||||
}
|
||||
|
||||
@@ -22,9 +22,24 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
@Retention(RUNTIME)
|
||||
public @interface RestCookie {
|
||||
|
||||
String name(); //cookie名
|
||||
/**
|
||||
* cookie名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String name();
|
||||
|
||||
int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||
/**
|
||||
* 转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int radix() default 10;
|
||||
|
||||
String comment() default ""; //备注描述
|
||||
/**
|
||||
* 备注描述
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String comment() default "";
|
||||
}
|
||||
|
||||
@@ -22,9 +22,24 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
@Retention(RUNTIME)
|
||||
public @interface RestHeader {
|
||||
|
||||
String name(); //参数名
|
||||
/**
|
||||
* Header参数名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String name();
|
||||
|
||||
int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||
/**
|
||||
* 转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int radix() default 10;
|
||||
|
||||
String comment() default ""; //备注描述
|
||||
/**
|
||||
* 备注描述
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String comment() default "";
|
||||
}
|
||||
|
||||
@@ -23,22 +23,17 @@ public abstract class RestHttpServlet<T> extends HttpBaseServlet {
|
||||
response.finishJson(output);
|
||||
return;
|
||||
}
|
||||
if (output.getContentType() != null) response.setContentType(output.getContentType());
|
||||
response.addHeader(output.getHeaders());
|
||||
response.addCookie(output.getCookies());
|
||||
|
||||
if (output.getResult() instanceof File) {
|
||||
response.finish((File) output.getResult());
|
||||
} else if (output.getResult() instanceof String) {
|
||||
response.finish((String) output.getResult());
|
||||
} else {
|
||||
response.finishJson(output.getResult());
|
||||
}
|
||||
}
|
||||
|
||||
protected void finishJsResult(final HttpResponse response, final String var, RestOutput output) throws IOException {
|
||||
if (output != null) {
|
||||
response.addHeader(output.getHeaders());
|
||||
response.addCookie(output.getCookies());
|
||||
}
|
||||
response.finishJsResult(var, output == null ? null : output.getResult());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.*;
|
||||
|
||||
/**
|
||||
* 只能依附在Service实现类的public方法上
|
||||
* value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService,的默认路径为/hello)。
|
||||
* 只能依附在Service实现类的public方法上 <br>
|
||||
* value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService,的默认路径为/hello)。 <br>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -24,25 +24,54 @@ import static java.lang.annotation.RetentionPolicy.*;
|
||||
@Repeatable(RestMappings.class)
|
||||
public @interface RestMapping {
|
||||
|
||||
boolean ignore() default false; //是否屏蔽该方法的转换
|
||||
/**
|
||||
* 是否屏蔽该方法的转换
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
|
||||
/**
|
||||
* 请求的方法名, 不能含特殊字符
|
||||
* 默认为方法名的小写(若方法名以createXXX、updateXXX、deleteXXX、queryXXX、findXXX、existsXXX且XXXService为Service的类名将只截取XXX之前)
|
||||
*
|
||||
* @return name
|
||||
* @return String
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
String comment() default ""; //备注描述, 对应@WebAction.comment
|
||||
/**
|
||||
* 备注描述, 对应@WebAction.comment
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String comment() default "";
|
||||
|
||||
boolean auth() default false; //是否鉴权,默认不鉴权, 对应@AuthIgnore
|
||||
/**
|
||||
* 是否鉴权,默认不鉴权, 对应@AuthIgnore
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean auth() default false;
|
||||
|
||||
int actionid() default 0; //操作ID值,鉴权时用到, 对应@WebAction.actionid
|
||||
/**
|
||||
* 操作ID值,鉴权时用到, 对应@WebAction.actionid
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int actionid() default 0;
|
||||
|
||||
int cacheseconds() default 0; // 结果缓存的秒数, 为0表示不缓存, 对应@HttpCacheable.seconds
|
||||
/**
|
||||
* 结果缓存的秒数, 为0表示不缓存, 对应@HttpCacheable.seconds
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int cacheseconds() default 0;
|
||||
|
||||
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@WebAction.methods
|
||||
/**
|
||||
* 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@WebAction.methods
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
String[] methods() default {};
|
||||
|
||||
String jsvar() default ""; //以application/javascript输出对象是指明js的对象名,该值存在时则忽略contentType()的值
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ public class RestOutput<T> {
|
||||
|
||||
private List<HttpCookie> cookies;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private T result;
|
||||
|
||||
public RestOutput() {
|
||||
@@ -58,6 +60,14 @@ public class RestOutput<T> {
|
||||
this.cookies = cookies;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 只能依附在Service类的方法的参数上, name值不能是'&'
|
||||
* name='#'表示截取uri最后一段
|
||||
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值
|
||||
*
|
||||
* 依附在RestService类的方法的参数上 <br>
|
||||
* name='&' 表示当前用户 <br>
|
||||
* name='#'表示截取uri最后一段 <br>
|
||||
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 <br>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -24,11 +26,35 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
@Retention(RUNTIME)
|
||||
public @interface RestParam {
|
||||
|
||||
String name(); //参数名 name值不能是'&'; name='#'表示截取uri最后一段; name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值
|
||||
//name='&'表示当前用户;
|
||||
/**
|
||||
* 参数名 <br>
|
||||
* name='&'表示当前用户; <br>
|
||||
* name='#'表示截取uri最后一段; <br>
|
||||
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 <br>
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String name();
|
||||
|
||||
int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||
/**
|
||||
* 转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int radix() default 10;
|
||||
|
||||
boolean required() default true; //参数是否必传
|
||||
/**
|
||||
* 参数是否必传
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean required() default true;
|
||||
|
||||
String comment() default ""; //备注描述
|
||||
/**
|
||||
* 备注描述
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String comment() default "";
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import java.lang.annotation.*;
|
||||
/**
|
||||
* 功能同JSR 315 (java-servlet 3.0) 规范中的 @WebInitParam
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@@ -18,9 +20,24 @@ import java.lang.annotation.*;
|
||||
@Documented
|
||||
public @interface WebInitParam {
|
||||
|
||||
/**
|
||||
* 参数名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* 参数值
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String value();
|
||||
|
||||
/**
|
||||
* 参数描述
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String description() default "";
|
||||
}
|
||||
|
||||
@@ -21,15 +21,45 @@ import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WebServlet {
|
||||
|
||||
/**
|
||||
* HttpServlet资源名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* 是否自动添加url前缀, 对应application.xml中servlets节点的path属性
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean repair() default true;
|
||||
|
||||
/**
|
||||
* url匹配规则
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* 模块ID,一个HttpServlet尽量只有提供一个模块的服务
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int moduleid() default 0;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*
|
||||
* @return WebInitParam[]
|
||||
*/
|
||||
WebInitParam[] initParams() default {};
|
||||
|
||||
String comment() default ""; //备注描述
|
||||
/**
|
||||
* 备注描述
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String comment() default "";
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.util.Comment;
|
||||
|
||||
/**
|
||||
* <blockquote><pre>
|
||||
@@ -22,15 +23,13 @@ import org.redkale.net.*;
|
||||
* 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
|
||||
* 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
|
||||
* 1.5 onClose WebSocket被关闭后回调此方法。
|
||||
*
|
||||
* 此模式下 以上方法都应该被重载。
|
||||
* 普通模式下 以上方法都应该被重载。
|
||||
*
|
||||
* 2) 原始二进制模式: 此模式有别于HTML5规范,可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下:
|
||||
* 2.1 onOpen 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断登录态。
|
||||
* 2.2 createGroupid 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断用户权限是否符合。
|
||||
* 2.3 onRead WebSocket成功连接后回调此方法, 由此方法处理原始的TCP连接, 同时业务代码去控制WebSocket的关闭。
|
||||
*
|
||||
* 此模式下 以上方法都应该被重载。
|
||||
* 2.3 onRead WebSocket成功连接后回调此方法, 由此方法处理原始的TCP连接, 需要业务代码去控制WebSocket的关闭。
|
||||
* 二进制模式下 以上方法都应该被重载。
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -39,24 +38,28 @@ import org.redkale.net.*;
|
||||
*/
|
||||
public abstract class WebSocket {
|
||||
|
||||
//消息不合法
|
||||
@Comment("消息不合法")
|
||||
public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2
|
||||
|
||||
//ws已经关闭
|
||||
@Comment("WebSocket已经关闭")
|
||||
public static final int RETCODE_WSOCKET_CLOSED = 1 << 2; //4
|
||||
|
||||
//socket的buffer不合法
|
||||
@Comment("Socket的buffer不合法")
|
||||
public static final int RETCODE_ILLEGALBUFFER = 1 << 3; //8
|
||||
|
||||
//ws发送消息异常
|
||||
@Comment("WebSocket发送消息异常")
|
||||
public static final int RETCODE_SENDEXCEPTION = 1 << 4; //16
|
||||
|
||||
@Comment("WebSocketEngine实例不存在")
|
||||
public static final int RETCODE_ENGINE_NULL = 1 << 5; //32
|
||||
|
||||
@Comment("WebSocketNode实例不存在")
|
||||
public static final int RETCODE_NODESERVICE_NULL = 1 << 6; //64
|
||||
|
||||
@Comment("WebSocket组为空, 表示无WebSocket连接")
|
||||
public static final int RETCODE_GROUP_EMPTY = 1 << 7; //128
|
||||
|
||||
@Comment("WebSocket已离线")
|
||||
public static final int RETCODE_WSOFFLINE = 1 << 8; //256
|
||||
|
||||
WebSocketRunner _runner; //不可能为空
|
||||
@@ -85,6 +88,7 @@ public abstract class WebSocket {
|
||||
* 发送消息体, 包含二进制/文本
|
||||
*
|
||||
* @param packet WebSocketPacket
|
||||
*
|
||||
* @return 0表示成功, 非0表示错误码
|
||||
*/
|
||||
public final int send(WebSocketPacket packet) {
|
||||
@@ -98,6 +102,7 @@ public abstract class WebSocket {
|
||||
* 发送单一的文本消息
|
||||
*
|
||||
* @param text 不可为空
|
||||
*
|
||||
* @return 0表示成功, 非0表示错误码
|
||||
*/
|
||||
public final int send(String text) {
|
||||
@@ -109,6 +114,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @param text 不可为空
|
||||
* @param last 是否最后一条
|
||||
*
|
||||
* @return 0表示成功, 非0表示错误码
|
||||
*/
|
||||
public final int send(String text, boolean last) {
|
||||
@@ -136,6 +142,7 @@ public abstract class WebSocket {
|
||||
* 发送单一的二进制消息
|
||||
*
|
||||
* @param data byte[]
|
||||
*
|
||||
* @return 0表示成功, 非0表示错误码
|
||||
*/
|
||||
public final int send(byte[] data) {
|
||||
@@ -147,6 +154,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @param data 不可为空
|
||||
* @param last 是否最后一条
|
||||
*
|
||||
* @return 0表示成功, 非0表示错误码
|
||||
*/
|
||||
public final int send(byte[] data, boolean last) {
|
||||
@@ -158,6 +166,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @param message 不可为空, 只能是String或者byte[]
|
||||
* @param last 是否最后一条
|
||||
*
|
||||
* @return 0表示成功, 非0表示错误码
|
||||
*/
|
||||
public final int send(Serializable message, boolean last) {
|
||||
@@ -170,6 +179,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @param groupid groupid
|
||||
* @param text 不可为空
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示异常
|
||||
*/
|
||||
public final int sendEachMessage(Serializable groupid, String text) {
|
||||
@@ -181,6 +191,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @param groupid groupid
|
||||
* @param data 不可为空
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示异常
|
||||
*/
|
||||
public final int sendEachMessage(Serializable groupid, byte[] data) {
|
||||
@@ -193,6 +204,7 @@ public abstract class WebSocket {
|
||||
* @param groupid groupid
|
||||
* @param text 不可为空
|
||||
* @param last 是否最后一条
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示异常
|
||||
*/
|
||||
public final int sendEachMessage(Serializable groupid, String text, boolean last) {
|
||||
@@ -205,6 +217,7 @@ public abstract class WebSocket {
|
||||
* @param groupid groupid
|
||||
* @param data 不可为空
|
||||
* @param last 是否最后一条
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示异常
|
||||
*/
|
||||
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) {
|
||||
@@ -216,6 +229,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @param groupid groupid
|
||||
* @param text 不可为空
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示异常
|
||||
*/
|
||||
public final int sendRecentMessage(Serializable groupid, String text) {
|
||||
@@ -227,6 +241,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @param groupid groupid
|
||||
* @param data 不可为空
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示异常
|
||||
*/
|
||||
public final int sendRecentMessage(Serializable groupid, byte[] data) {
|
||||
@@ -239,6 +254,7 @@ public abstract class WebSocket {
|
||||
* @param groupid groupid
|
||||
* @param text 不可为空
|
||||
* @param last 是否最后一条
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示异常
|
||||
*/
|
||||
public final int sendRecentMessage(Serializable groupid, String text, boolean last) {
|
||||
@@ -251,6 +267,7 @@ public abstract class WebSocket {
|
||||
* @param groupid groupid
|
||||
* @param data 不可为空
|
||||
* @param last 是否最后一条
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示异常
|
||||
*/
|
||||
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) {
|
||||
@@ -275,6 +292,7 @@ public abstract class WebSocket {
|
||||
* 获取在线用户的节点地址列表
|
||||
*
|
||||
* @param groupid groupid
|
||||
*
|
||||
* @return 地址列表
|
||||
*/
|
||||
protected final Collection<InetSocketAddress> getOnlineNodes(Serializable groupid) {
|
||||
@@ -285,6 +303,7 @@ public abstract class WebSocket {
|
||||
* 获取在线用户的详细连接信息
|
||||
*
|
||||
* @param groupid groupid
|
||||
*
|
||||
* @return 地址集合
|
||||
*/
|
||||
protected final Map<InetSocketAddress, List<String>> getOnlineRemoteAddress(Serializable groupid) {
|
||||
@@ -296,6 +315,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @param <T> 属性值的类型
|
||||
* @param name 属性名
|
||||
*
|
||||
* @return 属性值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -308,6 +328,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @param <T> 属性值的类型
|
||||
* @param name 属性名
|
||||
*
|
||||
* @return 属性值
|
||||
*/
|
||||
public final <T> T removeAttribute(String name) {
|
||||
@@ -374,6 +395,7 @@ public abstract class WebSocket {
|
||||
* 获取指定groupid的WebSocketGroup, 没有返回null
|
||||
*
|
||||
* @param groupid groupid
|
||||
*
|
||||
* @return WebSocketGroup
|
||||
*/
|
||||
protected final WebSocketGroup getWebSocketGroup(Serializable groupid) {
|
||||
@@ -394,6 +416,7 @@ public abstract class WebSocket {
|
||||
* 返回sessionid, null表示连接不合法或异常,默认实现是request.getSessionid(false),通常需要重写该方法
|
||||
*
|
||||
* @param request HttpRequest
|
||||
*
|
||||
* @return sessionid
|
||||
*/
|
||||
public Serializable onOpen(final HttpRequest request) {
|
||||
|
||||
@@ -23,11 +23,12 @@ import org.redkale.util.*;
|
||||
* WebSocketServlet
|
||||
* |
|
||||
* |
|
||||
* WebSocketEngine & WebSocketNode
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* WebSocketGroup1 WebSocketGroup2
|
||||
* WebSocketEngine
|
||||
* WebSocketNode
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* WebSocketGroup1 WebSocketGroup2
|
||||
* / \ / \
|
||||
* / \ / \
|
||||
* WebSocket1 WebSocket2 WebSocket3 WebSocket4
|
||||
@@ -41,8 +42,10 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public abstract class WebSocketServlet extends HttpServlet {
|
||||
|
||||
@Comment("WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒")
|
||||
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
|
||||
|
||||
@Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒")
|
||||
public static final int DEFAILT_LIVEINTERVAL = 60;
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
@@ -57,7 +60,7 @@ public abstract class WebSocketServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
|
||||
//是否用于二进制流传输
|
||||
@Comment("是否用于二进制流传输")
|
||||
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
|
||||
|
||||
@Resource(name = "$")
|
||||
|
||||
@@ -287,7 +287,7 @@ public final class SncpClient {
|
||||
}
|
||||
|
||||
private SncpFuture<byte[]> remote0(final BsonConvert bsonConvert, final JsonConvert jsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
|
||||
if ("rest".equalsIgnoreCase(transport.getKind())) {
|
||||
if ("rest".equalsIgnoreCase(transport.getSubprotocol())) {
|
||||
return remoteRest0(jsonConvert, transport, addr0, action, params);
|
||||
}
|
||||
return remoteSncp0(bsonConvert, transport, addr0, action, params);
|
||||
|
||||
@@ -80,7 +80,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.getClass().getSimpleName()).append("(type=").append(type.getName());
|
||||
sb.append(this.getClass().getSimpleName()).append(" (type=").append(type.getName());
|
||||
int len = maxClassNameLength - type.getName().length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
sb.append(' ');
|
||||
|
||||
@@ -84,13 +84,23 @@ public class DataSourceService implements DataSource, Service, AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int updateColumns(T bean, final String... columns) {
|
||||
return source.updateColumns(bean, columns);
|
||||
public <T> int updateColumn(T bean, final String... columns) {
|
||||
return source.updateColumn(bean, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int updateColumns(T bean, final FilterNode node, final String... columns) {
|
||||
return source.updateColumns(bean, node, columns);
|
||||
public <T> int updateColumn(T bean, final FilterNode node, final String... columns) {
|
||||
return source.updateColumn(bean, node, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int updateColumn(T bean, final SelectColumn selects) {
|
||||
return source.updateColumn(bean, selects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int updateColumn(T bean, final FilterNode node, final SelectColumn selects) {
|
||||
return source.updateColumn(bean, node, selects);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,6 +133,21 @@ public class DataSourceService implements DataSource, Service, AutoCloseable {
|
||||
return source.getNumberResult(entityClass, func, defVal, column, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterFuncColumn... columns) {
|
||||
return source.getNumberMap(entityClass, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) {
|
||||
return source.getNumberMap(entityClass, bean, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) {
|
||||
return source.getNumberMap(entityClass, node, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, FilterFunc func, final String funcColumn) {
|
||||
return source.queryColumnMap(entityClass, keyColumn, func, funcColumn);
|
||||
|
||||
@@ -10,12 +10,12 @@ import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 用于定义错误码的注解
|
||||
* 结果码定义范围:
|
||||
* // 10000001 - 19999999 预留给Redkale的核心包使用
|
||||
* // 20000001 - 29999999 预留给Redkale的扩展包使用
|
||||
* // 30000001 - 99999999 预留给Dev开发系统自身使用
|
||||
*
|
||||
* 用于定义错误码的注解 <br>
|
||||
* 结果码定义范围: <br>
|
||||
* // 10000001 - 19999999 预留给Redkale的核心包使用 <br>
|
||||
* // 20000001 - 29999999 预留给Redkale的扩展包使用 <br>
|
||||
* // 30000001 - 99999999 预留给Dev开发系统自身使用 <br>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
|
||||
@@ -8,13 +8,13 @@ package org.redkale.service;
|
||||
import org.redkale.convert.json.*;
|
||||
|
||||
/**
|
||||
* 通用的结果对象,在常见的HTTP+JSON接口中返回的结果需要含结果码,错误信息,和实体对象。
|
||||
* 通常前四位为模块,后四位为操作。
|
||||
* 结果码定义范围:
|
||||
* // 10000001 - 19999999 预留给Redkale的核心包使用
|
||||
* // 20000001 - 29999999 预留给Redkale的扩展包使用
|
||||
* // 30000001 - 99999999 预留给Dev开发系统自身使用
|
||||
*
|
||||
* 通用的结果对象,在常见的HTTP+JSON接口中返回的结果需要含结果码,错误信息,和实体对象。 <br>
|
||||
* 结果码定义通常前四位为模块,后四位为操作。<br>
|
||||
* 结果码定义范围: <br>
|
||||
* // 10000001 - 19999999 预留给Redkale的核心包使用 <br>
|
||||
* // 20000001 - 29999999 预留给Redkale的扩展包使用 <br>
|
||||
* // 30000001 - 99999999 预留给Dev开发系统自身使用 <br>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -22,13 +22,6 @@ import org.redkale.convert.json.*;
|
||||
*/
|
||||
public class RetResult<T> {
|
||||
|
||||
/**
|
||||
* 使用 RetResult.success() 方法代替
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
private static final RetResult SUCCESS = new RetResult();
|
||||
|
||||
protected int retcode;
|
||||
|
||||
protected String retinfo;
|
||||
@@ -119,10 +112,20 @@ public class RetResult<T> {
|
||||
this.retcode = retcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 结果信息,通常retcode != 0时值为错误信息
|
||||
*
|
||||
* @return 结果信息
|
||||
*/
|
||||
public String getRetinfo() {
|
||||
return retinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置结果信息
|
||||
*
|
||||
* @param retinfo 结果信息
|
||||
*/
|
||||
public void setRetinfo(String retinfo) {
|
||||
this.retinfo = retinfo;
|
||||
}
|
||||
@@ -136,6 +139,11 @@ public class RetResult<T> {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置结果对象
|
||||
*
|
||||
* @param result T
|
||||
*/
|
||||
public void setResult(T result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@@ -14,9 +14,24 @@ package org.redkale.source;
|
||||
* @author zhangjx
|
||||
*/
|
||||
public enum ColumnExpress {
|
||||
MOV, //直接赋值 col = val
|
||||
INC, //追加值 col = col + val
|
||||
MUL, //乘值 col = col * val
|
||||
AND, //与值 col = col & val
|
||||
ORR; //或值 col = col | val
|
||||
/**
|
||||
* 直接赋值 col = val
|
||||
*/
|
||||
MOV,
|
||||
/**
|
||||
* 追加值 col = col + val
|
||||
*/
|
||||
INC,
|
||||
/**
|
||||
* 乘值 col = col * val
|
||||
*/
|
||||
MUL,
|
||||
/**
|
||||
* 与值 col = col & val
|
||||
*/
|
||||
AND, //与值 col = col & val
|
||||
/**
|
||||
* 或值 col = col | val
|
||||
*/
|
||||
ORR;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import static org.redkale.source.ColumnExpress.*;
|
||||
|
||||
/**
|
||||
* ColumnValue主要用于多个字段更新的表达式。
|
||||
* 用于 DataSource.updateColumn 方法
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -37,26 +38,74 @@ public class ColumnValue {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同 mov 操作
|
||||
*
|
||||
* @param column 字段名
|
||||
* @param value 字段值
|
||||
*
|
||||
* @return ColumnValue
|
||||
*/
|
||||
public static ColumnValue create(String column, Serializable value) {
|
||||
return new ColumnValue(column, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 {column} = {value} 操作
|
||||
*
|
||||
* @param column 字段名
|
||||
* @param value 字段值
|
||||
*
|
||||
* @return ColumnValue
|
||||
*/
|
||||
public static ColumnValue mov(String column, Serializable value) {
|
||||
return new ColumnValue(column, MOV, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 {column} = {column} + {value} 操作
|
||||
*
|
||||
* @param column 字段名
|
||||
* @param value 字段值
|
||||
*
|
||||
* @return ColumnValue
|
||||
*/
|
||||
public static ColumnValue inc(String column, Serializable value) {
|
||||
return new ColumnValue(column, INC, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 {column} = {column} * {value} 操作
|
||||
*
|
||||
* @param column 字段名
|
||||
* @param value 字段值
|
||||
*
|
||||
* @return ColumnValue
|
||||
*/
|
||||
public static ColumnValue mul(String column, Serializable value) {
|
||||
return new ColumnValue(column, MUL, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 {column} = {column} & {value} 操作
|
||||
*
|
||||
* @param column 字段名
|
||||
* @param value 字段值
|
||||
*
|
||||
* @return ColumnValue
|
||||
*/
|
||||
public static ColumnValue and(String column, Serializable value) {
|
||||
return new ColumnValue(column, AND, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 {column} = {column} | {value} 操作
|
||||
*
|
||||
* @param column 字段名
|
||||
* @param value 字段值
|
||||
*
|
||||
* @return ColumnValue
|
||||
*/
|
||||
public static ColumnValue orr(String column, Serializable value) {
|
||||
return new ColumnValue(column, ORR, value);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.redkale.source;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
@@ -14,11 +13,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.sql.ConnectionPoolDataSource;
|
||||
import javax.xml.stream.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* DataSource的JDBC实现类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -30,26 +29,6 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
|
||||
public static final String DATASOURCE_CONFPATH = "DATASOURCE_CONFPATH";
|
||||
|
||||
static final String JDBC_CONNECTIONSMAX = "javax.persistence.connections.limit";
|
||||
|
||||
static final String JDBC_CONTAIN_SQLTEMPLATE = "javax.persistence.contain.sqltemplate";
|
||||
|
||||
static final String JDBC_NOTCONTAIN_SQLTEMPLATE = "javax.persistence.notcontain.sqltemplate";
|
||||
|
||||
static final String JDBC_TABLENOTEXIST_SQLSTATES = "javax.persistence.tablenotexist.sqlstates";
|
||||
|
||||
static final String JDBC_TABLECOPY_SQLTEMPLATE = "javax.persistence.tablecopy.sqltemplate";
|
||||
|
||||
static final String JDBC_URL = "javax.persistence.jdbc.url";
|
||||
|
||||
static final String JDBC_USER = "javax.persistence.jdbc.user";
|
||||
|
||||
static final String JDBC_PWD = "javax.persistence.jdbc.password";
|
||||
|
||||
static final String JDBC_DRIVER = "javax.persistence.jdbc.driver";
|
||||
|
||||
static final String JDBC_SOURCE = "javax.persistence.jdbc.source";
|
||||
|
||||
private static final Flipper FLIPPER_ONE = new Flipper(1);
|
||||
|
||||
final Logger logger = Logger.getLogger(DataDefaultSource.class.getSimpleName());
|
||||
@@ -66,9 +45,6 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
|
||||
private final JDBCPoolSource writePool;
|
||||
|
||||
@Resource(name = "property.datasource.nodeid")
|
||||
private int nodeid;
|
||||
|
||||
@Resource(name = "$")
|
||||
private DataCacheListener cacheListener;
|
||||
|
||||
@@ -182,58 +158,6 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
return map;
|
||||
}
|
||||
|
||||
static ConnectionPoolDataSource createDataSource(Properties property) {
|
||||
try {
|
||||
return createDataSource(property.getProperty(JDBC_SOURCE, property.getProperty(JDBC_DRIVER)),
|
||||
property.getProperty(JDBC_URL), property.getProperty(JDBC_USER), property.getProperty(JDBC_PWD));
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("(" + property + ") have no jdbc parameters", ex);
|
||||
}
|
||||
}
|
||||
|
||||
static ConnectionPoolDataSource createDataSource(final String source0, String url, String user, String password) throws Exception {
|
||||
String source = source0;
|
||||
if (source0.contains("Driver")) { //为了兼容JPA的配置文件
|
||||
switch (source0) {
|
||||
case "org.mariadb.jdbc.Driver":
|
||||
source = "org.mariadb.jdbc.MySQLDataSource";
|
||||
break;
|
||||
case "com.mysql.jdbc.Driver":
|
||||
source = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource";
|
||||
break;
|
||||
case "oracle.jdbc.driver.OracleDriver":
|
||||
source = "oracle.jdbc.pool.OracleConnectionPoolDataSource";
|
||||
break;
|
||||
case "org.postgresql.Driver":
|
||||
source = "org.postgresql.ds.PGConnectionPoolDataSource";
|
||||
break;
|
||||
case "com.microsoft.sqlserver.jdbc.SQLServerDriver":
|
||||
source = "com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource";
|
||||
break;
|
||||
}
|
||||
}
|
||||
final Class clazz = Class.forName(source);
|
||||
Object pdsource = clazz.newInstance();
|
||||
if (source.contains(".postgresql.")) {
|
||||
Class driver = Class.forName("org.postgresql.Driver");
|
||||
Properties properties = (Properties) driver.getMethod("parseURL", String.class, Properties.class).invoke(null, url, new Properties());
|
||||
clazz.getMethod("setServerName", String.class).invoke(pdsource, properties.getProperty("PGHOST"));
|
||||
clazz.getMethod("setDatabaseName", String.class).invoke(pdsource, properties.getProperty("PGDBNAME"));
|
||||
clazz.getMethod("setPortNumber", int.class).invoke(pdsource, Integer.parseInt(properties.getProperty("PGPORT", "5432")));
|
||||
} else {
|
||||
Method seturlm;
|
||||
try {
|
||||
seturlm = clazz.getMethod("setUrl", String.class);
|
||||
} catch (Exception e) {
|
||||
seturlm = clazz.getMethod("setURL", String.class);
|
||||
}
|
||||
seturlm.invoke(pdsource, url);
|
||||
}
|
||||
clazz.getMethod("setUser", String.class).invoke(pdsource, user);
|
||||
clazz.getMethod("setPassword", String.class).invoke(pdsource, password);
|
||||
return (ConnectionPoolDataSource) pdsource;
|
||||
}
|
||||
|
||||
public final String name() {
|
||||
return name;
|
||||
}
|
||||
@@ -271,7 +195,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
}
|
||||
|
||||
private <T> EntityInfo<T> loadEntityInfo(Class<T> clazz) {
|
||||
return EntityInfo.load(clazz, this.nodeid, this.cacheForbidden, this.readPool.props, this, fullloader);
|
||||
return EntityInfo.load(clazz, this.cacheForbidden, this.readPool.props, this, fullloader);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,6 +221,18 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
@Override
|
||||
public <T> void insert(T... values) {
|
||||
if (values.length == 0) return;
|
||||
if (values.length > 1) { //检查对象是否都是同一个Entity类
|
||||
Class clazz = null;
|
||||
for (T val : values) {
|
||||
if (clazz == null) {
|
||||
clazz = val.getClass();
|
||||
continue;
|
||||
}
|
||||
if (clazz != val.getClass()) {
|
||||
throw new RuntimeException("DataSource.insert must the same Class Entity, but diff is " + clazz + " and " + val.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
final EntityInfo<T> info = loadEntityInfo((Class<T>) values[0].getClass());
|
||||
if (info.isVirtualEntity()) {
|
||||
insert(null, info, values);
|
||||
@@ -317,32 +253,8 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final String sql = info.getInsertSQL(values[0]);
|
||||
final Class primaryType = info.getPrimary().type();
|
||||
final Attribute primary = info.getPrimary();
|
||||
final boolean distributed = info.distributed;
|
||||
Attribute<T, Serializable>[] attrs = info.insertAttributes;
|
||||
if (distributed && !info.initedPrimaryValue && primaryType.isPrimitive()) { //由DataSource生成主键
|
||||
synchronized (info) {
|
||||
if (!info.initedPrimaryValue) { //初始化最大主键值
|
||||
try {
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT MAX(" + info.getPrimarySQLColumn() + ") FROM " + info.getTable(values[0]));
|
||||
if (rs.next()) {
|
||||
if (primaryType == int.class) {
|
||||
int v = rs.getInt(1) / info.allocationSize;
|
||||
if (v > info.primaryValue.get()) info.primaryValue.set(v);
|
||||
} else {
|
||||
long v = rs.getLong(1) / info.allocationSize;
|
||||
if (v > info.primaryValue.get()) info.primaryValue.set(v);
|
||||
}
|
||||
}
|
||||
rs.close();
|
||||
stmt.close();
|
||||
} catch (SQLException se) {
|
||||
if (info.tableStrategy == null) throw se;
|
||||
}
|
||||
info.initedPrimaryValue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
conn.setReadOnly(false);
|
||||
PreparedStatement prestmt = createInsertPreparedStatement(conn, sql, info, values);
|
||||
try {
|
||||
prestmt.executeBatch();
|
||||
@@ -442,9 +354,16 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
|
||||
for (final T value : values) {
|
||||
int i = 0;
|
||||
if (info.distributed || info.autouuid) info.createPrimaryValue(value);
|
||||
if (info.autouuid) info.createPrimaryValue(value);
|
||||
for (Attribute<T, Serializable> attr : attrs) {
|
||||
prestmt.setObject(++i, attr.get(value));
|
||||
Serializable val = attr.get(value);
|
||||
if (val instanceof byte[]) {
|
||||
Blob blob = conn.createBlob();
|
||||
blob.setBytes(1, (byte[]) val);
|
||||
prestmt.setObject(++i, blob);
|
||||
} else {
|
||||
prestmt.setObject(++i, val);
|
||||
}
|
||||
}
|
||||
prestmt.addBatch();
|
||||
}
|
||||
@@ -473,6 +392,18 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
@Override
|
||||
public <T> int delete(T... values) {
|
||||
if (values.length == 0) return -1;
|
||||
if (values.length > 1) { //检查对象是否都是同一个Entity类
|
||||
Class clazz = null;
|
||||
for (T val : values) {
|
||||
if (clazz == null) {
|
||||
clazz = val.getClass();
|
||||
continue;
|
||||
}
|
||||
if (clazz != val.getClass()) {
|
||||
throw new RuntimeException("DataSource.delete must the same Class Entity, but diff is " + clazz + " and " + val.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
final EntityInfo<T> info = loadEntityInfo((Class<T>) values[0].getClass());
|
||||
if (info.isVirtualEntity()) { //虚拟表只更新缓存Cache
|
||||
return delete(null, info, values);
|
||||
@@ -516,6 +447,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
int c2 = 0;
|
||||
try {
|
||||
if (!info.isVirtualEntity()) {
|
||||
conn.setReadOnly(false);
|
||||
final Statement stmt = conn.createStatement();
|
||||
for (Serializable key : keys) {
|
||||
String sql = "DELETE FROM " + info.getTable(key) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(key);
|
||||
@@ -591,6 +523,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) + info.createSQLOrderby(flipper)
|
||||
+ ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit()));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
@@ -631,6 +564,18 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
@Override
|
||||
public <T> int update(T... values) {
|
||||
if (values.length == 0) return 0;
|
||||
if (values.length > 1) { //检查对象是否都是同一个Entity类
|
||||
Class clazz = null;
|
||||
for (T val : values) {
|
||||
if (clazz == null) {
|
||||
clazz = val.getClass();
|
||||
continue;
|
||||
}
|
||||
if (clazz != val.getClass()) {
|
||||
throw new RuntimeException("DataSource.update must the same Class Entity, but diff is " + clazz + " and " + val.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
final EntityInfo<T> info = loadEntityInfo((Class<T>) values[0].getClass());
|
||||
if (info.isVirtualEntity()) {
|
||||
return update(null, info, values);
|
||||
@@ -650,6 +595,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
if (!info.isVirtualEntity()) {
|
||||
final String updateSQL = info.getUpdateSQL(values[0]);
|
||||
final Attribute<T, Serializable> primary = info.getPrimary();
|
||||
conn.setReadOnly(false);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(updateSQL);
|
||||
Attribute<T, Serializable>[] attrs = info.updateAttributes;
|
||||
final boolean debugfinest = debug.get() && info.isLoggable(Level.FINEST);
|
||||
@@ -657,7 +603,14 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
for (final T value : values) {
|
||||
int k = 0;
|
||||
for (Attribute<T, Serializable> attr : attrs) {
|
||||
prestmt.setObject(++k, attr.get(value));
|
||||
Serializable val = attr.get(value);
|
||||
if (val instanceof byte[]) {
|
||||
Blob blob = conn.createBlob();
|
||||
blob.setBytes(1, (byte[]) val);
|
||||
prestmt.setObject(++k, blob);
|
||||
} else {
|
||||
prestmt.setObject(++k, val);
|
||||
}
|
||||
}
|
||||
prestmt.setObject(++k, primary.get(value));
|
||||
prestmt.addBatch();//------------------------------------------------------------
|
||||
@@ -726,16 +679,29 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
}
|
||||
}
|
||||
|
||||
private <T> int updateColumn(Connection conn, final EntityInfo<T> info, Serializable id, String column, Serializable value) {
|
||||
private <T> int updateColumn(Connection conn, final EntityInfo<T> info, Serializable id, String column, final Serializable value) {
|
||||
try {
|
||||
int c = -1;
|
||||
if (!info.isVirtualEntity()) {
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + info.getSQLColumn(null, column) + " = "
|
||||
+ info.formatToString(value) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
if (value instanceof byte[]) {
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + info.getSQLColumn(null, column) + " = ? WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
Blob blob = conn.createBlob();
|
||||
blob.setBytes(1, (byte[]) value);
|
||||
stmt.setBlob(1, blob);
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
} else {
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + info.getSQLColumn(null, column) + " = "
|
||||
+ info.formatToString(value) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
@@ -775,7 +741,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
}
|
||||
}
|
||||
|
||||
private <T> int updateColumn(Connection conn, final EntityInfo<T> info, String column, Serializable value, FilterNode node) {
|
||||
private <T> int updateColumn(Connection conn, final EntityInfo<T> info, String column, final Serializable value, FilterNode node) {
|
||||
try {
|
||||
int c = -1;
|
||||
if (!info.isVirtualEntity()) {
|
||||
@@ -790,14 +756,30 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0);
|
||||
join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0);
|
||||
}
|
||||
String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1))
|
||||
+ " SET " + info.getSQLColumn("a", column) + " = " + info.formatToString(value)
|
||||
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
if (value instanceof byte[]) {
|
||||
String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1))
|
||||
+ " SET " + info.getSQLColumn("a", column) + " = ?"
|
||||
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
Blob blob = conn.createBlob();
|
||||
blob.setBytes(1, (byte[]) value);
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setBlob(1, blob);
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
} else {
|
||||
String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1))
|
||||
+ " SET " + info.getSQLColumn("a", column) + " = " + info.formatToString(value)
|
||||
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
@@ -843,6 +825,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final List<Attribute<T, Serializable>> attrs = new ArrayList<>();
|
||||
final List<ColumnValue> cols = new ArrayList<>();
|
||||
final boolean virtual = info.isVirtualEntity();
|
||||
List<byte[]> blobs = null;
|
||||
for (ColumnValue col : values) {
|
||||
Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn());
|
||||
if (attr == null) continue;
|
||||
@@ -851,16 +834,35 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
if (!virtual) {
|
||||
if (setsql.length() > 0) setsql.append(", ");
|
||||
String c = info.getSQLColumn(null, col.getColumn());
|
||||
setsql.append(c).append(" = ").append(info.formatSQLValue(c, col));
|
||||
if (col.getValue() instanceof byte[]) {
|
||||
if (blobs == null) blobs = new ArrayList<>();
|
||||
blobs.add((byte[]) col.getValue());
|
||||
setsql.append(c).append(" = ?");
|
||||
} else {
|
||||
setsql.append(c).append(" = ").append(info.formatSQLValue(c, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
int c = -1;
|
||||
if (!virtual) {
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + ": " + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
conn.setReadOnly(false);
|
||||
if (blobs != null) {
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
int idx = 0;
|
||||
for (byte[] bs : blobs) {
|
||||
Blob blob = conn.createBlob();
|
||||
blob.setBytes(1, bs);
|
||||
stmt.setBlob(++idx, blob);
|
||||
}
|
||||
c = stmt.executeUpdate();
|
||||
stmt.close();
|
||||
} else {
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
@@ -929,6 +931,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final List<Attribute<T, Serializable>> attrs = new ArrayList<>();
|
||||
final List<ColumnValue> cols = new ArrayList<>();
|
||||
final boolean virtual = info.isVirtualEntity();
|
||||
List<byte[]> blobs = null;
|
||||
for (ColumnValue col : values) {
|
||||
Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn());
|
||||
if (attr == null) continue;
|
||||
@@ -937,7 +940,13 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
if (!virtual) {
|
||||
if (setsql.length() > 0) setsql.append(", ");
|
||||
String c = info.getSQLColumn("a", col.getColumn());
|
||||
setsql.append(c).append(" = ").append(info.formatSQLValue(c, col));
|
||||
if (col.getValue() instanceof byte[]) {
|
||||
if (blobs == null) blobs = new ArrayList<>();
|
||||
blobs.add((byte[]) col.getValue());
|
||||
setsql.append(c).append(" = ?");
|
||||
} else {
|
||||
setsql.append(c).append(" = ").append(info.formatSQLValue(c, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
int c = -1;
|
||||
@@ -958,9 +967,22 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
//注:LIMIT 仅支持MySQL 且在多表关联式会异常, 该BUG尚未解决
|
||||
sql += info.createSQLOrderby(flipper) + ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit()));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
conn.setReadOnly(false);
|
||||
if (blobs != null) {
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
int idx = 0;
|
||||
for (byte[] bs : blobs) {
|
||||
Blob blob = conn.createBlob();
|
||||
blob.setBytes(1, bs);
|
||||
stmt.setBlob(++idx, blob);
|
||||
}
|
||||
c = stmt.executeUpdate();
|
||||
stmt.close();
|
||||
} else {
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
@@ -973,53 +995,70 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对象指定的一些字段, 必须是Entity对象
|
||||
*
|
||||
* @param <T> Entity类的泛型
|
||||
* @param bean Entity对象
|
||||
* @param columns 需要更新的字段
|
||||
*
|
||||
* @return 更新的数据条数
|
||||
*/
|
||||
@Override
|
||||
public <T> int updateColumns(final T bean, final String... columns) {
|
||||
public <T> int updateColumn(final T bean, final String... columns) {
|
||||
return updateColumn(bean, SelectColumn.createIncludes(columns));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int updateColumn(final T bean, final SelectColumn selects) {
|
||||
final EntityInfo<T> info = loadEntityInfo((Class<T>) bean.getClass());
|
||||
if (info.isVirtualEntity()) {
|
||||
return updateColumns(null, info, bean, columns);
|
||||
return updateColumns(null, info, bean, selects);
|
||||
}
|
||||
Connection conn = createWriteSQLConnection();
|
||||
try {
|
||||
return updateColumns(conn, info, bean, columns);
|
||||
return updateColumns(conn, info, bean, selects);
|
||||
} finally {
|
||||
closeSQLConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final String... columns) {
|
||||
if (bean == null || columns.length < 1) return -1;
|
||||
private <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final SelectColumn selects) {
|
||||
if (bean == null || selects == null) return -1;
|
||||
try {
|
||||
final Class<T> clazz = (Class<T>) bean.getClass();
|
||||
StringBuilder setsql = new StringBuilder();
|
||||
final Serializable id = info.getPrimary().get(bean);
|
||||
final List<Attribute<T, Serializable>> attrs = new ArrayList<>();
|
||||
List<byte[]> blobs = null;
|
||||
final boolean virtual = info.isVirtualEntity();
|
||||
for (String col : columns) {
|
||||
Attribute<T, Serializable> attr = info.getUpdateAttribute(col);
|
||||
if (attr == null) continue;
|
||||
for (Attribute<T, Serializable> attr : info.updateAttributes) {
|
||||
if (!selects.test(attr.field())) continue;
|
||||
attrs.add(attr);
|
||||
if (!virtual) {
|
||||
if (setsql.length() > 0) setsql.append(", ");
|
||||
setsql.append(info.getSQLColumn(null, col)).append(" = ").append(info.formatToString(attr.get(bean)));
|
||||
setsql.append(info.getSQLColumn(null, attr.field()));
|
||||
Serializable val = attr.get(bean);
|
||||
if (val instanceof byte[]) {
|
||||
if (blobs == null) blobs = new ArrayList<>();
|
||||
blobs.add((byte[]) val);
|
||||
setsql.append(" = ?");
|
||||
} else {
|
||||
setsql.append(" = ").append(info.formatToString(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
int c = -1;
|
||||
if (!virtual) {
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(bean.getClass().getSimpleName() + ": " + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
conn.setReadOnly(false);
|
||||
if (blobs != null) {
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
int idx = 0;
|
||||
for (byte[] bs : blobs) {
|
||||
Blob blob = conn.createBlob();
|
||||
blob.setBytes(1, bs);
|
||||
stmt.setBlob(++idx, blob);
|
||||
}
|
||||
c = stmt.executeUpdate();
|
||||
stmt.close();
|
||||
} else {
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
@@ -1032,45 +1071,48 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对象指定的一些字段, 必须是Entity对象
|
||||
*
|
||||
* @param <T> Entity类的泛型
|
||||
* @param bean Entity对象
|
||||
* @param node 过滤node 不能为null
|
||||
* @param columns 需要更新的字段
|
||||
*
|
||||
* @return 更新的数据条数
|
||||
*/
|
||||
@Override
|
||||
public <T> int updateColumns(final T bean, final FilterNode node, final String... columns) {
|
||||
public <T> int updateColumn(final T bean, final FilterNode node, final String... columns) {
|
||||
return updateColumn(bean, node, SelectColumn.createIncludes(columns));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int updateColumn(final T bean, final FilterNode node, final SelectColumn selects) {
|
||||
final EntityInfo<T> info = loadEntityInfo((Class<T>) bean.getClass());
|
||||
if (info.isVirtualEntity()) {
|
||||
return updateColumns(null, info, bean, node, columns);
|
||||
return updateColumns(null, info, bean, node, selects);
|
||||
}
|
||||
Connection conn = createWriteSQLConnection();
|
||||
try {
|
||||
return updateColumns(conn, info, bean, node, columns);
|
||||
return updateColumns(conn, info, bean, node, selects);
|
||||
} finally {
|
||||
closeSQLConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final FilterNode node, final String... columns) {
|
||||
if (bean == null || node == null || columns.length < 1) return -1;
|
||||
private <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final FilterNode node, final SelectColumn selects) {
|
||||
if (bean == null || node == null || selects == null) return -1;
|
||||
try {
|
||||
final Class<T> clazz = (Class<T>) bean.getClass();
|
||||
StringBuilder setsql = new StringBuilder();
|
||||
final Serializable id = info.getPrimary().get(bean);
|
||||
final List<Attribute<T, Serializable>> attrs = new ArrayList<>();
|
||||
List<byte[]> blobs = null;
|
||||
final boolean virtual = info.isVirtualEntity();
|
||||
for (String col : columns) {
|
||||
Attribute<T, Serializable> attr = info.getUpdateAttribute(col);
|
||||
if (attr == null) continue;
|
||||
for (Attribute<T, Serializable> attr : info.updateAttributes) {
|
||||
if (!selects.test(attr.field())) continue;
|
||||
attrs.add(attr);
|
||||
if (!virtual) {
|
||||
if (setsql.length() > 0) setsql.append(", ");
|
||||
setsql.append(info.getSQLColumn("a", col)).append(" = ").append(info.formatToString(attr.get(bean)));
|
||||
setsql.append(info.getSQLColumn("a", attr.field()));
|
||||
Serializable val = attr.get(bean);
|
||||
if (val instanceof byte[]) {
|
||||
if (blobs == null) blobs = new ArrayList<>();
|
||||
blobs.add((byte[]) val);
|
||||
setsql.append(" = ?");
|
||||
} else {
|
||||
setsql.append(" = ").append(info.formatToString(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
int c = -1;
|
||||
@@ -1089,9 +1131,22 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
conn.setReadOnly(false);
|
||||
if (blobs != null) {
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
int idx = 0;
|
||||
for (byte[] bs : blobs) {
|
||||
Blob blob = conn.createBlob();
|
||||
blob.setBytes(1, bs);
|
||||
stmt.setBlob(++idx, blob);
|
||||
}
|
||||
c = stmt.executeUpdate();
|
||||
stmt.close();
|
||||
} else {
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
@@ -1156,6 +1211,80 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
return getNumberResult(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterFuncColumn... columns) {
|
||||
return getNumberMap(entityClass, (FilterNode) null, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) {
|
||||
return getNumberMap(entityClass, FilterNodeBean.createFilterNode(bean), columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) {
|
||||
if (columns == null || columns.length == 0) return new HashMap<>();
|
||||
final EntityInfo info = loadEntityInfo(entityClass);
|
||||
final Connection conn = createReadSQLConnection();
|
||||
final Map map = new HashMap<>();
|
||||
try {
|
||||
final EntityCache cache = info.getCache();
|
||||
if (cache != null && (info.isVirtualEntity() || cache.isFullLoaded())) {
|
||||
if (node == null || node.isCacheUseable(this)) {
|
||||
for (FilterFuncColumn ffc : columns) {
|
||||
for (String col : ffc.cols()) {
|
||||
map.put(ffc.col(col), cache.getNumberResult(ffc.func, ffc.defvalue, col, node));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (FilterFuncColumn ffc : columns) {
|
||||
for (String col : ffc.cols()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
sb.append(ffc.func.getColumn((col == null || col.isEmpty() ? "*" : ("a." + col))));
|
||||
}
|
||||
}
|
||||
final String sql = "SELECT " + sb + " FROM " + info.getTable(node) + " a"
|
||||
+ (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(sql);
|
||||
|
||||
ResultSet set = prestmt.executeQuery();
|
||||
if (set.next()) {
|
||||
int index = 0;
|
||||
for (FilterFuncColumn ffc : columns) {
|
||||
for (String col : ffc.cols()) {
|
||||
Object o = set.getObject(++index);
|
||||
Number rs = ffc.defvalue;
|
||||
if (o != null) rs = (Number) o;
|
||||
map.put(ffc.col(col), rs);
|
||||
}
|
||||
}
|
||||
}
|
||||
set.close();
|
||||
prestmt.close();
|
||||
return map;
|
||||
} catch (SQLException e) {
|
||||
if (info.tableStrategy != null && info.tablenotexistSqlstates.contains(';' + e.getSQLState() + ';')) {
|
||||
for (FilterFuncColumn ffc : columns) {
|
||||
for (String col : ffc.cols()) {
|
||||
map.put(ffc.col(col), ffc.defvalue);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (conn != null) closeSQLConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) {
|
||||
final EntityInfo info = loadEntityInfo(entityClass);
|
||||
@@ -1173,6 +1302,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : ("a." + column))) + " FROM " + info.getTable(node) + " a"
|
||||
+ (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(sql);
|
||||
Number rs = defVal;
|
||||
ResultSet set = prestmt.executeQuery();
|
||||
@@ -1220,6 +1350,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final String sql = "SELECT a." + sqlkey + ", " + func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : ("a." + funcColumn)))
|
||||
+ " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + sqlkey;
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(sql);
|
||||
Map<K, N> rs = new LinkedHashMap<>();
|
||||
ResultSet set = prestmt.executeQuery();
|
||||
@@ -1268,7 +1399,9 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final SelectColumn sels = selects;
|
||||
final String sql = "SELECT * FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
ps.setFetchSize(1);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
T rs = set.next() ? info.getValue(sels, set) : null;
|
||||
set.close();
|
||||
@@ -1318,7 +1451,9 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT a.* FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
ps.setFetchSize(1);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
T rs = set.next() ? info.getValue(sels, set) : null;
|
||||
set.close();
|
||||
@@ -1360,12 +1495,22 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
|
||||
final Connection conn = createReadSQLConnection();
|
||||
try {
|
||||
final Attribute<T, Serializable> attr = info.getAttribute(column);
|
||||
final String sql = "SELECT " + info.getSQLColumn(null, column) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
ps.setFetchSize(1);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
Serializable val = defValue;
|
||||
if (set.next()) val = (Serializable) set.getObject(1);
|
||||
if (set.next()) {
|
||||
if (attr.type() == byte[].class) {
|
||||
Blob blob = set.getBlob(1);
|
||||
if (blob != null) val = blob.getBytes(1, (int) blob.length());
|
||||
} else {
|
||||
val = (Serializable) set.getObject(1);
|
||||
}
|
||||
}
|
||||
set.close();
|
||||
ps.close();
|
||||
return val == null ? defValue : val;
|
||||
@@ -1392,15 +1537,25 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
|
||||
final Connection conn = createReadSQLConnection();
|
||||
try {
|
||||
final Attribute<T, Serializable> attr = info.getAttribute(column);
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT " + info.getSQLColumn("a", column) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
ps.setFetchSize(1);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
Serializable val = defValue;
|
||||
if (set.next()) val = (Serializable) set.getObject(1);
|
||||
if (set.next()) {
|
||||
if (attr.type() == byte[].class) {
|
||||
Blob blob = set.getBlob(1);
|
||||
if (blob != null) val = blob.getBytes(1, (int) blob.length());
|
||||
} else {
|
||||
val = (Serializable) set.getObject(1);
|
||||
}
|
||||
}
|
||||
set.close();
|
||||
ps.close();
|
||||
return val == null ? defValue : val;
|
||||
@@ -1429,6 +1584,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
try {
|
||||
final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
|
||||
if (log) logstr = clazz.getSimpleName() + " exists sql=" + sql;
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
boolean rs = set.next() ? (set.getInt(1) > 0) : false;
|
||||
@@ -1467,6 +1623,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT COUNT(" + info.getPrimarySQLColumn("a") + ") FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (log) logstr = clazz.getSimpleName() + " exists sql=" + sql;
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
boolean rs = set.next() ? (set.getInt(1) > 0) : false;
|
||||
@@ -1563,16 +1720,6 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
return rs;
|
||||
}
|
||||
|
||||
private <K extends Serializable, T> Map<K, T> formatMap(final Class<T> clazz, final Collection<T> list) {
|
||||
Map<K, T> map = new LinkedHashMap<>();
|
||||
if (list == null || list.isEmpty()) return map;
|
||||
final Attribute<T, K> attr = (Attribute<T, K>) loadEntityInfo(clazz).getPrimary();
|
||||
for (T t : list) {
|
||||
map.put(attr.get(t), t);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定字段值查询对象集合
|
||||
*
|
||||
@@ -1715,7 +1862,9 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) {
|
||||
logger.finest(clazz.getSimpleName() + " query sql=" + sql + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getOffset() + "," + flipper.getLimit())));
|
||||
}
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
if (flipper != null && flipper.getLimit() > 0) ps.setFetchSize(flipper.getLimit());
|
||||
final ResultSet set = ps.executeQuery();
|
||||
if (flipper != null && flipper.getOffset() > 0) set.absolute(flipper.getOffset());
|
||||
final int limit = flipper == null || flipper.getLimit() < 1 ? Integer.MAX_VALUE : flipper.getLimit();
|
||||
@@ -1767,6 +1916,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
private int[] directExecute(final Connection conn, String... sqls) {
|
||||
if (sqls.length == 0) return new int[0];
|
||||
try {
|
||||
conn.setReadOnly(false);
|
||||
final Statement stmt = conn.createStatement();
|
||||
final int[] rs = new int[sqls.length];
|
||||
int i = -1;
|
||||
@@ -1785,6 +1935,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final Connection conn = createReadSQLConnection();
|
||||
try {
|
||||
if (debug.get()) logger.finest("direct query sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
consumer.accept(set);
|
||||
|
||||
@@ -25,7 +25,7 @@ public interface DataSource {
|
||||
|
||||
//----------------------insert-----------------------------
|
||||
/**
|
||||
* 新增对象, 必须是Entity对象
|
||||
* 新增记录, 多对象必须是同一个Entity类 <br>
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param values Entity对象
|
||||
@@ -34,81 +34,385 @@ public interface DataSource {
|
||||
|
||||
//-------------------------delete--------------------------
|
||||
/**
|
||||
* 删除对象, 必须是Entity对象
|
||||
* 删除指定主键值的记录, 多对象必须是同一个Entity类 <br>
|
||||
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id} <br>
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param values Entity对象
|
||||
*
|
||||
* @return 删除的数据条数
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int delete(final T... values);
|
||||
|
||||
/**
|
||||
* 根据主键值删除数据
|
||||
* 等价SQL: DELETE FROM WHERE {primary} IN {ids}
|
||||
* 删除指定主键值的记录 <br>
|
||||
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids} <br>
|
||||
*
|
||||
* @param <T> Entity类的泛型
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param ids 主键值
|
||||
*
|
||||
* @return 删除的数据条数
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int delete(final Class<T> clazz, final Serializable... ids);
|
||||
|
||||
/**
|
||||
* 删除符合过滤条件的记录 <br>
|
||||
* 等价SQL: DELETE FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int delete(final Class<T> clazz, final FilterNode node);
|
||||
|
||||
|
||||
/**
|
||||
* 删除符合过滤条件且指定最大影响条数的记录 <br>
|
||||
* Flipper中offset字段将被忽略 <br>
|
||||
* 等价SQL: DELETE FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int delete(final Class<T> clazz, final Flipper flipper, final FilterNode node);
|
||||
|
||||
//------------------------update---------------------------
|
||||
/**
|
||||
* 更新对象, 必须是Entity对象
|
||||
* 更新记录, 多对象必须是同一个Entity类 <br>
|
||||
* 等价SQL: <br>
|
||||
* UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1} <br>
|
||||
* UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2} <br>
|
||||
* ··· <br>
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param values Entity对象
|
||||
*
|
||||
* @return 更新的数据条数
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int update(final T... values);
|
||||
|
||||
/**
|
||||
* 更新单个记录的单个字段 <br>
|
||||
* <b>注意</b>:即使字段标记为@Column(updatable=false)也会被更新 <br>
|
||||
* 等价SQL: UPDATE {table} SET {column} = {value} WHERE {primary} = {id} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param id 主键
|
||||
* @param column 待更新的字段名
|
||||
* @param value 更新值
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int updateColumn(final Class<T> clazz, final Serializable id, final String column, final Serializable value);
|
||||
|
||||
/**
|
||||
* 更新符合过滤条件记录的单个字段 <br>
|
||||
* <b>注意</b>:即使字段标记为@Column(updatable=false)也会被更新 <br>
|
||||
* 等价SQL: UPDATE {table} SET {column} = {value} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 待更新的字段名
|
||||
* @param value 更新值
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int updateColumn(final Class<T> clazz, final String column, final Serializable value, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 更新指定主键值记录的部分字段 <br>
|
||||
* 字段赋值操作选项见 ColumnExpress <br>
|
||||
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param id 主键
|
||||
* @param values 更新字段
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int updateColumn(final Class<T> clazz, final Serializable id, final ColumnValue... values);
|
||||
|
||||
/**
|
||||
* 更新符合过滤条件记录的部分字段 <br>
|
||||
* 字段赋值操作选项见 ColumnExpress <br>
|
||||
* <b>注意</b>:Entity类中标记为@Column(updatable=false)不会被更新 <br>
|
||||
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param node 过滤条件
|
||||
* @param values 更新字段
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int updateColumn(final Class<T> clazz, final FilterNode node, final ColumnValue... values);
|
||||
|
||||
/**
|
||||
* 更新符合过滤条件的记录的指定字段 <br>
|
||||
* Flipper中offset字段将被忽略 <br>
|
||||
* <b>注意</b>:Entity类中标记为@Column(updatable=false)不会被更新 <br>
|
||||
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param node 过滤条件
|
||||
* @param flipper 翻页对象
|
||||
* @param values 更新字段
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int updateColumn(final Class<T> clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values);
|
||||
|
||||
public <T> int updateColumns(final T bean, final String... columns);
|
||||
/**
|
||||
* 更新单个记录的指定字段 <br>
|
||||
* <b>注意</b>:Entity类中标记为@Column(updatable=false)不会被更新 <br>
|
||||
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param bean 待更新的Entity对象
|
||||
* @param columns 需更新的字段名
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int updateColumn(final T bean, final String... columns);
|
||||
|
||||
public <T> int updateColumns(final T bean, final FilterNode node, final String... columns);
|
||||
/**
|
||||
* 更新符合过滤条件记录的指定字段 <br>
|
||||
* <b>注意</b>:Entity类中标记为@Column(updatable=false)不会被更新 <br>
|
||||
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param bean 待更新的Entity对象
|
||||
* @param node 过滤条件
|
||||
* @param columns 需更新的字段名
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int updateColumn(final T bean, final FilterNode node, final String... columns);
|
||||
|
||||
/**
|
||||
* 更新单个记录的指定字段 <br>
|
||||
* <b>注意</b>:Entity类中标记为@Column(updatable=false)不会被更新 <br>
|
||||
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param bean 待更新的Entity对象
|
||||
* @param selects 指定字段
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int updateColumn(final T bean, final SelectColumn selects);
|
||||
|
||||
/**
|
||||
* 更新符合过滤条件记录的指定字段 <br>
|
||||
* <b>注意</b>:Entity类中标记为@Column(updatable=false)不会被更新 <br>
|
||||
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param bean 待更新的Entity对象
|
||||
* @param node 过滤条件
|
||||
* @param selects 指定字段
|
||||
*
|
||||
* @return 影响的记录条数
|
||||
*/
|
||||
public <T> int updateColumn(final T bean, final FilterNode node, final SelectColumn selects);
|
||||
|
||||
//############################################# 查询接口 #############################################
|
||||
//-----------------------getXXXXResult-----------------------------
|
||||
/**
|
||||
* 获取符合过滤条件记录的聚合结果, 无结果返回null <br>
|
||||
* 等价SQL: SELECT FUNC{column} FROM {table} <br>
|
||||
* 如 getNumberResult(Record.class, FilterFunc.COUNT, null) 等价于: SELECT COUNT(*) FROM {table} <br>
|
||||
*
|
||||
* @param entityClass Entity类
|
||||
* @param func 聚合函数
|
||||
* @param column 指定字段
|
||||
*
|
||||
* @return 聚合结果
|
||||
*/
|
||||
public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件记录的聚合结果, 无结果返回null <br>
|
||||
* 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean} <br>
|
||||
* 如 getNumberResult(Record.class, FilterFunc.COUNT, null, (FilterBean)null) 等价于: SELECT COUNT(*) FROM {table} <br>
|
||||
*
|
||||
* @param entityClass Entity类
|
||||
* @param func 聚合函数
|
||||
* @param column 指定字段
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 聚合结果
|
||||
*/
|
||||
public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件记录的聚合结果, 无结果返回null <br>
|
||||
* 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node} <br>
|
||||
* 如 getNumberResult(Record.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table} <br>
|
||||
*
|
||||
* @param entityClass Entity类
|
||||
* @param func 聚合函数
|
||||
* @param column 指定字段
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 聚合结果
|
||||
*/
|
||||
public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件记录的聚合结果, 无结果返回默认值 <br>
|
||||
* 等价SQL: SELECT FUNC{column} FROM {table} <br>
|
||||
* 如 getNumberResult(Record.class, FilterFunc.MAX, "createtime") 等价于: SELECT MAX(createtime) FROM {table} <br>
|
||||
*
|
||||
* @param entityClass Entity类
|
||||
* @param func 聚合函数
|
||||
* @param defVal 默认值
|
||||
* @param column 指定字段
|
||||
*
|
||||
* @return 聚合结果
|
||||
*/
|
||||
public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件记录的聚合结果, 无结果返回默认值 <br>
|
||||
* 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean} <br>
|
||||
* 如 getNumberResult(Record.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table} <br>
|
||||
*
|
||||
* @param entityClass Entity类
|
||||
* @param func 聚合函数
|
||||
* @param defVal 默认值
|
||||
* @param column 指定字段
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 聚合结果
|
||||
*/
|
||||
public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件记录的聚合结果, 无结果返回默认值 <br>
|
||||
* 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node} <br>
|
||||
* 如 getNumberResult(Record.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table} <br>
|
||||
*
|
||||
* @param entityClass Entity类
|
||||
* @param func 聚合函数
|
||||
* @param defVal 默认值
|
||||
* @param column 指定字段
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 聚合结果
|
||||
*/
|
||||
public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件记录的聚合结果Map <br>
|
||||
* 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} <br>
|
||||
* 如 getNumberMap(Record.class, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table} <br>
|
||||
*
|
||||
* @param <N> Number
|
||||
* @param entityClass Entity类
|
||||
* @param columns 聚合字段
|
||||
*
|
||||
* @return 聚合结果Map
|
||||
*/
|
||||
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterFuncColumn... columns);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件记录的聚合结果Map <br>
|
||||
* 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter bean} <br>
|
||||
* 如 getNumberMap(Record.class, (FilterBean)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table} <br>
|
||||
*
|
||||
* @param <N> Number
|
||||
* @param entityClass Entity类
|
||||
* @param bean 过滤条件
|
||||
* @param columns 聚合字段
|
||||
*
|
||||
* @return 聚合结果Map
|
||||
*/
|
||||
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件记录的聚合结果Map <br>
|
||||
* 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter node} <br>
|
||||
* 如 getNumberMap(Record.class, (FilterNode)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table} <br>
|
||||
*
|
||||
* @param <N> Number
|
||||
* @param entityClass Entity类
|
||||
* @param node 过滤条件
|
||||
* @param columns 聚合字段
|
||||
*
|
||||
* @return 聚合结果Map
|
||||
*/
|
||||
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的GROUP BY聚合结果Map <br>
|
||||
* 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} GROUP BY {keyColumn} <br>
|
||||
* 如 queryColumnMap(Record.class, "name", FilterFunc.MAX, "createtime") 等价于: SELECT name, MAX(createtime) FROM record GROUP BY name<br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <K> Key字段的数据类型
|
||||
* @param <N> Number
|
||||
* @param entityClass Entity类
|
||||
* @param keyColumn Key字段
|
||||
* @param func 聚合函数
|
||||
* @param funcColumn 聚合字段
|
||||
*
|
||||
* @return 聚合结果Map
|
||||
*/
|
||||
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, final FilterFunc func, final String funcColumn);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的GROUP BY聚合结果Map <br>
|
||||
* 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter bean} GROUP BY {keyColumn} <br>
|
||||
* 如 queryColumnMap(Record.class, "name", FilterFunc.MAX, "createtime", (FilterBean)null) 等价于: SELECT name, MAX(createtime) FROM record GROUP BY name<br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <K> Key字段的数据类型
|
||||
* @param <N> Number
|
||||
* @param entityClass Entity类
|
||||
* @param keyColumn Key字段
|
||||
* @param func 聚合函数
|
||||
* @param funcColumn 聚合字段
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 聚合结果Map
|
||||
*/
|
||||
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的GROUP BY聚合结果Map <br>
|
||||
* 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter node} GROUP BY {keyColumn} <br>
|
||||
* 如 queryColumnMap(Record.class, "name", FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT name, MAX(createtime) FROM record GROUP BY name<br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <K> Key字段的数据类型
|
||||
* @param <N> Number
|
||||
* @param entityClass Entity类
|
||||
* @param keyColumn Key字段
|
||||
* @param func 聚合函数
|
||||
* @param funcColumn 聚合字段
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 聚合结果Map
|
||||
*/
|
||||
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterNode node);
|
||||
|
||||
//-----------------------find----------------------------
|
||||
/**
|
||||
* 根据主键获取对象
|
||||
* 获取指定主键值的单个记录, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {primary} = {id} <br>
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param pk 主键值
|
||||
*
|
||||
@@ -116,43 +420,207 @@ public interface DataSource {
|
||||
*/
|
||||
public <T> T find(final Class<T> clazz, final Serializable pk);
|
||||
|
||||
/**
|
||||
* 获取指定主键值的单个记录, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param selects 指定字段
|
||||
* @param pk 主键值
|
||||
*
|
||||
* @return Entity对象
|
||||
*/
|
||||
public <T> T find(final Class<T> clazz, final SelectColumn selects, final Serializable pk);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 过滤字段名
|
||||
* @param key 过滤字段值
|
||||
*
|
||||
* @return Entity对象
|
||||
*/
|
||||
public <T> T find(final Class<T> clazz, final String column, final Serializable key);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {filter bean} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return Entity对象
|
||||
*/
|
||||
public <T> T find(final Class<T> clazz, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return Entity对象
|
||||
*/
|
||||
public <T> T find(final Class<T> clazz, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param selects 指定字段
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return Entity对象
|
||||
*/
|
||||
public <T> T find(final Class<T> clazz, final SelectColumn selects, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param selects 指定字段
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return Entity对象
|
||||
*/
|
||||
public <T> T find(final Class<T> clazz, final SelectColumn selects, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 字段名
|
||||
* @param pk 主键值
|
||||
*
|
||||
* @return Entity对象
|
||||
*/
|
||||
public <T> Serializable findColumn(final Class<T> clazz, final String column, final Serializable pk);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT {column} FROM {table} WHERE {filter bean} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 字段名
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 字段值
|
||||
*/
|
||||
public <T> Serializable findColumn(final Class<T> clazz, final String column, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值 <br>
|
||||
* 等价SQL: SELECT {column} FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 字段名
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 字段值
|
||||
*/
|
||||
public <T> Serializable findColumn(final Class<T> clazz, final String column, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值 <br>
|
||||
* 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 字段名
|
||||
* @param defValue 默认值
|
||||
* @param pk 主键值
|
||||
*
|
||||
* @return 字段值
|
||||
*/
|
||||
public <T> Serializable findColumn(final Class<T> clazz, final String column, final Serializable defValue, final Serializable pk);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值 <br>
|
||||
* 等价SQL: SELECT {column} FROM {table} WHERE {filter bean} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 字段名
|
||||
* @param defValue 默认值
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 字段值
|
||||
*/
|
||||
public <T> Serializable findColumn(final Class<T> clazz, final String column, final Serializable defValue, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值 <br>
|
||||
* 等价SQL: SELECT {column} FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 字段名
|
||||
* @param defValue 默认值
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 字段值
|
||||
*/
|
||||
public <T> Serializable findColumn(final Class<T> clazz, final String column, final Serializable defValue, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 判断是否存在主键值的记录 <br>
|
||||
* 等价SQL: SELECT COUNT(*) FROM {table} WHERE {primary} = {id} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param pk 主键值
|
||||
*
|
||||
* @return 是否存在
|
||||
*/
|
||||
public <T> boolean exists(final Class<T> clazz, final Serializable pk);
|
||||
|
||||
/**
|
||||
* 判断是否存在符合过滤条件的记录 <br>
|
||||
* 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter bean} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 是否存在
|
||||
*/
|
||||
public <T> boolean exists(final Class<T> clazz, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 判断是否存在符合过滤条件的记录 <br>
|
||||
* 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 是否存在
|
||||
*/
|
||||
public <T> boolean exists(final Class<T> clazz, final FilterNode node);
|
||||
|
||||
//-----------------------list set----------------------------
|
||||
/**
|
||||
* 根据指定字段值查询对象某个字段的集合
|
||||
* 查询符合过滤条件记录的某个字段Set集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {column} = {key} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 字段名
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param column 过滤字段名
|
||||
* @param key 过滤字段值
|
||||
@@ -161,89 +629,327 @@ public interface DataSource {
|
||||
*/
|
||||
public <T, V extends Serializable> HashSet<V> queryColumnSet(final String selectedColumn, final Class<T> clazz, final String column, final Serializable key);
|
||||
|
||||
public <T, V extends Serializable> HashSet<V> queryColumnSet(final String selectedColumn, final Class<T> clazz, final FilterBean bean);
|
||||
|
||||
public <T, V extends Serializable> HashSet<V> queryColumnSet(final String selectedColumn, final Class<T> clazz, final FilterNode node);
|
||||
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final String column, final Serializable key);
|
||||
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final FilterBean bean);
|
||||
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final FilterNode node);
|
||||
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterBean bean);
|
||||
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 根据指定参数查询对象某个字段的集合
|
||||
* 查询符合过滤条件记录的某个字段Set集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 字段名
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> HashSet<V> queryColumnSet(final String selectedColumn, final Class<T> clazz, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的某个字段Set集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> HashSet<V> queryColumnSet(final String selectedColumn, final Class<T> clazz, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的某个字段List集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {column} = {key} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param column 过滤字段名
|
||||
* @param key 过滤字段值
|
||||
*
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final String column, final Serializable key);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的某个字段List集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的某个字段List集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的某个字段List集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤Bean
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 结果集合
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的某个字段List集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> List<V> queryColumnList(final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的某个字段Sheet集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> Sheet<V> queryColumnSheet(final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的某个字段Sheet集合 <br>
|
||||
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param <V> 字段类型
|
||||
* @param selectedColumn 指定字段
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> Sheet<V> queryColumnSheet(final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 根据指定字段值查询对象集合
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 过滤字段名
|
||||
* @param key 过滤字段值
|
||||
*
|
||||
* @return Entity的List
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final String column, final Serializable key);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {filter bean} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param selects 指定字段
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param selects 指定字段
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param column 过滤字段名
|
||||
* @param key 过滤字段值
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final String column, final Serializable key);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param selects 指定字段
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的List集合 <br>
|
||||
* 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param selects 指定字段
|
||||
* @param flipper 翻页对象
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node);
|
||||
|
||||
//-----------------------sheet----------------------------
|
||||
/**
|
||||
* 根据指定参数查询对象某个对象的集合页
|
||||
* <p>
|
||||
* 查询符合过滤条件记录的Sheet集合 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤Bean
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return Entity的Sheet
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> Sheet<T> querySheet(final Class<T> clazz, final Flipper flipper, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的Sheet集合 <br>
|
||||
* 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> Sheet<T> querySheet(final Class<T> clazz, final Flipper flipper, final FilterNode node);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的Sheet集合 <br>
|
||||
* 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param selects 指定字段
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> Sheet<T> querySheet(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean);
|
||||
|
||||
/**
|
||||
* 查询符合过滤条件记录的Sheet集合 <br>
|
||||
* 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
|
||||
*
|
||||
* @param <T> Entity泛型
|
||||
* @param clazz Entity类
|
||||
* @param selects 指定字段
|
||||
* @param flipper 翻页对象
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return Entity的集合
|
||||
*/
|
||||
public <T> Sheet<T> querySheet(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node);
|
||||
|
||||
//-----------------------direct----------------------------
|
||||
/**
|
||||
* 直接本地执行SQL语句进行查询,远程模式不可用
|
||||
* 通常用于复杂的关联查询
|
||||
* 直接本地执行SQL语句进行查询,远程模式不可用 <br>
|
||||
* 通常用于复杂的关联查询 <br>
|
||||
*
|
||||
* @param sql SQL语句
|
||||
* @param consumer 回调函数
|
||||
@@ -251,8 +957,8 @@ public interface DataSource {
|
||||
public void directQuery(String sql, final Consumer<ResultSet> consumer);
|
||||
|
||||
/**
|
||||
* 直接本地执行SQL语句进行增删改操作,远程模式不可用
|
||||
* 通常用于复杂的更新操作
|
||||
* 直接本地执行SQL语句进行增删改操作,远程模式不可用 <br>
|
||||
* 通常用于复杂的更新操作 <br>
|
||||
*
|
||||
* @param sqls SQL语句
|
||||
*
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Target({FIELD})
|
||||
@Retention(RUNTIME)
|
||||
public @interface DistributeGenerator {
|
||||
|
||||
long initialValue() default 1;
|
||||
|
||||
/**
|
||||
* 如果allocationSize的值小于或等于1,则主键不会加上nodeid
|
||||
*
|
||||
* @return allocationSize
|
||||
*/
|
||||
int allocationSize() default 1000;
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Entity分库分表的注解,需要结合DistributeTableStrategy使用 <br>
|
||||
* 标记为 @DistributeTable的Entity类视为需要进行分库分表操作 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -8,6 +8,9 @@ package org.redkale.source;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 分表分库策略,结合@DistributeTable使用 <br>
|
||||
* 不能与@Cacheable同时使用 <br>
|
||||
* 使用分表分库功能重点是主键的生成策略,不同场景生成策略不一样 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -18,35 +21,19 @@ import java.io.Serializable;
|
||||
public interface DistributeTableStrategy<T> {
|
||||
|
||||
/**
|
||||
* 获取对象的表名
|
||||
* 查询单个对象时调用本方法获取表名
|
||||
* 获取对象的表名 <br>
|
||||
* 查询单个对象(DataSource.find)时调用本方法获取表名 <br>
|
||||
*
|
||||
* @param table 模板表的表名
|
||||
* @param primary 记录主键
|
||||
*
|
||||
* @return 带库名的全表名
|
||||
*/
|
||||
default String getTable(String table, Serializable primary) {
|
||||
return null;
|
||||
}
|
||||
public String getTable(String table, Serializable primary);
|
||||
|
||||
/**
|
||||
* 获取对象的表名
|
||||
* 查询、修改、删除对象时调用本方法获取表名
|
||||
* 注意: 需保证FilterNode过滤的结果集合必须在一个数据库表中
|
||||
*
|
||||
* @param table 模板表的表名
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 带库名的全表名
|
||||
*/
|
||||
default String getTable(String table, FilterNode node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的表名
|
||||
* 新增对象或更新单个对象时调用本方法获取表名
|
||||
* 获取对象的表名 <br>
|
||||
* 新增对象或更新单个对象(DataSource.insert、DataSource.update)时调用本方法获取表名 <br>
|
||||
*
|
||||
* @param table 模板表的表名
|
||||
* @param bean 实体对象
|
||||
@@ -54,4 +41,17 @@ public interface DistributeTableStrategy<T> {
|
||||
* @return 带库名的全表名
|
||||
*/
|
||||
public String getTable(String table, T bean);
|
||||
|
||||
/**
|
||||
* 获取对象的表名 <br>
|
||||
* 查询、修改、删除对象(DataSource.find、DataSource.query、DataSource.delete、DataSource.update)时调用本方法获取表名 <br>
|
||||
* 注意: 需保证FilterNode过滤的结果集合必须在一个数据库表中 <br>
|
||||
*
|
||||
* @param table 模板表的表名
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return 带库名的全表名
|
||||
*/
|
||||
public String getTable(String table, FilterNode node);
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import static org.redkale.source.FilterFunc.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* Entity数据的缓存类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -27,33 +28,51 @@ import org.redkale.util.*;
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class EntityCache<T> {
|
||||
|
||||
//日志
|
||||
private static final Logger logger = Logger.getLogger(EntityCache.class.getName());
|
||||
|
||||
private final ConcurrentHashMap<Serializable, T> map = new ConcurrentHashMap();
|
||||
//主键与对象的键值对
|
||||
private ConcurrentHashMap<Serializable, T> map = new ConcurrentHashMap();
|
||||
|
||||
// CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢;10w数据查询需要 0.062秒, 查询慢40%;
|
||||
private final Collection<T> list = new ConcurrentLinkedQueue();
|
||||
private Collection<T> list = new ConcurrentLinkedQueue();
|
||||
|
||||
//Flipper.sort转换成Comparator的缓存
|
||||
private final Map<String, Comparator<T>> sortComparators = new ConcurrentHashMap<>();
|
||||
|
||||
//Entity类
|
||||
private final Class<T> type;
|
||||
|
||||
//接口返回的对象是否需要复制一份
|
||||
private final boolean needcopy;
|
||||
|
||||
//Entity构建器
|
||||
private final Creator<T> creator;
|
||||
|
||||
//主键字段
|
||||
private final Attribute<T, Serializable> primary;
|
||||
|
||||
//新增时的复制器, 排除了标记为@Transient的字段
|
||||
private final Reproduce<T, T> newReproduce;
|
||||
|
||||
//修改时的复制器, 排除了标记为@Transient或@Column(updatable=false)的字段
|
||||
private final Reproduce<T, T> chgReproduce;
|
||||
|
||||
//是否已经全量加载过
|
||||
private volatile boolean fullloaded;
|
||||
|
||||
//Entity信息
|
||||
final EntityInfo<T> info;
|
||||
|
||||
public EntityCache(final EntityInfo<T> info) {
|
||||
//@Cacheable的定时更新秒数,为0表示不定时更新
|
||||
final int interval;
|
||||
|
||||
//@Cacheable的定时器
|
||||
private ScheduledThreadPoolExecutor scheduler;
|
||||
|
||||
public EntityCache(final EntityInfo<T> info, final Cacheable c) {
|
||||
this.info = info;
|
||||
this.interval = c == null ? 0 : c.interval();
|
||||
this.type = info.getType();
|
||||
this.creator = info.getCreator();
|
||||
this.primary = info.primary;
|
||||
@@ -80,15 +99,35 @@ public final class EntityCache<T> {
|
||||
|
||||
public void fullLoad() {
|
||||
if (info.fullloader == null) return;
|
||||
clear();
|
||||
this.fullloaded = false;
|
||||
ConcurrentHashMap newmap = new ConcurrentHashMap();
|
||||
List<T> all = info.fullloader.apply(info.source, type);
|
||||
if (all != null) {
|
||||
all.stream().filter(x -> x != null).forEach(x -> {
|
||||
this.map.put(this.primary.get(x), x);
|
||||
newmap.put(this.primary.get(x), x);
|
||||
});
|
||||
this.list.addAll(all);
|
||||
}
|
||||
this.list = all == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all);
|
||||
this.map = newmap;
|
||||
this.fullloaded = true;
|
||||
if (this.interval > 0) {
|
||||
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||
final Thread t = new Thread(r, "EntityCache-" + type + "-Thread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
this.scheduler.scheduleAtFixedRate(() -> {
|
||||
ConcurrentHashMap newmap2 = new ConcurrentHashMap();
|
||||
List<T> all2 = info.fullloader.apply(info.source, type);
|
||||
if (all2 != null) {
|
||||
all2.stream().filter(x -> x != null).forEach(x -> {
|
||||
newmap2.put(this.primary.get(x), x);
|
||||
});
|
||||
}
|
||||
this.list = all2 == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all2);
|
||||
this.map = newmap2;
|
||||
}, interval - System.currentTimeMillis() / 1000 % interval, interval, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public Class<T> getType() {
|
||||
@@ -97,8 +136,12 @@ public final class EntityCache<T> {
|
||||
|
||||
public void clear() {
|
||||
this.fullloaded = false;
|
||||
this.list.clear();
|
||||
this.map.clear();
|
||||
this.list = new ConcurrentLinkedQueue();
|
||||
this.map = new ConcurrentHashMap();
|
||||
if (this.scheduler != null) {
|
||||
this.scheduler.shutdownNow();
|
||||
this.scheduler = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFullLoaded() {
|
||||
|
||||
@@ -11,14 +11,13 @@ import java.lang.reflect.*;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.Level;
|
||||
import javax.persistence.*;
|
||||
import static org.redkale.source.DataDefaultSource.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* Entity操作类
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -29,94 +28,116 @@ import org.redkale.util.*;
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class EntityInfo<T> {
|
||||
|
||||
//全局静态资源
|
||||
private static final ConcurrentHashMap<Class, EntityInfo> entityInfos = new ConcurrentHashMap<>();
|
||||
|
||||
//日志
|
||||
private static final Logger logger = Logger.getLogger(EntityInfo.class);
|
||||
|
||||
//Entity类的类名
|
||||
//Entity类名
|
||||
private final Class<T> type;
|
||||
|
||||
//类对应的数据表名, 如果是VirtualEntity 类, 则该字段为null
|
||||
final String table;
|
||||
|
||||
//Entity构建器
|
||||
private final Creator<T> creator;
|
||||
|
||||
//主键
|
||||
final Attribute<T, Serializable> primary;
|
||||
|
||||
//Entity缓存对象
|
||||
private final EntityCache<T> cache;
|
||||
|
||||
//key是field的name, 不是sql字段。
|
||||
//存放所有与数据库对应的字段, 包括主键
|
||||
private final HashMap<String, Attribute<T, Serializable>> attributeMap = new HashMap<>();
|
||||
|
||||
//存放所有与数据库对应的字段, 包括主键
|
||||
final Attribute<T, Serializable>[] attributes;
|
||||
|
||||
//key是field的name, value是Column的别名,即数据库表的字段名
|
||||
//只有field.name 与 Column.name不同才存放在aliasmap里.
|
||||
private final Map<String, String> aliasmap;
|
||||
|
||||
//所有可更新字段,即排除了主键字段和标记为@Column(updatable=false)的字段
|
||||
private final Map<String, Attribute<T, Serializable>> updateAttributeMap = new HashMap<>();
|
||||
|
||||
final String containSQL; //用于反向LIKE使用
|
||||
//用于反向LIKE使用
|
||||
final String containSQL;
|
||||
|
||||
final String notcontainSQL; //用于反向LIKE使用
|
||||
//用于反向LIKE使用
|
||||
final String notcontainSQL;
|
||||
|
||||
final String tablenotexistSqlstates; //用于判断表不存在的使用, 多个SQLState用;隔开
|
||||
//用于判断表不存在的使用, 多个SQLState用;隔开
|
||||
final String tablenotexistSqlstates;
|
||||
|
||||
final String tablecopySQL; //用于复制表结构使用
|
||||
//用于复制表结构使用
|
||||
final String tablecopySQL;
|
||||
|
||||
final Set<String> tables = new HashSet<>(); //用于存在table_20160202类似这种分布式表
|
||||
//用于存在table_20160202类似这种分布式表
|
||||
final Set<String> tables = new HashSet<>();
|
||||
|
||||
//分表 策略
|
||||
final DistributeTableStrategy<T> tableStrategy;
|
||||
|
||||
//根据主键查找单个对象的SQL, 含 ?
|
||||
final String querySQL;
|
||||
|
||||
private final Attribute<T, Serializable>[] queryAttributes; //数据库中所有字段
|
||||
//数据库中所有字段
|
||||
private final Attribute<T, Serializable>[] queryAttributes;
|
||||
|
||||
//新增SQL, 含 ?,即排除了自增长主键和标记为@Column(insertable=false)的字段
|
||||
private final String insertSQL;
|
||||
|
||||
final Attribute<T, Serializable>[] insertAttributes; //数据库中所有可新增字段
|
||||
//数据库中所有可新增字段
|
||||
final Attribute<T, Serializable>[] insertAttributes;
|
||||
|
||||
//根据主键更新所有可更新字段的SQL,含 ?
|
||||
private final String updateSQL;
|
||||
|
||||
final Attribute<T, Serializable>[] updateAttributes; //数据库中所有可更新字段
|
||||
//数据库中所有可更新字段
|
||||
final Attribute<T, Serializable>[] updateAttributes;
|
||||
|
||||
//根据主键删除记录的SQL,含 ?
|
||||
private final String deleteSQL;
|
||||
|
||||
//日志级别,从LogLevel获取
|
||||
private final int logLevel;
|
||||
|
||||
//Flipper.sort转换成以ORDER BY开头SQL的缓存
|
||||
private final Map<String, String> sortOrderbySqls = new ConcurrentHashMap<>();
|
||||
|
||||
//---------------------计算主键值----------------------------
|
||||
private final int nodeid;
|
||||
|
||||
//是否由数据库生成主键值
|
||||
final boolean autoGenerated;
|
||||
|
||||
//是否UUID主键
|
||||
final boolean autouuid;
|
||||
|
||||
final boolean distributed;
|
||||
|
||||
boolean initedPrimaryValue = false;
|
||||
|
||||
final AtomicLong primaryValue = new AtomicLong(0);
|
||||
|
||||
final int allocationSize;
|
||||
|
||||
//所属的DataSource
|
||||
final DataSource source;
|
||||
|
||||
//全量数据的加载器
|
||||
final BiFunction<DataSource, Class, List> fullloader;
|
||||
//------------------------------------------------------------
|
||||
|
||||
static <T> EntityInfo<T> load(Class<T> clazz, final int nodeid, final boolean cacheForbidden, final Properties conf,
|
||||
/**
|
||||
* 加载EntityInfo
|
||||
*
|
||||
* @param type Entity类
|
||||
* @param cacheForbidden 是否禁用EntityCache
|
||||
* @param conf 配置信息, persistence.xml中的property节点值
|
||||
* @param source DataSource,可为null
|
||||
* @param fullloader 全量加载器,可为null
|
||||
*/
|
||||
static <T> EntityInfo<T> load(Class<T> clazz, final boolean cacheForbidden, final Properties conf,
|
||||
DataSource source, BiFunction<DataSource, Class, List> fullloader) {
|
||||
EntityInfo rs = entityInfos.get(clazz);
|
||||
if (rs != null) return rs;
|
||||
synchronized (entityInfos) {
|
||||
rs = entityInfos.get(clazz);
|
||||
if (rs == null) {
|
||||
if (nodeid < 0) throw new IllegalArgumentException("nodeid(" + nodeid + ") is illegal");
|
||||
rs = new EntityInfo(clazz, nodeid, cacheForbidden, conf, source, fullloader);
|
||||
rs = new EntityInfo(clazz, cacheForbidden, conf, source, fullloader);
|
||||
entityInfos.put(clazz, rs);
|
||||
if (rs.cache != null) {
|
||||
if (fullloader == null) throw new IllegalArgumentException(clazz.getName() + " auto loader is illegal");
|
||||
@@ -127,16 +148,32 @@ public final class EntityInfo<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Entity类对应的EntityInfo对象
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param clazz Entity类
|
||||
*
|
||||
* @return EntityInfo
|
||||
*/
|
||||
static <T> EntityInfo<T> get(Class<T> clazz) {
|
||||
return entityInfos.get(clazz);
|
||||
}
|
||||
|
||||
private EntityInfo(Class<T> type, int nodeid, final boolean cacheForbidden,
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param type Entity类
|
||||
* @param cacheForbidden 是否禁用EntityCache
|
||||
* @param conf 配置信息, persistence.xml中的property节点值
|
||||
* @param source DataSource,可为null
|
||||
* @param fullloader 全量加载器,可为null
|
||||
*/
|
||||
private EntityInfo(Class<T> type, final boolean cacheForbidden,
|
||||
Properties conf, DataSource source, BiFunction<DataSource, Class, List> fullloader) {
|
||||
this.type = type;
|
||||
this.source = source;
|
||||
//---------------------------------------------
|
||||
this.nodeid = nodeid >= 0 ? nodeid : 0;
|
||||
|
||||
LogLevel ll = type.getAnnotation(LogLevel.class);
|
||||
this.logLevel = ll == null ? Integer.MIN_VALUE : Level.parse(ll.value()).intValue();
|
||||
@@ -176,8 +213,6 @@ public final class EntityInfo<T> {
|
||||
List<Attribute<T, Serializable>> updateattrs = new ArrayList<>();
|
||||
boolean auto = false;
|
||||
boolean uuid = false;
|
||||
boolean sqldistribute = false;
|
||||
int allocationSize0 = 0;
|
||||
|
||||
do {
|
||||
for (Field field : cltmp.getDeclaredFields()) {
|
||||
@@ -205,17 +240,6 @@ public final class EntityInfo<T> {
|
||||
// if (gv != null && gv.strategy() != GenerationType.IDENTITY) {
|
||||
// throw new RuntimeException(cltmp.getName() + "'s @ID primary not a GenerationType.IDENTITY");
|
||||
// }
|
||||
DistributeGenerator dg = field.getAnnotation(DistributeGenerator.class);
|
||||
if (dg != null) {
|
||||
if (!field.getType().isPrimitive()) {
|
||||
throw new RuntimeException(cltmp.getName() + "'s @"
|
||||
+ DistributeGenerator.class.getSimpleName() + " primary must be primitive class type field");
|
||||
}
|
||||
sqldistribute = true;
|
||||
auto = false;
|
||||
allocationSize0 = dg.allocationSize();
|
||||
primaryValue.set(dg.initialValue());
|
||||
}
|
||||
if (gv != null && field.getType() == String.class) { //UUID
|
||||
uuid = true;
|
||||
auto = false;
|
||||
@@ -272,118 +296,211 @@ public final class EntityInfo<T> {
|
||||
}
|
||||
this.autoGenerated = auto;
|
||||
this.autouuid = uuid;
|
||||
this.distributed = sqldistribute;
|
||||
this.allocationSize = allocationSize0;
|
||||
//----------------cache--------------
|
||||
Cacheable c = type.getAnnotation(Cacheable.class);
|
||||
if (this.table == null || (!cacheForbidden && c != null && c.value())) {
|
||||
this.cache = new EntityCache<>(this);
|
||||
this.cache = new EntityCache<>(this, c);
|
||||
} else {
|
||||
this.cache = null;
|
||||
}
|
||||
if (conf == null) conf = new Properties();
|
||||
this.containSQL = conf.getProperty(JDBC_CONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) > 0");
|
||||
this.notcontainSQL = conf.getProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) = 0");
|
||||
this.containSQL = conf.getProperty(JDBCPoolSource.JDBC_CONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) > 0");
|
||||
this.notcontainSQL = conf.getProperty(JDBCPoolSource.JDBC_NOTCONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) = 0");
|
||||
|
||||
this.tablenotexistSqlstates = ";" + conf.getProperty(JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02") + ";";
|
||||
this.tablecopySQL = conf.getProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} LIKE ${oldtable}");
|
||||
this.tablenotexistSqlstates = ";" + conf.getProperty(JDBCPoolSource.JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02") + ";";
|
||||
this.tablecopySQL = conf.getProperty(JDBCPoolSource.JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} LIKE ${oldtable}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建主键值,目前只支持UUID赋值
|
||||
*
|
||||
* @param src Entity对象
|
||||
*/
|
||||
public void createPrimaryValue(T src) {
|
||||
if (autouuid) {
|
||||
getPrimary().set(src, Utility.uuid());
|
||||
return;
|
||||
}
|
||||
long v = allocationSize > 1 ? (primaryValue.incrementAndGet() * allocationSize + nodeid) : primaryValue.incrementAndGet();
|
||||
if (primary.type() == int.class || primary.type() == Integer.class) {
|
||||
getPrimary().set(src, (Integer) ((Long) v).intValue());
|
||||
} else {
|
||||
getPrimary().set(src, v);
|
||||
}
|
||||
if (autouuid) getPrimary().set(src, Utility.uuid());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Entity缓存器
|
||||
*
|
||||
* @return EntityCache
|
||||
*/
|
||||
public EntityCache<T> getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存器是否已经全量加载过
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isCacheFullLoaded() {
|
||||
return cache != null && cache.isFullLoaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Entity构建器
|
||||
*
|
||||
* @return Creator
|
||||
*/
|
||||
public Creator<T> getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Entity类名
|
||||
*
|
||||
* @return Class
|
||||
*/
|
||||
public Class<T> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否虚拟类
|
||||
* 判断Entity是否为虚拟类
|
||||
*
|
||||
* @return 是否虚拟类
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isVirtualEntity() {
|
||||
return table == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Entity的INSERT SQL
|
||||
*
|
||||
* @param bean Entity对象
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getInsertSQL(T bean) {
|
||||
if (this.tableStrategy == null) return insertSQL;
|
||||
return insertSQL.replace("${newtable}", getTable(bean));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Entity的UPDATE SQL
|
||||
*
|
||||
* @param bean Entity对象
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getUpdateSQL(T bean) {
|
||||
if (this.tableStrategy == null) return updateSQL;
|
||||
return updateSQL.replace("${newtable}", getTable(bean));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Entity的DELETE SQL
|
||||
*
|
||||
* @param bean Entity对象
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getDeleteSQL(T bean) {
|
||||
if (this.tableStrategy == null) return deleteSQL;
|
||||
return deleteSQL.replace("${newtable}", getTable(bean));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主键值获取Entity的表名
|
||||
*
|
||||
* @param primary Entity主键值
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getTable(Serializable primary) {
|
||||
if (tableStrategy == null) return table;
|
||||
String t = tableStrategy.getTable(table, primary);
|
||||
return t == null || t.isEmpty() ? table : t;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据过滤条件获取Entity的表名
|
||||
*
|
||||
* @param node 过滤条件
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getTable(FilterNode node) {
|
||||
if (tableStrategy == null) return table;
|
||||
String t = tableStrategy.getTable(table, node);
|
||||
return t == null || t.isEmpty() ? table : t;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Entity对象获取Entity的表名
|
||||
*
|
||||
* @param bean Entity对象
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getTable(T bean) {
|
||||
if (tableStrategy == null) return table;
|
||||
String t = tableStrategy.getTable(table, bean);
|
||||
return t == null || t.isEmpty() ? table : t;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主键字段的Attribute
|
||||
*
|
||||
* @return Attribute
|
||||
*/
|
||||
public Attribute<T, Serializable> getPrimary() {
|
||||
return this.primary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历数据库表对应的所有字段, 不包含@Transient字段
|
||||
*
|
||||
* @param action BiConsumer
|
||||
*/
|
||||
public void forEachAttribute(BiConsumer<String, Attribute<T, Serializable>> action) {
|
||||
this.attributeMap.forEach(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Entity字段名获取字段的Attribute
|
||||
*
|
||||
* @param fieldname Class字段名
|
||||
*
|
||||
* @return Attribute
|
||||
*/
|
||||
public Attribute<T, Serializable> getAttribute(String fieldname) {
|
||||
if (fieldname == null) return null;
|
||||
return this.attributeMap.get(fieldname);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Entity字段名获取可更新字段的Attribute
|
||||
*
|
||||
* @param fieldname Class字段名
|
||||
*
|
||||
* @return Attribute
|
||||
*/
|
||||
public Attribute<T, Serializable> getUpdateAttribute(String fieldname) {
|
||||
return this.updateAttributeMap.get(fieldname);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Entity类的字段名与表字段名s是否存在不一致的值
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isNoAlias() {
|
||||
return this.aliasmap == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Flipper获取ORDER BY的SQL语句,不存在Flipper或sort字段返回空字符串
|
||||
*
|
||||
* @param flipper 翻页对象
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
protected String createSQLOrderby(Flipper flipper) {
|
||||
if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty() || flipper.getSort().indexOf(';') >= 0 || flipper.getSort().indexOf('\n') >= 0) return "";
|
||||
if (flipper == null || flipper.getSort() == null) return "";
|
||||
final String sort = flipper.getSort();
|
||||
if (sort.isEmpty() || sort.indexOf(';') >= 0 || sort.indexOf('\n') >= 0) return "";
|
||||
String sql = this.sortOrderbySqls.get(sort);
|
||||
if (sql != null) return sql;
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
@@ -409,21 +526,47 @@ public final class EntityInfo<T> {
|
||||
return sql;
|
||||
}
|
||||
|
||||
//根据field字段名获取数据库对应的字段名
|
||||
/**
|
||||
* 根据field字段名获取数据库对应的字段名
|
||||
*
|
||||
* @param tabalis 表别名
|
||||
* @param fieldname 字段名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getSQLColumn(String tabalis, String fieldname) {
|
||||
return this.aliasmap == null ? (tabalis == null ? fieldname : (tabalis + '.' + fieldname))
|
||||
: (tabalis == null ? aliasmap.getOrDefault(fieldname, fieldname) : (tabalis + '.' + aliasmap.getOrDefault(fieldname, fieldname)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主键字段的表字段名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getPrimarySQLColumn() {
|
||||
return getSQLColumn(null, this.primary.field());
|
||||
}
|
||||
|
||||
//数据库字段名
|
||||
/**
|
||||
* 获取主键字段的带有表别名的表字段名
|
||||
*
|
||||
* @param tabalis 表别名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getPrimarySQLColumn(String tabalis) {
|
||||
return getSQLColumn(tabalis, this.primary.field());
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接UPDATE给字段赋值的SQL片段
|
||||
*
|
||||
* @param col 表字段名
|
||||
* @param cv ColumnValue
|
||||
*
|
||||
* @return CharSequence
|
||||
*/
|
||||
protected CharSequence formatSQLValue(String col, final ColumnValue cv) {
|
||||
if (cv == null) return null;
|
||||
switch (cv.getExpress()) {
|
||||
@@ -441,14 +584,33 @@ public final class EntityInfo<T> {
|
||||
return formatToString(cv.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有数据表字段的Attribute, 不包含@Transient字段
|
||||
*
|
||||
* @return Map
|
||||
*/
|
||||
protected Map<String, Attribute<T, Serializable>> getAttributes() {
|
||||
return attributeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断日志级别
|
||||
*
|
||||
* @param l Level
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isLoggable(Level l) {
|
||||
return l.intValue() >= this.logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字段值序列化为可SQL的字符串
|
||||
*
|
||||
* @param value 字段值
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
protected String formatToString(Object value) {
|
||||
if (value == null) return null;
|
||||
if (value instanceof CharSequence) {
|
||||
@@ -457,45 +619,64 @@ public final class EntityInfo<T> {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一行的ResultSet组装成一个Entity对象
|
||||
*
|
||||
* @param sels 指定字段
|
||||
* @param set ResultSet
|
||||
*
|
||||
* @return Entity对象
|
||||
* @throws SQLException SQLException
|
||||
*/
|
||||
protected T getValue(final SelectColumn sels, final ResultSet set) throws SQLException {
|
||||
T obj = creator.create();
|
||||
for (Attribute<T, Serializable> attr : queryAttributes) {
|
||||
if (sels == null || sels.test(attr.field())) {
|
||||
Serializable o = (Serializable) set.getObject(this.getSQLColumn(null, attr.field()));
|
||||
final Class t = attr.type();
|
||||
if (t.isPrimitive()) {
|
||||
if (o != null) {
|
||||
if (t == int.class) {
|
||||
o = ((Number) o).intValue();
|
||||
Serializable o;
|
||||
if (t == byte[].class) {
|
||||
Blob blob = set.getBlob(this.getSQLColumn(null, attr.field()));
|
||||
if (blob == null) {
|
||||
o = null;
|
||||
} else { //不支持超过2G的数据
|
||||
o = blob.getBytes(1, (int) blob.length());
|
||||
}
|
||||
} else {
|
||||
o = (Serializable) set.getObject(this.getSQLColumn(null, attr.field()));
|
||||
if (t.isPrimitive()) {
|
||||
if (o != null) {
|
||||
if (t == int.class) {
|
||||
o = ((Number) o).intValue();
|
||||
} else if (t == long.class) {
|
||||
o = ((Number) o).longValue();
|
||||
} else if (t == short.class) {
|
||||
o = ((Number) o).shortValue();
|
||||
} else if (t == float.class) {
|
||||
o = ((Number) o).floatValue();
|
||||
} else if (t == double.class) {
|
||||
o = ((Number) o).doubleValue();
|
||||
} else if (t == byte.class) {
|
||||
o = ((Number) o).byteValue();
|
||||
} else if (t == char.class) {
|
||||
o = (char) ((Number) o).intValue();
|
||||
}
|
||||
} else if (t == int.class) {
|
||||
o = 0;
|
||||
} else if (t == long.class) {
|
||||
o = ((Number) o).longValue();
|
||||
o = 0L;
|
||||
} else if (t == short.class) {
|
||||
o = ((Number) o).shortValue();
|
||||
o = (short) 0;
|
||||
} else if (t == float.class) {
|
||||
o = ((Number) o).floatValue();
|
||||
o = 0.0f;
|
||||
} else if (t == double.class) {
|
||||
o = ((Number) o).doubleValue();
|
||||
o = 0.0d;
|
||||
} else if (t == byte.class) {
|
||||
o = ((Number) o).byteValue();
|
||||
o = (byte) 0;
|
||||
} else if (t == boolean.class) {
|
||||
o = false;
|
||||
} else if (t == char.class) {
|
||||
o = (char) ((Number) o).intValue();
|
||||
o = (char) 0;
|
||||
}
|
||||
} else if (t == int.class) {
|
||||
o = 0;
|
||||
} else if (t == long.class) {
|
||||
o = 0L;
|
||||
} else if (t == short.class) {
|
||||
o = (short) 0;
|
||||
} else if (t == float.class) {
|
||||
o = 0.0f;
|
||||
} else if (t == double.class) {
|
||||
o = 0.0d;
|
||||
} else if (t == byte.class) {
|
||||
o = (byte) 0;
|
||||
} else if (t == boolean.class) {
|
||||
o = false;
|
||||
} else if (t == char.class) {
|
||||
o = (char) 0;
|
||||
}
|
||||
}
|
||||
attr.set(obj, o);
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package org.redkale.source;
|
||||
|
||||
/**
|
||||
*
|
||||
* 不被标记为@javax.persistence.Transient 的字段均视为过滤条件
|
||||
* FilterBean用于过滤条件, 所有的FilterBean都必须可以转换成FilterNode <br>
|
||||
*
|
||||
* 不被标记为@javax.persistence.Transient 的字段均视为过滤条件 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* @author zhangjx
|
||||
*/
|
||||
public interface FilterBean {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@ public @interface FilterColumn {
|
||||
long least() default 1;
|
||||
|
||||
/**
|
||||
* express的默认值根据字段类型的不同而不同:
|
||||
* 数组 --> IN
|
||||
* Range --> Between
|
||||
* 其他 --> =
|
||||
* express的默认值根据字段类型的不同而不同: <br>
|
||||
* 数组 --> IN <br>
|
||||
* Range --> Between <br>
|
||||
* 其他 --> = <br>
|
||||
*
|
||||
* @return 字段表达式
|
||||
*/
|
||||
|
||||
93
src/org/redkale/source/FilterFuncColumn.java
Normal file
93
src/org/redkale/source/FilterFuncColumn.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.util.Arrays;
|
||||
|
||||
/**
|
||||
* FilterFuncColumn用于getNumberMap获取列表似数据, getNumberResult获取单字段值, getNumberMap获取多字段值
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class FilterFuncColumn implements java.io.Serializable {
|
||||
|
||||
public static final String COLUMN_NULL = "*";
|
||||
|
||||
FilterFunc func;
|
||||
|
||||
String[] columns; //为null,将使用*代替
|
||||
|
||||
Number defvalue;
|
||||
|
||||
public FilterFuncColumn() {
|
||||
}
|
||||
|
||||
public static FilterFuncColumn create(final FilterFunc func) {
|
||||
return new FilterFuncColumn(func);
|
||||
}
|
||||
|
||||
public static FilterFuncColumn create(final FilterFunc func, final String... columns) {
|
||||
return new FilterFuncColumn(func, columns);
|
||||
}
|
||||
|
||||
public static FilterFuncColumn create(final FilterFunc func, final Number defvalue, final String... columns) {
|
||||
return new FilterFuncColumn(func, defvalue, columns);
|
||||
}
|
||||
|
||||
String[] cols() {
|
||||
return columns == null || columns.length == 0 ? new String[]{COLUMN_NULL} : columns;
|
||||
}
|
||||
|
||||
String col(String column) {
|
||||
return column == null || column.isEmpty() ? COLUMN_NULL : column;
|
||||
}
|
||||
|
||||
public FilterFuncColumn(final FilterFunc func) {
|
||||
this(func, (Number) null);
|
||||
}
|
||||
|
||||
public FilterFuncColumn(final FilterFunc func, final String... columns) {
|
||||
this(func, null, columns);
|
||||
}
|
||||
|
||||
public FilterFuncColumn(final FilterFunc func, final Number defvalue, final String... columns) {
|
||||
this.func = func;
|
||||
this.defvalue = defvalue;
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
public FilterFunc getFunc() {
|
||||
return func;
|
||||
}
|
||||
|
||||
public void setFunc(FilterFunc func) {
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
public String[] getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
public void setColumns(String[] columns) {
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
public Number getDefvalue() {
|
||||
return defvalue;
|
||||
}
|
||||
|
||||
public void setDefvalue(Number defvalue) {
|
||||
this.defvalue = defvalue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "{func:" + this.func + ", columns:" + Arrays.toString(this.columns) + ", defvalue:" + this.defvalue + "}";
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,9 @@ import static java.lang.annotation.ElementType.FIELD;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 默认情况下FilterBean下的过滤字段之间是AND关系。
|
||||
* 当需要使用OR或AND OR组合过滤查询时需要使用 FilterGroup。
|
||||
* FilterGroup 的value 必须是[OR]或者[AND]开头, 多级需要用点.分隔。 (注: 暂时不支持多级)
|
||||
* 默认情况下FilterBean下的过滤字段之间是AND关系。 <br>
|
||||
* 当需要使用OR或AND OR组合过滤查询时需要使用 FilterGroup。 <br>
|
||||
* FilterGroup 的value 必须是[OR]或者[AND]开头, 多级需要用点.分隔。 (注: 暂时不支持多级) <br>
|
||||
* 示例一:
|
||||
* <blockquote><pre>
|
||||
* public class TestFilterBean implements FilterBean {
|
||||
@@ -49,9 +49,9 @@ import java.lang.annotation.*;
|
||||
* private int birthday;
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* 转换的SQL语句为: WHERE id = ? AND ((desc LIKE ? AND name LIKE ?) OR (age = ? OR birthday = ?))
|
||||
* 因为默认是AND关系, @FilterGroup("") 等价于 @FilterGroup("[AND]")
|
||||
* 所以示例二的@FilterGroup("[OR]g1.[AND]subg1") 可以简化为 @FilterGroup("[OR]g1.subg1")
|
||||
* 转换的SQL语句为: WHERE id = ? AND ((desc LIKE ? AND name LIKE ?) OR (age = ? OR birthday = ?)) <br>
|
||||
* 因为默认是AND关系, @FilterGroup("") 等价于 @FilterGroup("[AND]") <br>
|
||||
* 所以示例二的@FilterGroup("[OR]g1.[AND]subg1") 可以简化为 @FilterGroup("[OR]g1.subg1") <br>
|
||||
*/
|
||||
/**
|
||||
* <p>
|
||||
|
||||
@@ -10,6 +10,8 @@ import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 关联表过滤条件 <br>
|
||||
* 关联关系表必须含主表, 不能是主表A关联表B,表B再关联表C,只能是主表A关联表B,主表A关联表C <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -31,13 +33,13 @@ public @interface FilterJoinColumn {
|
||||
|
||||
/**
|
||||
*
|
||||
* 多个关联字段, 默认使用join表(b)的主键, join表与被join表(a)的字段必须一样
|
||||
* 例如: SELECT a.* FROM user a INNER JOIN record b ON a.userid = b.userid AND a.usertype = b.usertype
|
||||
* 那么注解为: @FilterJoinColumn(table = Record.class, columns = {"userid", "usertype"})
|
||||
* 多个关联字段, 默认使用join表(b)的主键, join表与被join表(a)的字段必须一样 <br>
|
||||
* 例如: SELECT a.* FROM user a INNER JOIN record b ON a.userid = b.userid AND a.usertype = b.usertype <br>
|
||||
* 那么注解为: @FilterJoinColumn(table = Record.class, columns = {"userid", "usertype"}) <br>
|
||||
* <p>
|
||||
* columns中的字段名如果不一致,可以将两个字段名用=连接成一个字段名
|
||||
* 例如: SELECT a.* FROM user a INNER JOIN record b ON a.userid = b.buyerid AND a.usertype = b.usertype
|
||||
* 那么注解为: @FilterJoinColumn(table = Record.class, columns = {"userid=buyerid", "usertype"})
|
||||
* columns中的字段名如果不一致,可以将两个字段名用=连接成一个字段名 <br>
|
||||
* 例如: SELECT a.* FROM user a INNER JOIN record b ON a.userid = b.buyerid AND a.usertype = b.usertype <br>
|
||||
* 那么注解为: @FilterJoinColumn(table = Record.class, columns = {"userid=buyerid", "usertype"}) <br>
|
||||
*
|
||||
* @return 关联字段
|
||||
*/
|
||||
|
||||
@@ -10,9 +10,10 @@ import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.*;
|
||||
import static org.redkale.source.FilterExpress.EQUAL;
|
||||
import org.redkale.util.Attribute;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* @FilterJoinColumn对应的FilterNode对象
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -94,10 +95,7 @@ public class FilterJoinNode extends FilterNode {
|
||||
return this;
|
||||
}
|
||||
if (or == signor || this.column == null) {
|
||||
FilterNode[] newsiblings = new FilterNode[nodes.length + 1];
|
||||
System.arraycopy(nodes, 0, newsiblings, 0, nodes.length);
|
||||
newsiblings[nodes.length] = node;
|
||||
this.nodes = newsiblings;
|
||||
this.nodes = Utility.append(this.nodes, node);
|
||||
if (this.column == null) this.or = signor;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* FilterKey主要用于自身字段间的表达式, 如: a.recordid = a.parentid , a.parentid就需要FilterKey来表示 new FilterKey("parentid")
|
||||
*
|
||||
* <br>
|
||||
* 注意:该类型不支持表达式:FV_XXX、BETWEEN、NOTBETWEEN、IN、NOTIN
|
||||
*
|
||||
* <p>
|
||||
|
||||
@@ -10,15 +10,15 @@ import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import static org.redkale.source.FilterExpress.*;
|
||||
import org.redkale.util.Attribute;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 注意: <br>
|
||||
* column的值以#开头的视为虚拟字段,不在过滤范围内 <br>
|
||||
* 在调用 createSQLExpress 之前必须先调用 createSQLJoin <br>
|
||||
* 在调用 createPredicate 之前必须先调用 isCacheUseable
|
||||
*
|
||||
* 在调用 createPredicate 之前必须先调用 isCacheUseable <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -150,10 +150,7 @@ public class FilterNode { //FilterNode 不能实现Serializable接口, 否则
|
||||
return this;
|
||||
}
|
||||
if (or == signor) {
|
||||
FilterNode[] newsiblings = new FilterNode[nodes.length + 1];
|
||||
System.arraycopy(nodes, 0, newsiblings, 0, nodes.length);
|
||||
newsiblings[nodes.length] = node;
|
||||
this.nodes = newsiblings;
|
||||
this.nodes = Utility.append(this.nodes, node);
|
||||
return this;
|
||||
}
|
||||
FilterNode newnode = new FilterNode(this.column, this.express, this.itemand, this.value);
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.persistence.Transient;
|
||||
import static org.redkale.source.FilterExpress.*;
|
||||
import org.redkale.util.Attribute;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -117,10 +117,7 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
|
||||
return this;
|
||||
}
|
||||
if (or == signor) {
|
||||
FilterNodeBean[] newsiblings = new FilterNodeBean[nodeBeans.length + 1];
|
||||
System.arraycopy(nodeBeans, 0, newsiblings, 0, nodeBeans.length);
|
||||
newsiblings[nodeBeans.length] = node;
|
||||
this.nodeBeans = newsiblings;
|
||||
this.nodeBeans = Utility.append(this.nodeBeans, node);
|
||||
return this;
|
||||
}
|
||||
this.nodeBeans = new FilterNodeBean[]{new FilterNodeBean(this), node};
|
||||
|
||||
@@ -7,7 +7,7 @@ package org.redkale.source;
|
||||
|
||||
/**
|
||||
* FilterValue主要用于复杂的表达式。<br>
|
||||
* 例如: col / 10 = 3 、MOD(col, 8) > 0 这些都不是单独一个数值能表达的,因此需要FilterValue 才构建 8 、 > 、0 组合值.
|
||||
* 例如: col / 10 = 3 、MOD(col, 8) > 0 这些都不是单独一个数值能表达的,因此需要FilterValue 才构建 8 、 > 、0 组合值。
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.source;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 翻页对象, offset从0开始, limit必须大于0
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -40,6 +41,11 @@ public final class Flipper implements Serializable, Cloneable {
|
||||
this.offset = offset < 0 ? 0 : offset;
|
||||
}
|
||||
|
||||
public Flipper(int limit, String sortColumn) {
|
||||
this.limit = limit > 0 ? limit : DEFAULT_LIMIT;
|
||||
this.sort = sortColumn;
|
||||
}
|
||||
|
||||
public Flipper(int limit, int offset, String sortColumn) {
|
||||
this.limit = limit > 0 ? limit : DEFAULT_LIMIT;
|
||||
this.offset = offset < 0 ? 0 : offset;
|
||||
|
||||
@@ -27,6 +27,26 @@ import javax.sql.*;
|
||||
*/
|
||||
public class JDBCPoolSource {
|
||||
|
||||
static final String JDBC_CONNECTIONSMAX = "javax.persistence.connections.limit";
|
||||
|
||||
static final String JDBC_CONTAIN_SQLTEMPLATE = "javax.persistence.contain.sqltemplate";
|
||||
|
||||
static final String JDBC_NOTCONTAIN_SQLTEMPLATE = "javax.persistence.notcontain.sqltemplate";
|
||||
|
||||
static final String JDBC_TABLENOTEXIST_SQLSTATES = "javax.persistence.tablenotexist.sqlstates";
|
||||
|
||||
static final String JDBC_TABLECOPY_SQLTEMPLATE = "javax.persistence.tablecopy.sqltemplate";
|
||||
|
||||
static final String JDBC_URL = "javax.persistence.jdbc.url";
|
||||
|
||||
static final String JDBC_USER = "javax.persistence.jdbc.user";
|
||||
|
||||
static final String JDBC_PWD = "javax.persistence.jdbc.password";
|
||||
|
||||
static final String JDBC_DRIVER = "javax.persistence.jdbc.driver";
|
||||
|
||||
static final String JDBC_SOURCE = "javax.persistence.jdbc.source";
|
||||
|
||||
private static final Map<String, AbstractMap.SimpleEntry<WatchService, List<WeakReference<JDBCPoolSource>>>> maps = new HashMap<>();
|
||||
|
||||
private final AtomicLong usingCounter = new AtomicLong();
|
||||
@@ -97,6 +117,77 @@ public class JDBCPoolSource {
|
||||
}
|
||||
}
|
||||
|
||||
static ConnectionPoolDataSource createDataSource(Properties property) {
|
||||
try {
|
||||
return createDataSource(property.getProperty(JDBC_SOURCE, property.getProperty(JDBC_DRIVER)),
|
||||
property.getProperty(JDBC_URL), property.getProperty(JDBC_USER), property.getProperty(JDBC_PWD));
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("(" + property + ") have no jdbc parameters", ex);
|
||||
}
|
||||
}
|
||||
|
||||
static ConnectionPoolDataSource createDataSource(String source0, String url, String user, String password) throws Exception {
|
||||
String source = source0;
|
||||
if (source0 == null || source0.isEmpty()) {
|
||||
if (url.startsWith("jdbc:mysql:")) {
|
||||
source0 = "com.mysql.jdbc.Driver";
|
||||
} else if (url.startsWith("jdbc:mariadb:")) {
|
||||
source0 = "org.mariadb.jdbc.Driver";
|
||||
} else if (url.startsWith("jdbc:oracle:")) {
|
||||
source0 = "oracle.jdbc.driver.OracleDriver";
|
||||
} else if (url.startsWith("jdbc:postgresql:")) {
|
||||
source0 = "org.postgresql.Driver";
|
||||
} else if (url.startsWith("jdbc:microsoft:sqlserver:")) {
|
||||
source0 = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
|
||||
}
|
||||
}
|
||||
if (source0 != null && source0.contains("Driver")) { //为了兼容JPA的配置文件
|
||||
switch (source0) {
|
||||
case "org.mariadb.jdbc.Driver":
|
||||
source = "org.mariadb.jdbc.MySQLDataSource";
|
||||
break;
|
||||
case "com.mysql.cj.jdbc.Driver":
|
||||
case "com.mysql.jdbc.Driver":
|
||||
try {
|
||||
Class.forName("com.mysql.cj.jdbc.MysqlConnectionPoolDataSource");
|
||||
source = "com.mysql.cj.jdbc.MysqlConnectionPoolDataSource";
|
||||
} catch (Exception e) {
|
||||
source = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource";
|
||||
}
|
||||
break;
|
||||
case "oracle.jdbc.driver.OracleDriver":
|
||||
source = "oracle.jdbc.pool.OracleConnectionPoolDataSource";
|
||||
break;
|
||||
case "org.postgresql.Driver":
|
||||
source = "org.postgresql.ds.PGConnectionPoolDataSource";
|
||||
break;
|
||||
case "com.microsoft.sqlserver.jdbc.SQLServerDriver":
|
||||
source = "com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource";
|
||||
break;
|
||||
}
|
||||
}
|
||||
final Class clazz = Class.forName(source);
|
||||
Object pdsource = clazz.newInstance();
|
||||
if (source.contains(".postgresql.")) {
|
||||
Class driver = Class.forName("org.postgresql.Driver");
|
||||
Properties properties = (Properties) driver.getMethod("parseURL", String.class, Properties.class).invoke(null, url, new Properties());
|
||||
clazz.getMethod("setServerName", String.class).invoke(pdsource, properties.getProperty("PGHOST"));
|
||||
clazz.getMethod("setDatabaseName", String.class).invoke(pdsource, properties.getProperty("PGDBNAME"));
|
||||
clazz.getMethod("setPortNumber", int.class).invoke(pdsource, Integer.parseInt(properties.getProperty("PGPORT", "5432")));
|
||||
} else {
|
||||
Method seturlm;
|
||||
try {
|
||||
seturlm = clazz.getMethod("setUrl", String.class);
|
||||
} catch (Exception e) {
|
||||
seturlm = clazz.getMethod("setURL", String.class);
|
||||
}
|
||||
seturlm.invoke(pdsource, url);
|
||||
}
|
||||
clazz.getMethod("setUser", String.class).invoke(pdsource, user);
|
||||
clazz.getMethod("setPassword", String.class).invoke(pdsource, password);
|
||||
return (ConnectionPoolDataSource) pdsource;
|
||||
}
|
||||
|
||||
final boolean isMysql() {
|
||||
return source != null && source.getClass().getName().contains(".mysql.");
|
||||
}
|
||||
@@ -109,10 +200,10 @@ public class JDBCPoolSource {
|
||||
return source != null && source.getClass().getName().contains(".sqlserver.");
|
||||
}
|
||||
|
||||
final boolean isPostgresql () {
|
||||
final boolean isPostgresql() {
|
||||
return source != null && source.getClass().getName().contains(".postgresql.");
|
||||
}
|
||||
|
||||
|
||||
private void watch() throws IOException {
|
||||
if (dataSource.conf == null || dataSource.name == null) return;
|
||||
final String file = dataSource.conf.getFile();
|
||||
|
||||
@@ -8,6 +8,8 @@ package org.redkale.source;
|
||||
import java.util.function.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* 取值范围,包含两边的值
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -21,6 +23,34 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
|
||||
|
||||
public E getMax();
|
||||
|
||||
public static ByteRange create(byte min, byte max) {
|
||||
return new ByteRange(min, max);
|
||||
}
|
||||
|
||||
public static ShortRange create(short min, short max) {
|
||||
return new ShortRange(min, max);
|
||||
}
|
||||
|
||||
public static IntRange create(int min, int max) {
|
||||
return new IntRange(min, max);
|
||||
}
|
||||
|
||||
public static LongRange create(long min, long max) {
|
||||
return new LongRange(min, max);
|
||||
}
|
||||
|
||||
public static FloatRange create(float min, float max) {
|
||||
return new FloatRange(min, max);
|
||||
}
|
||||
|
||||
public static DoubleRange create(double min, double max) {
|
||||
return new DoubleRange(min, max);
|
||||
}
|
||||
|
||||
public static StringRange create(String min, String max) {
|
||||
return new StringRange(min, max);
|
||||
}
|
||||
|
||||
public static final class ByteRange implements Range<Byte> {
|
||||
|
||||
private Byte min = Byte.MIN_VALUE;
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.*;
|
||||
import java.util.function.*;
|
||||
|
||||
/**
|
||||
* VirtualEntity表示虚拟的数据实体类, 通常Entity都会映射到数据库中的某个表,而标记为VirtualEntity的Entity类只存在DataCache中
|
||||
* VirtualEntity表示虚拟的数据实体类, 通常Entity都会映射到数据库中的某个表,而标记为@VirtualEntity的Entity类只存在EntityCache中
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -24,12 +24,24 @@ import java.util.function.*;
|
||||
@Retention(RUNTIME)
|
||||
public @interface VirtualEntity {
|
||||
|
||||
//DataSource是否直接返回对象的真实引用, 而不是copy一份
|
||||
/**
|
||||
* DataSource是否直接返回对象的真实引用, 而不是copy一份
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean direct() default false;
|
||||
|
||||
//初始化时数据的加载器
|
||||
/**
|
||||
* 初始化时数据的加载器
|
||||
*
|
||||
* @return Class
|
||||
*/
|
||||
Class<? extends BiFunction<DataSource, Class, List>> loader() default DefaultFunctionLoader.class;
|
||||
|
||||
/**
|
||||
* 默认全量加载器
|
||||
*
|
||||
*/
|
||||
public static class DefaultFunctionLoader implements BiFunction<DataSource, Class, List> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,11 +39,13 @@ public abstract class AnyValue {
|
||||
*/
|
||||
public static final BiPredicate<String, String> EQUALSIGNORE = (name1, name2) -> name1.equalsIgnoreCase(name2);
|
||||
|
||||
private final BiPredicate<String, String> predicate;
|
||||
private boolean ignoreCase;
|
||||
|
||||
private Entry<String>[] stringValues = new Entry[0];
|
||||
private BiPredicate<String, String> predicate;
|
||||
|
||||
private Entry<AnyValue>[] entityValues = new Entry[0];
|
||||
private Entry<String>[] stringEntrys = new Entry[0];
|
||||
|
||||
private Entry<DefaultAnyValue>[] anyEntrys = new Entry[0];
|
||||
|
||||
/**
|
||||
* 创建空的DefaultAnyValue对象
|
||||
@@ -96,27 +98,19 @@ public abstract class AnyValue {
|
||||
* @param ignoreCase name是否不区分大小写
|
||||
*/
|
||||
public DefaultAnyValue(boolean ignoreCase) {
|
||||
this.ignoreCase = ignoreCase;
|
||||
this.predicate = ignoreCase ? EQUALSIGNORE : EQUALS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建DefaultAnyValue对象
|
||||
*
|
||||
* @param predicate name比较策略
|
||||
*/
|
||||
public DefaultAnyValue(BiPredicate<String, String> predicate) {
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建共享此内容的DefaultAnyValue对象
|
||||
*
|
||||
* @return DefaultAnyValue对象
|
||||
*/
|
||||
public DefaultAnyValue duplicate() {
|
||||
DefaultAnyValue rs = new DefaultAnyValue(this.predicate);
|
||||
rs.stringValues = this.stringValues;
|
||||
rs.entityValues = this.entityValues;
|
||||
DefaultAnyValue rs = new DefaultAnyValue(this.ignoreCase);
|
||||
rs.stringEntrys = this.stringEntrys;
|
||||
rs.anyEntrys = this.anyEntrys;
|
||||
return rs;
|
||||
}
|
||||
|
||||
@@ -124,13 +118,13 @@ public abstract class AnyValue {
|
||||
if (av == null) return this;
|
||||
if (av instanceof DefaultAnyValue) {
|
||||
final DefaultAnyValue adv = (DefaultAnyValue) av;
|
||||
if (adv.stringValues != null) {
|
||||
for (Entry<String> en : adv.stringValues) {
|
||||
if (adv.stringEntrys != null) {
|
||||
for (Entry<String> en : adv.stringEntrys) {
|
||||
this.addValue(en.name, en.value);
|
||||
}
|
||||
}
|
||||
if (adv.entityValues != null) {
|
||||
for (Entry<AnyValue> en : adv.entityValues) {
|
||||
if (adv.anyEntrys != null) {
|
||||
for (Entry<DefaultAnyValue> en : adv.anyEntrys) {
|
||||
this.addValue(en.name, en.value);
|
||||
}
|
||||
}
|
||||
@@ -155,13 +149,13 @@ public abstract class AnyValue {
|
||||
if (av == null) return this;
|
||||
if (av instanceof DefaultAnyValue) {
|
||||
final DefaultAnyValue adv = (DefaultAnyValue) av;
|
||||
if (adv.stringValues != null) {
|
||||
for (Entry<String> en : adv.stringValues) {
|
||||
if (adv.stringEntrys != null) {
|
||||
for (Entry<String> en : adv.stringEntrys) {
|
||||
this.setValue(en.name, en.value);
|
||||
}
|
||||
}
|
||||
if (adv.entityValues != null) {
|
||||
for (Entry<AnyValue> en : adv.entityValues) {
|
||||
if (adv.anyEntrys != null) {
|
||||
for (Entry<DefaultAnyValue> en : adv.anyEntrys) {
|
||||
this.setValue(en.name, en.value);
|
||||
}
|
||||
}
|
||||
@@ -184,21 +178,41 @@ public abstract class AnyValue {
|
||||
|
||||
@Override
|
||||
public Entry<String>[] getStringEntrys() {
|
||||
return stringValues;
|
||||
return stringEntrys;
|
||||
}
|
||||
|
||||
public void setStringEntrys(Entry<String>[] stringEntrys) {
|
||||
this.stringEntrys = stringEntrys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<AnyValue>[] getAnyEntrys() {
|
||||
return entityValues;
|
||||
return (Entry<AnyValue>[]) (Entry[]) anyEntrys;
|
||||
}
|
||||
|
||||
public void setAnyEntrys(Entry<DefaultAnyValue>[] anyEntrys) {
|
||||
this.anyEntrys = anyEntrys;
|
||||
}
|
||||
|
||||
public boolean isIgnoreCase() {
|
||||
return ignoreCase;
|
||||
}
|
||||
|
||||
public void setIgnoreCase(boolean ignoreCase) {
|
||||
this.ignoreCase = ignoreCase;
|
||||
if (this.predicate == null) {
|
||||
this.predicate = ignoreCase ? EQUALSIGNORE : EQUALS;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@java.beans.Transient
|
||||
public String[] getNames() {
|
||||
Set<String> set = new LinkedHashSet<>();
|
||||
for (Entry en : this.stringValues) {
|
||||
for (Entry en : this.stringEntrys) {
|
||||
set.add(en.name);
|
||||
}
|
||||
for (Entry en : this.entityValues) {
|
||||
for (Entry en : this.anyEntrys) {
|
||||
set.add(en.name);
|
||||
}
|
||||
return set.toArray(new String[set.size()]);
|
||||
@@ -206,12 +220,12 @@ public abstract class AnyValue {
|
||||
|
||||
@Override
|
||||
public String[] getValues(String... names) {
|
||||
return Entry.getValues(this.predicate, String.class, this.stringValues, names);
|
||||
return Entry.getValues(this.predicate, String.class, this.stringEntrys, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyValue[] getAnyValues(String... names) {
|
||||
return Entry.getValues(this.predicate, AnyValue.class, this.entityValues, names);
|
||||
return Entry.getValues(this.predicate, DefaultAnyValue.class, this.anyEntrys, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -220,8 +234,8 @@ public abstract class AnyValue {
|
||||
}
|
||||
|
||||
public DefaultAnyValue clear() {
|
||||
this.stringValues = new Entry[0];
|
||||
this.entityValues = new Entry[0];
|
||||
this.stringEntrys = new Entry[0];
|
||||
this.anyEntrys = new Entry[0];
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -230,7 +244,7 @@ public abstract class AnyValue {
|
||||
if (getValue(name) == null) {
|
||||
this.addValue(name, value);
|
||||
} else {
|
||||
for (Entry<String> en : this.stringValues) {
|
||||
for (Entry<String> en : this.stringEntrys) {
|
||||
if (predicate.test(en.name, name)) {
|
||||
en.value = value;
|
||||
return this;
|
||||
@@ -245,9 +259,9 @@ public abstract class AnyValue {
|
||||
if (getValue(name) == null) {
|
||||
this.addValue(name, value);
|
||||
} else {
|
||||
for (Entry<AnyValue> en : this.entityValues) {
|
||||
for (Entry<DefaultAnyValue> en : this.anyEntrys) {
|
||||
if (predicate.test(en.name, name)) {
|
||||
en.value = value;
|
||||
en.value = (DefaultAnyValue) value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -264,28 +278,19 @@ public abstract class AnyValue {
|
||||
}
|
||||
|
||||
public DefaultAnyValue addValue(String name, String value) {
|
||||
if (name == null) return this;
|
||||
int len = this.stringValues.length;
|
||||
Entry[] news = new Entry[len + 1];
|
||||
System.arraycopy(this.stringValues, 0, news, 0, len);
|
||||
news[len] = new Entry(name, value);
|
||||
this.stringValues = news;
|
||||
this.stringEntrys = Utility.append(this.stringEntrys, new Entry(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultAnyValue addValue(String name, AnyValue value) {
|
||||
if (name == null || value == null) return this;
|
||||
int len = this.entityValues.length;
|
||||
Entry[] news = new Entry[len + 1];
|
||||
System.arraycopy(this.entityValues, 0, news, 0, len);
|
||||
news[len] = new Entry(name, value);
|
||||
this.entityValues = news;
|
||||
this.anyEntrys = Utility.append(this.anyEntrys, new Entry(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyValue getAnyValue(String name) {
|
||||
for (Entry<AnyValue> en : this.entityValues) {
|
||||
for (Entry<DefaultAnyValue> en : this.anyEntrys) {
|
||||
if (predicate.test(en.name, name)) {
|
||||
return en.value;
|
||||
}
|
||||
@@ -295,7 +300,7 @@ public abstract class AnyValue {
|
||||
|
||||
@Override
|
||||
public String getValue(String name) {
|
||||
for (Entry<String> en : this.stringValues) {
|
||||
for (Entry<String> en : this.stringEntrys) {
|
||||
if (predicate.test(en.name, name)) {
|
||||
return en.value;
|
||||
}
|
||||
@@ -305,12 +310,12 @@ public abstract class AnyValue {
|
||||
|
||||
@Override
|
||||
public String[] getValues(String name) {
|
||||
return Entry.getValues(this.predicate, String.class, this.stringValues, name);
|
||||
return Entry.getValues(this.predicate, String.class, this.stringEntrys, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyValue[] getAnyValues(String name) {
|
||||
return Entry.getValues(this.predicate, AnyValue.class, this.entityValues, name);
|
||||
return Entry.getValues(this.predicate, DefaultAnyValue.class, this.anyEntrys, name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -321,11 +326,16 @@ public abstract class AnyValue {
|
||||
|
||||
T value;
|
||||
|
||||
@java.beans.ConstructorProperties({"name", "value"})
|
||||
public Entry(String name0, T value0) {
|
||||
this.name = name0;
|
||||
this.value = value0;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
@@ -377,9 +387,9 @@ public abstract class AnyValue {
|
||||
return new DefaultAnyValue();
|
||||
}
|
||||
|
||||
public String toString(int len) {
|
||||
if (len < 0) len = 0;
|
||||
char[] chars = new char[len];
|
||||
public String toString(int indent) { //indent: 缩进长度
|
||||
if (indent < 0) indent = 0;
|
||||
char[] chars = new char[indent];
|
||||
Arrays.fill(chars, ' ');
|
||||
final String space = new String(chars);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -388,7 +398,7 @@ public abstract class AnyValue {
|
||||
sb.append(space).append(" '").append(en.name).append("': '").append(en.value).append("',\r\n");
|
||||
}
|
||||
for (Entry<AnyValue> en : getAnyEntrys()) {
|
||||
sb.append(space).append(" '").append(en.name).append("': '").append(en.value.toString(len + 4)).append("',\r\n");
|
||||
sb.append(space).append(" '").append(en.name).append("': '").append(en.value.toString(indent + 4)).append("',\r\n");
|
||||
}
|
||||
sb.append(space).append('}');
|
||||
return sb.toString();
|
||||
|
||||
@@ -63,8 +63,8 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* 映射Field时,field必须满足以下条件之一: <br>
|
||||
* 1、field属性是public且非final <br>
|
||||
* 2、至少存在对应的getter、setter方法中的一个 <br>
|
||||
* 1、field属性是public且非final <br>
|
||||
* 2、至少存在对应的getter、setter方法中的一个 <br>
|
||||
* 当不存在getter方法时,get操作固定返回null <br>
|
||||
* 当不存在setter方法时,set操作为空方法 <br>
|
||||
* <p>
|
||||
@@ -296,7 +296,6 @@ public interface Attribute<T, F> {
|
||||
for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) {
|
||||
if (java.lang.reflect.Modifier.isStatic(method.getModifiers())) continue;
|
||||
if (!method.getName().startsWith("set")) continue;
|
||||
if (method.getReturnType() != void.class) continue;
|
||||
if (method.getParameterCount() != 1) continue;
|
||||
list.add(create(clazz, null, method));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user