# sql2java
**Repository Path**: l0km/sql2java
## Basic Information
- **Project Name**: sql2java
- **Description**: 轻量级数据库(SQL)访问代码(java)生成器
- **Primary Language**: Java
- **License**: BSD-2-Clause
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 17
- **Forks**: 7
- **Created**: 2019-11-19
- **Last Updated**: 2025-06-13
## Categories & Tags
**Categories**: database-dev
**Tags**: 数据库
## README
# sql2java
sql2java是一个轻量级数据库(SQL)访问代码(java)生成器,是在国外一款同名开源项目([https://sourceforge.net/projects/sql2java](https://sourceforge.net/projects/sql2java))的基础上重写的ORM工具,以maven 插件方式运行,目前在mysql下测试通过已经应用于实际项目开发中
- 生成的表记录的java bean类支持thrift/swift,swagger 注释,生成的java bean对象可以直接用于thrift/swfit服务的数据类型
- 支持表记录内存缓存(cache)机制
- 支持侦听器(listener)机制用于通知表记录增册改事件
- 支持事务(transaction)操作
- 支持自增长键(AUTO_INCREMENT)
- 支持自定义扩展模板(velocity),用户可以使用自定义的模板生成扩展代码
- 支持乐观锁(optimisticlock)
### 生成的java代码结构

### 生成的数据记录类示例

## 编译项目
编译要求 JDK 1.7,Maven 3.5 以上版本
# 下载源码
git clone https://gitee.com/l0km/sql2java.git
cd sql2java
# 编译全部
mvn install
>sql2java的maven插件已经发布到maven中央仓库,如果只是要运行maven插件无需编译本项目
## 快速使用说明
### 创建配置文件
如下创建一个最简的参数配置文件(mysql2java.properties),用于告诉sql2java如何生成java代码, "#"起始的行为注释
# 指定 JDBC driver,告诉sql2java如何连接数据
jdbc.driver=com.mysql.jdbc.Driver
# 对于 mysql 数据库,如果 useInformationSchema=false 或没指定,则无法获取表中的注释信息
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useInformationSchema=true
jdbc.username=root
jdbc.password=
jdbc.schema=test
# 定义生成代码的包名
codewriter.package=sql2java.test
### 生成代码
执行如下maven插件,即开始从数据库中读取表结构信息并生成对应的java代码到当前文件夹下(src/main)
$ mvn com.gitee.l0km:sql2java-maven-plugin:generate \
-Dsql2java.classpath=lib/mysql-connector-java-5.1.43-bin.jar \
-Dsql2java.propfile=mysql2java.properties
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building sql2java test 0.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- sql2java-maven-plugin:1.0.6-SNAPSHOT:generate (default-cli) @ sql2java-test ---
database properties initialization
[INFO] classpath: [file:/D:/j/sql2java.test/lib/mysql-connector-java-5.1.43-bin.jar]
Connecting to root on jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useInformationSchema=true ...
Connected.
Database server :MySQL.
Loading table list according to pattern t_% ...
table t_book found
table t_user found
samePrefix = [t_]
Loading columns ...
t_book.id INT default value: null
t_book.name VARCHAR default value: null
t_book.borrower INT default value: null
t_book found 3 columns
t_user.id INT AUTOINCREMENT default value: null
t_user.name VARCHAR default value: null
t_user.birthdate DATE default value: null
t_user.phone VARCHAR default value: null
t_user.address VARCHAR default value: null
t_user found 5 columns
Database::loadPrimaryKeys
Found primary key (seq,name) (1,id) for table 't_book'
Found primary key (seq,name) (1,id) for table 't_user'
Loading imported keys ...
t_book.id -> t_user.id found seq:1 foreign key name:fk_id
UPDATE_RULE:NO_ACTION DELETE_RULE:RESTRICT
Loading indexes ...
Found interesting index phone_UNIQUE on phone for table t_user
Loading procedures ...
Generating template /templates/velocity/java5g/perschema/constant.java.vm
.... writing to src/main\java\sql2java\test\Constant.java
java\sql2java\test\Constant.java done.
Generating template /templates/velocity/java5g/perschema/database.properties.vm
.... writing to src/main\resources/conf\database.properties
resources/conf\database.properties done.
Generating template /templates/velocity/java5g/perschema/gu.sql2java.irowmetadata.vm
.... writing to src/main\resources/META-INF/services\gu.sql2java.IRowMetaData
resources/META-INF/services\gu.sql2java.IRowMetaData done.
Generating template /templates/velocity/java5g/pertable/bean.java.vm
.... writing to src/main\java\sql2java\test\BookBean.java
java\sql2java\test\BookBean.java done.
Generating template /templates/velocity/java5g/pertable/manager.interface.java.vm
.... writing to src/main\java\sql2java\test\IBookManager.java
java\sql2java\test\IBookManager.java done.
Generating template /templates/velocity/java5g/pertable/metadata.java.vm
.... writing to src/main\java\sql2java\test\BookMetaData.java
java\sql2java\test\BookMetaData.java done.
Generating template /templates/velocity/java5g/pertable/bean.java.vm
.... writing to src/main\java\sql2java\test\UserBean.java
java\sql2java\test\UserBean.java done.
Generating template /templates/velocity/java5g/pertable/manager.interface.java.vm
.... writing to src/main\java\sql2java\test\IUserManager.java
java\sql2java\test\IUserManager.java done.
Generating template /templates/velocity/java5g/pertable/metadata.java.vm
.... writing to src/main\java\sql2java\test\UserMetaData.java
java\sql2java\test\UserMetaData.java done.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.248 s
[INFO] Finished at: 2019-12-16T13:51:20+08:00
[INFO] Final Memory: 13M/308M
[INFO] ------------------------------------------------------------------------
>com.gitee.l0km:sql2java-maven-plugin插件已经发布到maven中央仓库,无需编译本项目就可直接运行,上面的例子中没有指定插件的版本,则默认使用最新版本的插件
参数说明:
| name | description |
| :----------------- | :--------------------------------- |
| sql2java.classpath | 指定连接数据库的JDBC driver jar包 |
| sql2java.propfile | 指定sql2java生成代码必须的配置文件 |
## 完整示例
参见sql2java演示项目: [sql2java-example: sql2java调用示例 (gitee.com)](https://gitee.com/l0km/sql2java-example)
用户可以在此演示项目的基础上修改完成自己的ORM代码项目结构
## 实际应用示例
参见开源项目 facelog: [https://gitee.com/l0km/facelog/tree/master/db2](https://gitee.com/l0km/facelog/tree/master/db2 "https://gitee.com/l0km/facelog/tree/master/db2")
## 获取所有参数说明
为了控制java代码的生成方式,sql2java有几十个控制参数,默认情况下大部分可以不用修改,如果需要需要修改,就需要获取这些参数名字及说明,如下执行 maven 插件,显示所有配置说明以及插件本身的参数说明:
mvn com.gitee.l0km:sql2java-maven-plugin:help
如下执行 maven 插件,则在当前文件夹下生成一份名为my.properties的默认参数配置文件,你可以在此文件的基础上修改自己的代码生成配置:
$ mvn com.gitee.l0km:sql2java-maven-plugin:help -Dsql2java.output=my.properties
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building sql2java test 1.0.2
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- sql2java-maven-plugin:1.0.2:help (default-cli) @ sql2java-test ---
[INFO] OUTPUT PROPRETIES TO J:\sql2java.test\my.properties
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.495 s
[INFO] Finished at: 2019-11-20T15:25:27+08:00
[INFO] Final Memory: 10M/308M
[INFO] ------------------------------------------------------------------------
## 参数说明
SQL2JAVA的参数配置及说明如下
>参数以`参数名`=`参数值`形式定义,所有`#`开始的行皆为注释行
#______________________________________________
#
# (1/8) 数据库访问配置(必填)
#______________________________________________
# JDBC驱动类名
#jdbc.driver=org.hsqldb.jdbcDriver
# 数据库访问的URL
#jdbc.url=jdbc:hsqldb:hsql://localhost
# 访问数据库的用户名
#jdbc.username=sa
# 访问数据库的密码
#jdbc.password=
# 访问的数据库名
#jdbc.schema=null
#______________________________________________
#
# (2/8) 配置自增长键的检索方式(可选)
#______________________________________________
# 此部分配置用于在插入记录时获取自增长键的值
# 对于JDBC 3.0以上支持PreparedStatement.getGeneratedKeys方法
# (DatabaseMetaData.supportsGetGeneratedKeys()返回true)的JDBC 驱动不需要配置此部分
#
# generatedkey.retrieve 可选值:
#
# auto - [默认值]getGeneratedKeys方法自动获取,当使用JDBC 3.0 driver时适用.
#
# before - 在插入(insert)记录之前获取自增长键值
#
# after - 在插入(insert)记录之后获取自增长键值
#
# generatedkey.statement用于定义获取自增长键值的SQL语句
# If you set it to before or after you also need to configure the
# autogeneratedkey.statement properties.
#
占位符用于取代当前表名
# 占位符用于取代当前自增长键字段名
#
#generatedkey.retrieve=auto
#generatedkey.statement=
#______________________________________________
#
# (3/8) 生成代码配置(可选)
#______________________________________________
# 生成java代码的包名
codewriter.package=gu.sql2java.demo
# 生成java代码的文件夹
codewriter.destdir=src/main
# 生成扩展java代码的文件夹
#codewriter.destdir.extension=
# 生成扩展java代码的包名
#codewriter.package.extension=
# 定义被bean.converter.utils.java.vm 模板忽略的字段名
#general.beanconverter.tonative.ignore=create_time,update_time
# 生成的java bean是否支持facebook/swift 注释(annotation)
#swift.annotation.bean = true
# 生成的java bean是否支持swagger 注释(annotation)
#swagger.annotation.bean = true
# 对于有primitive 类型(Integer,Long,Double...)的字段是否生成primitive类型的setter方法
codewriter.bean.primitiveSetter = true
# generate Long setter for Date type
# 对于 java.util.Date 类型的字段是否生成 Long类型的setter方法
codewriter.bean.dateLongSetter = true
# Date 类型的JSON 序列化类型 :
# Long 系统时间(毫秒)
# String ISO8601 时间日期格式的字符串
# 当指定了 swift或jackon注解时有效
codewriter.bean.dateSerializeType = Long
# 当 dateSerializeType 为 String 的日期格式,默认为IS8601 格式
codewriter.bean.dateStringFormat=yyyy-MM-dd'T'HH:mm:ss.SSSZ
# modified,initialized 字段的类型(不要修改)
bitstate.type = int
# 字节数对应的java类型:
# byte[]
# java.nio.ByteBuffer
# default byte[] if not specialized
#binary.type = byte[]
# add @JsonRawValue @JsonDeserialize annotation for JSON field
#json.jackson.rawvalue = false
#set properties file name for database
database.properties.env=config_folder
database.properties.isdebug=false
database.properties.dir=resources/conf
database.properties=database.properties
#flag for compatible with axis2
#bean.compatible_axis2=true
# Property file to use when initializing Velocity
#codewriter.velocityprops=somefile
# 默认加载模板的路径(不要修改)
velocity.templates.loadingpath=/templates/velocity/includes
# 为每张表(pertable)和每个方案(perschema)加载子模板的加载起始路径(不要修改)
velocity.templates=/templates/velocity/
############ 扩展模板 ###############
# 用户可以通过下面的扩展模板参数,指定自己定义的模板(.vm)文件的位置
# sql2java generator会根据指定的参数执行扩展模板生成代码
# 扩展模板的加载路径
#velocity.templates.loadingpath.extension=
# 为每张表(pertable)和每个方案(perschema)加载扩展模板的加载起始路径
#velocity.templates.extension =
# sets a prefix to prepend to all generated classes
# useful if you are worried about namespace collision with reserved words
# or java.lang classes
codewriter.classprefix=
# 是否为字段生成默认值
codewriter.generate.defaultvalue=true
#______________________________________________
#
# (4/8) 表和模板过滤配置(可选)
#______________________________________________
#
# 通过表类型过滤
# 用于指定需要生成代码的表类型(用逗号分隔的表类型列表)
# 表类型定义为:TABLE, VIEW, SYSTEM TABLE, SYNONYM
jdbc.tabletypes=TABLE, VIEW
# 通过表名过滤
# 使用通配%来过滤需要生成代码的表名
# 你可以指定一个逗号分割的通配符过滤列表
# 比如 %_name,ul_% 只对后缀为_name,或前缀为ul_的表名生成代码
# 默认对schema中所有的表生成代码
jdbc.tablenamepattern=%
# 表名白名单/名单过滤(此特性暂未启用)
# 白名单
# 空格分隔的表名列表,只在此名单中的表才会被生成代码
# 黑名单
# 空格分隔的表名列表,在此名单中的表不会被生成代码
# 白名单和黑名单只能定义一个,如果同时指定了白名单和黑名单则忽略黑名单
# 白名单过滤
tables.include=
# 黑名单过滤
tables.exclude=
# 模板文件夹名白名单/名单过滤
# 白名单
# 空格分隔的表名列表,只在此名单中的模板文件夹中的模板才会被生成代码
# 黑名单
# 空格分隔的表名列表,在此名单中的模板文件夹中的模板不会被生成代码
# 白名单和黑名单只能定义一个,如果同时指定了白名单和黑名单则忽略黑名单
template.folder.exclude=
template.folder.include=java5g
# java5g : general bean & manager templates for java5
# (6/8) WHAT SHOULD BE DISPLAYED/NOT DISPLAYED ON THE FRONTEND ?
# empty means all fields
#______________________________________________
# 逗号分割的字段名列表定义只能本地可见的字段
# A comma separated list of field names in a specified table that can only be accessed locally
# Equivalent to defining in the annotation of a field: ' SCOPE@LOCAL@EPOSC '
# Example:
# table.user_info.scope.local=private_time,password
# 逗号分割的字段名列表定义THRIFT可见的字段
# A comma separated list of field names in a specified table that can be transmitted by RPC
# in addition to being accessible locally
# Equivalent to defining in the annotation of a field: ' SCOPE@THRIFT@EPOSC '
# Example:
# table.user_info.scope.thrift=password
# 逗号分割的字段名列表定义JSON可见的字段
# A comma separated list of field names in a specified table that can be serialized by JSON
# in addition to being accessible locally
# Equivalent to defining in the annotation of a field: ' SCOPE@JSON@EPOSC '
# Example:
# table.user_info.scope.json=private_time,password
#______________________________________________
#
# (7/8) JDBC 类型映射(可选)
#______________________________________________
#
# jdbc DATE类型映射的java类型,可选值:
# java.sql.Date
# java.util.Date
jdbc2java.date=java.util.Date
# jdbc TIME类型映射的java类型,可选值:
# java.sql.Time
# java.util.Date
jdbc2java.time=java.util.Date
# jdbc TIMESTAMP类型映射的java类型,可选值:
# java.sql.Timestamp
# java.util.Date
jdbc2java.timestamp=java.util.Date
#______________________________________________
#
# (8/8) 乐观锁配置(可选)
#______________________________________________
# optimisticlock.type 有两个可选项值:
# none - 乐观锁机制未启用(default).
# timestamp - 乐观锁字段包含 System.currentTimeMillis() 值.
#
# optimisticlock.column 定义乐观锁的字段名,如果字段名不存在则乐观锁机制不会启用
# 乐观锁的字段类型可以是java.lang.Long or java.lang.String.
optimisticlock.type=timestamp
optimisticlock.column=version_time
## 数据库字段编解码器
从 3.21.0 开始,sql2java增加了[ColumnCodec](sql2java-base/src/main/java/gu/sql2java/BaseColumnCodec.java)接口用于应用层自定义对数据字段的序列化和反序列化,序列化方法(`ColumnCodec.serialize`)用于将自定义类型数据转换为数据库字段存储类型,反序列化方法(`ColumnCodec.deserialize`)则将字段存储类型转换为自定义数据类型。对于JSON类型的支持使用[JsonColumnCodec](sql2java-base/src/main/java/gu/sql2java/json/JsonColumnCodec.java)实现,应用层可以参照`JsonColumnCodec`基于 [BaseColumnCodec](sql2java-base/src/main/java/gu/sql2java/BaseColumnCodec.java) 实现自定义的数据库字段编解码器。
## 自定义类型字段支持
从 3.21.0 开始,增加数据库字段注释标记 `ANN@@NNA` 和`TYPE@@EPYT`,应用层定义数据库字段的类型和编解码器,sql2java在生成代码时会自动根据此标记在生成指定类型的Java成员以及`@ColumnCodecConfig`注解。
### TYPE@@EPYT
定义字段类型,如`ATYPE@net.facelib.eam.interpreter.Rectangle@EPYT`,将字段类型定义为`net.facelib.eam.interpreter.Rectangle`
### ANN@@NNA
定义字段的注解,可定义多个,如`ANN@gu.sql2java.annotations.ColumnCodecConfig(net.facelib.eam.interpreter.sql2java.EamPlayColumnCodec.class)@NNA`,将在Java成员上生成注解`@gu.sql2java.annotations.ColumnCodecConfig(net.facelib.eam.interpreter.sql2java.EamPlayColumnCodec.class)`
### 示例
下面的表中`rect`字段定义了`TYPE@@EPYT`和`ANN@@NNA`
```sql
CREATE TABLE IF NOT EXISTS dc_device_channel (
`device_id` int NOT NULL COMMENT 'X@NAME:设备ID@X',
`sid` int NOT NULL DEFAULT 0 COMMENT 'X@NAME:物理屏幕ID@x',
`area` varchar(32) NOT NULL COMMENT 'X@NAME:显示区域ID@x',
`rect` varchar(256) DEFAULT NULL COMMENT 'ANN@gu.sql2java.annotations.ColumnCodecConfig(net.facelib.eam.interpreter.sql2java.EamPlayColumnCodec.class)@NNATYPE@net.facelib.eam.interpreter.Rectangle@EPYTX@NAME:显示区域坐标@x,对应EamPlayer的defineChannel语法',
`channel` varchar(32) NOT NULL COMMENT 'X@NAME:频道ID@x,显示区域对应的频道',
`run_tasks` text DEFAULT NULL COMMENT 'X@NAME:播放任务@X描述,设备实际播放的任务描述,由设备端写入,对应EamPlayer的definePlanTask和defineTrigger语法',
PRIMARY KEY(`device_id`,`sid`,`area`),
FOREIGN KEY (device_id) REFERENCES dc_device(id) ON DELETE CASCADE,
INDEX (channel)
)COMMENT 'X@NAME:设备显示区域频道记录@X' DEFAULT CHARSET=utf8;
```
生成的java类中对应`rect`的示例
```java
/** comments:X@NAME:显示区域坐标@x,对应EamPlayer的defineChannel语法 */
@ApiModelProperty(value = "X@NAME:显示区域坐标@x,对应EamPlayer的defineChannel语法" ,dataType="Rectangle")
@CodegenLength(max=256)@CodegenInvalidValue
@ExcelColumn(sort=4)
@gu.sql2java.annotations.ColumnCodecConfig(net.facelib.eam.interpreter.sql2java.EamPlayColumnCodec.class)
private net.facelib.eam.interpreter.Rectangle rect;
```
getter/setter方法的对应类型为`net.facelib.eam.interpreter.Rectangle`
getter方法
```java
@ThriftField(value=7)
@JsonProperty("rect")
public net.facelib.eam.interpreter.Rectangle getRect(){
return rect;
}
```
setter方法
```java
@ThriftField(name="rect")
@JsonProperty("rect")
public void setRect(net.facelib.eam.interpreter.Rectangle newVal)
{
modified |= DC_DEVICE_CHANNEL_ID_RECT_MASK;
initialized |= DC_DEVICE_CHANNEL_ID_RECT_MASK;
if (Objects.equals(newVal, rect)) {
return;
}
rect = newVal;
}
```
read/write方法对应的类型为rect字段的数据存储类型
read方法
```java
public String readRect(){
return metaData.columnCodecs[DC_DEVICE_CHANNEL_ID_RECT].serialize(rect,String.class);
}
```
write方法
```java
public void writeRect(String newVal){
modified |= DC_DEVICE_CHANNEL_ID_RECT_MASK;
initialized |= DC_DEVICE_CHANNEL_ID_RECT_MASK;
rect = metaData.columnCodecs[DC_DEVICE_CHANNEL_ID_RECT].deserialize(newVal,metaData.fieldOf(DC_DEVICE_CHANNEL_ID_RECT).getGenericType());
}
```
### SCOPE@@EPOSC
字段注释标记 `SCOPE@@EPOSC` 用于定义字段可见度,具体格式为
```
SCOPE@(LOCAL|JSON|THRIFT)@EPOSC
```
可定义三种可见度
- LOCAL 本地可见
- JSON JSON序列化/反序列化可见
- THRIFT Thrift RPC 序列化/反序列化可见
如下示例,下面的表定义中,`private_time`字段定义了`SCOPE@@EPOSC`注释标记,指定了该字段仅本地可见。也就是说只有服务端本身可以读写该字段。
```sql
CREATE TABLE IF NOT EXISTS dc_device (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'X@NAME:设备id@X',
`name` varchar(32) DEFAULT NULL COMMENT 'X@NAME:设备名称@X,用户指定',
`physical_address` varchar(32) NOT NULL UNIQUE COMMENT '设备X@NAME:物理地址@X,MAC地址,IMEI或其他设备识别码',
`private_time` bigint DEFAULT 0 COMMENT 'SCOPE@LOCAL@EPOSC设备令牌创建的时间戳(毫秒),每次创建设备令牌都会修改此字段',
`os_arch` varchar(64) DEFAULT NULL COMMENT 'X@NAME:操作系统平台@X,操作系统名称及版本及硬件架构名称,例如:Windows-x86_64,Linux-x86_64,Android-arm...'
) COMMENT 'X@NAME:前端设备记录@X,前端设备基本信息' DEFAULT CHARSET=utf8;
```
sql2java-generator生成的`DeviceBean`对象中`dc_device.private_time`字段的对应成员`privateTime`定义代码如下:
```java
/** comments:设备令牌创建的时间戳(毫秒),每次创建设备令牌都会修改此字段 */
@ApiModelProperty(value = "设备令牌创建的时间戳(毫秒),每次创建设备令牌都会修改此字段" ,dataType="Long")
@CodegenDefaultvalue("0")@CodegenInvalidValue("-1")
@ExcelColumn(sort=9)
@com.alibaba.fastjson.annotation.JSONField(serialize = false,deserialize = false)
@com.fasterxml.jackson.annotation.JsonIgnore
private Long privateTime;
/**
* Getter method for {@link #privateTime}.
* Meta Data Information (in progress):
*
* - full name: dc_device.private_time
* - comments: 设备令牌创建的时间戳(毫秒),每次创建设备令牌都会修改此字段
* - default value: '0'
* - column size: 19
* - JDBC type returned by the driver: Types.BIGINT
*
*
* @return the value of privateTime
*/
@JsonIgnore
public Long getPrivateTime(){
return privateTime;
}
```
可以看到,`privateTime`成员定义的Jackson注解`@JsonIgnore`和fastjson注解`@JSONField`指定该字段在JSON序列化和反序列化时忽略应该字段。同时`privateTime`的getter方法上也没有thrift字段注解`@ThriftField`,代表应该字段也没有被定义为Thrift成员字段。
swift2thrift-maven-plugin插件生成的IDL中就不会有`privateTime`字段:
```idl
struct DeviceBean {
1: required bool _new;
2: required i32 modified;
3: required i32 initialized;
4: optional i32 id;
5: optional string name;
6: optional string physicalAddress;
7: optional string osArch;
}
```
## Boolean字段支持
3.19.0之前不论是`TINYINT,SMALLINT,INT`类型对应的Java类型都是`Integer`,3.19.0版本之后,细分了整数类型字段生成的Java数据类型,如下表
| SQL 类型 | Java字段类型 | 说明 |
| -------------------- | ----------------- | ------------------------------------------------------------ |
| TINYINT/BOOL/BOOLEAN | `Byte` /`Boolean` | 如果数据库字段注释(COMMENT)中定义了`[NUM,Boolean]`,则生成的Java成员类型为`Boolean`,否则为`Byte` |
| SMALLINT | `Short` | |
| INT | `Integer` | |
示例:
```sql
CREATE TABLE IF NOT EXISTS dc_device (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '设备id',
`online` boolean DEFAULT false COMMENT '[NUM,Boolean],是否在线标记'
);
```
如上定义`boolean/bool`或`TINYINT`类型字段在生成的Java Bean中`online`字段将被定义为`Boolean`类型。
> **注意:**在注释中`[NUM,Boolean]`标记必须在字符串起始位置。
## JSON字段支持
从3.8版本之后,sql2java支持对保存JSON数据的String字段自动序列化和反序列。
### 开启json.jackson.rawvalue
要开启此特性,需要做如下两步简单的设置
> 开启此特性生成的代码需要fastjson,jackson库的支持
- 修改 sql2java.properties
将`json.jackson.rawvalue`置为`true`,即增加如下设置
```properties
#
# add @JsonRawValue @JsonDeserialize annotation for JSON field
#json.jackson.rawvalue = true
```
- 修改建表语句
在需要定义为保存JSON数据的字段的注释中增加[JSON_STR,...]前缀,通过此前缀指定该字段要保存JSON数据(只对Java类型为String的字段有效)
示例如下:
```sql
CREATE TABLE IF NOT EXISTS dc_device_group (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '设备组id',
`name` varchar(32) NOT NULL COMMENT '设备组名',
`address` varchar(128) DEFAULT NULL COMMENT '设备组详细地址:区/街道门牌',
`props` text DEFAULT NULL COMMENT '[JSON_STR,obj]JSON格式的扩展字段(最大64KB),用于定义扩展信息,online_time:开机时间,offline_time:关机时间,close_time:闭站时间,operator:操作人',
`create_time` timestamp DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) COMMENT '设备组信息' DEFAULT CHARSET=utf8;
```
上面的示例中`props`字段的注释以`[JSON_STR,obj]`为前缀,即定义该字段为JSON字段,JSON字段的类型为Object
只要做了上面两项目修改,重新执行Sql2java生成代码就可以了。
### [JSON_STR]
生成代码时会根据`[JSON_STR,...]`来确定JSON字段的类型
| [JSON_STR]格式 | JSON字段类型 |
| ------------------------------------- | ------------------------------- |
| [JSON_STR,array] | com.alibaba.fastjson.JSONArray |
| [JSON_STR,object] | com.alibaba.fastjson.JSONObject |
| [JSON_STR,obj] | com.alibaba.fastjson.JSONObject |
| [JSON_STR] | com.alibaba.fastjson.JSON |
| [JSON_STR,com.mycompany.product.User] | com.mycompany.product.User |
### 生成代码示例
props 字段定义代码:
```java
/** comments:[JSON_STR]JSON格式的扩展字段(最大64KB),用于定义扩展信息,online_time:开机时间,offline_time:关机时间,close_time:闭站时间,operator:操作人 */
@ApiModelProperty(value = "[JSON_STR]JSON格式的扩展字段(最大64KB),用于定义扩展信息,online_time:开机时间,offline_time:关机时间,close_time:闭站时间,operator:操作人" ,dataType="String")
@CodegenLength(max=65535)@CodegenInvalidValue
@com.fasterxml.jackson.annotation.JsonRawValue
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = gu.sql2java.json.RawJsonDeserializer.class)
private com.alibaba.fastjson.JSONObject props;
```
getter/setter方法代码,以String类型对字段进行读写:
```java
@ThriftField(value=16)
@JsonProperty("props")
public String getProps(){
return null == props ? null : props.toJSONString();
}
@ThriftField(name="props")
@JsonProperty("props")
public void setProps(String newVal)
{
modified |= DC_DEVICE_GROUP_ID_PROPS_MASK;
initialized |= DC_DEVICE_GROUP_ID_PROPS_MASK;
props = com.alibaba.fastjson.JSONObject.parseObject(newVal,com.alibaba.fastjson.JSONObject.class);
}
```
额外生成的read/write方法代码,提供对JSON对象直接读写:
```java
/**
* read method for {@link #props}
* @return the JSON value of props
*/
public com.alibaba.fastjson.JSONObject readProps(){
return props;
}
/**
* write method for {@link #props} with JSON object.
*/
public void writeProps(com.alibaba.fastjson.JSONObject newVal){
modified |= DC_DEVICE_GROUP_ID_PROPS_MASK;
initialized |= DC_DEVICE_GROUP_ID_PROPS_MASK;
if (Objects.equals(newVal, props)) {
return;
}
props = newVal;
}
```
### 序列化效果
```
{
"modified": 0,
"initialized": 65535,
"new": false,
"id": 5,
"name": "连平客运总站",
"address": null,
"props": {
"offline_time": "22:00",
"online_time": "8:00",
"operator": "unknow"
},
"createTime": "2022-07-06T12:41:56.000+0800",
"updateTime": "2022-07-06T12:41:56.000+0800"
}
```
## Excel 注解支持
自从3.10开始,sql2java提供基于[apache/poi](https://github.com/apache/poi)实现的数据记录导出为Excel格式数据流或文件的功能.参见[sql2java-excel/README.md](sql2java-excel/README.md).
sql2java-excel支持注解方式配置每张表每个字段的Excel导出配置。sql2java-generator在生成Java Bean代码时支持从SQL 表和字段注释COMMENT中根据读取预定义的标记,在Java Bean类代码中生成[@ExcelSheet](sql2java-excel/src/main/java/gu/sql2java/excel/annotations/ExcelSheet.java),[@ExcelColumn](sql2java-excel/src/main/java/gu/sql2java/excel/annotations/ExcelColumn.java)注解。
### 开启excel.annotation.bean
要开启Excel注解生成特性,需要做如下两步设置
> 开启此特性生成的代码需要sql2java-excel库的支持
- 修改 sql2java.properties
将`excel.annotation.bean`置为`true`,即增加如下设置
```properties
# generate @ExcelSheet,@ExcelColumn annotation if true
excel.annotation.bean = true
```
- 修改建表语句
在数据库建表语句的表和字段注释(COMMENT)上根据需要定义特殊的EXCEL 标记,示例如下:
下面的CREATE TABLE SQL语句示例中用到了很多`X@....@X`标记,这些标记内容就是为sql2java-generator在生成JavaBean代码时用于识别生成`@ExcelSheet,@ExcelColumn`注解的特殊标记(tag):
```sql
CREATE TABLE IF NOT EXISTS dc_device (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'X@NAME:设备id@X',
`group_id` int(11) DEFAULT 1 COMMENT 'X@NAME:所属设备组id@X',
`features` int(11) DEFAULT 0 COMMENT 'X@NAME:设备组特性标志@X,应用层定义',
`name` varchar(32) DEFAULT NULL COMMENT 'X@NAME:设备名称@X,用户指定',
`physical_address` varchar(32) NOT NULL UNIQUE COMMENT '设备X@NAME:物理地址@X,MAC地址,IMEI或其他设备识别码',
`address_type`varchar(16) NOT NULL DEFAULT 'MAC' COMMENT '设备物理X@NAME:地址类型@X(MAC,IMEI...),默认6字节MAC地址(HEX)',
`iot_card` varchar(32) DEFAULT NULL UNIQUE COMMENT '设备X@NAME:物联网卡编号@X,例如联通叫iccid',
`status` varchar(32) NOT NULL DEFAULT 'ENABLE' COMMENT 'X@NAME:设备状态@X,X@VALUES:ENABLE:正常,DISABLE:禁用,MAINTAIN:维护,PENDING:挂起(待审核)@X',
`private_time` bigint DEFAULT 0 COMMENT '设备令牌创建的时间戳(毫秒),每次创建设备令牌都会修改此字段',
`screen_info` varchar(32) DEFAULT NULL COMMENT 'X@NAME:设备屏幕信息@X,格式示例:15H1080x960--15(英)寸横屏分辨率1080x960,21V960x1080--21(英)寸横屏分辨率960x1080',
`fixed_mode` varchar(8) DEFAULT 'FLOOR' COMMENT '设备X@NAME:安装方式@X,X@VALUES:HANG:悬挂,FLOOR:落地@X',
`os_arch` varchar(64) DEFAULT NULL COMMENT 'X@NAME:操作系统平台@X,操作系统名称及版本及硬件架构名称,例如:Windows-x86_64,Linux-x86_64,Android-arm...',
`network` varchar(32) DEFAULT NULL COMMENT 'X@NAME:网络连接类型@X:4G,WIFI,ETHERNET',
`version_info`varchar(32) DEFAULT NULL COMMENT '设备端应用程序的X@NAME:版本@X号,格式由应用层定义',
`model` varchar(32) DEFAULT NULL COMMENT 'X@NAME:设备型号@X',
`vendor` varchar(32) DEFAULT NULL COMMENT 'X@NAME:设备供应商@X',
`device_detail` varchar(512) DEFAULT NULL COMMENT '[JSON_STR,obj]X@NAME:设备产品详细信息@X,以JSON格式定义设备详细信息,JSON字段:X@NAMES:device_name:产品名称,manufacturer:制造商,made_date:生产日期@X,应用层可根据需要扩展',
`props` text DEFAULT NULL COMMENT '[JSON_STR,obj]JSON格式的扩展字段(最大64KB),用于定义扩展信息,X@NAMES:last_active_time:上次在线时间,disk_capacity:磁盘容量,status_comment:状态变更说明@X',
`plan_id` varchar(64) DEFAULT NULL COMMENT 'X@NAME:当前节目ID@X',
`target_id` varchar(64) DEFAULT NULL COMMENT 'X@NAME:目标节目ID@X',
`remark` varchar(256) DEFAULT NULL COMMENT 'X@NAME:备注@X',
`create_time` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT 'X@NAME:记录创建时间@X',
`update_time` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'X@NAME:记录修改时间@X'
) COMMENT 'X@NAME:前端设备记录@X,前端设备基本信息,X@SHEET:titleFillColor=YELLOW,hideColumns=private_time|props|device_detail@X' DEFAULT CHARSET=utf8;
```
有了这些标记,sql2java-generator就会为dc_device表生成如下的带有`@ExcelSheet,@ExcelColumn`注解的Java Bean代码:
```java
/**
* DeviceBean is a mapping of dc_device Table.
*
Meta Data Information (in progress):
*
* - comments: 前端设备记录,前端设备基本信息,
*
* @author guyadong
*/
@ExcelSheet(sheetName="dc_device",title="前端设备记录",titleFillColor="YELLOW",hideColumns={"private_time","props","device_detail"})
public final class DeviceBean extends BaseRow
implements Serializable,Constant
{
private static final long serialVersionUID = -3495281739409382636L;
/** comments:设备id */
@ExcelColumn(sort=1,name="设备id")
private Integer id;
/** comments:所属设备组id */
@ExcelColumn(sort=2,name="所属设备组id")
private Integer groupId;
/** comments:设备组特性标志,应用层定义 */
@ExcelColumn(sort=3,name="设备组特性标志")
private Integer features;
/** comments:设备名称,用户指定 */
@ExcelColumn(sort=4,name="设备名称")
private String name;
/** comments:设备物理地址,MAC地址,IMEI或其他设备识别码 */
@ExcelColumn(sort=5,name="物理地址")
private String physicalAddress;
/** comments:设备物理地址类型(MAC,IMEI...),默认6字节MAC地址(HEX) */
@ExcelColumn(sort=6,name="地址类型")
private String addressType;
/** comments:设备物联网卡编号,例如联通叫iccid */
@ExcelColumn(sort=7,name="物联网卡编号")
private String iotCard;
/** comments:设备状态,ENABLE:正常,DISABLE:禁用,MAINTAIN:维护,PENDING:挂起(待审核) */
@ExcelColumn(sort=8,name="设备状态",readConverterExp="ENABLE=正常, DISABLE=禁用, MAINTAIN=维护, PENDING=挂起(待审核)")
private String status;
/** comments:设备令牌创建的时间戳(毫秒),每次创建设备令牌都会修改此字段 */
@ExcelColumn(sort=9)
private Long privateTime;
/** comments:设备屏幕信息,格式示例:15H1080x960--15(英)寸横屏分辨率1080x960,21V960x1080--21(英)寸横屏分辨率960x1080 */
@ExcelColumn(sort=10,name="设备屏幕信息")
private String screenInfo;
/** comments:设备安装方式,HANG:悬挂,FLOOR:落地 */
@ExcelColumn(sort=11,name="安装方式",readConverterExp="HANG=悬挂, FLOOR=落地")
private String fixedMode;
/** comments:操作系统平台,操作系统名称及版本及硬件架构名称,例如:Windows-x86_64,Linux-x86_64,Android-arm... */
@ExcelColumn(sort=12,name="操作系统平台")
private String osArch;
/** comments:网络连接类型:4G,WIFI,ETHERNET */
@ExcelColumn(sort=13,name="网络连接类型")
private String network;
/** comments:设备端应用程序的版本号,格式由应用层定义 */
@ExcelColumn(sort=14,name="版本")
private String versionInfo;
/** comments:设备型号 */
@ExcelColumn(sort=15,name="设备型号")
private String model;
/** comments:设备供应商 */
@ExcelColumn(sort=16,name="设备供应商")
private String vendor;
/** comments:设备产品详细信息,以JSON格式定义设备详细信息,JSON字段:device_name:产品名称,manufacturer:制造商,made_date:生产日期,应用层可根据需要扩展 */
@ExcelColumns({
@ExcelColumn(sort=17,name="设备产品详细信息"),
@ExcelColumn(sort=17,columnName="device_detail.device_name",name="产品名称"),
@ExcelColumn(sort=17,columnName="device_detail.manufacturer",name="制造商"),
@ExcelColumn(sort=17,columnName="device_detail.made_date",name="生产日期")
})
@com.fasterxml.jackson.annotation.JsonRawValue
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = gu.sql2java.json.RawJsonDeserializer.class)
private com.alibaba.fastjson.JSONObject deviceDetail;
/** comments:JSON格式的扩展字段(最大64KB),用于定义扩展信息,last_active_time:上次在线时间,disk_capacity:磁盘容量,status_comment:状态变更说明 */
@ExcelColumns({
@ExcelColumn(sort=18),
@ExcelColumn(sort=18,columnName="props.last_active_time",name="上次在线时间"),
@ExcelColumn(sort=18,columnName="props.disk_capacity",name="磁盘容量"),
@ExcelColumn(sort=18,columnName="props.status_comment",name="状态变更说明")
})
@com.fasterxml.jackson.annotation.JsonRawValue
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = gu.sql2java.json.RawJsonDeserializer.class)
private com.alibaba.fastjson.JSONObject props;
/** comments:当前节目ID */
@ExcelColumn(sort=19,name="当前节目ID")
private String planId;
/** comments:目标节目ID */
@ExcelColumn(sort=20,name="目标节目ID")
private String targetId;
/** comments:备注 */
@ExcelColumn(sort=21,name="备注")
private String remark;
/** comments:记录创建时间 */
@ExcelColumn(sort=22,name="记录创建时间")
private java.util.Date createTime;
/** comments:记录修改时间 */
@ExcelColumn(sort=23,name="记录修改时间")
private java.util.Date updateTime;
//////////////////////////////
///// 。。其他代码。。 ////
//////////////////////////////
}
```
### EXCEL 标记
本节说明说明EXCEL 标记`X@...@X`的定义方式
#### NAME tag
`X@NAME:xxxx@X` 为表/字段名字定义标记,定义表或字段在输出EXCEL数据的标题或列名,当出现在表的注释语句(COMMENT)中时它对应生成`@ExcelSheet`的`title`,出现在字段的注释语句(COMMENT)中时它对应生成`@ExcelColumn`的`name`.
#### NAMES tag
`X@NAMES:a=b,c=d@X`为多符号定义标记,定义表或字段在在输出EXCEL数据时,子成员变量的列名.当出现在表的注释语句(COMMENT)中时它对应生成表成员或子成员字段的`@ExceColumn`注解代码以定义表成员或子成员字段的列名.出现在字段的注释语句(COMMENT)中且该字段被标记为JSON object字段时它对应生成JSON子成员字段的`@ExcelColumn`注解代码以定义JSON子成员字段的列名.
在上面的示例中,`device_detail`字段被定义为JSON object字段,且COMMENT中定义了NAMES tag:`X@NAMES:device_name:产品名称,manufacturer:制造商,made_date:生产日期@X`,相当就定义了该JSON成员中的`device_name,device_name,manufacturer,made_date`子成员字段在输出到EXCEL时对应的列名.
> 如果实际该JSON 字段保存的字段不至这三个,还有`location`字段没有定义列名,输出该字段的列名是什么呢?没定义的情况默认就是`locaiton`
#### VALUES tag
`X@VALUES:a=b,c=d@X`为多值转换定义标记,用于定义在字段的值在输出到EXCEL时实际内容.对应于`@ExcelColumn`的`readConverterExp`.
如上示例中`dc_device.status`字段定义了VALUES tag:`X@VALUES:ENABLE:正常,CLOSED:关闭,MAINTAIN:维护,PENDING:挂起(待审核)@X`,即代表当月`status`字段值为`ENABLE`时,输出到Excel的值为`正常`,以此类推.
#### COLUMN tag
`X@COLUMN:a=b,c=d@X`为`@ExcelColumn`注解全字段定义标记,`@ExcelColumn`注解定义有数十个可配置的参数字段,`name,readConverterExp`只是常用的字段,所以定义了专用的NAME tag,VALUES tag,对于其他参数,都可以通COLUMN tag来进行定义.
#### SHEET tag
`X@SHEET:a=b,c=d@X`为`@ExcelSheet`注解全字段定义标记,`@ExcelSheet`注解定义有数十个可配置的参数字段,`name`只是常用的字段,所以定义了专用的NAME tag,对于其他参数,都可以通SHEET tag来进行定义.
上面的示例中dc_device表的注释(COMMENT)中使用了SHEET tag定义了:`@ExcelSheet`的`titleFillColor,hideColumns`:`X@SHEET:titleFillColor=YELLOW,hideColumns=private_time|props|device_detail@X`
> 注意在这里,`@ExcelSheet`的`hideColumns`的类型是String数组,在SHEET tag中对于数组类型的字段定义多个元素时要用`|`分割.
#### 专用标记优先于原则
即专用标记优先于COLUMN tag,SHEET tag定义
对于一个字段如果使用COLUMN tag,定义了`name`,同时也用NAME tag定义了列名,这种情况下优先使用NAME tag定义的值,对于SHEET tag也是一样的逻辑。
## Geomerty类型字段支持
```sql
CREATE TABLE dc_spot (
`id` int(11) PRIMARY KEY AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL COMMENT '地点名称',
`spot` point DEFAULT NULL COMMENT '经纬度点'
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
```
从3.18.0版本开始,支持MySQL(above 5.7)的所有空间数据类型([Spatial Data Types](https://dev.mysql.com/doc/refman/5.7/en/spatial-type-overview.html))即几何类型(Geomerty)字段自动生成[JTS](https://www.osgeo.org/projects/jts/) 的 `com.vividsolutions.jts.geom.Geometry` 类型的成员.
如下为MySQL空间数据类型名与JTS 几何对象的对应表
| MySQL空间数据类型名 | JTS类 |
| ------------------- | ---------------------------------------------- |
| GEOMETRY | com.vividsolutions.jts.geom.Geometry |
| POINT | com.vividsolutions.jts.geom.Point |
| LINESTRING | com.vividsolutions.jts.geom.LineString |
| POLYGON | com.vividsolutions.jts.geom.Polygon |
| MULTIPOINT | com.vividsolutions.jts.geom.GeometryCollection |
| MULTILINESTRING | com.vividsolutions.jts.geom.GeometryCollection |
| MULTIPOLYGON | com.vividsolutions.jts.geom.GeometryCollection |
| GEOMETRYCOLLECTION | com.vividsolutions.jts.geom.GeometryCollection |
### 配置参数
为控制生成的代码中对于Geomerty类型字段的getter/setter方法的类型,增加了`codewrite.bean.geometry.serial.type`参数
以下`codewrite.bean.geometry.serial.type`参数的说明
- STRING
【默认值】当`codewrite.bean.geometry.serial.type=STRING` 时,Geomerty类型字段的getter/setter方法的类型为String,即对外使用WKT字符串来表示几何对象
因为String是简单类型,这种方式可读性好,比较通用,符合常规场景下对Geometry类型的处理
生成代码示例:
```java
private com.vividsolutions.jts.geom.Point spot;
/** 将spot转为WKT字符串返回 */
public String getSpot(){
return null == spot ? null : spot.toText();
}
/** 将WKT字符串转为Point保存到spot */
public void setSpot(String newVal){
spot = (null == newVal || newVal.isEmpty())? null : GeometryDataCodec.DEFAULT_INSTANCE.fromWKTUnchecked(newVal,Point.class);
}
/** 返回sport原始对象 */
public com.vividsolutions.jts.geom.Point readSpot(){
return spot;
}
/** 修改spot原始对象 */
public void writeSpot(com.vividsolutions.jts.geom.Point newVal){
spot = newVal;
}
```
- JTS
当`codewrite.bean.geometry.serial.type=JTS` 时,JTS Geomertry 类型直接用于Geomerty类型字段的getter/setter方法的类型。
生成代码示例:
```java
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = gu.sql2java.geometry.jackson.GeometryDeserializer.class)
@com.fasterxml.jackson.databind.annotation.JsonSerialize(using = gu.sql2java.geometry.jackson.GeometrySerializer.class)
@com.alibaba.fastjson.annotation.JSONField(serializeUsing=gu.sql2java.geometry.fastjson.PointCodec.class,deserializeUsing=gu.sql2java.geometry.fastjson.PointCodec.class)
private com.vividsolutions.jts.geom.Point spot;
/** 返回sport原始对象 */
public com.vividsolutions.jts.geom.Point getSpot(){
return spot;
}
/** 修改spot原始对象 */
public void setSpot(com.vividsolutions.jts.geom.Point newVal){
spot = newVal;
}
```
不论`codewrite.bean.geometry.serial.type`参数如何选择,Geomerty类型字段生成的Java Bean的对应成员类型都为 JTS Geomerty类型。
### jackson支持
JTS Geometry对象不是标准的Java Bean不能自动被Jackson执行序列化和反序列化。所以需要为 Geometry对象实现自定义的序列化器和反序列化器。
JTS Geometry类序列化器实现 [gu.sql2java.geometry.jackson.GeometryDeserializer](sql2java-base/src/main/java/gu/sql2java/geometry/jackson/GeometryDeserializer.java)
JTS Geometry类反序列化器实现 [gu.sql2java.geometry.jackson.GeometrySerializer](sql2java-base/src/main/java/gu/sql2java/geometry/jackson/GeometrySerializer.java)
如下可以在以使用`@JsonDeserialize`和`@JsonSerialize`注解定义类成员字段的自定义序列化和反序列化器:
```java
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = gu.sql2java.geometry.jackson.GeometryDeserializer.class)
@com.fasterxml.jackson.databind.annotation.JsonSerialize(using = gu.sql2java.geometry.jackson.GeometrySerializer.class)
private com.vividsolutions.jts.geom.Point spot;
```
直接引用自定义序列化和反序列化器示例参见:[GeometryJacksonTest](sql2java-base/src/test/java/gu/sql2java/GeometryJacksonTest.java)
### fastjson支持
JTS Geometry对象不是标准的Java Bean不能自动被fastjson执行序列化和反序列化。所以需要为 Geometry对象实现自定义的序列化器和反序列化器。
fastjson 为JTS Geometry类型自定义的序列化和反序列化实现的基类为[gu.sql2java.geometry.fastjson.GeometryCodec](sql2java-base/src/main/java/gu/sql2java/geometry/fastjson/GeometryCodec.java)
如下为详细的JTS Geometry类与fastjson自定义序列化反序列化类对应表
| JTS Geometry类 | fastjson自定义序列化反序列化类 |
| ---------------------------------------------- | ------------------------------------------------------------ |
| com.vividsolutions.jts.geom.Geometry | [gu.sql2java.geometry.fastjson.GeometryCodec](sql2java-base/src/main/java/gu/sql2java/geometry/fastjson/GeometryCodec.java) |
| com.vividsolutions.jts.geom.Point | [gu.sql2java.geometry.fastjson.PointCodec](sql2java-base/src/main/java/gu/sql2java/geometry/fastjson/PointCodec.java) |
| com.vividsolutions.jts.geom.LineString | [gu.sql2java.geometry.fastjson.LineStringCodec](sql2java-base/src/main/java/gu/sql2java/geometry/fastjson/LineStringCodec.java) |
| com.vividsolutions.jts.geom.Polygon | [gu.sql2java.geometry.fastjson.PolygonCodec](sql2java-base/src/main/java/gu/sql2java/geometry/fastjson/PolygonCodec.java) |
| com.vividsolutions.jts.geom.GeometryCollection | [gu.sql2java.geometry.fastjson.GeometryCollectionCodec](sql2java-base/src/main/java/gu/sql2java/geometry/fastjson/GeometryCollectionCodec.java) |
#### 注解引用示例
如下可以在以使用`@JSONField`注解定义类成员字段的自定义序列化和反序列化器:
```java
@com.alibaba.fastjson.annotation.JSONField(
serializeUsing=gu.sql2java.geometry.fastjson.PointCodec.class,
deserializeUsing=gu.sql2java.geometry.fastjson.PointCodec.class)
private com.vividsolutions.jts.geom.Point spot;
```
直接引用自定义序列化和反序列化器示例参见:[gu.sql2java.GeometryFastjsonTest](sql2java-base/src/test/java/gu/sql2java/GeometryFastjsonTest.java)
#### GeometryInit
[gu.sql2java.geometry.fastjson.GeometryInit](sql2java-base/src/main/java/gu/sql2java/geometry/fastjson/GeometryInit.java) 用于为fastjson指定JTS Geometry类型的全局序列化反序列器,即将上述的所有fastjson自定义序列化反序列化器实例设置为fastjson的全局序列化反序列化器
当需要单独对JTS Geometry进行序列化和反序列化时,可以使用此`GeometryInit.init()`完成全局初始化,以确保fastjson能正确处理JTS Geometry对象
示例如下:
```java
@Test
public void test2Fastjson() {
try {
GeometryInit.init();
String wkt = "POINT (1 -1)";
Point point = GeometryDataCodec.DEFAULT_INSTANCE.fromWKT(wkt,Point.class);
log("point {}",JSON.toJSONString(point));
assertTrue(wkt.equals(point.toText()));
} catch (ParseException e) {
e.printStackTrace();
assertTrue(false);
}
}
```
## 静态字段过滤
不论是Thrift RPC还是SpringWeb服务,服务方法的输入和输出参数都要通过网络在Server/Client之间传输。实现数据对象传输,发送端需要对数据对象进行序列化(JSON或二进制数据流),接收端需要对收到的数据反序列化还原为原始的数据对象。
从3.32.0版本开始,sql2java增加了静态字段过滤功能,是指在生成sql2java的数据库表记录对象类时,允许指定字段的可见度(ColumnVisibility)。
**可见度**是指字段对数据接收端是否可见,只要控制数据库表对象在序列化/反序列化时忽略不可见字段,数据接收端最终收到数据库对象中就不会包含该字段。
具体实现就是sql2java-generator会根据字段的可见度要求,对于不可见字段,在生成对应字段代码时增加Jackson,Fastjson,Thrift注解(Annotation)指定在表对象序列化/反序列化时忽略该字段。示例参见 **《SCOPE@@EPOSC》**章节
### 字段可见度定义
sql2java中字段可见度定义对应枚举类型`gu.sql2java.ColumnVisibility`
| 枚举变量 | JSON是否可见 | Thrift是否可见 | 说明 |
| ----------- | ------------ | -------------- | ------------------------------------------------------------ |
| **DEFAULT** | true | true | 默认:全可见 |
| **LOCAL** | false | false | 仅本地可见,与远端(Spring WEB,Thrift RPC)交互时不可见 |
| **THRIFT** | false | true | Thrift RPC 传输时可见,Thrft RPC 客户端与服务端交互时可见 |
| **JSON** | true | false | JSON序列化时可见,Spring WEB 客户端与服务端 交互时可见(Spring WEB是基于jackson实现序列化和反序列化的),使用fastjson对数据库对象进行序列化和反序列化时同样有效。 |
### 定义可见度
定义字段可见度的方式有两种,一种是在建表语句的字段注释中通过字段注释标记`SCOPE@@EPOSC`来定义,参见 **《SCOPE@@EPOSC》**章节
另一种是在sql2java参数配置文件(sql2java.properties)中定义,参见 **《参数说明》**章节`(6/8)`段
## 注解
### @Sql2javaLocalConfig
从3.32.6版本开始新增加了`@Sql2javaLocalConfig`注解,定义在服务方法或服务类上,用于定义Sql2java的运行时配置,目前只有一个字段`resetModifiedIfEqual`,如果将指定的Java Bean类型定义在`resetModifiedIfEqual`,则fastjson,jackson反序列化器在对JSON字段成功反序列化为JavaBean对象后,会调用`BasesBean.resetModifiedIfEqual`对解析的对象进行归一化处理,确保只有与数据库已经存在记录不相等的字段对应的modified标记才被置1。
#### RuntimeConfigInterceptor
在spring环境定义在服务类或服务方法上的`@Sql2javaLocalConfig`注解生效需要`RuntimeConfigInterceptor`的配合。`RuntimeConfigInterceptor`负责在HTTP请求解析之前为请求方法安装sql2java运行时配置对象( `gu.sql2java.config.RuntimeConfig`),方法调用结束删除。
`RuntimeConfig`中保存了由`@Sql2javaLocalConfig`注解生成的配置。
#### 启动RuntimeConfig拦截器
如下在Spring服务启动注解(`@SpringBootApplication`)上指定扫描`RuntimeConfigInterceptor`拦截器`FilterInterceptorConfig`所在包就可以启动服务方法拦截器,激活了服务方法上定义的beanfilter过滤器
```java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import gu.sql2java.config.spring.FilterInterceptorConfig;
/**
* 应用服务启动配置
*/
@SpringBootApplication(scanBasePackageClasses = {RuntimeConfigInterceptor.class})
public class ApplicationBoot{
//////..../////
}
```