80 Commits
1.4.0 ... 1.5.1

Author SHA1 Message Date
Redkale
d891c6c8dc 2017-03-08 14:25:03 +08:00
Redkale
0a4a88ed5a 2017-03-08 14:03:05 +08:00
Redkale
3c02219da0 2017-03-08 09:57:08 +08:00
Redkale
65efc3372e 2017-03-07 17:44:50 +08:00
Redkale
3acea66788 2017-03-07 15:47:39 +08:00
Redkale
b448514e40 2017-03-07 11:58:27 +08:00
Redkale
024147344b 2017-03-07 10:09:26 +08:00
Redkale
52a34d3871 2017-03-07 09:42:03 +08:00
Redkale
1ada26e4dd 2017-03-07 09:39:16 +08:00
Redkale
94d1b61f81 2017-03-06 15:18:50 +08:00
Redkale
29299edb90 2017-03-06 14:45:35 +08:00
Redkale
a2e2c5e178 2017-03-06 12:02:00 +08:00
Redkale
8ae39df2e8 2017-03-06 09:29:25 +08:00
Redkale
508b269a82 2017-03-03 18:40:45 +08:00
Redkale
a8627b6105 2017-03-03 18:21:09 +08:00
Redkale
8fee6b2c68 2017-03-03 18:05:11 +08:00
Redkale
dd58571ffd 2017-03-03 17:50:50 +08:00
Redkale
8c25683cc5 增加javadoc注释 2017-03-03 14:49:32 +08:00
Redkale
a96f003b8c 2017-03-03 11:45:55 +08:00
Redkale
ff01443246 2017-03-02 19:59:42 +08:00
Redkale
e915a253f8 2017-03-02 16:03:29 +08:00
Redkale
b463389733 2017-03-02 15:43:34 +08:00
Redkale
41c97b92c7 删除DataSource.updateColumns方法,使用 DataSource.updateColumn代替 2017-03-02 14:21:47 +08:00
Redkale
1142f81e9c 删掉HttpResponse.finishJsResult方法 2017-03-02 14:13:29 +08:00
Redkale
5bc9f77b7b 修复HttpRequest.getBody由于URLDecode导致的BUG 2017-03-02 12:35:14 +08:00
Redkale
c5d0582807 修复HttpRequest.getBody由于URLDecode导致内容多余的BUG 2017-03-02 11:52:22 +08:00
Redkale
525e65d152 增加javadoc注释 2017-03-02 10:41:15 +08:00
Redkale
d948c7af47 增加javadoc注释 2017-03-02 10:02:45 +08:00
Redkale
11a29b4ed6 2017-03-01 20:59:00 +08:00
Redkale
e31c4a3041 DataSource增加支持Blob(byte[])类型字段功能 2017-02-28 13:37:38 +08:00
Redkale
2928d5fc93 2017-02-28 10:01:13 +08:00
Redkale
12fc6f7f10 2017-02-27 17:30:03 +08:00
Redkale
4bd8c207b4 增加javadoc注释 2017-02-27 17:15:14 +08:00
Redkale
be030a3640 增加javadoc注释 2017-02-27 17:11:23 +08:00
Redkale
ebaa250f7b 增加javadoc注释 2017-02-27 17:07:00 +08:00
Redkale
826a2d7ee6 增加javadoc注释 2017-02-27 15:06:31 +08:00
Redkale
e476cf8176 2017-02-27 13:54:23 +08:00
Redkale
03115694f9 增加javadoc注释 2017-02-27 12:01:39 +08:00
Redkale
26ffb04834 增加javadoc注释 2017-02-27 11:56:18 +08:00
Redkale
2979fcc33d 增加javadoc注释 2017-02-27 11:19:40 +08:00
Redkale
9a29a11e22 2017-02-25 12:53:54 +08:00
Redkale
d73a27be71 2017-02-25 12:44:24 +08:00
Redkale
d77f424504 2017-02-25 12:27:19 +08:00
Redkale
3142ad6041 2017-02-24 15:46:00 +08:00
Redkale
6044f014c7 2017-02-24 09:04:16 +08:00
Redkale
d1cfdfa14f 2017-02-23 20:33:03 +08:00
Redkale
8f9bfc3f28 兼容javax.persistence.jdbc.driver为空的情况 2017-02-23 20:16:58 +08:00
Redkale
9ae847d392 FilterFuncColumn支持多字段名 2017-02-23 19:26:13 +08:00
Redkale
178226b730 DataSource增加getNumberMap方法,用于查询多个字段的统计值 2017-02-23 19:06:40 +08:00
Redkale
801e45abce 2017-02-23 17:45:01 +08:00
Redkale
cd54a7040a 2017-02-23 15:40:17 +08:00
Redkale
3bd880b061 2017-02-23 15:29:04 +08:00
Redkale
bf355cce28 2017-02-23 15:15:43 +08:00
Redkale
58d08c5787 @Cacheable增加定时更新缓存功能 2017-02-22 20:57:13 +08:00
Redkale
a778af73d8 修复HttpRequest.getRemoteAddress()方法中channel已关闭会抛空指针异常的BUG 2017-02-22 16:06:40 +08:00
Redkale
7ca95c3549 2017-02-22 09:10:40 +08:00
Redkale
54933ac3ac Utility增加 删除掉字符串数组中包含指定的字符串 方法 2017-02-21 20:27:19 +08:00
Redkale
255048bf5b Response增加output字段,便于RecycleListener获取输出结果 2017-02-21 16:35:08 +08:00
Redkale
206fa19f3e 2017-02-17 14:12:33 +08:00
Redkale
f0e9047f8c 2017-02-17 14:02:24 +08:00
Redkale
698966d551 删掉 DistributeGenerator 功能 2017-02-16 11:29:49 +08:00
Redkale
fc6b5cb458 2017-02-15 20:56:28 +08:00
Redkale
9eee3bfa58 增加 java.util.logging.FileHandler.unusual 特性 2017-02-15 20:46:30 +08:00
Redkale
0adc0845fd 2017-02-15 20:07:30 +08:00
Redkale
92f98eff5f 2017-02-15 09:57:10 +08:00
Redkale
d97f8acf23 2017-02-10 20:08:47 +08:00
Redkale
47e14bf2ec 2017-02-10 17:38:51 +08:00
Redkale
8eed4083bc 2017-02-10 16:55:39 +08:00
Redkale
9ef7641cd1 2017-02-09 16:44:31 +08:00
Redkale
bafb6065c8 2017-02-09 14:53:16 +08:00
Redkale
8b2460b8ab 2017-02-09 13:38:24 +08:00
Redkale
827172e743 2017-02-09 11:52:04 +08:00
Redkale
fa9bd30de5 2017-02-04 15:01:39 +08:00
Redkale
78e66ff74b 2017-02-04 14:59:10 +08:00
Redkale
7e8d1c3567 2017-02-04 09:04:35 +08:00
Redkale
c3a7603674 2017-02-03 17:25:56 +08:00
Redkale
4b8cfbba00 2017-02-03 16:52:57 +08:00
Redkale
30771e5366 2017-02-03 16:43:12 +08:00
Redkale
313c7f4ba1 2017-02-03 16:37:08 +08:00
Redkale
ece4215a8a 2017-02-03 15:03:11 +08:00
117 changed files with 3285 additions and 1273 deletions

140
pom.xml Normal file
View 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>

View File

@@ -42,8 +42,8 @@
一个组包含多个NODE 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内 一个组包含多个NODE 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内
一个group节点对应一个 Transport 对象。 一个group节点对应一个 Transport 对象。
name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。 name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
protocol只能是UDP TCP 默认TCP protocol范围:UDP TCP 默认TCP
kind: 与SNCP服务连接时的数据传输类型可选值有:rest(不区分大小写);值为空或空字符串表示按SNCP协议传输; 为rest表示按REST传输。默认值为空 subprotocol: 子协议,预留字段。默认值为空
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息 就必须有group节点信息。 注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息 就必须有group节点信息。
--> -->
<group name="" protocol="TCP"> <group name="" protocol="TCP">

View File

@@ -16,6 +16,8 @@ java.util.logging.FileHandler.limit = 10485760
java.util.logging.FileHandler.count = 100 java.util.logging.FileHandler.count = 100
java.util.logging.FileHandler.encoding = UTF-8 java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%u.log 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.FileHandler.append = true
#java.util.logging.ConsoleHandler.level = FINE #java.util.logging.ConsoleHandler.level = FINE

View File

@@ -16,6 +16,7 @@
oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource
com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
因此 com.mysql.jdbc.Driver 会被自动转换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource 因此 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.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.user" value="root"/>

View File

@@ -1,4 +1,4 @@
/******************************************************************************* /** *****************************************************************************
* Copyright (c) 2008 - 2013 Oracle Corporation. All rights reserved. * Copyright (c) 2008 - 2013 Oracle Corporation. All rights reserved.
* *
* This program and the accompanying materials are made available under the * 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.1
* Linda DeMichiel - Java Persistence 2.0 * Linda DeMichiel - Java Persistence 2.0
* *
******************************************************************************/ ***************************************************************************** */
package javax.persistence; package javax.persistence;
import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.TYPE;
@@ -28,18 +28,27 @@ import java.lang.annotation.Target;
* subclasses; it can be overridden by specifying * subclasses; it can be overridden by specifying
* <code>Cacheable</code> on a subclass. * <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. * not be cached by the provider.
* *
* @since Java Persistence 2.0 * @since Java Persistence 2.0
*/ */
@Target( { TYPE }) @Target({TYPE})
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface Cacheable { public @interface Cacheable {
/** /**
* (Optional) Whether or not the entity should be cached. * (Optional) Whether or not the entity should be cached.
*
* @return boolean * @return boolean
*/ */
boolean value() default true; boolean value() default true;
/**
* (Optional) 定时自动更新缓存的周期秒数为0表示不做定时更新 大于0表示每经过interval秒后会自动从数据库中拉取数据更新Cache
*
* @return int
*/
int interval() default 0;
} }

View File

@@ -15,15 +15,17 @@ import org.redkale.source.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* API接口文档生成类作用生成Application实例中所有HttpServer的可用HttpServlet的API接口方法 <br>
* 继承 HttpBaseServlet 是为了获取 WebAction 信息 * 继承 HttpBaseServlet 是为了获取 WebAction 信息
* *
* <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
*/ */
public class ApiDocs extends HttpBaseServlet { public class ApiDocs extends HttpBaseServlet {
private final Application app; private final Application app; //Application全局对象
public ApiDocs(Application app) { public ApiDocs(Application app) {
this.app = app; this.app = app;

View File

@@ -30,9 +30,17 @@ import org.redkale.watch.WatchFactory;
import org.w3c.dom.*; import org.w3c.dom.*;
/** /**
* 编译时需要加入: -XDignore.symbol.file=true *
* 进程启动类,全局对象。 <br>
* <pre>
* 程序启动执行步骤:
* 1、读取application.xml
* 2、进行classpath扫描动态加载Service与Servlet
* 3、优先加载所有SNCP协议的服务再加载其他协议服务
* 4、最后进行Service、Servlet与其他资源之间的依赖注入
* </pre>
* <p> * <p>
* 进程启动类程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet 优先加载所有SNCP协议的服务 再加载其他协议服务, 最后进行Service、Servlet与其他资源之间的依赖注入。 * 编译时需要加入: -XDignore.symbol.file=true
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
@@ -40,65 +48,99 @@ import org.w3c.dom.*;
*/ */
public final class Application { public final class Application {
//当前进程启动的时间, 类型: long /**
* 当前进程启动的时间, 类型: long
*/
public static final String RESNAME_APP_TIME = "APP_TIME"; public static final String RESNAME_APP_TIME = "APP_TIME";
//当前进程的根目录, 类型String、File、Path /**
* 当前进程的根目录, 类型String、File、Path
*/
public static final String RESNAME_APP_HOME = "APP_HOME"; 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"; public static final String RESNAME_APP_GRES = "APP_GRES";
//当前进程节点的name 类型String /**
* 当前进程节点的name 类型String
*/
public static final String RESNAME_APP_NODE = "APP_NODE"; public static final String RESNAME_APP_NODE = "APP_NODE";
//当前进程节点的IP地址 类型InetAddress、String /**
* 当前进程节点的IP地址 类型InetAddress、String
*/
public static final String RESNAME_APP_ADDR = "APP_ADDR"; 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"; public static final String RESNAME_SERVER_ADDR = "SERVER_ADDR";
//当前SNCP Server所属的组 类型: String /**
* 当前SNCP Server所属的组 类型: String
*/
public static final String RESNAME_SERVER_GROUP = "SERVER_GROUP"; 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; public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT;
//每个地址对应的Group名
final Map<InetSocketAddress, String> globalNodes = new HashMap<>(); final Map<InetSocketAddress, String> globalNodes = new HashMap<>();
//协议地址的Group集合
final Map<String, GroupInfo> globalGroups = new HashMap<>(); final Map<String, GroupInfo> globalGroups = new HashMap<>();
//本地IP地址
final InetAddress localAddress; final InetAddress localAddress;
//CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>(); final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
//DataSource 资源
final List<DataSource> dataSources = new CopyOnWriteArrayList<>(); final List<DataSource> dataSources = new CopyOnWriteArrayList<>();
//NodeServer 资源
final List<NodeServer> servers = new CopyOnWriteArrayList<>(); final List<NodeServer> servers = new CopyOnWriteArrayList<>();
//传输端的ByteBuffer对象池
final ObjectPool<ByteBuffer> transportBufferPool; final ObjectPool<ByteBuffer> transportBufferPool;
//传输端的线程池
final ExecutorService transportExecutor; final ExecutorService transportExecutor;
//传输端的ChannelGroup
final AsynchronousChannelGroup transportChannelGroup; final AsynchronousChannelGroup transportChannelGroup;
//全局根ResourceFactory
final ResourceFactory resourceFactory = ResourceFactory.root(); final ResourceFactory resourceFactory = ResourceFactory.root();
//临时计数器
CountDownLatch servicecdl; //会出现两次赋值 CountDownLatch servicecdl; //会出现两次赋值
//-------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------
//是否用于main方法运行
private final boolean singletonrun; private final boolean singletonrun;
//根WatchFactory
private final WatchFactory watchFactory = WatchFactory.root(); private final WatchFactory watchFactory = WatchFactory.root();
//进程根目录
private final File home; private final File home;
//日志
private final Logger logger; private final Logger logger;
//服务配置项
private final AnyValue config; private final AnyValue config;
//服务启动时间
private final long startTime = System.currentTimeMillis(); private final long startTime = System.currentTimeMillis();
//Server启动的计数器用于确保所有Server都启动完后再进行下一步处理
private final CountDownLatch serversLatch; private final CountDownLatch serversLatch;
private Application(final AnyValue config) { private Application(final AnyValue config) {
@@ -164,7 +206,8 @@ public final class Application {
Properties prop = new Properties(); Properties prop = new Properties();
final String handlers = properties.getProperty("handlers"); final String handlers = properties.getProperty("handlers");
if (handlers != null && handlers.contains("java.util.logging.FileHandler")) { 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()) { if (!prop.isEmpty()) {
String prefix = fileHandlerClass + "."; String prefix = fileHandlerClass + ".";
@@ -189,7 +232,7 @@ public final class Application {
} }
this.logger = Logger.getLogger(this.getClass().getSimpleName()); this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1); this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
logger.log(Level.INFO, "------------------------------- Redkale ------------------------------"); logger.log(Level.INFO, "------------------------------- Redkale -------------------------------");
//------------------配置 <transport> 节点 ------------------ //------------------配置 <transport> 节点 ------------------
ObjectPool<ByteBuffer> transportPool = null; ObjectPool<ByteBuffer> transportPool = null;
ExecutorService transportExec = null; ExecutorService transportExec = null;
@@ -333,7 +376,7 @@ public final class Application {
} }
GroupInfo ginfo = globalGroups.get(group); GroupInfo ginfo = globalGroups.get(group);
if (ginfo == null) { 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); globalGroups.put(group, ginfo);
} }
for (AnyValue node : conf.getAnyValues("node")) { for (AnyValue node : conf.getAnyValues("node")) {
@@ -423,13 +466,27 @@ public final class Application {
channel.write(buffer); channel.write(buffer);
buffer.clear(); buffer.clear();
channel.configureBlocking(false); channel.configureBlocking(false);
channel.read(buffer); try {
buffer.flip(); channel.read(buffer);
byte[] bytes = new byte[buffer.remaining()]; buffer.flip();
buffer.get(bytes); byte[] bytes = new byte[buffer.remaining()];
channel.close(); buffer.get(bytes);
logger.info(new String(bytes)); channel.close();
Thread.sleep(500); 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 { public void start() throws Exception {
@@ -463,7 +520,7 @@ public final class Application {
for (final AnyValue serconf : serconfs) { for (final AnyValue serconf : serconfs) {
Thread thread = new Thread() { 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"); setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
this.setDaemon(true); this.setDaemon(true);
} }

View File

@@ -28,9 +28,9 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class ClassFilter<T> { 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<>(); //符合条件的结果 private final Set<FilterEntry<T>> entrys = new HashSet<>(); //符合条件的结果

View File

@@ -9,6 +9,7 @@ import java.net.InetSocketAddress;
import java.util.*; import java.util.*;
/** /**
* 协议地址组合对象, 对应application.xml 中 resources-&#62;group 节点信息
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -17,21 +18,21 @@ import java.util.*;
*/ */
public class GroupInfo { 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-&#62;group-&#62;node节点信息
public GroupInfo() { 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.name = name;
this.protocol = protocol; this.protocol = protocol;
this.kind = kind; this.subprotocol = subprotocol;
this.addrs = addrs; this.addrs = addrs;
} }
@@ -51,12 +52,12 @@ public class GroupInfo {
this.protocol = protocol; this.protocol = protocol;
} }
public String getKind() { public String getSubprotocol() {
return kind; return subprotocol;
} }
public void setKind(String kind) { public void setSubprotocol(String subprotocol) {
this.kind = kind; this.subprotocol = subprotocol;
} }
public Set<InetSocketAddress> getAddrs() { public Set<InetSocketAddress> getAddrs() {

View File

@@ -70,12 +70,12 @@ public class LogFileHandler extends Handler {
throwable = sw.toString(); throwable = sw.toString();
} }
return String.format(format, return String.format(format,
System.currentTimeMillis(), System.currentTimeMillis(),
source, source,
record.getLoggerName(), record.getLoggerName(),
record.getLevel().getName(), record.getLevel().getName(),
message, message,
throwable); throwable);
} }
} }
@@ -84,9 +84,13 @@ public class LogFileHandler extends Handler {
private String pattern; private String pattern;
private String unusual; //不为null表示将 WARNING、SEVERE 级别的日志写入单独的文件中
private int limit; //文件大小限制 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; //文件限制 private int count = 1; //文件限制
@@ -94,11 +98,17 @@ public class LogFileHandler extends Handler {
private boolean append; 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 File logfile;
private OutputStream stream; private File logunusualfile;
private OutputStream logstream;
private OutputStream logunusualstream;
public LogFileHandler() { public LogFileHandler() {
updateTomorrow(); updateTomorrow();
@@ -114,7 +124,7 @@ public class LogFileHandler extends Handler {
cal.set(Calendar.MILLISECOND, 0); cal.set(Calendar.MILLISECOND, 0);
cal.add(Calendar.DAY_OF_YEAR, 1); cal.add(Calendar.DAY_OF_YEAR, 1);
long t = cal.getTimeInMillis(); long t = cal.getTimeInMillis();
if (this.tomorrow != t) index.set(0); if (this.tomorrow != t) logindex.set(0);
this.tomorrow = t; this.tomorrow = t;
} }
@@ -131,35 +141,59 @@ public class LogFileHandler extends Handler {
while (true) { while (true) {
try { try {
LogRecord record = records.take(); LogRecord record = records.take();
final boolean bigger = (limit > 0 && limit <= length.get()); final boolean bigger = (limit > 0 && limit <= loglength.get());
if (bigger || tomorrow <= record.getMillis()) { final boolean changeday = tomorrow <= record.getMillis();
if (bigger || changeday) {
updateTomorrow(); updateTomorrow();
if (stream != null) { if (logstream != null) {
stream.close(); logstream.close();
if (bigger) { 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); 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); 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); Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
} }
stream = null; logstream = null;
} }
} }
if (stream == null) { if (unusual != null && changeday && logunusualstream != null) {
index.incrementAndGet(); 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(); 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 = 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(); logfile.getParentFile().mkdirs();
length.set(logfile.length()); loglength.set(logfile.length());
stream = new FileOutputStream(logfile, append); 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 message = getFormatter().format(record);
String encoding = getEncoding(); String encoding = getEncoding();
byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding); byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding);
stream.write(bytes); logstream.write(bytes);
length.addAndGet(bytes.length); 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) { } catch (Exception e) {
ErrorManager err = getErrorManager(); ErrorManager err = getErrorManager();
if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE); if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE);
@@ -177,30 +211,39 @@ public class LogFileHandler extends Handler {
private void configure() { private void configure() {
LogManager manager = LogManager.getLogManager(); LogManager manager = LogManager.getLogManager();
String cname = LogFileHandler.class.getName(); String cname = LogFileHandler.class.getName();
pattern = manager.getProperty(cname + ".pattern"); this.pattern = manager.getProperty(cname + ".pattern");
if (pattern == null) { if (this.pattern == null) {
pattern = "logs-%m/" + getPrefix() + "log-%d.log"; this.pattern = "logs-%m/" + getPrefix() + "log-%d.log";
} else { } else {
int pos = pattern.lastIndexOf('/'); int pos = this.pattern.lastIndexOf('/');
if (pos > 0) { 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 { } 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"); String limitstr = manager.getProperty(cname + ".limit");
try { try {
if (limitstr != null) limit = Math.abs(Integer.decode(limitstr)); if (limitstr != null) this.limit = Math.abs(Integer.decode(limitstr));
} catch (Exception e) { } catch (Exception e) {
} }
String countstr = manager.getProperty(cname + ".count"); String countstr = manager.getProperty(cname + ".count");
try { 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) { } catch (Exception e) {
} }
String appendstr = manager.getProperty(cname + ".append"); String appendstr = manager.getProperty(cname + ".append");
try { 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) { } catch (Exception e) {
} }
String levelstr = manager.getProperty(cname + ".level"); String levelstr = manager.getProperty(cname + ".level");
@@ -256,7 +299,7 @@ public class LogFileHandler extends Handler {
@Override @Override
public void flush() { public void flush() {
try { try {
if (stream != null) stream.flush(); if (logstream != null) logstream.flush();
} catch (Exception e) { } catch (Exception e) {
ErrorManager err = getErrorManager(); ErrorManager err = getErrorManager();
if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE); if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE);
@@ -266,7 +309,7 @@ public class LogFileHandler extends Handler {
@Override @Override
public void close() throws SecurityException { public void close() throws SecurityException {
try { try {
if (stream != null) stream.close(); if (logstream != null) logstream.close();
} catch (Exception e) { } catch (Exception e) {
ErrorManager err = getErrorManager(); ErrorManager err = getErrorManager();
if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE); if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE);

View File

@@ -29,7 +29,7 @@ import org.redkale.util.*;
@NodeProtocol({"HTTP"}) @NodeProtocol({"HTTP"})
public class NodeHttpServer extends NodeServer { public class NodeHttpServer extends NodeServer {
protected final boolean rest; protected final boolean rest; //是否加载REST服务 为true加载rest节点信息并将所有可REST化的Service生成RestHttpServlet
protected final HttpServer httpServer; protected final HttpServer httpServer;
@@ -131,7 +131,7 @@ public class NodeHttpServer extends NodeServer {
if (as.getKey().length() > max) max = as.getKey().length(); if (as.getKey().length() > max) max = as.getKey().length();
} }
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { 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++) { for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' '); sb.append(' ');
} }
@@ -202,7 +202,7 @@ public class NodeHttpServer extends NodeServer {
if (as.getKey().length() > max) max = as.getKey().length(); if (as.getKey().length() > max) max = as.getKey().length();
} }
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { 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++) { for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' '); sb.append(' ');
} }

View File

@@ -9,6 +9,7 @@ import java.util.Objects;
import org.redkale.service.Service; import org.redkale.service.Service;
/** /**
* NodeServer的拦截类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -17,10 +18,22 @@ import org.redkale.service.Service;
*/ */
public class NodeInterceptor { public class NodeInterceptor {
/** *
* Server.start之前调用 <br>
* NodeServer.start的部署是先执行NodeInterceptor.preStart再执行 Server.start 方法
*
* @param server NodeServer
*/
public void preStart(NodeServer server) { public void preStart(NodeServer server) {
} }
/**
* Server.shutdown之前调用 <br>
* NodeServer.shutdown的部署是先执行NodeInterceptor.preShutdown再执行 Server.sshutdown 方法
*
* @param server NodeServer
*/
public void preShutdown(NodeServer server) { public void preShutdown(NodeServer server) {
} }

View File

@@ -276,8 +276,8 @@ public abstract class NodeServer {
if (WebSocketNode.class.isAssignableFrom(type)) continue; if (WebSocketNode.class.isAssignableFrom(type)) continue;
} }
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty()); 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)); interceptorServiceWrappers.add(new NodeInterceptor.InterceptorServiceWrapper(entry.getName(), type, oldother));
continue; continue;
} }
@@ -347,7 +347,7 @@ public abstract class NodeServer {
}); });
if (sb != null) { if (sb != null) {
remoteServiceWrappers.forEach(y -> { 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 ----------------- //----------------- init -----------------
@@ -362,7 +362,7 @@ public abstract class NodeServer {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
y.getService().init(y.getConf()); y.getService().init(y.getConf());
long e = System.currentTimeMillis() - s; 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 { } finally {
clds.countDown(); clds.countDown();
} }
@@ -403,7 +403,7 @@ public abstract class NodeServer {
Transport first = transports.get(0); Transport first = transports.get(0);
GroupInfo ginfo = application.findGroupInfo(first.getName()); GroupInfo ginfo = application.findGroupInfo(first.getName());
Transport newTransport = new Transport(groupid, ginfo.getProtocol(), application.getWatchFactory(), 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) { synchronized (application.resourceFactory) {
transport = application.resourceFactory.find(groupid, Transport.class); transport = application.resourceFactory.find(groupid, Transport.class);
if (transport == null) { if (transport == null) {
@@ -429,7 +429,7 @@ public abstract class NodeServer {
Set<InetSocketAddress> addrs = ginfo.copyAddrs(); Set<InetSocketAddress> addrs = ginfo.copyAddrs();
if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> "); if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> ");
transport = new Transport(group, ginfo.getProtocol(), application.getWatchFactory(), 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); application.resourceFactory.register(group, transport);
} }
return transport; return transport;

View File

@@ -61,7 +61,7 @@ public class NodeSncpServer extends NodeServer {
List<SncpServlet> servlets = sncpServer.getSncpServlets(); List<SncpServlet> servlets = sncpServer.getSncpServlets();
Collections.sort(servlets); Collections.sort(servlets);
for (SncpServlet en : 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()); if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
} }

View File

@@ -9,9 +9,9 @@ import java.lang.reflect.*;
import java.util.*; import java.util.*;
/** /**
* 对象数组的序列化不包含int[]、long[]这样的primitive class数组. * 数组的序列化操作类 <br>
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short对于大于32767长度的数组传输会影响性能所以没有采用int存储。 * 对象数组的反序列化不包含int[]、long[]这样的primitive class数组。 <br>
* 支持一定程度的泛型。 * 支持一定程度的泛型。 <br>
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -8,9 +8,9 @@ package org.redkale.convert;
import java.lang.reflect.*; import java.lang.reflect.*;
/** /**
* 对象数组的序列化不包含int[]、long[]这样的primitive class数组. * 数组的序列化操作类 <br>
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short对于大于32767长度的数组传输会影响性能所以没有必要采用int存储。 * 对象数组的序列化不包含int[]、long[]这样的primitive class数组。 <br>
* 支持一定程度的泛型。 * 支持一定程度的泛型。 <br>
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -11,9 +11,8 @@ import java.lang.reflect.Type;
import java.util.Collection; import java.util.Collection;
/** /**
* 对象集合的反序列化. * Collection的反序列化操作类 <br>
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short对于大于32767长度的集合传输会影响性能所以没有采用int存储。 * 支持一定程度的泛型。 <br>
* 支持一定程度的泛型。
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -9,9 +9,8 @@ import java.lang.reflect.*;
import java.util.Collection; import java.util.Collection;
/** /**
* 对象集合的序列化. * Collection的序列化操作类 <br>
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short对于大于32767长度的集合传输会影响性能所以没有采用int存储。 * 支持一定程度的泛型。 <br>
* 支持一定程度的泛型。
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -6,7 +6,7 @@
package org.redkale.convert; package org.redkale.convert;
/** /**
* 序列化操作类 * 序列化/反序列化操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -10,11 +10,12 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.*; import java.lang.annotation.*;
/** /**
* 用于类名的别名 类似javax.persistence.Table * 用于类名的别名, 该值必须是全局唯一 <br>
* 该值必须是全局唯一 * 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.writeClassName(String value) 。
* 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.writeClassName(String value) 。 *
* <p>
* 详情见: https://redkale.org
* *
* <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx
*/ */
@Inherited @Inherited
@@ -23,5 +24,10 @@ import java.lang.annotation.*;
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface ConvertEntity { public @interface ConvertEntity {
/**
* 别名值
*
* @return String
*/
String value(); String value();
} }

View File

@@ -5,8 +5,11 @@
package org.redkale.convert; package org.redkale.convert;
/** /**
* 序列化自定义异常类
*
* <p>
* 详情见: https://redkale.org
* *
* <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx
*/ */
public class ConvertException extends RuntimeException { public class ConvertException extends RuntimeException {

View File

@@ -17,6 +17,7 @@ import org.redkale.convert.ext.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* 序列化模块的工厂类用于注册自定义的序列化类型获取Convert
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: 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(double[].class, DoubleArraySimpledCoder.instance);
this.register(String[].class, StringArraySimpledCoder.instance); this.register(String[].class, StringArraySimpledCoder.instance);
//--------------------------------------------------------- //---------------------------------------------------------
this.register(AnyValue.class, Creator.create(AnyValue.DefaultAnyValue.class));
this.register(HttpCookie.class, new Creator<HttpCookie>() { this.register(HttpCookie.class, new Creator<HttpCookie>() {
@Override @Override
@Creator.ConstructorParameters({"name", "value"}) @Creator.ConstructorParameters({"name", "value"})

View File

@@ -6,8 +6,11 @@
package org.redkale.convert; package org.redkale.convert;
/** /**
* 序列化类型枚举,结合&#64;ConvertColumn使用
*
* <p>
* 详情见: https://redkale.org
* *
* <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx
*/ */
public enum ConvertType { public enum ConvertType {

View File

@@ -9,6 +9,7 @@ import java.lang.reflect.*;
import org.redkale.util.Attribute; import org.redkale.util.Attribute;
/** /**
* 字段的反序列化操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -8,6 +8,7 @@ package org.redkale.convert;
import java.lang.reflect.Type; import java.lang.reflect.Type;
/** /**
* 反序列化操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -9,6 +9,7 @@ import java.lang.reflect.*;
import org.redkale.util.Attribute; import org.redkale.util.Attribute;
/** /**
* 字段的序列化操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -8,6 +8,7 @@ package org.redkale.convert;
import java.lang.reflect.Type; import java.lang.reflect.Type;
/** /**
* 序列化操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -11,6 +11,7 @@ import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
/** /**
* Map的反序列化操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -10,6 +10,7 @@ import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
/** /**
* Map的序列化操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -13,6 +13,7 @@ import java.util.Set;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* 自定义对象的反序列化操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: 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.isSynthetic()) continue;
if (method.getName().length() < 4) continue; if (method.getName().length() < 4) continue;
if (!method.getName().startsWith("set")) continue; if (!method.getName().startsWith("set")) continue;
if (method.getAnnotation(java.beans.Transient.class) != null) continue;
if (method.getParameterTypes().length != 1) continue; if (method.getParameterTypes().length != 1) continue;
if (method.getReturnType() != void.class) continue; if (method.getReturnType() != void.class) continue;
if (reversible && (cps == null || !ObjectEncoder.contains(cps, ConvertFactory.readGetSetFieldName(method)))) { if (reversible && (cps == null || !ObjectEncoder.contains(cps, ConvertFactory.readGetSetFieldName(method)))) {

View File

@@ -10,6 +10,7 @@ import java.util.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* 自定义对象的序列化操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: 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().length() < 3) continue;
if (method.getName().equals("getClass")) continue; if (method.getName().equals("getClass")) continue;
if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) 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.getParameterTypes().length != 0) continue;
if (method.getReturnType() == void.class) continue; if (method.getReturnType() == void.class) continue;
if (reversible && (cps == null || !contains(cps, ConvertFactory.readGetSetFieldName(method)))) { if (reversible && (cps == null || !contains(cps, ConvertFactory.readGetSetFieldName(method)))) {

View File

@@ -6,6 +6,7 @@
package org.redkale.convert; package org.redkale.convert;
/** /**
* 反序列化的数据读取流
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -42,6 +43,7 @@ public abstract class Reader {
* 读取对象的类名, 返回 null 表示对象为null 返回空字符串表示当前class与返回的class一致返回非空字符串表示class是当前class的子类。 * 读取对象的类名, 返回 null 表示对象为null 返回空字符串表示当前class与返回的class一致返回非空字符串表示class是当前class的子类。
* *
* @param clazz 类名 * @param clazz 类名
*
* @return 返回字段数 * @return 返回字段数
*/ */
public String readObjectB(final Class clazz) { public String readObjectB(final Class clazz) {
@@ -86,6 +88,7 @@ public abstract class Reader {
* 根据字段读取字段对应的DeMember * 根据字段读取字段对应的DeMember
* *
* @param members DeMember的全量集合 * @param members DeMember的全量集合
*
* @return 匹配的DeMember * @return 匹配的DeMember
*/ */
public abstract DeMember readFieldName(final DeMember[] members); public abstract DeMember readFieldName(final DeMember[] members);

View File

@@ -9,6 +9,8 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
/** /**
* 简易类的序列化和反序列化操作类
* 能序列化为Boolean、Number或者字符串的类视为简易类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -8,6 +8,7 @@ package org.redkale.convert;
import org.redkale.util.Attribute; import org.redkale.util.Attribute;
/** /**
* 序列化的数据输出流
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -7,6 +7,7 @@ package org.redkale.convert.bson;
import java.nio.*; import java.nio.*;
import java.util.function.*; import java.util.function.*;
import org.redkale.util.Utility;
/** /**
* *
@@ -78,20 +79,14 @@ public class BsonByteBufferWriter extends BsonWriter {
if (!buffer.hasRemaining()) { if (!buffer.hasRemaining()) {
buffer.flip(); buffer.flip();
buffer = supplier.get(); buffer = supplier.get();
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1]; this.buffers = Utility.append(this.buffers, buffer);
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
bufs[this.buffers.length] = buffer;
this.buffers = bufs;
this.index++; this.index++;
} }
int len = buffer.remaining(); int len = buffer.remaining();
int size = 0; int size = 0;
while (len < byteLength) { while (len < byteLength) {
buffer = supplier.get(); buffer = supplier.get();
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1]; this.buffers = Utility.append(this.buffers, buffer);
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
bufs[this.buffers.length] = buffer;
this.buffers = bufs;
len += buffer.remaining(); len += buffer.remaining();
size++; size++;
} }

View File

@@ -15,21 +15,21 @@ import org.redkale.util.*;
/** /**
* <blockquote><pre> * <blockquote><pre>
* BSON协议格式: * BSON协议格式:
* 1). 基本数据类型: 直接转换成byte[] * 1) 基本数据类型: 直接转换成byte[]
* 2). SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。 * 2) SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。
* 3). String: length(4 bytes) + byte[](utf8); * 3) String: length(4 bytes) + byte[](utf8);
* 4). 数组: length(4 bytes) + byte[]... * 4) 数组: length(4 bytes) + byte[]...
* 5). Object: * 5) Object:
* 1. realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名) * 1 realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名)
* 2. 空字符串(SmallString) * 2 空字符串(SmallString)
* 3. SIGN_OBJECTB 标记位值固定为0xBB (short) * 3 SIGN_OBJECTB 标记位值固定为0xBB (short)
* 4. 循环字段值: * 4 循环字段值:
* 4.1 SIGN_HASNEXT 标记位值固定为1 (byte) * 4.1 SIGN_HASNEXT 标记位值固定为1 (byte)
* 4.2 字段类型; 1-9为基本类型和字符串; 101-109为基本类型和字符串的数组; 127为Object * 4.2 字段类型; 1-9为基本类型和字符串; 101-109为基本类型和字符串的数组; 127为Object
* 4.3 字段名 (SmallString) * 4.3 字段名 (SmallString)
* 4.4 字段的值Object * 4.4 字段的值Object
* 5. SIGN_NONEXT 标记位值固定为0 (byte) * 5 SIGN_NONEXT 标记位值固定为0 (byte)
* 6. SIGN_OBJECTE 标记位值固定为0xEE (short) * 6 SIGN_OBJECTE 标记位值固定为0xEE (short)
* *
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>

View File

@@ -7,6 +7,7 @@ package org.redkale.convert.bson;
import java.io.Serializable; import java.io.Serializable;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.util.AnyValue;
/** /**
* *
@@ -27,6 +28,9 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
static { static {
instance.register(Serializable.class, objectDecoder); instance.register(Serializable.class, objectDecoder);
instance.register(Serializable.class, objectEncoder); 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) { private BsonFactory(BsonFactory parent, boolean tiny) {

View File

@@ -5,7 +5,6 @@
*/ */
package org.redkale.convert.bson; package org.redkale.convert.bson;
import java.util.function.*;
import org.redkale.convert.*; import org.redkale.convert.*;
import static org.redkale.convert.Reader.SIGN_NULL; import static org.redkale.convert.Reader.SIGN_NULL;
import org.redkale.convert.ext.*; import org.redkale.convert.ext.*;
@@ -42,19 +41,7 @@ public class BsonReader extends Reader {
} }
public static ObjectPool<BsonReader> createPool(int max) { public static ObjectPool<BsonReader> createPool(int max) {
return new ObjectPool<BsonReader>(max, new Creator<BsonReader>() { return new ObjectPool<>(max, (Object... params) -> new BsonReader(), null, (t) -> t.recycle());
@Override
public BsonReader create(Object... params) {
return new BsonReader();
}
}, null, new Predicate<BsonReader>() {
@Override
public boolean test(BsonReader t) {
return t.recycle();
}
});
} }
public BsonReader(byte[] bytes) { public BsonReader(byte[] bytes) {
@@ -164,7 +151,7 @@ public class BsonReader extends Reader {
if (bt == Reader.SIGN_NULL) return null; if (bt == Reader.SIGN_NULL) return null;
if (bt != SIGN_OBJECTB) { if (bt != SIGN_OBJECTB) {
throw new ConvertException("a bson object must begin with " + (SIGN_OBJECTB) throw new ConvertException("a bson object must begin with " + (SIGN_OBJECTB)
+ " (position = " + position + ") but '" + currentByte() + "'"); + " (position = " + position + ") but '" + currentByte() + "'");
} }
return ""; return "";
} }
@@ -173,7 +160,7 @@ public class BsonReader extends Reader {
public final void readObjectE(final Class clazz) { public final void readObjectE(final Class clazz) {
if (readShort() != SIGN_OBJECTE) { if (readShort() != SIGN_OBJECTE) {
throw new ConvertException("a bson object must end with " + (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(); byte b = readByte();
if (b == SIGN_HASNEXT) return true; if (b == SIGN_HASNEXT) return true;
if (b != SIGN_NONEXT) throw new ConvertException("hasNext option must be (" + (SIGN_HASNEXT) 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; return false;
} }
@@ -272,19 +259,19 @@ public class BsonReader extends Reader {
@Override @Override
public int readInt() { public int readInt() {
return ((content[++this.position] & 0xff) << 24) | ((content[++this.position] & 0xff) << 16) 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 @Override
public long readLong() { public long readLong() {
return ((((long) content[++this.position] & 0xff) << 56) return ((((long) content[++this.position] & 0xff) << 56)
| (((long) content[++this.position] & 0xff) << 48) | (((long) content[++this.position] & 0xff) << 48)
| (((long) content[++this.position] & 0xff) << 40) | (((long) content[++this.position] & 0xff) << 40)
| (((long) content[++this.position] & 0xff) << 32) | (((long) content[++this.position] & 0xff) << 32)
| (((long) content[++this.position] & 0xff) << 24) | (((long) content[++this.position] & 0xff) << 24)
| (((long) content[++this.position] & 0xff) << 16) | (((long) content[++this.position] & 0xff) << 16)
| (((long) content[++this.position] & 0xff) << 8) | (((long) content[++this.position] & 0xff) << 8)
| (((long) content[++this.position] & 0xff))); | (((long) content[++this.position] & 0xff)));
} }
@Override @Override

View File

@@ -6,7 +6,6 @@
package org.redkale.convert.bson; package org.redkale.convert.bson;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.function.Predicate;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -28,19 +27,7 @@ public class BsonWriter extends Writer {
protected boolean tiny; protected boolean tiny;
public static ObjectPool<BsonWriter> createPool(int max) { public static ObjectPool<BsonWriter> createPool(int max) {
return new ObjectPool<BsonWriter>(max, new Creator<BsonWriter>() { return new ObjectPool<>(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle());
@Override
public BsonWriter create(Object... params) {
return new BsonWriter();
}
}, null, new Predicate<BsonWriter>() {
@Override
public boolean test(BsonWriter t) {
return t.recycle();
}
});
} }
public byte[] toArray() { public byte[] toArray() {
@@ -82,6 +69,7 @@ public class BsonWriter extends Writer {
* 扩充指定长度的缓冲区 * 扩充指定长度的缓冲区
* *
* @param len 扩容长度 * @param len 扩容长度
*
* @return 固定0 * @return 固定0
*/ */
protected int expand(int len) { protected int expand(int len) {

View File

@@ -84,20 +84,14 @@ public class JsonByteBufferWriter extends JsonWriter {
if (!buffer.hasRemaining()) { if (!buffer.hasRemaining()) {
buffer.flip(); buffer.flip();
buffer = supplier.get(); buffer = supplier.get();
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1]; this.buffers = Utility.append(this.buffers, buffer);
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
bufs[this.buffers.length] = buffer;
this.buffers = bufs;
this.index++; this.index++;
} }
int len = buffer.remaining(); int len = buffer.remaining();
int size = 0; int size = 0;
while (len < byteLength) { while (len < byteLength) {
buffer = supplier.get(); buffer = supplier.get();
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1]; this.buffers = Utility.append(this.buffers, buffer);
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
bufs[this.buffers.length] = buffer;
this.buffers = bufs;
len += buffer.remaining(); len += buffer.remaining();
size++; size++;
} }

View File

@@ -10,7 +10,7 @@ import java.math.BigInteger;
import java.net.*; import java.net.*;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.ext.*; 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(DLong.class, DLongSimpledCoder.DLongJsonSimpledCoder.instance);
instance.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance); instance.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance);
instance.register(Serializable.class, instance.loadEncoder(Object.class)); 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) { private JsonFactory(JsonFactory parent, boolean tiny) {

View File

@@ -17,47 +17,65 @@ import org.redkale.util.*;
import org.redkale.watch.*; import org.redkale.watch.*;
/** /**
* 服务器上下文对象
*
* <p>
* 详情见: https://redkale.org
* *
* <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx
*/ */
public class Context { public class Context {
private static final Charset UTF8 = Charset.forName("UTF-8"); private static final Charset UTF8 = Charset.forName("UTF-8");
//服务启动时间
protected final long serverStartTime; protected final long serverStartTime;
//Server的线程池
protected final ExecutorService executor; protected final ExecutorService executor;
//ByteBuffer的容量默认8K
protected final int bufferCapacity; protected final int bufferCapacity;
//ByteBuffer对象池
protected final ObjectPool<ByteBuffer> bufferPool; protected final ObjectPool<ByteBuffer> bufferPool;
//Response对象池
protected final ObjectPool<Response> responsePool; protected final ObjectPool<Response> responsePool;
//服务的根Servlet
protected final PrepareServlet prepare; protected final PrepareServlet prepare;
//服务的监听地址
private final InetSocketAddress address; private final InetSocketAddress address;
//字符集
protected final Charset charset; protected final Charset charset;
//请求内容的大小上限, 默认64K
protected final int maxbody; protected final int maxbody;
//IO读取的超时时间
protected final int readTimeoutSecond; protected final int readTimeoutSecond;
//IO写入的超时时间
protected final int writeTimeoutSecond; protected final int writeTimeoutSecond;
//日志Logger
protected final Logger logger; protected final Logger logger;
//BSON操作工厂
protected final BsonFactory bsonFactory; protected final BsonFactory bsonFactory;
//JSON操作工厂
protected final JsonFactory jsonFactory; protected final JsonFactory jsonFactory;
//监控对象
protected final WatchFactory watch; protected final WatchFactory watch;
public Context(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool, 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 maxbody, Charset charset, InetSocketAddress address, final PrepareServlet prepare, final WatchFactory watch,
final int readTimeoutSecond, final int writeTimeoutSecond) { final int readTimeoutSecond, final int writeTimeoutSecond) {
this.serverStartTime = serverStartTime; this.serverStartTime = serverStartTime;
this.logger = logger; this.logger = logger;
this.executor = executor; this.executor = executor;

View File

@@ -11,8 +11,11 @@ import java.util.logging.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* 根Servlet的处理逻辑类
*
* <p>
* 详情见: https://redkale.org
* *
* <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@@ -14,6 +14,9 @@ import java.util.logging.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* 根Servlet 一个Server只能存在一个根Servlet
*
* 用于分发Request请求
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -13,6 +13,7 @@ import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
/** /**
* 协议底层Server
* *
* <p> 详情见: https://redkale.org * <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx

View File

@@ -11,6 +11,7 @@ import org.redkale.convert.bson.BsonConvert;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
/** /**
* 协议请求对象
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -10,6 +10,7 @@ import java.nio.channels.CompletionHandler;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
/** /**
* 协议响应对象
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -29,6 +30,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
private boolean inited = true; private boolean inited = true;
protected Object output; //输出的结果对象
protected BiConsumer<R, Response<C, R>> recycleListener; protected BiConsumer<R, Response<C, R>> recycleListener;
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() { 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[]>() { private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() {
@Override @Override
public void completed(Integer result, ByteBuffer[] attachments) { public void completed(final Integer result, final ByteBuffer[] attachments) {
int index = -1; int index = -1;
for (int i = 0; i < attachments.length; i++) { for (int i = 0; i < attachments.length; i++) {
if (attachments[i].hasRemaining()) { if (attachments[i].hasRemaining()) {
index = i; index = i;
break; break;
} else {
context.offerBuffer(attachments[i]);
} }
} }
if (index == 0) { if (index >= 0) {
channel.write(attachments, attachments, this); channel.write(attachments, index, attachments.length - index, 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);
} else { } else {
for (ByteBuffer attachment : attachments) {
context.offerBuffer(attachment);
}
finish(); finish();
} }
} }
@Override @Override
public void failed(Throwable exc, ByteBuffer[] attachments) { public void failed(Throwable exc, final ByteBuffer[] attachments) {
for (ByteBuffer attachment : attachments) { for (ByteBuffer attachment : attachments) {
context.offerBuffer(attachment); context.offerBuffer(attachment);
} }
@@ -113,6 +113,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
} }
recycleListener = null; recycleListener = null;
} }
this.output = null;
request.recycle(); request.recycle();
if (channel != null) { if (channel != null) {
if (keepAlive) { if (keepAlive) {
@@ -143,6 +144,19 @@ public abstract class Response<C extends Context, R extends Request<C>> {
this.recycleListener = recycleListener; this.recycleListener = recycleListener;
} }
public Object getOutput() {
return output;
}
/**
* 是否已关闭
*
* @return boolean
*/
public boolean isClosed() {
return !this.inited;
}
public void finish() { public void finish() {
this.finish(false); this.finish(false);
} }
@@ -153,6 +167,17 @@ public abstract class Response<C extends Context, R extends Request<C>> {
this.context.responsePool.offer(this); 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) { public void finish(ByteBuffer buffer) {
this.channel.write(buffer, buffer, finishHandler); this.channel.write(buffer, buffer, finishHandler);
} }

View File

@@ -9,6 +9,7 @@ import org.redkale.util.AnyValue;
import java.io.IOException; import java.io.IOException;
/** /**
* 协议请求处理类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -44,7 +44,7 @@ public final class Transport {
protected final String name; //即<group>的name属性 protected final String name; //即<group>的name属性
protected final String kind; //即<group>的kind属性 protected final String subprotocol; //即<group>的subprotocol属性
protected final boolean tcp; protected final boolean tcp;
@@ -62,16 +62,16 @@ public final class Transport {
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>(); 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) { 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) { final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
this.name = name; this.name = name;
this.watch = watch; this.watch = watch;
this.kind = kind == null ? "" : kind.trim(); this.subprotocol = subprotocol == null ? "" : subprotocol.trim();
this.protocol = protocol; this.protocol = protocol;
this.tcp = "TCP".equalsIgnoreCase(protocol); this.tcp = "TCP".equalsIgnoreCase(protocol);
this.group = transportChannelGroup; this.group = transportChannelGroup;
@@ -83,14 +83,17 @@ public final class Transport {
public Transport(final Collection<Transport> transports) { public Transport(final Collection<Transport> transports) {
Transport first = null; Transport first = null;
List<String> tmpgroup = new ArrayList<>(); List<String> tmpgroup = new ArrayList<>();
for (Transport t : transports) { if (transports != null) {
if (first == null) first = t; for (Transport t : transports) {
tmpgroup.add(t.name); 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 //必须按字母排列顺序确保相同内容的transport列表组合的name相同而不会因为list的顺序不同产生不同的name
this.name = tmpgroup.stream().sorted().collect(Collectors.joining(";")); this.name = tmpgroup.stream().sorted().collect(Collectors.joining(";"));
this.watch = first.watch; this.watch = first.watch;
this.kind = first.kind; this.subprotocol = first.subprotocol;
this.protocol = first.protocol; this.protocol = first.protocol;
this.tcp = "TCP".equalsIgnoreCase(first.protocol); this.tcp = "TCP".equalsIgnoreCase(first.protocol);
this.group = first.group; this.group = first.group;
@@ -118,8 +121,8 @@ public final class Transport {
return name; return name;
} }
public String getKind() { public String getSubprotocol() {
return kind; return subprotocol;
} }
public void close() { public void close() {

View File

@@ -8,8 +8,11 @@ package org.redkale.net;
import java.util.concurrent.*; import java.util.concurrent.*;
/** /**
* 协议处理的自定义线程类
*
* <p>
* 详情见: https://redkale.org
* *
* <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx
*/ */
public class WorkThread extends Thread { public class WorkThread extends Thread {

View File

@@ -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 {
}

View File

@@ -16,6 +16,7 @@ import org.redkale.util.*;
import org.redkale.watch.*; import org.redkale.watch.*;
/** /**
* HTTP服务的上下文对象
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -27,10 +28,10 @@ public class HttpContext extends Context {
protected final SecureRandom random = new SecureRandom(); protected final SecureRandom random = new SecureRandom();
public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare, ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) { WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) {
super(serverStartTime, logger, executor, bufferCapacity, bufferPool, responsePool, maxbody, charset, 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())); random.setSeed(Math.abs(System.nanoTime()));
} }

View File

@@ -17,6 +17,8 @@ import org.redkale.util.*;
import org.redkale.watch.*; import org.redkale.watch.*;
/** /**
* HTTP Servlet的总入口请求在HttpPrepareServlet中进行分流。 <br>
* 一个HttpServer只有一个HttpPrepareServlet 用于管理所有HttpServlet。 <br>
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -89,6 +91,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
} }
} }
} }
//找不到匹配的HttpServlet则使用静态资源HttpResourceServlet
if (servlet == null) servlet = this.resourceHttpServlet; if (servlet == null) servlet = this.resourceHttpServlet;
servlet.execute(request, response); servlet.execute(request, response);
} catch (Exception e) { } 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 @Override
public void addServlet(HttpServlet servlet, Object prefix, AnyValue conf, String... mappings) { public void addServlet(HttpServlet servlet, Object prefix, AnyValue conf, String... mappings) {
if (prefix == null) prefix = ""; if (prefix == null) prefix = "";
@@ -107,12 +118,12 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix
} }
} }
synchronized (allMapStrings) { synchronized (allMapStrings) { //需要整段锁住
for (String mapping : mappings) { for (String mapping : mappings) {
if (mapping == null) continue; if (mapping == null) continue;
if (!prefix.toString().isEmpty()) mapping = prefix + mapping; if (!prefix.toString().isEmpty()) mapping = prefix + mapping;
if (contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式)) if (Utility.contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
if (mapping.charAt(0) != '^') mapping = '^' + mapping; if (mapping.charAt(0) != '^') mapping = '^' + mapping;
if (mapping.endsWith("/*")) { if (mapping.endsWith("/*")) {
mapping = mapping.substring(0, mapping.length() - 1) + ".*"; 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; * 设置静态资源HttpServlet
for (char ch : Utility.charArray(string)) { *
for (char ch2 : values) { * @param servlet HttpServlet
if (ch == ch2) return true; */
}
}
return false;
}
public void setResourceServlet(HttpServlet servlet) { public void setResourceServlet(HttpServlet servlet) {
if (servlet != null) { if (servlet != null) {
this.resourceHttpServlet = servlet; this.resourceHttpServlet = servlet;
} }
} }
/**
* 获取静态资源HttpServlet
*
* @return HttpServlet
*/
public HttpServlet getResourceServlet() { public HttpServlet getResourceServlet() {
return this.resourceHttpServlet; return this.resourceHttpServlet;
} }

View File

@@ -22,7 +22,7 @@ import org.redkale.util.ByteArray;
* 例如简单的翻页查询 <br> * 例如简单的翻页查询 <br>
* /pipes/record/query/offset:0/limit:20 <br> * /pipes/record/query/offset:0/limit:20 <br>
* 获取页号: int offset = request.getRequstURIPath("offset:", 0); <br> * 获取页号: int offset = request.getRequstURIPath("offset:", 0); <br>
* 获取行数: int limit = request.getRequstURIPath("limit:", 10); * 获取行数: int limit = request.getRequstURIPath("limit:", 10); <br>
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
@@ -236,7 +236,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 地址 * @return 地址
*/ */
public SocketAddress getRemoteAddress() { 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) { public String getSessionid(boolean create) {
String sessionid = getCookie(SESSIONID_NAME, null); String sessionid = getCookie(SESSIONID_NAME, null);
if (create && (sessionid == null || sessionid.isEmpty())) { if (create && (sessionid == null || sessionid.isEmpty())) {
sessionid = ((HttpContext) context).createSessionid(); sessionid = context.createSessionid();
this.newsessionid = sessionid; this.newsessionid = sessionid;
} }
return sessionid; return sessionid;
@@ -368,6 +368,18 @@ public class HttpRequest extends Request<HttpContext> {
return newsessionid; return newsessionid;
} }
/**
* 指定值更新sessionid
*
* @param newsessionid 新sessionid值
*
* @return 新的sessionid值
*/
public String changeSessionid(String newsessionid) {
this.newsessionid = newsessionid == null ? context.createSessionid() : newsessionid.trim();
return newsessionid;
}
/** /**
* 使sessionid失效 * 使sessionid失效
*/ */
@@ -1109,17 +1121,18 @@ public class HttpRequest extends Request<HttpContext> {
* @return Flipper翻页对象 * @return Flipper翻页对象
*/ */
public org.redkale.source.Flipper getFlipper(String name, boolean needcreate, int maxLimit) { 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); org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name);
if (flipper == null) { if (flipper == null) {
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
int limit = getRequstURIPath("limit:", maxLimit); int limit = getRequstURIPath("limit:", maxLimit);
int offset = getRequstURIPath("offset:", 0); int offset = getRequstURIPath("offset:", 0);
String sort = getRequstURIPath("sort:", ""); String sort = getRequstURIPath("sort:", "");
if (limit > 0) flipper = new org.redkale.source.Flipper(limit, offset, 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); flipper.setLimit(maxLimit);
} }
if (flipper != null || !needcreate) return flipper; if (flipper != null || !needcreate) return flipper;
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
return new org.redkale.source.Flipper(maxLimit); return new org.redkale.source.Flipper(maxLimit);
} }
} }

View File

@@ -18,6 +18,7 @@ import java.util.regex.*;
import org.redkale.util.AnyValue; import org.redkale.util.AnyValue;
/** /**
* 静态资源HttpServlet
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -21,9 +21,9 @@ import org.redkale.util.AnyValue.Entry;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。 * Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。 <br>
* 同时提供发送json的系列接口: public void finishJson(Type type, Object obj) * 同时提供发送json的系列接口: public void finishJson(Type type, Object obj) <br>
* Redkale提倡http+json的接口风格 所以主要输出的数据格式为json 同时提供异步接口。 * Redkale提倡http+json的接口风格 所以主要输出的数据格式为json 同时提供异步接口。 <br>
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
@@ -184,14 +184,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @return HttpResponse * @return HttpResponse
*/ */
public HttpResponse addCookie(HttpCookie... cookies) { public HttpResponse addCookie(HttpCookie... cookies) {
if (this.cookies == null) { this.cookies = Utility.append(this.cookies, cookies);
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;
}
return this; return this;
} }
@@ -203,18 +196,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @return HttpResponse * @return HttpResponse
*/ */
public HttpResponse addCookie(Collection<HttpCookie> cookies) { public HttpResponse addCookie(Collection<HttpCookie> cookies) {
if (cookies == null || cookies.isEmpty()) return this; this.cookies = Utility.append(this.cookies, cookies);
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;
}
return this; return this;
} }
@@ -225,6 +207,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
*/ */
public void finishJson(final Object obj) { public void finishJson(final Object obj) {
this.contentType = "text/plain; charset=utf-8"; this.contentType = "text/plain; charset=utf-8";
if (this.recycleListener != null) this.output = obj;
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), 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) { public void finishJson(final JsonConvert convert, final Object obj) {
this.contentType = "text/plain; charset=utf-8"; this.contentType = "text/plain; charset=utf-8";
if (this.recycleListener != null) this.output = obj;
finish(convert.convertTo(context.getBufferSupplier(), 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) { public void finishJson(final Type type, final Object obj) {
this.contentType = "text/plain; charset=utf-8"; this.contentType = "text/plain; charset=utf-8";
this.output = obj;
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), type, 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) { public void finishJson(final JsonConvert convert, final Type type, final Object obj) {
this.contentType = "text/plain; charset=utf-8"; this.contentType = "text/plain; charset=utf-8";
if (this.recycleListener != null) this.output = obj;
finish(convert.convertTo(context.getBufferSupplier(), type, 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) { public void finishJson(final Object... objs) {
this.contentType = "text/plain; charset=utf-8"; this.contentType = "text/plain; charset=utf-8";
if (this.recycleListener != null) this.output = objs;
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), 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) { public void finishJson(final org.redkale.service.RetResult ret) {
this.contentType = "text/plain; charset=utf-8"; this.contentType = "text/plain; charset=utf-8";
if (this.recycleListener != null) this.output = ret;
if (ret != null && !ret.isSuccess()) { if (ret != null && !ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())); this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
this.header.addValue("retinfo", ret.getRetinfo()); 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) { public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) {
this.contentType = "text/plain; charset=utf-8"; this.contentType = "text/plain; charset=utf-8";
if (this.recycleListener != null) this.output = ret;
if (ret != null && !ret.isSuccess()) { if (ret != null && !ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())); this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
this.header.addValue("retinfo", ret.getRetinfo()); this.header.addValue("retinfo", ret.getRetinfo());
@@ -301,35 +290,13 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
finish(convert.convertTo(context.getBufferSupplier(), ret)); 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 输出内容 * @param obj 输出内容
*/ */
public void finish(String obj) { public void finish(String obj) {
if (this.recycleListener != null) this.output = obj;
if (obj == null || obj.isEmpty()) { if (obj == null || obj.isEmpty()) {
final ByteBuffer headbuf = createHeader(); final ByteBuffer headbuf = createHeader();
headbuf.flip(); headbuf.flip();
@@ -389,6 +356,23 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
super.finish(buffer404.duplicate()); 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按响应结果输出 * 将指定ByteBuffer按响应结果输出
* *

View File

@@ -16,6 +16,7 @@ import org.redkale.util.*;
import org.redkale.watch.WatchFactory; import org.redkale.watch.WatchFactory;
/** /**
* Http服务器
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -37,24 +38,88 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
super.init(config); 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) { public HttpServer addHttpServlet(String prefix, HttpServlet servlet, String... mappings) {
this.prepare.addServlet(servlet, prefix, null, mappings); this.prepare.addServlet(servlet, prefix, null, mappings);
return this; return this;
} }
/**
* 添加HttpServlet
*
* @param servlet HttpServlet
* @param mappings 匹配规则
*
* @return HttpServer
*/
public HttpServer addHttpServlet(HttpServlet servlet, String... mappings) { public HttpServer addHttpServlet(HttpServlet servlet, String... mappings) {
this.prepare.addServlet(servlet, null, null, mappings); this.prepare.addServlet(servlet, null, null, mappings);
return this; 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); 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) { 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); 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( 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) { final String name, Class<S> serviceType, final S service, final Class<T> baseServletClass, final String prefix, AnyValue conf) {
RestHttpServlet servlet = null; RestHttpServlet servlet = null;

View File

@@ -8,6 +8,7 @@ package org.redkale.net.http;
import java.util.*; import java.util.*;
/** /**
* MimeType
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -100,6 +101,7 @@ public class MimeType {
contentTypes.put("oda", "application/oda"); contentTypes.put("oda", "application/oda");
contentTypes.put("ogg", "application/ogg"); contentTypes.put("ogg", "application/ogg");
contentTypes.put("out", "text/plain"); contentTypes.put("out", "text/plain");
contentTypes.put("pac", "application/x-javascript-config");
contentTypes.put("pbm", "image/x-portable-bitmap"); contentTypes.put("pbm", "image/x-portable-bitmap");
contentTypes.put("pct", "image/pict"); contentTypes.put("pct", "image/pict");
contentTypes.put("pdf", "application/pdf"); contentTypes.put("pdf", "application/pdf");

View File

@@ -15,6 +15,7 @@ import java.util.regex.*;
import org.redkale.util.AnyValue.DefaultAnyValue; import org.redkale.util.AnyValue.DefaultAnyValue;
/** /**
* HTTP的文件上传请求的上下文对象
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -77,10 +78,21 @@ public final class MultiContext {
return null; return null;
} }
/**
* 判断请求是否包含上传文件
*
* @return boolean
*/
public boolean isMultipart() { public boolean isMultipart() {
return this.boundary != null; return this.boundary != null;
} }
/**
* 获取上传文件信息列表
*
* @return Iterable
* @throws IOException IOException
*/
public Iterable<MultiPart> parts() throws IOException { public Iterable<MultiPart> parts() throws IOException {
if (!isMultipart()) return emptyIterable; if (!isMultipart()) return emptyIterable;
final String boundarystr = "--" + this.boundary; final String boundarystr = "--" + this.boundary;

View File

@@ -254,7 +254,6 @@ public final class Rest {
final int maxStack = 3 + params.length; final int maxStack = 3 + params.length;
List<int[]> varInsns = new ArrayList<>(); List<int[]> varInsns = new ArrayList<>();
int maxLocals = 4; int maxLocals = 4;
final String jsvar = entry.jsvar.isEmpty() ? null : entry.jsvar;
int argIndex = 0; int argIndex = 0;
List<Object[]> paramlist = new ArrayList<>(); List<Object[]> paramlist = new ArrayList<>();
@@ -731,214 +730,110 @@ public final class Rest {
mv.visitInsn(RETURN); mv.visitInsn(RETURN);
} else if (returnType == boolean.class) { } else if (returnType == boolean.class) {
mv.visitVarInsn(ISTORE, maxLocals); mv.visitVarInsn(ISTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ILOAD, maxLocals);
mv.visitVarInsn(ILOAD, maxLocals); mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", 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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (returnType == byte.class) { } else if (returnType == byte.class) {
mv.visitVarInsn(ISTORE, maxLocals); mv.visitVarInsn(ISTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ILOAD, maxLocals);
mv.visitVarInsn(ILOAD, maxLocals); mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", 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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (returnType == short.class) { } else if (returnType == short.class) {
mv.visitVarInsn(ISTORE, maxLocals); mv.visitVarInsn(ISTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ILOAD, maxLocals);
mv.visitVarInsn(ILOAD, maxLocals); mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", 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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (returnType == char.class) { } else if (returnType == char.class) {
mv.visitVarInsn(ISTORE, maxLocals); mv.visitVarInsn(ISTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ILOAD, maxLocals);
mv.visitVarInsn(ILOAD, maxLocals); mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", 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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (returnType == int.class) { } else if (returnType == int.class) {
mv.visitVarInsn(ISTORE, maxLocals); mv.visitVarInsn(ISTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ILOAD, maxLocals);
mv.visitVarInsn(ILOAD, maxLocals); mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", 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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (returnType == float.class) { } else if (returnType == float.class) {
mv.visitVarInsn(FSTORE, maxLocals); mv.visitVarInsn(FSTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(FLOAD, maxLocals);
mv.visitVarInsn(FLOAD, maxLocals); mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", 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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (returnType == long.class) { } else if (returnType == long.class) {
mv.visitVarInsn(LSTORE, maxLocals); mv.visitVarInsn(LSTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(LLOAD, maxLocals);
mv.visitVarInsn(LLOAD, maxLocals); mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", 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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals += 2; maxLocals += 2;
} else if (returnType == double.class) { } else if (returnType == double.class) {
mv.visitVarInsn(DSTORE, maxLocals); mv.visitVarInsn(DSTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(DLOAD, maxLocals);
mv.visitVarInsn(DLOAD, maxLocals); mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", 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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals += 2; maxLocals += 2;
} else if (returnType == String.class) { } else if (returnType == String.class) {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, maxLocals);
mv.visitVarInsn(ALOAD, maxLocals); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", 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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (returnType == File.class) { } else if (returnType == File.class) {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, maxLocals);
mv.visitVarInsn(ALOAD, maxLocals); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false);
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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (RetResult.class.isAssignableFrom(returnType)) { } else if (RetResult.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, maxLocals);
mv.visitVarInsn(ALOAD, maxLocals); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (RestOutput.class.isAssignableFrom(returnType)) { } else if (RestOutput.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 2);
mv.visitVarInsn(ALOAD, 2); mv.visitVarInsn(ALOAD, maxLocals);
mv.visitVarInsn(ALOAD, maxLocals); mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "finishJson", "(" + respDesc + restoutputDesc + ")V", false);
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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面 } else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, maxLocals);
mv.visitVarInsn(ALOAD, maxLocals); mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false);
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.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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else { } else {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
if (jsvar == null) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, maxLocals);
mv.visitVarInsn(ALOAD, maxLocals); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
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.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} }
@@ -1020,7 +915,6 @@ public final class Rest {
this.actionid = mapping.actionid(); this.actionid = mapping.actionid();
this.cacheseconds = mapping.cacheseconds(); this.cacheseconds = mapping.cacheseconds();
this.comment = mapping.comment(); this.comment = mapping.comment();
this.jsvar = mapping.jsvar();
} }
public final Method mappingMethod; public final Method mappingMethod;
@@ -1039,8 +933,6 @@ public final class Rest {
public final int cacheseconds; public final int cacheseconds;
public final String jsvar;
@RestMapping() @RestMapping()
void mapping() { //用于获取Mapping 默认值 void mapping() { //用于获取Mapping 默认值
} }

View File

@@ -22,9 +22,24 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface RestCookie { 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 "";
} }

View File

@@ -22,9 +22,24 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface RestHeader { 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 "";
} }

View File

@@ -23,22 +23,17 @@ public abstract class RestHttpServlet<T> extends HttpBaseServlet {
response.finishJson(output); response.finishJson(output);
return; return;
} }
if (output.getContentType() != null) response.setContentType(output.getContentType());
response.addHeader(output.getHeaders()); response.addHeader(output.getHeaders());
response.addCookie(output.getCookies()); response.addCookie(output.getCookies());
if (output.getResult() instanceof File) { if (output.getResult() instanceof File) {
response.finish((File) output.getResult()); response.finish((File) output.getResult());
} else if (output.getResult() instanceof String) {
response.finish((String) output.getResult());
} else { } else {
response.finishJson(output.getResult()); 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());
}
} }

View File

@@ -10,8 +10,8 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*; import static java.lang.annotation.RetentionPolicy.*;
/** /**
* 只能依附在Service实现类的public方法上 * 只能依附在Service实现类的public方法上 <br>
* value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService的默认路径为/hello)。 * value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService的默认路径为/hello)。 <br>
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
@@ -24,25 +24,54 @@ import static java.lang.annotation.RetentionPolicy.*;
@Repeatable(RestMappings.class) @Repeatable(RestMappings.class)
public @interface RestMapping { public @interface RestMapping {
boolean ignore() default false; //是否屏蔽该方法的转换 /**
* 是否屏蔽该方法的转换
*
* @return boolean
*/
boolean ignore() default false;
/** /**
* 请求的方法名, 不能含特殊字符 * 请求的方法名, 不能含特殊字符
* 默认为方法名的小写(若方法名以createXXX、updateXXX、deleteXXX、queryXXX、findXXX、existsXXX且XXXService为Service的类名将只截取XXX之前) * 默认为方法名的小写(若方法名以createXXX、updateXXX、deleteXXX、queryXXX、findXXX、existsXXX且XXXService为Service的类名将只截取XXX之前)
* *
* @return name * @return String
*/ */
String name() default ""; String name() default "";
String comment() default ""; //备注描述, 对应&#64;WebAction.comment /**
* 备注描述, 对应&#64;WebAction.comment
*
* @return String
*/
String comment() default "";
boolean auth() default false; //是否鉴权,默认不鉴权, 对应&#64;AuthIgnore /**
* 是否鉴权,默认不鉴权, 对应&#64;AuthIgnore
*
* @return boolean
*/
boolean auth() default false;
int actionid() default 0; //操作ID值鉴权时用到, 对应&#64;WebAction.actionid /**
* 操作ID值鉴权时用到, 对应&#64;WebAction.actionid
*
* @return int
*/
int actionid() default 0;
int cacheseconds() default 0; // 结果缓存的秒数, 为0表示不缓存, 对应&#64;HttpCacheable.seconds /**
* 结果缓存的秒数, 为0表示不缓存, 对应&#64;HttpCacheable.seconds
*
* @return int
*/
int cacheseconds() default 0;
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应&#64;WebAction.methods /**
* 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应&#64;WebAction.methods
*
* @return String[]
*/
String[] methods() default {};
String jsvar() default ""; //以application/javascript输出对象是指明js的对象名该值存在时则忽略contentType()的值
} }

View File

@@ -23,6 +23,8 @@ public class RestOutput<T> {
private List<HttpCookie> cookies; private List<HttpCookie> cookies;
private String contentType;
private T result; private T result;
public RestOutput() { public RestOutput() {
@@ -58,6 +60,14 @@ public class RestOutput<T> {
this.cookies = cookies; this.cookies = cookies;
} }
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public T getResult() { public T getResult() {
return result; return result;
} }

View File

@@ -10,9 +10,11 @@ import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* 只能依附在Service类的方法的参数上, name值不能是'&#38;' *
* name='#'表示截取uri最后一段 * 依附在RestService类的方法的参数上 <br>
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 * name='&#38;' 表示当前用户 <br>
* name='#'表示截取uri最后一段 <br>
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 <br>
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
@@ -24,11 +26,35 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface RestParam { public @interface RestParam {
String name(); //参数名 name值不能是'&'; name='#'表示截取uri最后一段; name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 //name='&'表示当前用户;
/**
* 参数名 <br>
* name='&#38;'表示当前用户; <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 "";
} }

View File

@@ -10,7 +10,9 @@ import java.lang.annotation.*;
/** /**
* 功能同JSR 315 (java-servlet 3.0) 规范中的 @WebInitParam * 功能同JSR 315 (java-servlet 3.0) 规范中的 @WebInitParam
* *
* <p> 详情见: https://redkale.org * <p>
* 详情见: https://redkale.org
*
* @author zhangjx * @author zhangjx
*/ */
@Target({ElementType.TYPE}) @Target({ElementType.TYPE})
@@ -18,9 +20,24 @@ import java.lang.annotation.*;
@Documented @Documented
public @interface WebInitParam { public @interface WebInitParam {
/**
* 参数名
*
* @return String
*/
String name(); String name();
/**
* 参数值
*
* @return String
*/
String value(); String value();
/**
* 参数描述
*
* @return String
*/
String description() default ""; String description() default "";
} }

View File

@@ -21,15 +21,45 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface WebServlet { public @interface WebServlet {
/**
* HttpServlet资源名
*
* @return String
*/
String name() default ""; String name() default "";
/**
* 是否自动添加url前缀, 对应application.xml中servlets节点的path属性
*
* @return boolean
*/
boolean repair() default true; boolean repair() default true;
/**
* url匹配规则
*
* @return String[]
*/
String[] value() default {}; String[] value() default {};
/**
* 模块ID一个HttpServlet尽量只有提供一个模块的服务
*
* @return int
*/
int moduleid() default 0; int moduleid() default 0;
/**
* 参数
*
* @return WebInitParam[]
*/
WebInitParam[] initParams() default {}; WebInitParam[] initParams() default {};
String comment() default ""; //备注描述 /**
* 备注描述
*
* @return String
*/
String comment() default "";
} }

View File

@@ -11,6 +11,7 @@ import java.net.*;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.util.Comment;
/** /**
* <blockquote><pre> * <blockquote><pre>
@@ -22,15 +23,13 @@ import org.redkale.net.*;
* 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。 * 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
* 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。 * 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
* 1.5 onClose WebSocket被关闭后回调此方法。 * 1.5 onClose WebSocket被关闭后回调此方法。
* * 普通模式下 以上方法都应该被重载。
* 此模式下 以上方法都应该被重载。
* *
* 2) 原始二进制模式: 此模式有别于HTML5规范可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下: * 2) 原始二进制模式: 此模式有别于HTML5规范可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下:
* 2.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。 * 2.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。
* 2.2 createGroupid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。 * 2.2 createGroupid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。
* 2.3 onRead WebSocket成功连接后回调此方法 由此方法处理原始的TCP连接 同时业务代码去控制WebSocket的关闭。 * 2.3 onRead WebSocket成功连接后回调此方法 由此方法处理原始的TCP连接 需要业务代码去控制WebSocket的关闭。
* * 二进制模式下 以上方法都应该被重载。
* 此模式下 以上方法都应该被重载。
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -39,24 +38,28 @@ import org.redkale.net.*;
*/ */
public abstract class WebSocket { public abstract class WebSocket {
//消息不合法 @Comment("消息不合法")
public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2 public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2
//ws已经关闭 @Comment("WebSocket已经关闭")
public static final int RETCODE_WSOCKET_CLOSED = 1 << 2; //4 public static final int RETCODE_WSOCKET_CLOSED = 1 << 2; //4
//socket的buffer不合法 @Comment("Socket的buffer不合法")
public static final int RETCODE_ILLEGALBUFFER = 1 << 3; //8 public static final int RETCODE_ILLEGALBUFFER = 1 << 3; //8
//ws发送消息异常 @Comment("WebSocket发送消息异常")
public static final int RETCODE_SENDEXCEPTION = 1 << 4; //16 public static final int RETCODE_SENDEXCEPTION = 1 << 4; //16
@Comment("WebSocketEngine实例不存在")
public static final int RETCODE_ENGINE_NULL = 1 << 5; //32 public static final int RETCODE_ENGINE_NULL = 1 << 5; //32
@Comment("WebSocketNode实例不存在")
public static final int RETCODE_NODESERVICE_NULL = 1 << 6; //64 public static final int RETCODE_NODESERVICE_NULL = 1 << 6; //64
@Comment("WebSocket组为空, 表示无WebSocket连接")
public static final int RETCODE_GROUP_EMPTY = 1 << 7; //128 public static final int RETCODE_GROUP_EMPTY = 1 << 7; //128
@Comment("WebSocket已离线")
public static final int RETCODE_WSOFFLINE = 1 << 8; //256 public static final int RETCODE_WSOFFLINE = 1 << 8; //256
WebSocketRunner _runner; //不可能为空 WebSocketRunner _runner; //不可能为空
@@ -85,6 +88,7 @@ public abstract class WebSocket {
* 发送消息体, 包含二进制/文本 * 发送消息体, 包含二进制/文本
* *
* @param packet WebSocketPacket * @param packet WebSocketPacket
*
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final int send(WebSocketPacket packet) { public final int send(WebSocketPacket packet) {
@@ -98,6 +102,7 @@ public abstract class WebSocket {
* 发送单一的文本消息 * 发送单一的文本消息
* *
* @param text 不可为空 * @param text 不可为空
*
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final int send(String text) { public final int send(String text) {
@@ -109,6 +114,7 @@ public abstract class WebSocket {
* *
* @param text 不可为空 * @param text 不可为空
* @param last 是否最后一条 * @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final int send(String text, boolean last) { public final int send(String text, boolean last) {
@@ -136,6 +142,7 @@ public abstract class WebSocket {
* 发送单一的二进制消息 * 发送单一的二进制消息
* *
* @param data byte[] * @param data byte[]
*
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final int send(byte[] data) { public final int send(byte[] data) {
@@ -147,6 +154,7 @@ public abstract class WebSocket {
* *
* @param data 不可为空 * @param data 不可为空
* @param last 是否最后一条 * @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final int send(byte[] data, boolean last) { public final int send(byte[] data, boolean last) {
@@ -158,6 +166,7 @@ public abstract class WebSocket {
* *
* @param message 不可为空, 只能是String或者byte[] * @param message 不可为空, 只能是String或者byte[]
* @param last 是否最后一条 * @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final int send(Serializable message, boolean last) { public final int send(Serializable message, boolean last) {
@@ -170,6 +179,7 @@ public abstract class WebSocket {
* *
* @param groupid groupid * @param groupid groupid
* @param text 不可为空 * @param text 不可为空
*
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendEachMessage(Serializable groupid, String text) { public final int sendEachMessage(Serializable groupid, String text) {
@@ -181,6 +191,7 @@ public abstract class WebSocket {
* *
* @param groupid groupid * @param groupid groupid
* @param data 不可为空 * @param data 不可为空
*
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendEachMessage(Serializable groupid, byte[] data) { public final int sendEachMessage(Serializable groupid, byte[] data) {
@@ -193,6 +204,7 @@ public abstract class WebSocket {
* @param groupid groupid * @param groupid groupid
* @param text 不可为空 * @param text 不可为空
* @param last 是否最后一条 * @param last 是否最后一条
*
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendEachMessage(Serializable groupid, String text, boolean last) { public final int sendEachMessage(Serializable groupid, String text, boolean last) {
@@ -205,6 +217,7 @@ public abstract class WebSocket {
* @param groupid groupid * @param groupid groupid
* @param data 不可为空 * @param data 不可为空
* @param last 是否最后一条 * @param last 是否最后一条
*
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) { public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) {
@@ -216,6 +229,7 @@ public abstract class WebSocket {
* *
* @param groupid groupid * @param groupid groupid
* @param text 不可为空 * @param text 不可为空
*
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendRecentMessage(Serializable groupid, String text) { public final int sendRecentMessage(Serializable groupid, String text) {
@@ -227,6 +241,7 @@ public abstract class WebSocket {
* *
* @param groupid groupid * @param groupid groupid
* @param data 不可为空 * @param data 不可为空
*
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendRecentMessage(Serializable groupid, byte[] data) { public final int sendRecentMessage(Serializable groupid, byte[] data) {
@@ -239,6 +254,7 @@ public abstract class WebSocket {
* @param groupid groupid * @param groupid groupid
* @param text 不可为空 * @param text 不可为空
* @param last 是否最后一条 * @param last 是否最后一条
*
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendRecentMessage(Serializable groupid, String text, boolean last) { public final int sendRecentMessage(Serializable groupid, String text, boolean last) {
@@ -251,6 +267,7 @@ public abstract class WebSocket {
* @param groupid groupid * @param groupid groupid
* @param data 不可为空 * @param data 不可为空
* @param last 是否最后一条 * @param last 是否最后一条
*
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) { public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) {
@@ -275,6 +292,7 @@ public abstract class WebSocket {
* 获取在线用户的节点地址列表 * 获取在线用户的节点地址列表
* *
* @param groupid groupid * @param groupid groupid
*
* @return 地址列表 * @return 地址列表
*/ */
protected final Collection<InetSocketAddress> getOnlineNodes(Serializable groupid) { protected final Collection<InetSocketAddress> getOnlineNodes(Serializable groupid) {
@@ -285,6 +303,7 @@ public abstract class WebSocket {
* 获取在线用户的详细连接信息 * 获取在线用户的详细连接信息
* *
* @param groupid groupid * @param groupid groupid
*
* @return 地址集合 * @return 地址集合
*/ */
protected final Map<InetSocketAddress, List<String>> getOnlineRemoteAddress(Serializable groupid) { protected final Map<InetSocketAddress, List<String>> getOnlineRemoteAddress(Serializable groupid) {
@@ -296,6 +315,7 @@ public abstract class WebSocket {
* *
* @param <T> 属性值的类型 * @param <T> 属性值的类型
* @param name 属性名 * @param name 属性名
*
* @return 属性值 * @return 属性值
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -308,6 +328,7 @@ public abstract class WebSocket {
* *
* @param <T> 属性值的类型 * @param <T> 属性值的类型
* @param name 属性名 * @param name 属性名
*
* @return 属性值 * @return 属性值
*/ */
public final <T> T removeAttribute(String name) { public final <T> T removeAttribute(String name) {
@@ -374,6 +395,7 @@ public abstract class WebSocket {
* 获取指定groupid的WebSocketGroup, 没有返回null * 获取指定groupid的WebSocketGroup, 没有返回null
* *
* @param groupid groupid * @param groupid groupid
*
* @return WebSocketGroup * @return WebSocketGroup
*/ */
protected final WebSocketGroup getWebSocketGroup(Serializable groupid) { protected final WebSocketGroup getWebSocketGroup(Serializable groupid) {
@@ -394,6 +416,7 @@ public abstract class WebSocket {
* 返回sessionid, null表示连接不合法或异常,默认实现是request.getSessionid(false),通常需要重写该方法 * 返回sessionid, null表示连接不合法或异常,默认实现是request.getSessionid(false),通常需要重写该方法
* *
* @param request HttpRequest * @param request HttpRequest
*
* @return sessionid * @return sessionid
*/ */
public Serializable onOpen(final HttpRequest request) { public Serializable onOpen(final HttpRequest request) {

View File

@@ -23,11 +23,12 @@ import org.redkale.util.*;
* WebSocketServlet * WebSocketServlet
* | * |
* | * |
* WebSocketEngine &#38; WebSocketNode * WebSocketEngine
* / \ * WebSocketNode
* / \ * / \
* / \ * / \
* WebSocketGroup1 WebSocketGroup2 * / \
* WebSocketGroup1 WebSocketGroup2
* / \ / \ * / \ / \
* / \ / \ * / \ / \
* WebSocket1 WebSocket2 WebSocket3 WebSocket4 * WebSocket1 WebSocket2 WebSocket3 WebSocket4
@@ -41,8 +42,10 @@ import org.redkale.util.*;
*/ */
public abstract class WebSocketServlet extends HttpServlet { public abstract class WebSocketServlet extends HttpServlet {
@Comment("WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒")
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval"; public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
@Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒")
public static final int DEFAILT_LIVEINTERVAL = 60; public static final int DEFAILT_LIVEINTERVAL = 60;
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); 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; protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
@Resource(name = "$") @Resource(name = "$")

View File

@@ -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) { 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 remoteRest0(jsonConvert, transport, addr0, action, params);
} }
return remoteSncp0(bsonConvert, transport, addr0, action, params); return remoteSncp0(bsonConvert, transport, addr0, action, params);

View File

@@ -80,7 +80,7 @@ public final class SncpDynServlet extends SncpServlet {
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); 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(); int len = maxClassNameLength - type.getName().length();
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
sb.append(' '); sb.append(' ');

View File

@@ -84,13 +84,23 @@ public class DataSourceService implements DataSource, Service, AutoCloseable {
} }
@Override @Override
public <T> int updateColumns(T bean, final String... columns) { public <T> int updateColumn(T bean, final String... columns) {
return source.updateColumns(bean, columns); return source.updateColumn(bean, columns);
} }
@Override @Override
public <T> int updateColumns(T bean, final FilterNode node, final String... columns) { public <T> int updateColumn(T bean, final FilterNode node, final String... columns) {
return source.updateColumns(bean, node, 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 @Override
@@ -123,6 +133,21 @@ public class DataSourceService implements DataSource, Service, AutoCloseable {
return source.getNumberResult(entityClass, func, defVal, column, node); 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 @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) { 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); return source.queryColumnMap(entityClass, keyColumn, func, funcColumn);

View File

@@ -10,12 +10,12 @@ import java.lang.reflect.*;
import java.util.*; import java.util.*;
/** /**
* 用于定义错误码的注解 * 用于定义错误码的注解 <br>
* 结果码定义范围: * 结果码定义范围: <br>
* // 10000001 - 19999999 预留给Redkale的核心包使用 * // 10000001 - 19999999 预留给Redkale的核心包使用 <br>
* // 20000001 - 29999999 预留给Redkale的扩展包使用 * // 20000001 - 29999999 预留给Redkale的扩展包使用 <br>
* // 30000001 - 99999999 预留给Dev开发系统自身使用 * // 30000001 - 99999999 预留给Dev开发系统自身使用 <br>
* * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx

View File

@@ -8,13 +8,13 @@ package org.redkale.service;
import org.redkale.convert.json.*; import org.redkale.convert.json.*;
/** /**
* 通用的结果对象在常见的HTTP+JSON接口中返回的结果需要含结果码错误信息和实体对象。 * 通用的结果对象在常见的HTTP+JSON接口中返回的结果需要含结果码错误信息和实体对象。 <br>
* 通常前四位为模块,后四位为操作。 * 结果码定义通常前四位为模块,后四位为操作。<br>
* 结果码定义范围: * 结果码定义范围: <br>
* // 10000001 - 19999999 预留给Redkale的核心包使用 * // 10000001 - 19999999 预留给Redkale的核心包使用 <br>
* // 20000001 - 29999999 预留给Redkale的扩展包使用 * // 20000001 - 29999999 预留给Redkale的扩展包使用 <br>
* // 30000001 - 99999999 预留给Dev开发系统自身使用 * // 30000001 - 99999999 预留给Dev开发系统自身使用 <br>
* * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
@@ -22,13 +22,6 @@ import org.redkale.convert.json.*;
*/ */
public class RetResult<T> { public class RetResult<T> {
/**
* 使用 RetResult.success() 方法代替
*
* @deprecated
*/
private static final RetResult SUCCESS = new RetResult();
protected int retcode; protected int retcode;
protected String retinfo; protected String retinfo;
@@ -119,10 +112,20 @@ public class RetResult<T> {
this.retcode = retcode; this.retcode = retcode;
} }
/**
* 结果信息通常retcode != 0时值为错误信息
*
* @return 结果信息
*/
public String getRetinfo() { public String getRetinfo() {
return retinfo; return retinfo;
} }
/**
* 设置结果信息
*
* @param retinfo 结果信息
*/
public void setRetinfo(String retinfo) { public void setRetinfo(String retinfo) {
this.retinfo = retinfo; this.retinfo = retinfo;
} }
@@ -136,6 +139,11 @@ public class RetResult<T> {
return result; return result;
} }
/**
* 设置结果对象
*
* @param result T
*/
public void setResult(T result) { public void setResult(T result) {
this.result = result; this.result = result;
} }

View File

@@ -14,9 +14,24 @@ package org.redkale.source;
* @author zhangjx * @author zhangjx
*/ */
public enum ColumnExpress { public enum ColumnExpress {
MOV, //直接赋值 col = val /**
INC, //追加值 col = col + val * 直接赋值 col = val
MUL, //乘值 col = col * val */
AND, //与值 col = col & val MOV,
ORR; //或值 col = col | val /**
* 追加值 col = col + val
*/
INC,
/**
* 乘值 col = col * val
*/
MUL,
/**
* 与值 col = col &#38; val
*/
AND, //与值 col = col & val
/**
* 或值 col = col | val
*/
ORR;
} }

View File

@@ -10,6 +10,7 @@ import static org.redkale.source.ColumnExpress.*;
/** /**
* ColumnValue主要用于多个字段更新的表达式。 * ColumnValue主要用于多个字段更新的表达式。
* 用于 DataSource.updateColumn 方法
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -37,26 +38,74 @@ public class ColumnValue {
this.value = value; this.value = value;
} }
/**
* 同 mov 操作
*
* @param column 字段名
* @param value 字段值
*
* @return ColumnValue
*/
public static ColumnValue create(String column, Serializable value) { public static ColumnValue create(String column, Serializable value) {
return new ColumnValue(column, value); return new ColumnValue(column, value);
} }
/**
* 返回 {column} = {value} 操作
*
* @param column 字段名
* @param value 字段值
*
* @return ColumnValue
*/
public static ColumnValue mov(String column, Serializable value) { public static ColumnValue mov(String column, Serializable value) {
return new ColumnValue(column, MOV, value); return new ColumnValue(column, MOV, value);
} }
/**
* 返回 {column} = {column} + {value} 操作
*
* @param column 字段名
* @param value 字段值
*
* @return ColumnValue
*/
public static ColumnValue inc(String column, Serializable value) { public static ColumnValue inc(String column, Serializable value) {
return new ColumnValue(column, INC, value); return new ColumnValue(column, INC, value);
} }
/**
* 返回 {column} = {column} * {value} 操作
*
* @param column 字段名
* @param value 字段值
*
* @return ColumnValue
*/
public static ColumnValue mul(String column, Serializable value) { public static ColumnValue mul(String column, Serializable value) {
return new ColumnValue(column, MUL, value); return new ColumnValue(column, MUL, value);
} }
/**
* 返回 {column} = {column} &#38; {value} 操作
*
* @param column 字段名
* @param value 字段值
*
* @return ColumnValue
*/
public static ColumnValue and(String column, Serializable value) { public static ColumnValue and(String column, Serializable value) {
return new ColumnValue(column, AND, value); return new ColumnValue(column, AND, value);
} }
/**
* 返回 {column} = {column} | {value} 操作
*
* @param column 字段名
* @param value 字段值
*
* @return ColumnValue
*/
public static ColumnValue orr(String column, Serializable value) { public static ColumnValue orr(String column, Serializable value) {
return new ColumnValue(column, ORR, value); return new ColumnValue(column, ORR, value);
} }

View File

@@ -6,7 +6,6 @@
package org.redkale.source; package org.redkale.source;
import java.io.*; import java.io.*;
import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
@@ -14,11 +13,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.*; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.sql.ConnectionPoolDataSource;
import javax.xml.stream.*; import javax.xml.stream.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* DataSource的JDBC实现类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -30,26 +29,6 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
public static final String DATASOURCE_CONFPATH = "DATASOURCE_CONFPATH"; 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); private static final Flipper FLIPPER_ONE = new Flipper(1);
final Logger logger = Logger.getLogger(DataDefaultSource.class.getSimpleName()); 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; private final JDBCPoolSource writePool;
@Resource(name = "property.datasource.nodeid")
private int nodeid;
@Resource(name = "$") @Resource(name = "$")
private DataCacheListener cacheListener; private DataCacheListener cacheListener;
@@ -182,58 +158,6 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
return map; 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() { public final String name() {
return name; return name;
} }
@@ -271,7 +195,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
} }
private <T> EntityInfo<T> loadEntityInfo(Class<T> clazz) { 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 @Override
public <T> void insert(T... values) { public <T> void insert(T... values) {
if (values.length == 0) return; 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()); final EntityInfo<T> info = loadEntityInfo((Class<T>) values[0].getClass());
if (info.isVirtualEntity()) { if (info.isVirtualEntity()) {
insert(null, info, values); 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 String sql = info.getInsertSQL(values[0]);
final Class primaryType = info.getPrimary().type(); final Class primaryType = info.getPrimary().type();
final Attribute primary = info.getPrimary(); final Attribute primary = info.getPrimary();
final boolean distributed = info.distributed;
Attribute<T, Serializable>[] attrs = info.insertAttributes; Attribute<T, Serializable>[] attrs = info.insertAttributes;
if (distributed && !info.initedPrimaryValue && primaryType.isPrimitive()) { //由DataSource生成主键 conn.setReadOnly(false);
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;
}
}
}
PreparedStatement prestmt = createInsertPreparedStatement(conn, sql, info, values); PreparedStatement prestmt = createInsertPreparedStatement(conn, sql, info, values);
try { try {
prestmt.executeBatch(); prestmt.executeBatch();
@@ -442,9 +354,16 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
for (final T value : values) { for (final T value : values) {
int i = 0; int i = 0;
if (info.distributed || info.autouuid) info.createPrimaryValue(value); if (info.autouuid) info.createPrimaryValue(value);
for (Attribute<T, Serializable> attr : attrs) { 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(); prestmt.addBatch();
} }
@@ -473,6 +392,18 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
@Override @Override
public <T> int delete(T... values) { public <T> int delete(T... values) {
if (values.length == 0) return -1; 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()); final EntityInfo<T> info = loadEntityInfo((Class<T>) values[0].getClass());
if (info.isVirtualEntity()) { //虚拟表只更新缓存Cache if (info.isVirtualEntity()) { //虚拟表只更新缓存Cache
return delete(null, info, values); return delete(null, info, values);
@@ -516,6 +447,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
int c2 = 0; int c2 = 0;
try { try {
if (!info.isVirtualEntity()) { if (!info.isVirtualEntity()) {
conn.setReadOnly(false);
final Statement stmt = conn.createStatement(); final Statement stmt = conn.createStatement();
for (Serializable key : keys) { for (Serializable key : keys) {
String sql = "DELETE FROM " + info.getTable(key) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(key); 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) : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) + info.createSQLOrderby(flipper)
+ ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit())); + ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit()));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
conn.setReadOnly(false);
final Statement stmt = conn.createStatement(); final Statement stmt = conn.createStatement();
c = stmt.executeUpdate(sql); c = stmt.executeUpdate(sql);
stmt.close(); stmt.close();
@@ -631,6 +564,18 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
@Override @Override
public <T> int update(T... values) { public <T> int update(T... values) {
if (values.length == 0) return 0; 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()); final EntityInfo<T> info = loadEntityInfo((Class<T>) values[0].getClass());
if (info.isVirtualEntity()) { if (info.isVirtualEntity()) {
return update(null, info, values); return update(null, info, values);
@@ -650,6 +595,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
if (!info.isVirtualEntity()) { if (!info.isVirtualEntity()) {
final String updateSQL = info.getUpdateSQL(values[0]); final String updateSQL = info.getUpdateSQL(values[0]);
final Attribute<T, Serializable> primary = info.getPrimary(); final Attribute<T, Serializable> primary = info.getPrimary();
conn.setReadOnly(false);
final PreparedStatement prestmt = conn.prepareStatement(updateSQL); final PreparedStatement prestmt = conn.prepareStatement(updateSQL);
Attribute<T, Serializable>[] attrs = info.updateAttributes; Attribute<T, Serializable>[] attrs = info.updateAttributes;
final boolean debugfinest = debug.get() && info.isLoggable(Level.FINEST); 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) { for (final T value : values) {
int k = 0; int k = 0;
for (Attribute<T, Serializable> attr : attrs) { 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.setObject(++k, primary.get(value));
prestmt.addBatch();//------------------------------------------------------------ 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 { try {
int c = -1; int c = -1;
if (!info.isVirtualEntity()) { if (!info.isVirtualEntity()) {
String sql = "UPDATE " + info.getTable(id) + " SET " + info.getSQLColumn(null, column) + " = " if (value instanceof byte[]) {
+ info.formatToString(value) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id); 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); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
final Statement stmt = conn.createStatement(); conn.setReadOnly(false);
c = stmt.executeUpdate(sql); final PreparedStatement stmt = conn.prepareStatement(sql);
stmt.close(); 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(); 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 { try {
int c = -1; int c = -1;
if (!info.isVirtualEntity()) { if (!info.isVirtualEntity()) {
@@ -790,14 +756,30 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0); join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0);
join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0);
} }
String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) if (value instanceof byte[]) {
+ " SET " + info.getSQLColumn("a", column) + " = " + info.formatToString(value) String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1))
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + " SET " + info.getSQLColumn("a", column) + " = ?"
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql); : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
final Statement stmt = conn.createStatement(); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
c = stmt.executeUpdate(sql); conn.setReadOnly(false);
stmt.close(); 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(); 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<Attribute<T, Serializable>> attrs = new ArrayList<>();
final List<ColumnValue> cols = new ArrayList<>(); final List<ColumnValue> cols = new ArrayList<>();
final boolean virtual = info.isVirtualEntity(); final boolean virtual = info.isVirtualEntity();
List<byte[]> blobs = null;
for (ColumnValue col : values) { for (ColumnValue col : values) {
Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn()); Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn());
if (attr == null) continue; if (attr == null) continue;
@@ -851,16 +834,35 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
if (!virtual) { if (!virtual) {
if (setsql.length() > 0) setsql.append(", "); if (setsql.length() > 0) setsql.append(", ");
String c = info.getSQLColumn(null, col.getColumn()); 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; int c = -1;
if (!virtual) { if (!virtual) {
String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id); 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); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + ": " + sql);
final Statement stmt = conn.createStatement(); conn.setReadOnly(false);
c = stmt.executeUpdate(sql); if (blobs != null) {
stmt.close(); 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(); 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<Attribute<T, Serializable>> attrs = new ArrayList<>();
final List<ColumnValue> cols = new ArrayList<>(); final List<ColumnValue> cols = new ArrayList<>();
final boolean virtual = info.isVirtualEntity(); final boolean virtual = info.isVirtualEntity();
List<byte[]> blobs = null;
for (ColumnValue col : values) { for (ColumnValue col : values) {
Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn()); Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn());
if (attr == null) continue; if (attr == null) continue;
@@ -937,7 +940,13 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
if (!virtual) { if (!virtual) {
if (setsql.length() > 0) setsql.append(", "); if (setsql.length() > 0) setsql.append(", ");
String c = info.getSQLColumn("a", col.getColumn()); 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; int c = -1;
@@ -958,9 +967,22 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
//注LIMIT 仅支持MySQL 且在多表关联式会异常, 该BUG尚未解决 //注LIMIT 仅支持MySQL 且在多表关联式会异常, 该BUG尚未解决
sql += info.createSQLOrderby(flipper) + ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit())); 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); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
final Statement stmt = conn.createStatement(); conn.setReadOnly(false);
c = stmt.executeUpdate(sql); if (blobs != null) {
stmt.close(); 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(); 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 @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()); final EntityInfo<T> info = loadEntityInfo((Class<T>) bean.getClass());
if (info.isVirtualEntity()) { if (info.isVirtualEntity()) {
return updateColumns(null, info, bean, columns); return updateColumns(null, info, bean, selects);
} }
Connection conn = createWriteSQLConnection(); Connection conn = createWriteSQLConnection();
try { try {
return updateColumns(conn, info, bean, columns); return updateColumns(conn, info, bean, selects);
} finally { } finally {
closeSQLConnection(conn); closeSQLConnection(conn);
} }
} }
private <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final String... columns) { private <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final SelectColumn selects) {
if (bean == null || columns.length < 1) return -1; if (bean == null || selects == null) return -1;
try { try {
final Class<T> clazz = (Class<T>) bean.getClass(); final Class<T> clazz = (Class<T>) bean.getClass();
StringBuilder setsql = new StringBuilder(); StringBuilder setsql = new StringBuilder();
final Serializable id = info.getPrimary().get(bean); final Serializable id = info.getPrimary().get(bean);
final List<Attribute<T, Serializable>> attrs = new ArrayList<>(); final List<Attribute<T, Serializable>> attrs = new ArrayList<>();
List<byte[]> blobs = null;
final boolean virtual = info.isVirtualEntity(); final boolean virtual = info.isVirtualEntity();
for (String col : columns) { for (Attribute<T, Serializable> attr : info.updateAttributes) {
Attribute<T, Serializable> attr = info.getUpdateAttribute(col); if (!selects.test(attr.field())) continue;
if (attr == null) continue;
attrs.add(attr); attrs.add(attr);
if (!virtual) { if (!virtual) {
if (setsql.length() > 0) setsql.append(", "); 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; int c = -1;
if (!virtual) { if (!virtual) {
String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id); 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); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(bean.getClass().getSimpleName() + ": " + sql);
final Statement stmt = conn.createStatement(); conn.setReadOnly(false);
c = stmt.executeUpdate(sql); if (blobs != null) {
stmt.close(); 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(); 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 @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()); final EntityInfo<T> info = loadEntityInfo((Class<T>) bean.getClass());
if (info.isVirtualEntity()) { if (info.isVirtualEntity()) {
return updateColumns(null, info, bean, node, columns); return updateColumns(null, info, bean, node, selects);
} }
Connection conn = createWriteSQLConnection(); Connection conn = createWriteSQLConnection();
try { try {
return updateColumns(conn, info, bean, node, columns); return updateColumns(conn, info, bean, node, selects);
} finally { } finally {
closeSQLConnection(conn); closeSQLConnection(conn);
} }
} }
private <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final FilterNode node, final String... columns) { 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 || columns.length < 1) return -1; if (bean == null || node == null || selects == null) return -1;
try { try {
final Class<T> clazz = (Class<T>) bean.getClass(); final Class<T> clazz = (Class<T>) bean.getClass();
StringBuilder setsql = new StringBuilder(); StringBuilder setsql = new StringBuilder();
final Serializable id = info.getPrimary().get(bean); final Serializable id = info.getPrimary().get(bean);
final List<Attribute<T, Serializable>> attrs = new ArrayList<>(); final List<Attribute<T, Serializable>> attrs = new ArrayList<>();
List<byte[]> blobs = null;
final boolean virtual = info.isVirtualEntity(); final boolean virtual = info.isVirtualEntity();
for (String col : columns) { for (Attribute<T, Serializable> attr : info.updateAttributes) {
Attribute<T, Serializable> attr = info.getUpdateAttribute(col); if (!selects.test(attr.field())) continue;
if (attr == null) continue;
attrs.add(attr); attrs.add(attr);
if (!virtual) { if (!virtual) {
if (setsql.length() > 0) setsql.append(", "); 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; 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 == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
final Statement stmt = conn.createStatement(); conn.setReadOnly(false);
c = stmt.executeUpdate(sql); if (blobs != null) {
stmt.close(); 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(); 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)); 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 @Override
public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) {
final EntityInfo info = loadEntityInfo(entityClass); 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" 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)); + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement prestmt = conn.prepareStatement(sql); final PreparedStatement prestmt = conn.prepareStatement(sql);
Number rs = defVal; Number rs = defVal;
ResultSet set = prestmt.executeQuery(); 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))) 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; + " 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); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement prestmt = conn.prepareStatement(sql); final PreparedStatement prestmt = conn.prepareStatement(sql);
Map<K, N> rs = new LinkedHashMap<>(); Map<K, N> rs = new LinkedHashMap<>();
ResultSet set = prestmt.executeQuery(); ResultSet set = prestmt.executeQuery();
@@ -1268,7 +1399,9 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
final SelectColumn sels = selects; final SelectColumn sels = selects;
final String sql = "SELECT * FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk); 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); 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); final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(1);
final ResultSet set = ps.executeQuery(); final ResultSet set = ps.executeQuery();
T rs = set.next() ? info.getValue(sels, set) : null; T rs = set.next() ? info.getValue(sels, set) : null;
set.close(); 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 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)); 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); 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); final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(1);
final ResultSet set = ps.executeQuery(); final ResultSet set = ps.executeQuery();
T rs = set.next() ? info.getValue(sels, set) : null; T rs = set.next() ? info.getValue(sels, set) : null;
set.close(); set.close();
@@ -1360,12 +1495,22 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
final Connection conn = createReadSQLConnection(); final Connection conn = createReadSQLConnection();
try { 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); 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); 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); final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(1);
final ResultSet set = ps.executeQuery(); final ResultSet set = ps.executeQuery();
Serializable val = defValue; 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(); set.close();
ps.close(); ps.close();
return val == null ? defValue : val; return val == null ? defValue : val;
@@ -1392,15 +1537,25 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
final Connection conn = createReadSQLConnection(); final Connection conn = createReadSQLConnection();
try { try {
final Attribute<T, Serializable> attr = info.getAttribute(column);
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis(); 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 join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); 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)); 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); 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); final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(1);
final ResultSet set = ps.executeQuery(); final ResultSet set = ps.executeQuery();
Serializable val = defValue; 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(); set.close();
ps.close(); ps.close();
return val == null ? defValue : val; return val == null ? defValue : val;
@@ -1429,6 +1584,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
try { try {
final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk); final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
if (log) logstr = clazz.getSimpleName() + " exists sql=" + sql; 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 PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
final ResultSet set = ps.executeQuery(); final ResultSet set = ps.executeQuery();
boolean rs = set.next() ? (set.getInt(1) > 0) : false; 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 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)); 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; 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 PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
final ResultSet set = ps.executeQuery(); final ResultSet set = ps.executeQuery();
boolean rs = set.next() ? (set.getInt(1) > 0) : false; boolean rs = set.next() ? (set.getInt(1) > 0) : false;
@@ -1563,16 +1720,6 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
return rs; 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)) { if (debug.get() && info.isLoggable(Level.FINEST)) {
logger.finest(clazz.getSimpleName() + " query sql=" + sql + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getOffset() + "," + flipper.getLimit()))); 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); 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(); final ResultSet set = ps.executeQuery();
if (flipper != null && flipper.getOffset() > 0) set.absolute(flipper.getOffset()); if (flipper != null && flipper.getOffset() > 0) set.absolute(flipper.getOffset());
final int limit = flipper == null || flipper.getLimit() < 1 ? Integer.MAX_VALUE : flipper.getLimit(); 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) { private int[] directExecute(final Connection conn, String... sqls) {
if (sqls.length == 0) return new int[0]; if (sqls.length == 0) return new int[0];
try { try {
conn.setReadOnly(false);
final Statement stmt = conn.createStatement(); final Statement stmt = conn.createStatement();
final int[] rs = new int[sqls.length]; final int[] rs = new int[sqls.length];
int i = -1; int i = -1;
@@ -1785,6 +1935,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
final Connection conn = createReadSQLConnection(); final Connection conn = createReadSQLConnection();
try { try {
if (debug.get()) logger.finest("direct query sql=" + sql); 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 PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
final ResultSet set = ps.executeQuery(); final ResultSet set = ps.executeQuery();
consumer.accept(set); consumer.accept(set);

View File

@@ -25,7 +25,7 @@ public interface DataSource {
//----------------------insert----------------------------- //----------------------insert-----------------------------
/** /**
* 新增对象, 必须是Entity对象 * 新增记录, 多对象必须是同一个Entity类 <br>
* *
* @param <T> 泛型 * @param <T> 泛型
* @param values Entity对象 * @param values Entity对象
@@ -34,81 +34,385 @@ public interface DataSource {
//-------------------------delete-------------------------- //-------------------------delete--------------------------
/** /**
* 删除对象, 必须是Entity对象 * 删除指定主键值的记录, 多对象必须是同一个Entity类 <br>
* 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id} <br>
* *
* @param <T> 泛型 * @param <T> 泛型
* @param values Entity对象 * @param values Entity对象
* *
* @return 删除的数据条数 * @return 影响的记录条数
*/ */
public <T> int delete(final T... values); public <T> int delete(final T... values);
/** /**
* 根据主键值删除数据 * 删除指定主键值的记录 <br>
* 等价SQL: DELETE FROM WHERE {primary} IN {ids} * 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids} <br>
* *
* @param <T> Entity类的泛型 * @param <T> Entity泛型
* @param clazz Entity类 * @param clazz Entity类
* @param ids 主键值 * @param ids 主键值
* *
* @return 删除的数据条数 * @return 影响的记录条数
*/ */
public <T> int delete(final Class<T> clazz, final Serializable... ids); 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); 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); public <T> int delete(final Class<T> clazz, final Flipper flipper, final FilterNode node);
//------------------------update--------------------------- //------------------------update---------------------------
/** /**
* 更新对象, 必须是Entity对象 * 更新记录, 多对象必须是同一个Entity类 <br>
* 等价SQL: <br>
* UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id1} <br>
* UPDATE {table} SET column1 = value1, column2 = value2, &#183;&#183;&#183; WHERE {primary} = {id2} <br>
* &#183;&#183;&#183; <br>
* *
* @param <T> 泛型 * @param <T> 泛型
* @param values Entity对象 * @param values Entity对象
* *
* @return 更新的数据条数 * @return 影响的记录条数
*/ */
public <T> int update(final T... values); public <T> int update(final T... values);
/**
* 更新单个记录的单个字段 <br>
* <b>注意</b>:即使字段标记为&#064;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); public <T> int updateColumn(final Class<T> clazz, final Serializable id, final String column, final Serializable value);
/**
* 更新符合过滤条件记录的单个字段 <br>
* <b>注意</b>:即使字段标记为&#064;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); 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}, &#183;&#183;&#183; 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); public <T> int updateColumn(final Class<T> clazz, final Serializable id, final ColumnValue... values);
/**
* 更新符合过滤条件记录的部分字段 <br>
* 字段赋值操作选项见 ColumnExpress <br>
* <b>注意</b>Entity类中标记为&#064;Column(updatable=false)不会被更新 <br>
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, &#183;&#183;&#183; 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); public <T> int updateColumn(final Class<T> clazz, final FilterNode node, final ColumnValue... values);
/**
* 更新符合过滤条件的记录的指定字段 <br>
* Flipper中offset字段将被忽略 <br>
* <b>注意</b>Entity类中标记为&#064;Column(updatable=false)不会被更新 <br>
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, &#183;&#183;&#183; 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 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类中标记为&#064;Column(updatable=false)不会被更新 <br>
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, &#183;&#183;&#183; 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类中标记为&#064;Column(updatable=false)不会被更新 <br>
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, &#183;&#183;&#183; 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类中标记为&#064;Column(updatable=false)不会被更新 <br>
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, &#183;&#183;&#183; 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类中标记为&#064;Column(updatable=false)不会被更新 <br>
* 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, &#183;&#183;&#183; 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----------------------------- //-----------------------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); 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); 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); 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); 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); 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); 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}, &#183;&#183;&#183; 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}, &#183;&#183;&#183; 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}, &#183;&#183;&#183; 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); 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); 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); 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---------------------------- //-----------------------find----------------------------
/** /**
* 根据主键获取对象 * 获取指定主键值的单个记录, 返回null表示不存在值 <br>
* 等价SQL: SELECT * FROM {table} WHERE {primary} = {id} <br>
* *
* @param <T> 泛型 * @param <T> Entity泛型
* @param clazz Entity类 * @param clazz Entity类
* @param pk 主键值 * @param pk 主键值
* *
@@ -116,43 +420,207 @@ public interface DataSource {
*/ */
public <T> T find(final Class<T> clazz, final Serializable pk); public <T> T find(final Class<T> clazz, final Serializable pk);
/**
* 获取指定主键值的单个记录, 返回null表示不存在值 <br>
* 等价SQL: SELECT {column1},{column2}, &#183;&#183;&#183; 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); 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); 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); 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); public <T> T find(final Class<T> clazz, final FilterNode node);
/**
* 获取符合过滤条件单个记录, 返回null表示不存在值 <br>
* 等价SQL: SELECT {column1},{column2}, &#183;&#183;&#183; 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); public <T> T find(final Class<T> clazz, final SelectColumn selects, final FilterBean bean);
/**
* 获取符合过滤条件单个记录, 返回null表示不存在值 <br>
* 等价SQL: SELECT {column1},{column2}, &#183;&#183;&#183; 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); public <T> boolean exists(final Class<T> clazz, final FilterNode node);
//-----------------------list set---------------------------- //-----------------------list set----------------------------
/** /**
* 根据指定字段值查询对象某个字段的集合 * 查询符合过滤条件记录的某个字段Set集合 <br>
* 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {column} = {key} <br>
* *
* @param <T> Entity泛型 * @param <T> Entity泛型
* @param <V> 字段类型 * @param <V> 字段类型
* @param selectedColumn 字段 * @param selectedColumn 指定字段
* @param clazz Entity类 * @param clazz Entity类
* @param column 过滤字段名 * @param column 过滤字段名
* @param key 过滤字段值 * @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 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 <T> Entity泛型
* @param <V> 字段类型 * @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 clazz Entity类
* @param flipper 翻页对象 * @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); 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); 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 <T> Entity泛型
* @param clazz Entity类 * @param clazz Entity类
* @param column 过滤字段名 * @param column 过滤字段名
* @param key 过滤字段值 * @param key 过滤字段值
* *
* @return Entity的List * @return Entity的集合
*/ */
public <T> List<T> queryList(final Class<T> clazz, final String column, final Serializable key); 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); 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); public <T> List<T> queryList(final Class<T> clazz, final FilterNode node);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT {column1},{column2}, &#183;&#183;&#183; 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); public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final FilterBean bean);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT {column1},{column2}, &#183;&#183;&#183; 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); 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); 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); 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); public <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final FilterNode node);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT {column1},{column2}, &#183;&#183;&#183; 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); public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT {column1},{column2}, &#183;&#183;&#183; 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); public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node);
//-----------------------sheet---------------------------- //-----------------------sheet----------------------------
/** /**
* 根据指定参数查询对象某个对象的集合页 * 查询符合过滤条件记录的Sheet集合 <br>
* <p> * 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <T> Entity泛型 * @param <T> Entity泛型
* @param clazz Entity类 * @param clazz Entity类
* @param flipper 翻页对象 * @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); 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); public <T> Sheet<T> querySheet(final Class<T> clazz, final Flipper flipper, final FilterNode node);
/**
* 查询符合过滤条件记录的Sheet集合 <br>
* 等价SQL: SELECT {column1},{column2}, &#183;&#183;&#183; 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); public <T> Sheet<T> querySheet(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean);
/**
* 查询符合过滤条件记录的Sheet集合 <br>
* 等价SQL: SELECT {column1},{column2}, &#183;&#183;&#183; 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); public <T> Sheet<T> querySheet(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node);
//-----------------------direct---------------------------- //-----------------------direct----------------------------
/** /**
* 直接本地执行SQL语句进行查询远程模式不可用 * 直接本地执行SQL语句进行查询远程模式不可用 <br>
* 通常用于复杂的关联查询 * 通常用于复杂的关联查询 <br>
* *
* @param sql SQL语句 * @param sql SQL语句
* @param consumer 回调函数 * @param consumer 回调函数
@@ -251,8 +957,8 @@ public interface DataSource {
public void directQuery(String sql, final Consumer<ResultSet> consumer); public void directQuery(String sql, final Consumer<ResultSet> consumer);
/** /**
* 直接本地执行SQL语句进行增删改操作远程模式不可用 * 直接本地执行SQL语句进行增删改操作远程模式不可用 <br>
* 通常用于复杂的更新操作 * 通常用于复杂的更新操作 <br>
* *
* @param sqls SQL语句 * @param sqls SQL语句
* *

View File

@@ -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;
}

View File

@@ -10,6 +10,8 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Entity分库分表的注解需要结合DistributeTableStrategy使用 <br>
* 标记为 &#64;DistributeTable的Entity类视为需要进行分库分表操作 <br>
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -8,6 +8,9 @@ package org.redkale.source;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 分表分库策略,结合&#64;DistributeTable使用 <br>
* 不能与&#64;Cacheable同时使用 <br>
* 使用分表分库功能重点是主键的生成策略,不同场景生成策略不一样 <br>
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -18,35 +21,19 @@ import java.io.Serializable;
public interface DistributeTableStrategy<T> { public interface DistributeTableStrategy<T> {
/** /**
* 获取对象的表名 * 获取对象的表名 <br>
* 查询单个对象时调用本方法获取表名 * 查询单个对象DataSource.find时调用本方法获取表名 <br>
* *
* @param table 模板表的表名 * @param table 模板表的表名
* @param primary 记录主键 * @param primary 记录主键
* *
* @return 带库名的全表名 * @return 带库名的全表名
*/ */
default String getTable(String table, Serializable primary) { public String getTable(String table, Serializable primary);
return null;
}
/** /**
* 获取对象的表名 * 获取对象的表名 <br>
* 查询、修改、删除对象时调用本方法获取表名 * 新增对象或更新单个对象DataSource.insert、DataSource.update时调用本方法获取表名 <br>
* 注意: 需保证FilterNode过滤的结果集合必须在一个数据库表中
*
* @param table 模板表的表名
* @param node 过滤条件
*
* @return 带库名的全表名
*/
default String getTable(String table, FilterNode node) {
return null;
}
/**
* 获取对象的表名
* 新增对象或更新单个对象时调用本方法获取表名
* *
* @param table 模板表的表名 * @param table 模板表的表名
* @param bean 实体对象 * @param bean 实体对象
@@ -54,4 +41,17 @@ public interface DistributeTableStrategy<T> {
* @return 带库名的全表名 * @return 带库名的全表名
*/ */
public String getTable(String table, T bean); 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);
} }

View File

@@ -17,6 +17,7 @@ import static org.redkale.source.FilterFunc.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* Entity数据的缓存类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -27,33 +28,51 @@ import org.redkale.util.*;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class EntityCache<T> { public final class EntityCache<T> {
//日志
private static final Logger logger = Logger.getLogger(EntityCache.class.getName()); 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%; // 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<>(); private final Map<String, Comparator<T>> sortComparators = new ConcurrentHashMap<>();
//Entity类
private final Class<T> type; private final Class<T> type;
//接口返回的对象是否需要复制一份
private final boolean needcopy; private final boolean needcopy;
//Entity构建器
private final Creator<T> creator; private final Creator<T> creator;
//主键字段
private final Attribute<T, Serializable> primary; private final Attribute<T, Serializable> primary;
//新增时的复制器, 排除了标记为&#064;Transient的字段
private final Reproduce<T, T> newReproduce; private final Reproduce<T, T> newReproduce;
//修改时的复制器, 排除了标记为&#064;Transient或&#064;Column(updatable=false)的字段
private final Reproduce<T, T> chgReproduce; private final Reproduce<T, T> chgReproduce;
//是否已经全量加载过
private volatile boolean fullloaded; private volatile boolean fullloaded;
//Entity信息
final EntityInfo<T> info; final EntityInfo<T> info;
public EntityCache(final EntityInfo<T> info) { //&#064;Cacheable的定时更新秒数为0表示不定时更新
final int interval;
//&#064;Cacheable的定时器
private ScheduledThreadPoolExecutor scheduler;
public EntityCache(final EntityInfo<T> info, final Cacheable c) {
this.info = info; this.info = info;
this.interval = c == null ? 0 : c.interval();
this.type = info.getType(); this.type = info.getType();
this.creator = info.getCreator(); this.creator = info.getCreator();
this.primary = info.primary; this.primary = info.primary;
@@ -80,15 +99,35 @@ public final class EntityCache<T> {
public void fullLoad() { public void fullLoad() {
if (info.fullloader == null) return; if (info.fullloader == null) return;
clear(); this.fullloaded = false;
ConcurrentHashMap newmap = new ConcurrentHashMap();
List<T> all = info.fullloader.apply(info.source, type); List<T> all = info.fullloader.apply(info.source, type);
if (all != null) { if (all != null) {
all.stream().filter(x -> x != null).forEach(x -> { 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; 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() { public Class<T> getType() {
@@ -97,8 +136,12 @@ public final class EntityCache<T> {
public void clear() { public void clear() {
this.fullloaded = false; this.fullloaded = false;
this.list.clear(); this.list = new ConcurrentLinkedQueue();
this.map.clear(); this.map = new ConcurrentHashMap();
if (this.scheduler != null) {
this.scheduler.shutdownNow();
this.scheduler = null;
}
} }
public boolean isFullLoaded() { public boolean isFullLoaded() {

View File

@@ -11,14 +11,13 @@ import java.lang.reflect.*;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*; import java.util.function.*;
import java.util.logging.Level; import java.util.logging.Level;
import javax.persistence.*; import javax.persistence.*;
import static org.redkale.source.DataDefaultSource.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* Entity操作类
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -29,94 +28,116 @@ import org.redkale.util.*;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class EntityInfo<T> { public final class EntityInfo<T> {
//全局静态资源
private static final ConcurrentHashMap<Class, EntityInfo> entityInfos = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<Class, EntityInfo> entityInfos = new ConcurrentHashMap<>();
//日志
private static final Logger logger = Logger.getLogger(EntityInfo.class); private static final Logger logger = Logger.getLogger(EntityInfo.class);
//Entity类的类 //Entity类名
private final Class<T> type; private final Class<T> type;
//类对应的数据表名, 如果是VirtualEntity 类, 则该字段为null //类对应的数据表名, 如果是VirtualEntity 类, 则该字段为null
final String table; final String table;
//Entity构建器
private final Creator<T> creator; private final Creator<T> creator;
//主键 //主键
final Attribute<T, Serializable> primary; final Attribute<T, Serializable> primary;
//Entity缓存对象
private final EntityCache<T> cache; private final EntityCache<T> cache;
//key是field的name 不是sql字段。 //key是field的name 不是sql字段。
//存放所有与数据库对应的字段, 包括主键 //存放所有与数据库对应的字段, 包括主键
private final HashMap<String, Attribute<T, Serializable>> attributeMap = new HashMap<>(); private final HashMap<String, Attribute<T, Serializable>> attributeMap = new HashMap<>();
//存放所有与数据库对应的字段, 包括主键
final Attribute<T, Serializable>[] attributes; final Attribute<T, Serializable>[] attributes;
//key是field的name value是Column的别名即数据库表的字段名 //key是field的name value是Column的别名即数据库表的字段名
//只有field.name 与 Column.name不同才存放在aliasmap里. //只有field.name 与 Column.name不同才存放在aliasmap里.
private final Map<String, String> aliasmap; private final Map<String, String> aliasmap;
//所有可更新字段,即排除了主键字段和标记为&#064;Column(updatable=false)的字段
private final Map<String, Attribute<T, Serializable>> updateAttributeMap = new HashMap<>(); 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; final DistributeTableStrategy<T> tableStrategy;
//根据主键查找单个对象的SQL
final String querySQL; final String querySQL;
private final Attribute<T, Serializable>[] queryAttributes; //数据库中所有字段 //数据库中所有字段
private final Attribute<T, Serializable>[] queryAttributes;
//新增SQL 含 ?,即排除了自增长主键和标记为&#064;Column(insertable=false)的字段
private final String insertSQL; private final String insertSQL;
final Attribute<T, Serializable>[] insertAttributes; //数据库中所有可新增字段 //数据库中所有可新增字段
final Attribute<T, Serializable>[] insertAttributes;
//根据主键更新所有可更新字段的SQL
private final String updateSQL; private final String updateSQL;
final Attribute<T, Serializable>[] updateAttributes; //数据库中所有可更新字段 //数据库中所有可更新字段
final Attribute<T, Serializable>[] updateAttributes;
//根据主键删除记录的SQL
private final String deleteSQL; private final String deleteSQL;
//日志级别从LogLevel获取
private final int logLevel; private final int logLevel;
//Flipper.sort转换成以ORDER BY开头SQL的缓存
private final Map<String, String> sortOrderbySqls = new ConcurrentHashMap<>(); private final Map<String, String> sortOrderbySqls = new ConcurrentHashMap<>();
//---------------------计算主键值---------------------------- //是否由数据库生成主键值
private final int nodeid;
final boolean autoGenerated; final boolean autoGenerated;
//是否UUID主键
final boolean autouuid; final boolean autouuid;
final boolean distributed; //所属的DataSource
boolean initedPrimaryValue = false;
final AtomicLong primaryValue = new AtomicLong(0);
final int allocationSize;
final DataSource source; final DataSource source;
//全量数据的加载器
final BiFunction<DataSource, Class, List> fullloader; 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) { DataSource source, BiFunction<DataSource, Class, List> fullloader) {
EntityInfo rs = entityInfos.get(clazz); EntityInfo rs = entityInfos.get(clazz);
if (rs != null) return rs; if (rs != null) return rs;
synchronized (entityInfos) { synchronized (entityInfos) {
rs = entityInfos.get(clazz); rs = entityInfos.get(clazz);
if (rs == null) { if (rs == null) {
if (nodeid < 0) throw new IllegalArgumentException("nodeid(" + nodeid + ") is illegal"); rs = new EntityInfo(clazz, cacheForbidden, conf, source, fullloader);
rs = new EntityInfo(clazz, nodeid, cacheForbidden, conf, source, fullloader);
entityInfos.put(clazz, rs); entityInfos.put(clazz, rs);
if (rs.cache != null) { if (rs.cache != null) {
if (fullloader == null) throw new IllegalArgumentException(clazz.getName() + " auto loader is illegal"); 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) { static <T> EntityInfo<T> get(Class<T> clazz) {
return entityInfos.get(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) { Properties conf, DataSource source, BiFunction<DataSource, Class, List> fullloader) {
this.type = type; this.type = type;
this.source = source; this.source = source;
//--------------------------------------------- //---------------------------------------------
this.nodeid = nodeid >= 0 ? nodeid : 0;
LogLevel ll = type.getAnnotation(LogLevel.class); LogLevel ll = type.getAnnotation(LogLevel.class);
this.logLevel = ll == null ? Integer.MIN_VALUE : Level.parse(ll.value()).intValue(); 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<>(); List<Attribute<T, Serializable>> updateattrs = new ArrayList<>();
boolean auto = false; boolean auto = false;
boolean uuid = false; boolean uuid = false;
boolean sqldistribute = false;
int allocationSize0 = 0;
do { do {
for (Field field : cltmp.getDeclaredFields()) { for (Field field : cltmp.getDeclaredFields()) {
@@ -205,17 +240,6 @@ public final class EntityInfo<T> {
// if (gv != null && gv.strategy() != GenerationType.IDENTITY) { // if (gv != null && gv.strategy() != GenerationType.IDENTITY) {
// throw new RuntimeException(cltmp.getName() + "'s @ID primary not a 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 if (gv != null && field.getType() == String.class) { //UUID
uuid = true; uuid = true;
auto = false; auto = false;
@@ -272,118 +296,211 @@ public final class EntityInfo<T> {
} }
this.autoGenerated = auto; this.autoGenerated = auto;
this.autouuid = uuid; this.autouuid = uuid;
this.distributed = sqldistribute;
this.allocationSize = allocationSize0;
//----------------cache-------------- //----------------cache--------------
Cacheable c = type.getAnnotation(Cacheable.class); Cacheable c = type.getAnnotation(Cacheable.class);
if (this.table == null || (!cacheForbidden && c != null && c.value())) { if (this.table == null || (!cacheForbidden && c != null && c.value())) {
this.cache = new EntityCache<>(this); this.cache = new EntityCache<>(this, c);
} else { } else {
this.cache = null; this.cache = null;
} }
if (conf == null) conf = new Properties(); if (conf == null) conf = new Properties();
this.containSQL = conf.getProperty(JDBC_CONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) > 0"); this.containSQL = conf.getProperty(JDBCPoolSource.JDBC_CONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) > 0");
this.notcontainSQL = conf.getProperty(JDBC_NOTCONTAIN_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.tablenotexistSqlstates = ";" + conf.getProperty(JDBCPoolSource.JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02") + ";";
this.tablecopySQL = conf.getProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} LIKE ${oldtable}"); this.tablecopySQL = conf.getProperty(JDBCPoolSource.JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} LIKE ${oldtable}");
} }
/**
* 创建主键值目前只支持UUID赋值
*
* @param src Entity对象
*/
public void createPrimaryValue(T src) { public void createPrimaryValue(T src) {
if (autouuid) { if (autouuid) getPrimary().set(src, Utility.uuid());
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);
}
} }
/**
* 获取Entity缓存器
*
* @return EntityCache
*/
public EntityCache<T> getCache() { public EntityCache<T> getCache() {
return cache; return cache;
} }
/**
* 判断缓存器是否已经全量加载过
*
* @return boolean
*/
public boolean isCacheFullLoaded() { public boolean isCacheFullLoaded() {
return cache != null && cache.isFullLoaded(); return cache != null && cache.isFullLoaded();
} }
/**
* 获取Entity构建器
*
* @return Creator
*/
public Creator<T> getCreator() { public Creator<T> getCreator() {
return creator; return creator;
} }
/**
* 获取Entity类名
*
* @return Class
*/
public Class<T> getType() { public Class<T> getType() {
return type; return type;
} }
/** /**
* 是否虚拟类 * 判断Entity是否虚拟类
* *
* @return 是否虚拟类 * @return boolean
*/ */
public boolean isVirtualEntity() { public boolean isVirtualEntity() {
return table == null; return table == null;
} }
/**
* 获取Entity的INSERT SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getInsertSQL(T bean) { public String getInsertSQL(T bean) {
if (this.tableStrategy == null) return insertSQL; if (this.tableStrategy == null) return insertSQL;
return insertSQL.replace("${newtable}", getTable(bean)); return insertSQL.replace("${newtable}", getTable(bean));
} }
/**
* 获取Entity的UPDATE SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getUpdateSQL(T bean) { public String getUpdateSQL(T bean) {
if (this.tableStrategy == null) return updateSQL; if (this.tableStrategy == null) return updateSQL;
return updateSQL.replace("${newtable}", getTable(bean)); return updateSQL.replace("${newtable}", getTable(bean));
} }
/**
* 获取Entity的DELETE SQL
*
* @param bean Entity对象
*
* @return String
*/
public String getDeleteSQL(T bean) { public String getDeleteSQL(T bean) {
if (this.tableStrategy == null) return deleteSQL; if (this.tableStrategy == null) return deleteSQL;
return deleteSQL.replace("${newtable}", getTable(bean)); return deleteSQL.replace("${newtable}", getTable(bean));
} }
/**
* 根据主键值获取Entity的表名
*
* @param primary Entity主键值
*
* @return String
*/
public String getTable(Serializable primary) { public String getTable(Serializable primary) {
if (tableStrategy == null) return table; if (tableStrategy == null) return table;
String t = tableStrategy.getTable(table, primary); String t = tableStrategy.getTable(table, primary);
return t == null || t.isEmpty() ? table : t; return t == null || t.isEmpty() ? table : t;
} }
/**
* 根据过滤条件获取Entity的表名
*
* @param node 过滤条件
*
* @return String
*/
public String getTable(FilterNode node) { public String getTable(FilterNode node) {
if (tableStrategy == null) return table; if (tableStrategy == null) return table;
String t = tableStrategy.getTable(table, node); String t = tableStrategy.getTable(table, node);
return t == null || t.isEmpty() ? table : t; return t == null || t.isEmpty() ? table : t;
} }
/**
* 根据Entity对象获取Entity的表名
*
* @param bean Entity对象
*
* @return String
*/
public String getTable(T bean) { public String getTable(T bean) {
if (tableStrategy == null) return table; if (tableStrategy == null) return table;
String t = tableStrategy.getTable(table, bean); String t = tableStrategy.getTable(table, bean);
return t == null || t.isEmpty() ? table : t; return t == null || t.isEmpty() ? table : t;
} }
/**
* 获取主键字段的Attribute
*
* @return Attribute
*/
public Attribute<T, Serializable> getPrimary() { public Attribute<T, Serializable> getPrimary() {
return this.primary; return this.primary;
} }
/**
* 遍历数据库表对应的所有字段, 不包含&#64;Transient字段
*
* @param action BiConsumer
*/
public void forEachAttribute(BiConsumer<String, Attribute<T, Serializable>> action) { public void forEachAttribute(BiConsumer<String, Attribute<T, Serializable>> action) {
this.attributeMap.forEach(action); this.attributeMap.forEach(action);
} }
/**
* 根据Entity字段名获取字段的Attribute
*
* @param fieldname Class字段名
*
* @return Attribute
*/
public Attribute<T, Serializable> getAttribute(String fieldname) { public Attribute<T, Serializable> getAttribute(String fieldname) {
if (fieldname == null) return null; if (fieldname == null) return null;
return this.attributeMap.get(fieldname); return this.attributeMap.get(fieldname);
} }
/**
* 根据Entity字段名获取可更新字段的Attribute
*
* @param fieldname Class字段名
*
* @return Attribute
*/
public Attribute<T, Serializable> getUpdateAttribute(String fieldname) { public Attribute<T, Serializable> getUpdateAttribute(String fieldname) {
return this.updateAttributeMap.get(fieldname); return this.updateAttributeMap.get(fieldname);
} }
/**
* 判断Entity类的字段名与表字段名s是否存在不一致的值
*
* @return boolean
*/
public boolean isNoAlias() { public boolean isNoAlias() {
return this.aliasmap == null; return this.aliasmap == null;
} }
/**
* 根据Flipper获取ORDER BY的SQL语句不存在Flipper或sort字段返回空字符串
*
* @param flipper 翻页对象
*
* @return String
*/
protected String createSQLOrderby(Flipper flipper) { 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(); final String sort = flipper.getSort();
if (sort.isEmpty() || sort.indexOf(';') >= 0 || sort.indexOf('\n') >= 0) return "";
String sql = this.sortOrderbySqls.get(sort); String sql = this.sortOrderbySqls.get(sort);
if (sql != null) return sql; if (sql != null) return sql;
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
@@ -409,21 +526,47 @@ public final class EntityInfo<T> {
return sql; return sql;
} }
//根据field字段名获取数据库对应的字段名 /**
* 根据field字段名获取数据库对应的字段名
*
* @param tabalis 表别名
* @param fieldname 字段名
*
* @return String
*/
public String getSQLColumn(String tabalis, String fieldname) { public String getSQLColumn(String tabalis, String fieldname) {
return this.aliasmap == null ? (tabalis == null ? fieldname : (tabalis + '.' + fieldname)) return this.aliasmap == null ? (tabalis == null ? fieldname : (tabalis + '.' + fieldname))
: (tabalis == null ? aliasmap.getOrDefault(fieldname, fieldname) : (tabalis + '.' + aliasmap.getOrDefault(fieldname, fieldname))); : (tabalis == null ? aliasmap.getOrDefault(fieldname, fieldname) : (tabalis + '.' + aliasmap.getOrDefault(fieldname, fieldname)));
} }
/**
* 获取主键字段的表字段名
*
* @return String
*/
public String getPrimarySQLColumn() { public String getPrimarySQLColumn() {
return getSQLColumn(null, this.primary.field()); return getSQLColumn(null, this.primary.field());
} }
//数据库字段名 /**
* 获取主键字段的带有表别名的表字段名
*
* @param tabalis 表别名
*
* @return String
*/
public String getPrimarySQLColumn(String tabalis) { public String getPrimarySQLColumn(String tabalis) {
return getSQLColumn(tabalis, this.primary.field()); return getSQLColumn(tabalis, this.primary.field());
} }
/**
* 拼接UPDATE给字段赋值的SQL片段
*
* @param col 表字段名
* @param cv ColumnValue
*
* @return CharSequence
*/
protected CharSequence formatSQLValue(String col, final ColumnValue cv) { protected CharSequence formatSQLValue(String col, final ColumnValue cv) {
if (cv == null) return null; if (cv == null) return null;
switch (cv.getExpress()) { switch (cv.getExpress()) {
@@ -441,14 +584,33 @@ public final class EntityInfo<T> {
return formatToString(cv.getValue()); return formatToString(cv.getValue());
} }
/**
* 获取所有数据表字段的Attribute, 不包含&#64;Transient字段
*
* @return Map
*/
protected Map<String, Attribute<T, Serializable>> getAttributes() { protected Map<String, Attribute<T, Serializable>> getAttributes() {
return attributeMap; return attributeMap;
} }
/**
* 判断日志级别
*
* @param l Level
*
* @return boolean
*/
public boolean isLoggable(Level l) { public boolean isLoggable(Level l) {
return l.intValue() >= this.logLevel; return l.intValue() >= this.logLevel;
} }
/**
* 将字段值序列化为可SQL的字符串
*
* @param value 字段值
*
* @return String
*/
protected String formatToString(Object value) { protected String formatToString(Object value) {
if (value == null) return null; if (value == null) return null;
if (value instanceof CharSequence) { if (value instanceof CharSequence) {
@@ -457,45 +619,64 @@ public final class EntityInfo<T> {
return String.valueOf(value); 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 { protected T getValue(final SelectColumn sels, final ResultSet set) throws SQLException {
T obj = creator.create(); T obj = creator.create();
for (Attribute<T, Serializable> attr : queryAttributes) { for (Attribute<T, Serializable> attr : queryAttributes) {
if (sels == null || sels.test(attr.field())) { if (sels == null || sels.test(attr.field())) {
Serializable o = (Serializable) set.getObject(this.getSQLColumn(null, attr.field()));
final Class t = attr.type(); final Class t = attr.type();
if (t.isPrimitive()) { Serializable o;
if (o != null) { if (t == byte[].class) {
if (t == int.class) { Blob blob = set.getBlob(this.getSQLColumn(null, attr.field()));
o = ((Number) o).intValue(); 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) { } else if (t == long.class) {
o = ((Number) o).longValue(); o = 0L;
} else if (t == short.class) { } else if (t == short.class) {
o = ((Number) o).shortValue(); o = (short) 0;
} else if (t == float.class) { } else if (t == float.class) {
o = ((Number) o).floatValue(); o = 0.0f;
} else if (t == double.class) { } else if (t == double.class) {
o = ((Number) o).doubleValue(); o = 0.0d;
} else if (t == byte.class) { } else if (t == byte.class) {
o = ((Number) o).byteValue(); o = (byte) 0;
} else if (t == boolean.class) {
o = false;
} else if (t == char.class) { } 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); attr.set(obj, o);

View File

@@ -3,14 +3,16 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package org.redkale.source; package org.redkale.source;
/** /**
* FilterBean用于过滤条件 所有的FilterBean都必须可以转换成FilterNode <br>
* *
* 不被标记为&#64;javax.persistence.Transient 的字段均视为过滤条件 * 不被标记为&#64;javax.persistence.Transient 的字段均视为过滤条件 <br>
*
* <p>
* 详情见: https://redkale.org
* *
* <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx
*/ */
public interface FilterBean { public interface FilterBean {

View File

@@ -38,10 +38,10 @@ public @interface FilterColumn {
long least() default 1; long least() default 1;
/** /**
* express的默认值根据字段类型的不同而不同: * express的默认值根据字段类型的不同而不同: <br>
* 数组 --&gt; IN * 数组 --&gt; IN <br>
* Range --&gt; Between * Range --&gt; Between <br>
* 其他 --&gt; = * 其他 --&gt; = <br>
* *
* @return 字段表达式 * @return 字段表达式
*/ */

View 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 + "}";
}
}

View File

@@ -10,9 +10,9 @@ import static java.lang.annotation.ElementType.FIELD;
import java.lang.annotation.*; import java.lang.annotation.*;
/** /**
* 默认情况下FilterBean下的过滤字段之间是AND关系。 * 默认情况下FilterBean下的过滤字段之间是AND关系。 <br>
* 当需要使用OR或AND OR组合过滤查询时需要使用 FilterGroup。 * 当需要使用OR或AND OR组合过滤查询时需要使用 FilterGroup。 <br>
* FilterGroup 的value 必须是[OR]或者[AND]开头, 多级需要用点.分隔。 (注: 暂时不支持多级) * FilterGroup 的value 必须是[OR]或者[AND]开头, 多级需要用点.分隔。 (注: 暂时不支持多级) <br>
* 示例一: * 示例一:
* <blockquote><pre> * <blockquote><pre>
* public class TestFilterBean implements FilterBean { * public class TestFilterBean implements FilterBean {
@@ -49,9 +49,9 @@ import java.lang.annotation.*;
* private int birthday; * private int birthday;
* } * }
* </pre></blockquote> * </pre></blockquote>
* 转换的SQL语句为: WHERE id = ? AND ((desc LIKE ? AND name LIKE ?) OR (age = ? OR birthday = ?)) * 转换的SQL语句为: WHERE id = ? AND ((desc LIKE ? AND name LIKE ?) OR (age = ? OR birthday = ?)) <br>
* 因为默认是AND关系 &#64;FilterGroup("") 等价于 &#64;FilterGroup("[AND]") * 因为默认是AND关系 &#64;FilterGroup("") 等价于 &#64;FilterGroup("[AND]") <br>
* 所以示例二的&#64;FilterGroup("[OR]g1.[AND]subg1") 可以简化为 &#64;FilterGroup("[OR]g1.subg1") * 所以示例二的&#64;FilterGroup("[OR]g1.[AND]subg1") 可以简化为 &#64;FilterGroup("[OR]g1.subg1") <br>
*/ */
/** /**
* <p> * <p>

View File

@@ -10,6 +10,8 @@ import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* 关联表过滤条件 <br>
* 关联关系表必须含主表, 不能是主表A关联表B表B再关联表C只能是主表A关联表B主表A关联表C <br>
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -31,13 +33,13 @@ public @interface FilterJoinColumn {
/** /**
* *
* 多个关联字段, 默认使用join表(b)的主键, join表与被join表(a)的字段必须一样 * 多个关联字段, 默认使用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 * 例如: SELECT a.* FROM user a INNER JOIN record b ON a.userid = b.userid AND a.usertype = b.usertype <br>
* 那么注解为: &#64;FilterJoinColumn(table = Record.class, columns = {"userid", "usertype"}) * 那么注解为: &#64;FilterJoinColumn(table = Record.class, columns = {"userid", "usertype"}) <br>
* <p> * <p>
* columns中的字段名如果不一致可以将两个字段名用=连接成一个字段名 * columns中的字段名如果不一致可以将两个字段名用=连接成一个字段名 <br>
* 例如: SELECT a.* FROM user a INNER JOIN record b ON a.userid = b.buyerid AND a.usertype = b.usertype * 例如: SELECT a.* FROM user a INNER JOIN record b ON a.userid = b.buyerid AND a.usertype = b.usertype <br>
* 那么注解为: &#64;FilterJoinColumn(table = Record.class, columns = {"userid=buyerid", "usertype"}) * 那么注解为: &#64;FilterJoinColumn(table = Record.class, columns = {"userid=buyerid", "usertype"}) <br>
* *
* @return 关联字段 * @return 关联字段
*/ */

View File

@@ -10,9 +10,10 @@ import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.*; import java.util.function.*;
import static org.redkale.source.FilterExpress.EQUAL; import static org.redkale.source.FilterExpress.EQUAL;
import org.redkale.util.Attribute; import org.redkale.util.*;
/** /**
* &#64;FilterJoinColumn对应的FilterNode对象
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -94,10 +95,7 @@ public class FilterJoinNode extends FilterNode {
return this; return this;
} }
if (or == signor || this.column == null) { if (or == signor || this.column == null) {
FilterNode[] newsiblings = new FilterNode[nodes.length + 1]; this.nodes = Utility.append(this.nodes, node);
System.arraycopy(nodes, 0, newsiblings, 0, nodes.length);
newsiblings[nodes.length] = node;
this.nodes = newsiblings;
if (this.column == null) this.or = signor; if (this.column == null) this.or = signor;
return this; return this;
} }

View File

@@ -9,7 +9,7 @@ import java.util.Objects;
/** /**
* FilterKey主要用于自身字段间的表达式, 如: a.recordid = a.parentid , a.parentid就需要FilterKey来表示 new FilterKey("parentid") * FilterKey主要用于自身字段间的表达式, 如: a.recordid = a.parentid , a.parentid就需要FilterKey来表示 new FilterKey("parentid")
* * <br>
* 注意该类型不支持表达式FV_XXX、BETWEEN、NOTBETWEEN、IN、NOTIN * 注意该类型不支持表达式FV_XXX、BETWEEN、NOTBETWEEN、IN、NOTIN
* *
* <p> * <p>

View File

@@ -10,15 +10,15 @@ import java.lang.reflect.Array;
import java.util.*; import java.util.*;
import java.util.function.*; import java.util.function.*;
import static org.redkale.source.FilterExpress.*; import static org.redkale.source.FilterExpress.*;
import org.redkale.util.Attribute; import org.redkale.util.*;
/** /**
* 注意: <br> * 注意: <br>
* column的值以#开头的视为虚拟字段,不在过滤范围内 <br> * column的值以#开头的视为虚拟字段,不在过滤范围内 <br>
* 在调用 createSQLExpress 之前必须先调用 createSQLJoin <br> * 在调用 createSQLExpress 之前必须先调用 createSQLJoin <br>
* 在调用 createPredicate 之前必须先调用 isCacheUseable * 在调用 createPredicate 之前必须先调用 isCacheUseable <br>
*
* *
* <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
@@ -150,10 +150,7 @@ public class FilterNode { //FilterNode 不能实现Serializable接口 否则
return this; return this;
} }
if (or == signor) { if (or == signor) {
FilterNode[] newsiblings = new FilterNode[nodes.length + 1]; this.nodes = Utility.append(this.nodes, node);
System.arraycopy(nodes, 0, newsiblings, 0, nodes.length);
newsiblings[nodes.length] = node;
this.nodes = newsiblings;
return this; return this;
} }
FilterNode newnode = new FilterNode(this.column, this.express, this.itemand, this.value); FilterNode newnode = new FilterNode(this.column, this.express, this.itemand, this.value);

View File

@@ -11,7 +11,7 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.Transient; import javax.persistence.Transient;
import static org.redkale.source.FilterExpress.*; 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; return this;
} }
if (or == signor) { if (or == signor) {
FilterNodeBean[] newsiblings = new FilterNodeBean[nodeBeans.length + 1]; this.nodeBeans = Utility.append(this.nodeBeans, node);
System.arraycopy(nodeBeans, 0, newsiblings, 0, nodeBeans.length);
newsiblings[nodeBeans.length] = node;
this.nodeBeans = newsiblings;
return this; return this;
} }
this.nodeBeans = new FilterNodeBean[]{new FilterNodeBean(this), node}; this.nodeBeans = new FilterNodeBean[]{new FilterNodeBean(this), node};

View File

@@ -7,7 +7,7 @@ package org.redkale.source;
/** /**
* FilterValue主要用于复杂的表达式。<br> * FilterValue主要用于复杂的表达式。<br>
* 例如: col / 10 = 3 、MOD(col, 8) &gt; 0 这些都不是单独一个数值能表达的因此需要FilterValue 才构建 8 、 &gt; 、0 组合值. * 例如: col / 10 = 3 、MOD(col, 8) &gt; 0 这些都不是单独一个数值能表达的因此需要FilterValue 才构建 8 、 &gt; 、0 组合值
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -8,6 +8,7 @@ package org.redkale.source;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 翻页对象, offset从0开始, limit必须大于0
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -40,6 +41,11 @@ public final class Flipper implements Serializable, Cloneable {
this.offset = offset < 0 ? 0 : offset; 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) { public Flipper(int limit, int offset, String sortColumn) {
this.limit = limit > 0 ? limit : DEFAULT_LIMIT; this.limit = limit > 0 ? limit : DEFAULT_LIMIT;
this.offset = offset < 0 ? 0 : offset; this.offset = offset < 0 ? 0 : offset;

View File

@@ -27,6 +27,26 @@ import javax.sql.*;
*/ */
public class JDBCPoolSource { 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 static final Map<String, AbstractMap.SimpleEntry<WatchService, List<WeakReference<JDBCPoolSource>>>> maps = new HashMap<>();
private final AtomicLong usingCounter = new AtomicLong(); 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() { final boolean isMysql() {
return source != null && source.getClass().getName().contains(".mysql."); return source != null && source.getClass().getName().contains(".mysql.");
} }
@@ -109,7 +200,7 @@ public class JDBCPoolSource {
return source != null && source.getClass().getName().contains(".sqlserver."); return source != null && source.getClass().getName().contains(".sqlserver.");
} }
final boolean isPostgresql () { final boolean isPostgresql() {
return source != null && source.getClass().getName().contains(".postgresql."); return source != null && source.getClass().getName().contains(".postgresql.");
} }

View File

@@ -8,6 +8,8 @@ package org.redkale.source;
import java.util.function.*; import java.util.function.*;
/** /**
*
* 取值范围,包含两边的值
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -21,6 +23,34 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
public E getMax(); 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> { public static final class ByteRange implements Range<Byte> {
private Byte min = Byte.MIN_VALUE; private Byte min = Byte.MIN_VALUE;

View File

@@ -12,7 +12,7 @@ import java.util.*;
import java.util.function.*; import java.util.function.*;
/** /**
* VirtualEntity表示虚拟的数据实体类 通常Entity都会映射到数据库中的某个表而标记为VirtualEntity的Entity类只存在DataCache中 * VirtualEntity表示虚拟的数据实体类 通常Entity都会映射到数据库中的某个表而标记为&#64;VirtualEntity的Entity类只存在EntityCache中
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -24,12 +24,24 @@ import java.util.function.*;
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface VirtualEntity { public @interface VirtualEntity {
//DataSource是否直接返回对象的真实引用 而不是copy一份 /**
* DataSource是否直接返回对象的真实引用 而不是copy一份
*
* @return boolean
*/
boolean direct() default false; boolean direct() default false;
//初始化时数据的加载器 /**
* 初始化时数据的加载器
*
* @return Class
*/
Class<? extends BiFunction<DataSource, Class, List>> loader() default DefaultFunctionLoader.class; Class<? extends BiFunction<DataSource, Class, List>> loader() default DefaultFunctionLoader.class;
/**
* 默认全量加载器
*
*/
public static class DefaultFunctionLoader implements BiFunction<DataSource, Class, List> { public static class DefaultFunctionLoader implements BiFunction<DataSource, Class, List> {
@Override @Override

View File

@@ -39,11 +39,13 @@ public abstract class AnyValue {
*/ */
public static final BiPredicate<String, String> EQUALSIGNORE = (name1, name2) -> name1.equalsIgnoreCase(name2); 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对象 * 创建空的DefaultAnyValue对象
@@ -96,27 +98,19 @@ public abstract class AnyValue {
* @param ignoreCase name是否不区分大小写 * @param ignoreCase name是否不区分大小写
*/ */
public DefaultAnyValue(boolean ignoreCase) { public DefaultAnyValue(boolean ignoreCase) {
this.ignoreCase = ignoreCase;
this.predicate = ignoreCase ? EQUALSIGNORE : EQUALS; this.predicate = ignoreCase ? EQUALSIGNORE : EQUALS;
} }
/**
* 创建DefaultAnyValue对象
*
* @param predicate name比较策略
*/
public DefaultAnyValue(BiPredicate<String, String> predicate) {
this.predicate = predicate;
}
/** /**
* 创建共享此内容的DefaultAnyValue对象 * 创建共享此内容的DefaultAnyValue对象
* *
* @return DefaultAnyValue对象 * @return DefaultAnyValue对象
*/ */
public DefaultAnyValue duplicate() { public DefaultAnyValue duplicate() {
DefaultAnyValue rs = new DefaultAnyValue(this.predicate); DefaultAnyValue rs = new DefaultAnyValue(this.ignoreCase);
rs.stringValues = this.stringValues; rs.stringEntrys = this.stringEntrys;
rs.entityValues = this.entityValues; rs.anyEntrys = this.anyEntrys;
return rs; return rs;
} }
@@ -124,13 +118,13 @@ public abstract class AnyValue {
if (av == null) return this; if (av == null) return this;
if (av instanceof DefaultAnyValue) { if (av instanceof DefaultAnyValue) {
final DefaultAnyValue adv = (DefaultAnyValue) av; final DefaultAnyValue adv = (DefaultAnyValue) av;
if (adv.stringValues != null) { if (adv.stringEntrys != null) {
for (Entry<String> en : adv.stringValues) { for (Entry<String> en : adv.stringEntrys) {
this.addValue(en.name, en.value); this.addValue(en.name, en.value);
} }
} }
if (adv.entityValues != null) { if (adv.anyEntrys != null) {
for (Entry<AnyValue> en : adv.entityValues) { for (Entry<DefaultAnyValue> en : adv.anyEntrys) {
this.addValue(en.name, en.value); this.addValue(en.name, en.value);
} }
} }
@@ -155,13 +149,13 @@ public abstract class AnyValue {
if (av == null) return this; if (av == null) return this;
if (av instanceof DefaultAnyValue) { if (av instanceof DefaultAnyValue) {
final DefaultAnyValue adv = (DefaultAnyValue) av; final DefaultAnyValue adv = (DefaultAnyValue) av;
if (adv.stringValues != null) { if (adv.stringEntrys != null) {
for (Entry<String> en : adv.stringValues) { for (Entry<String> en : adv.stringEntrys) {
this.setValue(en.name, en.value); this.setValue(en.name, en.value);
} }
} }
if (adv.entityValues != null) { if (adv.anyEntrys != null) {
for (Entry<AnyValue> en : adv.entityValues) { for (Entry<DefaultAnyValue> en : adv.anyEntrys) {
this.setValue(en.name, en.value); this.setValue(en.name, en.value);
} }
} }
@@ -184,21 +178,41 @@ public abstract class AnyValue {
@Override @Override
public Entry<String>[] getStringEntrys() { public Entry<String>[] getStringEntrys() {
return stringValues; return stringEntrys;
}
public void setStringEntrys(Entry<String>[] stringEntrys) {
this.stringEntrys = stringEntrys;
} }
@Override @Override
public Entry<AnyValue>[] getAnyEntrys() { 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 @Override
@java.beans.Transient
public String[] getNames() { public String[] getNames() {
Set<String> set = new LinkedHashSet<>(); Set<String> set = new LinkedHashSet<>();
for (Entry en : this.stringValues) { for (Entry en : this.stringEntrys) {
set.add(en.name); set.add(en.name);
} }
for (Entry en : this.entityValues) { for (Entry en : this.anyEntrys) {
set.add(en.name); set.add(en.name);
} }
return set.toArray(new String[set.size()]); return set.toArray(new String[set.size()]);
@@ -206,12 +220,12 @@ public abstract class AnyValue {
@Override @Override
public String[] getValues(String... names) { 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 @Override
public AnyValue[] getAnyValues(String... names) { 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 @Override
@@ -220,8 +234,8 @@ public abstract class AnyValue {
} }
public DefaultAnyValue clear() { public DefaultAnyValue clear() {
this.stringValues = new Entry[0]; this.stringEntrys = new Entry[0];
this.entityValues = new Entry[0]; this.anyEntrys = new Entry[0];
return this; return this;
} }
@@ -230,7 +244,7 @@ public abstract class AnyValue {
if (getValue(name) == null) { if (getValue(name) == null) {
this.addValue(name, value); this.addValue(name, value);
} else { } else {
for (Entry<String> en : this.stringValues) { for (Entry<String> en : this.stringEntrys) {
if (predicate.test(en.name, name)) { if (predicate.test(en.name, name)) {
en.value = value; en.value = value;
return this; return this;
@@ -245,9 +259,9 @@ public abstract class AnyValue {
if (getValue(name) == null) { if (getValue(name) == null) {
this.addValue(name, value); this.addValue(name, value);
} else { } else {
for (Entry<AnyValue> en : this.entityValues) { for (Entry<DefaultAnyValue> en : this.anyEntrys) {
if (predicate.test(en.name, name)) { if (predicate.test(en.name, name)) {
en.value = value; en.value = (DefaultAnyValue) value;
return this; return this;
} }
} }
@@ -264,28 +278,19 @@ public abstract class AnyValue {
} }
public DefaultAnyValue addValue(String name, String value) { public DefaultAnyValue addValue(String name, String value) {
if (name == null) return this; this.stringEntrys = Utility.append(this.stringEntrys, new Entry(name, value));
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;
return this; return this;
} }
public DefaultAnyValue addValue(String name, AnyValue value) { public DefaultAnyValue addValue(String name, AnyValue value) {
if (name == null || value == null) return this; if (name == null || value == null) return this;
int len = this.entityValues.length; this.anyEntrys = Utility.append(this.anyEntrys, new Entry(name, value));
Entry[] news = new Entry[len + 1];
System.arraycopy(this.entityValues, 0, news, 0, len);
news[len] = new Entry(name, value);
this.entityValues = news;
return this; return this;
} }
@Override @Override
public AnyValue getAnyValue(String name) { public AnyValue getAnyValue(String name) {
for (Entry<AnyValue> en : this.entityValues) { for (Entry<DefaultAnyValue> en : this.anyEntrys) {
if (predicate.test(en.name, name)) { if (predicate.test(en.name, name)) {
return en.value; return en.value;
} }
@@ -295,7 +300,7 @@ public abstract class AnyValue {
@Override @Override
public String getValue(String name) { public String getValue(String name) {
for (Entry<String> en : this.stringValues) { for (Entry<String> en : this.stringEntrys) {
if (predicate.test(en.name, name)) { if (predicate.test(en.name, name)) {
return en.value; return en.value;
} }
@@ -305,12 +310,12 @@ public abstract class AnyValue {
@Override @Override
public String[] getValues(String name) { 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 @Override
public AnyValue[] getAnyValues(String name) { 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; T value;
@java.beans.ConstructorProperties({"name", "value"})
public Entry(String name0, T value0) { public Entry(String name0, T value0) {
this.name = name0; this.name = name0;
this.value = value0; this.value = value0;
} }
public String getName() {
return name;
}
public T getValue() { public T getValue() {
return value; return value;
} }
@@ -377,9 +387,9 @@ public abstract class AnyValue {
return new DefaultAnyValue(); return new DefaultAnyValue();
} }
public String toString(int len) { public String toString(int indent) { //indent: 缩进长度
if (len < 0) len = 0; if (indent < 0) indent = 0;
char[] chars = new char[len]; char[] chars = new char[indent];
Arrays.fill(chars, ' '); Arrays.fill(chars, ' ');
final String space = new String(chars); final String space = new String(chars);
StringBuilder sb = new StringBuilder(); 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"); sb.append(space).append(" '").append(en.name).append("': '").append(en.value).append("',\r\n");
} }
for (Entry<AnyValue> en : getAnyEntrys()) { 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('}'); sb.append(space).append('}');
return sb.toString(); return sb.toString();

View File

@@ -63,8 +63,8 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* 映射Field时field必须满足以下条件之一 <br> * 映射Field时field必须满足以下条件之一 <br>
* 1、field属性是public且非final <br> * 1、field属性是public且非final <br>
* 2、至少存在对应的getter、setter方法中的一个 <br> * 2、至少存在对应的getter、setter方法中的一个 <br>
* 当不存在getter方法时get操作固定返回null <br> * 当不存在getter方法时get操作固定返回null <br>
* 当不存在setter方法时set操作为空方法 <br> * 当不存在setter方法时set操作为空方法 <br>
* <p> * <p>
@@ -296,7 +296,6 @@ public interface Attribute<T, F> {
for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) { for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) {
if (java.lang.reflect.Modifier.isStatic(method.getModifiers())) continue; if (java.lang.reflect.Modifier.isStatic(method.getModifiers())) continue;
if (!method.getName().startsWith("set")) continue; if (!method.getName().startsWith("set")) continue;
if (method.getReturnType() != void.class) continue;
if (method.getParameterCount() != 1) continue; if (method.getParameterCount() != 1) continue;
list.add(create(clazz, null, method)); list.add(create(clazz, null, method));
} }

Some files were not shown because too many files have changed in this diff Show More