3 Star 11 Fork 5

10km / sql2java

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
BSD-2-Clause

sql2java

sql2java是一个轻量级数据库(SQL)访问代码(java)生成器,是在国外一款同名开源项目(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代码结构

bean

生成的数据记录类示例

bean

编译项目

编译要求 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演示项目: https://gitee.com/l0km/sqljava-test

用户可以在此演示项目的基础上修改完成自己的ORM代码项目结构

实际应用示例

参见开源项目 facelog: 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.
# <TABLE> 占位符用于取代当前表名
# <KEY> 占位符用于取代当前自增长键字段名
#
#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

#______________________________________________
#
# (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接口用于应用层自定义对数据字段的序列化和反序列化,序列化方法(ColumnCodec.serialize)用于将自定义类型数据转换为数据库字段存储类型,反序列化方法(ColumnCodec.deserialize)则将字段存储类型转换为自定义数据类型。对于JSON类型的支持使用JsonColumnCodec实现,应用层可以参照JsonColumnCodec基于 BaseColumnCodec 实现自定义的数据库字段编解码器。

自定义义类型字段支持

从 3.21.0 开始,增加数据库字段注释标记 ANN@@NNATYPE@@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@@EPYTANN@@NNA

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的示例

    /** 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方法

    @ThriftField(value=7)
    @JsonProperty("rect")
    public net.facelib.eam.interpreter.Rectangle getRect(){
        return rect;
    }

setter方法

    @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方法

    public String readRect(){
        return metaData.columnCodecs[DC_DEVICE_CHANNEL_ID_RECT].serialize(rect,String.class);
    }

write方法

    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());
    }

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

示例:

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/boolTINYINT类型字段在生成的Java Bean中online字段将被定义为Boolean类型。

**注意:**在注释中[NUM,Boolean]标记必须在字符串起始位置。

JSON字段支持

从3.8版本之后,sql2java支持对保存JSON数据的String字段自动序列化和反序列。

开启json.jackson.rawvalue

要开启此特性,需要做如下两步简单的设置

开启此特性生成的代码需要fastjson,jackson库的支持

  • 修改 sql2java.properties

    json.jackson.rawvalue置为true,即增加如下设置

# 
# add @JsonRawValue @JsonDeserialize annotation for JSON field 
#json.jackson.rawvalue = true
  • 修改建表语句

    在需要定义为保存JSON数据的字段的注释中增加[JSON_STR,...]前缀,通过此前缀指定该字段要保存JSON数据(只对Java类型为String的字段有效)

    示例如下:

    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 字段定义代码:

    /** 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类型对字段进行读写:

    @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对象直接读写:

    /** 
     * read method for {@link #props}<br>
     * @return the JSON value of props
     */
    public com.alibaba.fastjson.JSONObject readProps(){
        return props;
    }
    /** 
     * write method for {@link #props} with JSON object.<br>
     */
    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实现的数据记录导出为Excel格式数据流或文件的功能.参见sql2java-excel/README.md.

sql2java-excel支持注解方式配置每张表每个字段的Excel导出配置。sql2java-generator在生成Java Bean代码时支持从SQL 表和字段注释COMMENT中根据读取预定义的标记,在Java Bean类代码中生成@ExcelSheet,@ExcelColumn注解。

开启excel.annotation.bean

要开启Excel注解生成特性,需要做如下两步设置

开启此特性生成的代码需要sql2java-excel库的支持

  • 修改 sql2java.properties

    excel.annotation.bean置为true,即增加如下设置

# generate @ExcelSheet,@ExcelColumn annotation if true
excel.annotation.bean = true  
  • 修改建表语句

在数据库建表语句的表和字段注释(COMMENT)上根据需要定义特殊的EXCEL 标记,示例如下:

下面的CREATE TABLE SQL语句示例中用到了很多X@....@X标记,这些标记内容就是为sql2java-generator在生成JavaBean代码时用于识别生成@ExcelSheet,@ExcelColumn注解的特殊标记(tag):

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',
  `token_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=token_time|props|device_detail@X' DEFAULT CHARSET=utf8;

有了这些标记,sql2java-generator就会为dc_device表生成如下的带有@ExcelSheet,@ExcelColumn注解的Java Bean代码:

/**
 * DeviceBean is a mapping of dc_device Table.
 * <br>Meta Data Information (in progress):
 * <ul>
 *    <li>comments: 前端设备记录,前端设备基本信息, </li>
 * </ul>
 * @author guyadong
*/
@ExcelSheet(sheetName="dc_device",title="前端设备记录",titleFillColor="YELLOW",hideColumns={"token_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 tokenTime;

    /** 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)中时它对应生成@ExcelSheettitle,出现在字段的注释语句(COMMENT)中时它对应生成@ExcelColumnname.

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时实际内容.对应于@ExcelColumnreadConverterExp.

如上示例中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定义了:@ExcelSheettitleFillColor,hideColumns:X@SHEET:titleFillColor=YELLOW,hideColumns=token_time|props|device_detail@X

注意在这里,@ExcelSheethideColumns的类型是String数组,在SHEET tag中对于数组类型的字段定义多个元素时要用|分割.

专用标记优先于原则

即专用标记优先于COLUMN tag,SHEET tag定义

对于一个字段如果使用COLUMN tag,定义了name,同时也用NAME tag定义了列名,这种情况下优先使用NAME tag定义的值,对于SHEET tag也是一样的逻辑。

Geomerty类型字段支持

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)即几何类型(Geomerty)字段自动生成JTScom.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类型的处理

生成代码示例:

	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方法的类型。

生成代码示例:

    @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

JTS Geometry类反序列化器实现 gu.sql2java.geometry.jackson.GeometrySerializer

如下可以在以使用@JsonDeserialize@JsonSerialize注解定义类成员字段的自定义序列化和反序列化器:

    @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

fastjson支持

JTS Geometry对象不是标准的Java Bean不能自动被fastjson执行序列化和反序列化。所以需要为 Geometry对象实现自定义的序列化器和反序列化器。

fastjson 为JTS Geometry类型自定义的序列化和反序列化实现的基类为gu.sql2java.geometry.fastjson.GeometryCodec

如下为详细的JTS Geometry类与fastjson自定义序列化反序列化类对应表

JTS Geometry类 fastjson自定义序列化反序列化类
com.vividsolutions.jts.geom.Geometry gu.sql2java.geometry.fastjson.GeometryCodec
com.vividsolutions.jts.geom.Point gu.sql2java.geometry.fastjson.PointCodec
com.vividsolutions.jts.geom.LineString gu.sql2java.geometry.fastjson.LineStringCodec
com.vividsolutions.jts.geom.Polygon gu.sql2java.geometry.fastjson.PolygonCodec
com.vividsolutions.jts.geom.GeometryCollection gu.sql2java.geometry.fastjson.GeometryCollectionCodec

注解引用示例

如下可以在以使用@JSONField注解定义类成员字段的自定义序列化和反序列化器:

  	@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

GeometryInit

gu.sql2java.geometry.fastjson.GeometryInit 用于为fastjson指定JTS Geometry类型的全局序列化反序列器,即将上述的所有fastjson自定义序列化反序列化器实例设置为fastjson的全局序列化反序列化器

当需要单独对JTS Geometry进行序列化和反序列化时,可以使用此GeometryInit.init()完成全局初始化,以确保fastjson能正确处理JTS Geometry对象

示例如下:

    @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);
        }
    }
Copyright (c) 2017, 10km All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

简介

轻量级数据库(SQL)访问代码(java)生成器 展开 收起
Java
BSD-2-Clause
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/l0km/sql2java.git
git@gitee.com:l0km/sql2java.git
l0km
sql2java
sql2java
master

搜索帮助