JDBC
JDBC 持久化模块针对关系型数据库(RDBMS)数据存取的一套简单解决方案,主要关注数据存取的效率、易用性、稳定和透明,其具备以下功能特征:
- 基于 JDBC 框架 API 进行轻量封装,结构简单、便于开发、调试和维护;
- 优化批量数据更新、标准化结果集、预编译 SQL 语句处理;
- 支持单实体 ORM 操作,无需编写 SQL 语句;
- 提供脚手架工具,快速生成数据实体类,支持链式调用;
- 支持通过存储器注解自定义 SQL 语句或从配置文件中动态加载 SQL 并自动执行;
- 支持 结果集与值对象的自动装配,支持自定义装配规则;
- 支持多数据源,默认支持 C3P0、DBCP、Druid、HikariCP、JNDI 连接池配置,支持数据源扩展;
- 支持多种数据库(如:Oracle、MySQL、SQLServer、SQLite、H2、PostgreSQL 等);
- 支持面向对象的数据库查询封装,有助于减少或降低程序编译期错误;
- 支持数据库事务嵌套;
- 支持数据库视图和存储过程;
Maven包依赖
<dependency>
<groupId>net.ymate.platform</groupId>
<artifactId>ymate-platform-persistence-jdbc</artifactId>
<version>2.1.4-dev</version>
</dependency>
模块配置
配置文件参数说明
#-------------------------------------
# JDBC持久化模块初始化参数
#-------------------------------------
# 默认数据源名称, 默认值: default
ymp.configs.persistence.jdbc.ds_default_name=
# 数据源列表, 多个数据源名称间用'|'分隔, 默认值: default
ymp.configs.persistence.jdbc.ds_name_list=
# 是否自动连接, 即模块初始化时完成连接动作, 默认值: false
ymp.configs.persistence.jdbc.ds.default.auto_connection=true
# 是否显示执行的SQL语句, 默认值: false
ymp.configs.persistence.jdbc.ds.default.show_sql=true
# 是否开启堆栈跟踪, 默认值: false
ymp.configs.persistence.jdbc.ds.default.stack_traces=true
# 堆栈跟踪层级深度, 默认值: 0(即全部)
ymp.configs.persistence.jdbc.ds.default.stack_trace_depth=
# 堆栈跟踪包名前缀过滤, 默认值: 空
ymp.configs.persistence.jdbc.ds.default.stack_trace_packages=
# 自定义引用标识符, 根据数据库类型进行设置, 默认值: 空
ymp.configs.persistence.jdbc.ds.default.identifier_quote=
# 数据库表前缀名称, 多个前缀名称间用'|'分隔, 默认值: 空
ymp.configs.persistence.jdbc.ds.default.table_prefix=
# 数据源适配器, 可选值为已知适配器名称或自定义适配置类名称, 默认值: default, 目前支持已知适配器[default|dbcp|c3p0|druid|hikaricp|jndi|...]
ymp.configs.persistence.jdbc.ds.default.adapter_class=dbcp
# 数据源适配器配置文件,可选参数,若未设置或设置的文件路径无效将被忽略,默认值为空
ymp.configs.persistence.jdbc.ds.default.config_file=
# 数据库类型, 可选参数, 默认值将通过连接字符串分析获得, 目前支持[mysql|oracle|sqlserver|db2|sqlite|postgresql|hsqldb|h2]
ymp.configs.persistence.jdbc.ds.default.type=
# 数据库方言, 可选参数, 自定义方言将覆盖默认配置
ymp.configs.persistence.jdbc.ds.default.dialect_class=
# 数据库连接驱动, 可选参数, 框架默认将根据数据库类型进行自动匹配
ymp.configs.persistence.jdbc.ds.default.driver_class=
# 数据库连接字符串, 必填参数
ymp.configs.persistence.jdbc.ds.default.connection_url=jdbc:mysql://localhost:3306/db_name?useUnicode=true&useSSL=false&characterEncoding=UTF-8
# 数据库访问用户名称, 必填参数
ymp.configs.persistence.jdbc.ds.default.username=root
# 数据库访问密码, 可选参数, 经过默认密码处理器加密后的admin字符串为wRI2rASW58E
ymp.configs.persistence.jdbc.ds.default.password=wRI2rASW58E
# 数据库访问密码是否已加密, 默认值: false
ymp.configs.persistence.jdbc.ds.default.password_encrypted=true
# 数据库密码处理器, 可选参数, 用于对已加密码数据库访问密码进行解密, 默认值: 空
ymp.configs.persistence.jdbc.ds.default.password_class=
配置注解参数说明
当 JDBC 持久化模块初始化时,若在配置文件中存在数据源相关配置,则基于注解的数据源配置将全部失效。
@DatabaseConf
| 配置项 | 描述 |
|---|---|
| dsDefaultName | 默认数据源名称 |
| value | 数据源配置 |
@DatabaseDataSource
| 配置项 | 描述 |
|---|---|
| name | 数据源名称 |
| connectionUrl | 数据库连接字符串 |
| username | 数据库访问用户名称 |
| password | 数据库访问密码 |
| passwordEncrypted | 数据库访问密码是否已加密 |
| passwordClass | 数据库密码处理器 |
| type | 数据库类型 |
| dialectClass | 数据库方言 |
| adapterClass | 数据源适配器 |
| configFile | 数据源适配器配置文件 |
| driverClass | 数据库默认驱动类名称 |
| autoConnection | 是否自动连接 |
| showSql | 是否显示执行的 SQL 语句 |
| stackTraces | 是否开启堆栈跟踪 |
| stackTraceDepth | 堆栈跟踪层级深度 |
| stackTracePackages | 堆栈跟踪过滤包名前缀集合 |
| tablePrefix | 数据库表前缀名称 |
| identifierQuote | 数据库引用标识符 |
模块事件
事件枚举对象 DatabaseEvent 包括以下事件类型:
| 事务类型 | 说明 |
|---|---|
| QUERY_AFTER | 执行查询操作后 |
| INSERT_AFTER | 执行插入操作后 |
| INSERT_IF_NOT_EXIST_AFTER | 执行插入(如果记录不存在)操作后 |
| UPDATE_AFTER | 执行更新操作后 |
| UPSERT_AFTER | 执行更新插入操作后 |
| REMOVE_AFTER | 执行删除操作后 |
数据源(DataSource)
多数据源连接
JDBC 持久化模块默认支持多数据源配置,下面通过简单的配置来展示如何连接多个数据库:
# 定义两个数据源分别用于连接MySQL和Oracle数据库,同时指定默认数据源为default(即MySQL数据库)
ymp.configs.persistence.jdbc.ds_default_name=default
ymp.configs.persistence.jdbc.ds_name_list=default|oracledb
# 连接到MySQL数据库的数据源配置
ymp.configs.persistence.jdbc.ds.default.connection_url=jdbc:mysql://localhost:3306/mydb
ymp.configs.persistence.jdbc.ds.default.username=root
ymp.configs.persistence.jdbc.ds.default.password=123456
# 连接到Oracle数据库的数据源配置
ymp.configs.persistence.jdbc.ds.oracledb.connection_url=jdbc:oracle:thin:@localhost:1521:ORCL
ymp.configs.persistence.jdbc.ds.oracledb.username=ORCL
ymp.configs.persistence.jdbc.ds.oracledb.password=123456
从上述配置中可以看出,配置不同的数据源时只需要定义数据源名称列表,再根据列表逐一配置即可。
通过注解方式配置多数据源,如下所示:
@DatabaseConf(dsDefaultName = "default", value = {
@DatabaseDataSource(name = "default",
connectionUrl = "jdbc:mysql://localhost:3306/mydb",
username = "root",
password = "123456"),
@DatabaseDataSource(name = "oracledb",
connectionUrl = "jdbc:oracle:thin:@localhost:1521:ORCL",
username = "ORCL",
password = "123456")
})
连接池配置
JDBC 持久化模块提供的数据源类型如下:
| 名称 | 类型 | 描述 |
|---|---|---|
| default | DefaultDataSourceAdapter | 默认数据源适配器,通过 DriverManager 直接连接数据库,建议仅用于测试。 |
| c3p0 | C3P0DataSourceAdapter | 基于 C3P0 连接池的数据源适配器。 |
| dbcp | DBCPDataSourceAdapter | 基于 DBCP 连接池的数据源适配器。 |
| druid | DruidDataSourceAdapter | 基于阿里巴巴开源的 Druid 连接池的数据源适配器。 |
| hikaricp | HikariCPDataSourceAdapter | 基于 HikariCP 连接池的数据源适配器。 |
| jndi | JNDIDataSourceAdapter | 基于 JNDI 的数据源适配器。 |
只需根据实际情况调整对应数据源名称的配置,如:
ymp.configs.persistence.jdbc.ds.default.adapter_class=dbcp
通过注解配置方式,如下所示:
@DatabaseConf(dsDefaultName = "default",
value = {
@DatabaseDataSource(name = "default",
connectionUrl = "jdbc:mysql://localhost:3306/mydb",
username = "root",
password = "123456",
adapterClass = DBCPDataSourceAdapter.class)
})
针对于 dbcp、druid、hikaricp 和 c3p0 连接池的配置文件及内容,请将对应的配置文件(如:dbcp.properties、c3p0.properties等)放置在工程的 classpath 根路径下,若上述配置文件不存在,JDBC 持久化模块在初始化时将自动创建。
另外,dbcp、druid、hikaricp 和 c3p0 连接池支持根据数据源名称进行单独配置(如:dbcp_oracledb.properties,此文件将优先于 dbcp.properties 被加载),其中 druid 连接池可以兼容 dbcp 连接池的配置文件。
当然,也可以通过 IDatabaseDataSourceAdapter 接口自行实现,框架针对该接口提供了 AbstractDatabaseDataSourceAdapter 抽象类,直接继承即可。
数据库连接持有者(IDatabaseConnectionHolder)
用于记录真正的数据库连接对象(Connection)原始的状态及与数据源对应关系,在 JDBC 持久化模块中获取到的所有连接对象均由数据库连接持有者对象包装,基于数据库连接持有者接口可以进行如下操作:
public class Main {
public static void main(String[] args) throws Exception {
try (IApplication application = YMP.run(args)) {
if (application.isInitialized()) {
// 获取当前容器内JDBC模块实例
IDatabase database = application.getModuleManager().getModule(JDBC.class);
// 获取默认数据源的连接持有者实例,等同于:database.getConnectionHolder("default");
IDatabaseConnectionHolder connectionHolder = database.getDefaultConnectionHolder();
// 获取指定名称的数据源连接持有者实例
connectionHolder = database.getConnectionHolder("oracledb");
// 获取连接对象
Connection connection = connectionHolder.getConnection();
// 获取数据源配置对象
IDatabaseDataSourceConfig dataSourceConfig = connectionHolder.getDataSourceConfig();
// 获取当前数据源适配器对象
IDatabaseDataSourceAdapter dataSourceAdapter = connectionHolder.getDataSourceAdapter();
// 获取当前数据库方言
IDialect dialect = connectionHolder.getDialect();
// 获取当前连接持有者所属JDBC模块实例
IDatabase owner = connectionHolder.getOwner();
}
}
}
}
数据实体(Entity)
数据实体是以对象的形式与数据库表之间的一种映射关系,实体中的属性与表中字段一一对应。
数据实体类包含以下几个部份:
- 基本属性:注解配置属性与字段之间的关系及属性的 Getter 和 Setter 方法。
- FIELDS:字段名常量。
- Builder:基本属性构建器类,支持以链式调用方式为实体属性赋值。
- FieldConditionBuilder:属性条件构建器类,为具体实体属性构建字段查询条件。
实体类与表对应关系示例
假定数据库中的 ym_user 数据表结构如下:
CREATE TABLE `ym_user` (
`id` varchar(32) NOT NULL COMMENT '用户唯一标识',
`username` varchar(32) DEFAULT NULL COMMENT '用户名称',
`nickname` varchar(32) DEFAULT NULL COMMENT '昵称',
`gender` varchar(1) DEFAULT NULL COMMENT '性别',
`age` int(2) unsigned DEFAULT '0' COMMENT '年龄',
`avatar_url` varchar(255) DEFAULT NULL COMMENT '头像URL地址',
`password` varchar(32) DEFAULT NULL COMMENT '登录密码',
`email` varchar(100) DEFAULT NULL COMMENT '电子邮件',
`mobile` varchar(20) DEFAULT NULL COMMENT '手机号码',
`type` smallint(2) unsigned DEFAULT '0' COMMENT '类型',
`status` smallint(2) unsigned DEFAULT '0' COMMENT '状态',
`create_time` bigint(13) NOT NULL COMMENT '注册时间',
`last_modify_time` bigint(13) DEFAULT '0' COMMENT '最后修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息';
通过实体生成工具自动构建的实体类如下:
@Entity(UserEntity.TABLE_NAME)
@Comment("用户信息")
public class UserEntity extends BaseEntity<UserEntity, String> {
private static final long serialVersionUID = 1L;
@Id
@Property(name = FIELDS.ID, nullable = false, length = 32)
@Comment("用户唯一标识")
@PropertyState(propertyName = FIELDS.ID)
private String id;
@Property(name = FIELDS.USERNAME, length = 32)
@Comment("用户名称")
@PropertyState(propertyName = FIELDS.USERNAME)
private String username;
@Property(name = FIELDS.NICKNAME, length = 32)
@Comment("昵称")
@PropertyState(propertyName = FIELDS.NICKNAME)
private String nickname;
@Property(name = FIELDS.GENDER, length = 1)
@Comment("性别")
@PropertyState(propertyName = FIELDS.GENDER)
private String gender;
@Property(name = FIELDS.AGE, length = 2)
@Comment("年龄")
@PropertyState(propertyName = FIELDS.AGE)
private Integer age;
@Property(name = FIELDS.AVATAR_URL, length = 255)
@Comment("头像URL地址")
@PropertyState(propertyName = FIELDS.AVATAR_URL)
private String avatarUrl;
@Property(name = FIELDS.PASSWORD, length = 32)
@Comment("登录密码")
@PropertyState(propertyName = FIELDS.PASSWORD)
private String password;
@Property(name = FIELDS.EMAIL, length = 100)
@Comment("电子邮件")
@PropertyState(propertyName = FIELDS.EMAIL)
private String email;
@Property(name = FIELDS.MOBILE, length = 20)
@Comment("手机号码")
@PropertyState(propertyName = FIELDS.MOBILE)
private String mobile;
@Property(name = FIELDS.TYPE, unsigned = true, length = 2)
@Default("0")
@Comment("类型")
@PropertyState(propertyName = FIELDS.TYPE)
private Integer type;
@Property(name = FIELDS.STATUS, unsigned = true, length = 2)
@Default("0")
@Comment("状态")
@PropertyState(propertyName = FIELDS.STATUS)
private Integer status;
@Property(name = FIELDS.CREATE_TIME, nullable = false, length = 13)
@Comment("注册时间")
@PropertyState(propertyName = FIELDS.CREATE_TIME)
@Readonly
private Long createTime;
@Property(name = FIELDS.LAST_MODIFY_TIME, length = 13)
@Default("0")
@Comment("最后修改时间")
@PropertyState(propertyName = FIELDS.LAST_MODIFY_TIME)
private Long lastModifyTime;
public UserEntity() {
}
public UserEntity(IDatabase dbOwner) {
super(dbOwner);
}
public UserEntity(String id, Long createTime) {
this.id = id;
this.createTime = createTime;
}
public UserEntity(IDatabase dbOwner, String id, Long createTime) {
super(dbOwner);
this.id = id;
this.createTime = createTime;
}
public UserEntity(String id, String username, String nickname, String gender, Integer age, String avatarUrl, String password, String email, String mobile, Integer type, Integer status, Long createTime, Long lastModifyTime) {
this.id = id;
this.username = username;
this.nickname = nickname;
this.gender = gender;
this.age = age;
this.avatarUrl = avatarUrl;
this.password = password;
this.email = email;
this.mobile = mobile;
this.type = type;
this.status = status;
this.createTime = createTime;
this.lastModifyTime = lastModifyTime;
}
public UserEntity(IDatabase dbOwner, String id, String username, String nickname, String gender, Integer age, String avatarUrl, String password, String email, String mobile, Integer type, Integer status, Long createTime, Long lastModifyTime) {
super(dbOwner);
this.id = id;
this.username = username;
this.nickname = nickname;
this.gender = gender;
this.age = age;
this.avatarUrl = avatarUrl;
this.password = password;
this.email = email;
this.mobile = mobile;
this.type = type;
this.status = status;
this.createTime = createTime;
this.lastModifyTime = lastModifyTime;
}
@Override
public String getId() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public Long getLastModifyTime() {
return lastModifyTime;
}
public void setLastModifyTime(Long lastModifyTime) {
this.lastModifyTime = lastModifyTime;
}
public Builder bind() {
return new Builder(this);
}
public static Builder builder() {
return new Builder();
}
public static Builder builder(IDatabase dbOwner) {
return new Builder(dbOwner);
}
public static class Builder {
private final UserEntity targetEntity;
public Builder() {
targetEntity = new UserEntity();
}
public Builder(IDatabase dbOwner) {
targetEntity = new UserEntity(dbOwner);
}
public Builder(UserEntity targetEntity) {
this.targetEntity = targetEntity;
}
public UserEntity build() {
return targetEntity;
}
public IDatabaseConnectionHolder connectionHolder() {
return targetEntity.getConnectionHolder();
}
public Builder connectionHolder(IDatabaseConnectionHolder connectionHolder) {
targetEntity.setConnectionHolder(connectionHolder);
return this;
}
public IDatabase dbOwner() {
return targetEntity.getDbOwner();
}
public Builder dbOwner(IDatabase dbOwner) {
targetEntity.setDbOwner(dbOwner);
return this;
}
public String dataSourceName() {
return targetEntity.getDataSourceName();
}
public Builder dataSourceName(String dataSourceName) {
targetEntity.setDataSourceName(dataSourceName);
return this;
}
public IShardingable shardingable() {
return targetEntity.getShardingable();
}
public Builder shardingable(IShardingable shardingable) {
targetEntity.setShardingable(shardingable);
return this;
}
public String id() {
return targetEntity.getId();
}
public Builder id(String id) {
targetEntity.setId(id);
return this;
}
public String username() {
return targetEntity.getUsername();
}
public Builder username(String username) {
targetEntity.setUsername(username);
return this;
}
public String nickname() {
return targetEntity.getNickname();
}
public Builder nickname(String nickname) {
targetEntity.setNickname(nickname);
return this;
}
public String gender() {
return targetEntity.getGender();
}
public Builder gender(String gender) {
targetEntity.setGender(gender);
return this;
}
public Integer age() {
return targetEntity.getAge();
}
public Builder age(Integer age) {
targetEntity.setAge(age);
return this;
}
public String avatarUrl() {
return targetEntity.getAvatarUrl();
}
public Builder avatarUrl(String avatarUrl) {
targetEntity.setAvatarUrl(avatarUrl);
return this;
}
public String password() {
return targetEntity.getPassword();
}
public Builder password(String password) {
targetEntity.setPassword(password);
return this;
}
public String email() {
return targetEntity.getEmail();
}
public Builder email(String email) {
targetEntity.setEmail(email);
return this;
}
public String mobile() {
return targetEntity.getMobile();
}
public Builder mobile(String mobile) {
targetEntity.setMobile(mobile);
return this;
}
public Integer type() {
return targetEntity.getType();
}
public Builder type(Integer type) {
targetEntity.setType(type);
return this;
}
public Integer status() {
return targetEntity.getStatus();
}
public Builder status(Integer status) {
targetEntity.setStatus(status);
return this;
}
public Long createTime() {
return targetEntity.getCreateTime();
}
public Builder createTime(Long createTime) {
targetEntity.setCreateTime(createTime);
return this;
}
public Long lastModifyTime() {
return targetEntity.getLastModifyTime();
}
public Builder lastModifyTime(Long lastModifyTime) {
targetEntity.setLastModifyTime(lastModifyTime);
return this;
}
}
public interface FIELDS {
String ID = "id";
String USERNAME = "username";
String NICKNAME = "nickname";
String GENDER = "gender";
String AGE = "age";
String AVATAR_URL = "avatar_url";
String PASSWORD = "password";
String EMAIL = "email";
String MOBILE = "mobile";
String TYPE = "type";
String STATUS = "status";
String CREATE_TIME = "create_time";
String LAST_MODIFY_TIME = "last_modify_time";
}
public static final String TABLE_NAME = "user";
public static FieldConditionBuilder conditionBuilder() {
return new FieldConditionBuilder();
}
public static FieldConditionBuilder conditionBuilder(String prefix) {
return new FieldConditionBuilder(prefix);
}
public static FieldConditionBuilder conditionBuilder(Query<?> query) {
return conditionBuilder(query, null);
}
public static FieldConditionBuilder conditionBuilder(Query<?> query, String prefix) {
return new FieldConditionBuilder(query.owner(), query.dataSourceName(), prefix);
}
public static FieldConditionBuilder conditionBuilder(UserEntity entity) {
return conditionBuilder(entity, null);
}
public static FieldConditionBuilder conditionBuilder(UserEntity entity, String prefix) {
return new FieldConditionBuilder(entity.doGetSafeOwner(), entity.getDataSourceName(), prefix);
}
public static FieldConditionBuilder conditionBuilder(IDatabase owner, String prefix) {
return new FieldConditionBuilder(owner, prefix);
}
public static FieldConditionBuilder conditionBuilder(IDatabase owner, String dataSourceName, String prefix) {
return new FieldConditionBuilder(owner, dataSourceName, prefix);
}
public static class FieldConditionBuilder extends AbstractFieldConditionBuilder {
public FieldConditionBuilder() {
super(null, null, null);
}
public FieldConditionBuilder(String prefix) {
super(null, null, prefix);
}
public FieldConditionBuilder(Query<?> query) {
super(query.owner(), null, null);
}
public FieldConditionBuilder(Query<?> query, String prefix) {
super(query.owner(), query.dataSourceName(), prefix);
}
public FieldConditionBuilder(IDatabase owner) {
super(owner, null, null);
}
public FieldConditionBuilder(IDatabase owner, String prefix) {
super(owner, null, prefix);
}
public FieldConditionBuilder(IDatabase owner, String dataSourceName, String prefix) {
super(owner, dataSourceName, prefix);
}
public FieldCondition id() {
return createFieldCondition(UserEntity.FIELDS.ID);
}
public FieldCondition username() {
return createFieldCondition(UserEntity.FIELDS.USERNAME);
}
public FieldCondition nickname() {
return createFieldCondition(UserEntity.FIELDS.NICKNAME);
}
public FieldCondition gender() {
return createFieldCondition(UserEntity.FIELDS.GENDER);
}
public FieldCondition age() {
return createFieldCondition(UserEntity.FIELDS.AGE);
}
public FieldCondition avatarUrl() {
return createFieldCondition(UserEntity.FIELDS.AVATAR_URL);
}
public FieldCondition password() {
return createFieldCondition(UserEntity.FIELDS.PASSWORD);
}
public FieldCondition email() {
return createFieldCondition(UserEntity.FIELDS.EMAIL);
}
public FieldCondition mobile() {
return createFieldCondition(UserEntity.FIELDS.MOBILE);
}
public FieldCondition type() {
return createFieldCondition(UserEntity.FIELDS.TYPE);
}
public FieldCondition status() {
return createFieldCondition(UserEntity.FIELDS.STATUS);
}
public FieldCondition createTime() {
return createFieldCondition(UserEntity.FIELDS.CREATE_TIME);
}
public FieldCondition lastModifyTime() {
return createFieldCondition(UserEntity.FIELDS.LAST_MODIFY_TIME);
}
}
}