如何使用 EclipseLink 和 Joda-Time 将 UTC 日期时间存储到数据库中?

2023-12-07

我一直在摸索以下 EclipseLink乔达时间转换器长时间存储日期时间UTC进入MySQL数据库完全没有成功。

import java.util.Date;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
import org.joda.time.DateTime;

public final class JodaDateTimeConverter implements Converter {

    private static final long serialVersionUID = 1L;

    @Override
    public Object convertObjectValueToDataValue(Object objectValue, Session session) {
        //Code to convert org.joda.time.DateTime to java.util.Date in UTC.
        //Currently dealing with the following line
        //that always uses the system local time zone which is incorrect.
        //It should be in the UTC zone.
        return objectValue instanceof DateTime ? ((DateTime) objectValue).toDate() : null;
    }

    @Override
    public Object convertDataValueToObjectValue(Object dataValue, Session session) {
        return dataValue instanceof Date ? new DateTime((Date) dataValue) : null;
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public void initialize(DatabaseMapping databaseMapping, Session session) {
        databaseMapping.getField().setType(java.util.Date.class);
    }
}

The objectValue的参数convertObjectValueToDataValue()方法是一个instanceOf DateTime这已经是根据 UTC 时区。因此,我避免了.withZone(DateTimeZone.UTC).

There is already a separate converter on the client that converts a String representation of date-time to org.joda.time.DateTime in UTC before sending it to EJBs).

This ((DateTime) objectValue).toDate()在返回语句中convertObjectValueToDataValue()方法始终采用应位于 UTC 时区的系统本地时区。

无论如何,日期时间应根据 UTC 时区插入 MySQL。

The best / ideal solution would be if it handles date-time of Joda similar to Hibernate


EDIT:

类型的属性org.joda.time.DateTime作为示例,在模型类中指定如下。

@Column(name = "discount_start_date", columnDefinition = "DATETIME")
@Converter(name = "dateTimeConverter", converterClass = JodaDateTimeConverter.class)
@Convert("dateTimeConverter")
private DateTime discountStartDate; //Getter and setter.    

DateJava 中与时区无关。它总是采用 UTC(默认情况下并且总是)但是当Date / Timestamp通过 JDBC 驱动程序传递到数据库,它根据 JVM 时区解释日期/时间,而 JVM 时区又默认为系统时区(本机操作系统时区)。

因此,除非显式强制 MySQL JDBC 驱动程序使用 UTC 区域或 JVM 本身设置为使用该区域,否则它不会存储Date / Timestamp使用 UTC 进入目标数据库,即使 MySQL 本身配置为使用 UTCdefault_time_zone='+00:00' in my.ini or my.cnf in the [mysqld]部分。像Oracle这样的一些数据库可能支持带时区的时间戳,这可能是我不熟悉的例外(未经测试,因为我目前没有该环境)。

void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException

将指定参数设置为给定值java.sql.Timestamp价值, 使用给定的 Calendar 对象。驱动程序使用 Calendar 对象 构造一个 SQLTIMESTAMP值,然后驱动程序发送到 数据库。通过 Calendar 对象,驱动程序可以计算 考虑到自定义时区的时间戳。If no Calendar目的 指定时,驱动程序使用默认时区,即 运行应用程序的虚拟机.

这可以通过检查调用来进一步澄清setTimestampInternal()MySQL JDBC 驱动程序实现的方法。

请参阅以下内容two致电setTimestampInternal()两个重载版本中的方法setTimestamp() method.

/**
 * Set a parameter to a java.sql.Timestamp value. The driver converts this
 * to a SQL TIMESTAMP value when it sends it to the database.
 *
 * @param parameterIndex the first parameter is 1...
 * @param x the parameter value
 *
 * @throws SQLException if a database access error occurs
 */
public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
    setTimestampInternal(parameterIndex, x, this.connection.getDefaultTimeZone());
}

/**
 * Set a parameter to a java.sql.Timestamp value. The driver converts this
 * to a SQL TIMESTAMP value when it sends it to the database.
 *
 * @param parameterIndex the first parameter is 1, the second is 2, ...
 * @param x the parameter value
 * @param cal the calendar specifying the timezone to use
 *
 * @throws SQLException if a database-access error occurs.
 */
public void setTimestamp(int parameterIndex, java.sql.Timestamp x,Calendar cal) throws SQLException {
    setTimestampInternal(parameterIndex, x, cal.getTimeZone());
}

When no Calendar实例指定为PreparedStatement#setTimestamp()方法,将使用默认时区(this.connection.getDefaultTimeZone()).


在由连接/JNDI 支持的应用程序服务器/Servlet 容器中使用连接池时,访问或操作数据源,例如,

  • com.mysql.jdbc.jdbc2.optional.MysqlXADataSource (xa)
  • com.mysql.jdbc.jdbc2.optional.MysqlDataSource(非 XA)

需要强制 MySQL JDBC 驱动程序使用我们感兴趣的所需时区 (UTC),需要通过连接 URL 的查询字符串提供以下两个参数。

  • useLegacyDatetimeCode=false
  • serverTimezone=UTC

I am not familiar with the history of MySQL JDBC drivers but in relatively older versions of MySQL drivers, this parameter useLegacyDatetimeCode may not be needed. Thus, one may require to adjust oneself in that case.

对于应用程序服务器(例如 GlassFish),可以在创建 JDBC 领域以及服务器本身内部的 JDBC 连接池以及其他可配置属性时进行设置,可以使用管理 Web GUI 工具或在domain.xml直接地。domain.xml如下所示(使用 XA 数据源)。

<jdbc-connection-pool datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"
                      name="jdbc_pool"
                      res-type="javax.sql.XADataSource">

  <property name="password" value="password"></property>
  <property name="databaseName" value="database_name"></property>
  <property name="serverName" value="localhost"></property>
  <property name="user" value="root"></property>
  <property name="portNumber" value="3306"></property>
  <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
  <property name="characterEncoding" value="UTF-8"></property>
  <property name="useUnicode" value="true"></property>
  <property name="characterSetResults" value="UTF-8"></property>
  <!-- The following two of our interest -->
  <property name="serverTimezone" value="UTC"></property>
  <property name="useLegacyDatetimeCode" value="false"></property>
</jdbc-connection-pool>

<jdbc-resource pool-name="jdbc_pool" 
               description="description"
               jndi-name="jdbc/pool">
</jdbc-resource>

对于 WildFly,它们可以在以下位置进行配置:standalone-xx.yy.xml使用 CLI 命令或使用管理 Web GUI 工具(使用 XA 数据源)。

<xa-datasource jndi-name="java:jboss/datasources/datasource_name"
               pool-name="pool_name"
               enabled="true"
               use-ccm="true">

    <xa-datasource-property name="DatabaseName">database_name</xa-datasource-property>
    <xa-datasource-property name="ServerName">localhost</xa-datasource-property>
    <xa-datasource-property name="PortNumber">3306</xa-datasource-property>
    <xa-datasource-property name="UseUnicode">true</xa-datasource-property>
    <xa-datasource-property name="CharacterEncoding">UTF-8</xa-datasource-property>
    <!-- The following two of our interest -->
    <xa-datasource-property name="UseLegacyDatetimeCode">false</xa-datasource-property>
    <xa-datasource-property name="ServerTimezone">UTC</xa-datasource-property>

    <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
    <driver>mysql</driver>
    <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>

    <xa-pool>
        <min-pool-size>5</min-pool-size>
        <max-pool-size>15</max-pool-size>
    </xa-pool>

    <security>
        <user-name>root</user-name>
        <password>password</password>
    </security>

    <validation>
        <valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
        <background-validation>true</background-validation>
        <exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
    </validation>

    <statement>
        <share-prepared-statements>true</share-prepared-statements>
    </statement>
</xa-datasource>

<drivers>
    <driver name="mysql" module="com.mysql">
        <driver-class>com.mysql.jdbc.Driver</driver-class>
    </driver>
</drivers>

The same thing is applicable to non-XA datasources. They can directly be appended to the connection URL itself in that case.

所有提到的这些属性将被设置为 JDBC 驱动程序中可用的提到的类,即com.mysql.jdbc.jdbc2.optional.MysqlXADataSource在这两种情况下,都在此类中使用各自的 setter 方法。

例如,如果直接使用核心 JDBC API,或者 Tomcat 中的连接池,则可以将它们直接设置为连接 URL(在context.xml)

<Context antiJARLocking="true" path="/path">
    <Resource name="jdbc/pool" 
              auth="Container"
              type="javax.sql.DataSource"
              maxActive="100"
              maxIdle="30"
              maxWait="10000"
              username="root"
              password="password"
              driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/database_name?useEncoding=true&amp;characterEncoding=UTF-8&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTC"/>
</Context>

额外的 :

如果目标数据库服务器运行在 DST 敏感区域并且未关闭夏令时 (DST),则会导致问题。最好将数据库服务器配置为使用不受 DST 影响的标准时区,如 UTC 或 GMT。 UTC 通常优于 GMT,但两者在这方面是相似的。直接引用自这个链接.

如果您确实更喜欢使用当地时区,我建议至少 关闭夏令时,因为日期不明确 您的数据库可能是一场真正的噩梦。

例如,如果您正在构建电话服务并且正在使用 您的数据库服务器上的夏令时,然后您要求 麻烦:无法判断是否有顾客打来电话 从“2008-10-26 02:30:00”到“2008-10-26 02:35:00”实际调用 5 分钟或 1 小时 5 分钟(假设夏令时 发生在 10 月 26 日凌晨 3 点)!

顺便说一句,我放弃了 EclipseLink 的专有转换器,因为 JPA 2.1 提供了自己的标准转换器可以根据需要将其移植到不同的 JPA 提供程序,而无需进行少量修改或根本无需修改。现在看起来如下所示java.util.Date也被替换为java.sql.Timestamp.

import java.sql.Timestamp;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

@Converter(autoApply = true)
public final class JodaDateTimeConverter implements AttributeConverter<DateTime, Timestamp> {

    @Override
    public Timestamp convertToDatabaseColumn(DateTime dateTime) {
        return dateTime == null ? null : new Timestamp(dateTime.withZone(DateTimeZone.UTC).getMillis());
    }

    @Override
    public DateTime convertToEntityAttribute(Timestamp timestamp) {
        return timestamp == null ? null : new DateTime(timestamp, DateTimeZone.UTC);
    }
}

然后,相关应用程序客户端(Servlet / JSP / JSF /远程桌面客户端等)独自负责根据适当的用户时区转换日期/时间,同时向最终用户显示或呈现日期/时间为简洁起见,本答案未涵盖此内容,并且根据当前问题的性质,该内容属于题外话。

Those null checks in the converter are also not needed as it is also solely the responsibility of the associated application client(s) unless some fields are optional.

现在一切都很顺利。欢迎任何其他建议/推荐。对于任何对我无知的人的批评是非常受欢迎的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 EclipseLink 和 Joda-Time 将 UTC 日期时间存储到数据库中? 的相关文章

  • Innodb页面大小设置

    在innodb中 页面大小默认为16kb 如何将页面大小设置为 8kb 是否有在源编译步骤中设置的选项 您不需要在源编译步骤中指定页面大小 MySQL 5 6 及更高版本支持不同的页面大小 无需重新编译 但是 您必须在初始化 InnoDB
  • 根据最大值连接表

    这是我正在谈论的内容的一个简化示例 Table students exam results id name id student id score date 1 Jim 1 1 73 8 1 09 2 Joe 2 1 67 9 2 09 3
  • 如何在 bash 上运行 MySQL 命令?

    以下代码在命令行上运行 mysql user myusername password mypassword database mydatabase execute DROP DATABASE myusername CREATE DATABA
  • MySQL SELECT OpenCarts 数据库中的重复行

    只是玩一下 OpenCart DB 看看我是否能学到一些东西 如果我使用以下SELECT结果返回重复的行 SELECT DISTINCT p product id AS pid p model AS modelo SUBSTRING p m
  • 从 CSV 到 MySQL 的换行问题

    我正在将 csv 文件导入 MySQL 除了文件中的换行符之外 一切正常 我的 csv 行之一如下所示 42 E A R Classic Earplugs ear images ear classic jpg 5 Proven size s
  • 如何使用 SQL 对项目进行排序,然后按另一个条件再次排序

    我正在使用 MySQL 我想对记录进行排序 或者我想对记录进行分组 然后按另一个条件再次对其进行排序 例如我有 6 个项目 Names Group Jack G1 Dian G2 Emily G2 Dean G1 Teddy G2 Gabe
  • 性能:cakephp-mysql 中的 UUID 与自动递增

    我正在搜索 cakePHP 生成的 UUID 32 个字符长 是否比自动增量在性能上更快 插入和选择操作的比较 我应该使用 cakePHP 生成的 UUID 还是使用 MySQL 的简单自动增量生成的 UUID 这是我发现的一个案例研究 但
  • 使用 MySQL 的 CURDATE() 或 PHP 的 date() 更快?

    使用mysql查询是不是更快 SELECT CURDATE as today 或 PHP 语句 curdate date Y m d 同样的答案是否适用于使用date VS MySQL 的NOW and CURTIME 如果您只是执行查询以
  • JP QL - 一对多关系中的过滤结果

    我在尝试构建 JPQL 查询时陷入困境 并希望比我拥有更多 JPA 经验的人能够提供帮助 考虑以下两个实体 class Author String name OneToMany mappedBy author Set
  • MySQL,连接两列

    MySQL 表中有两列 SUBJECT and YEAR 我想生成一个字母数字唯一编号 其中包含主题和年份的串联数据 我怎样才能做到这一点 是否可以使用像这样的简单运算符 您可以使用CONCAT http dev mysql com doc
  • 如何在php/mysql中使用事务

    我正在使用 php mysql 我知道 mysql 中的事务 但不能在我的脚本中使用 下面是我的脚本 如何在我的代码中使用 php 事务 即 BEGIN ROLLBACK COMMIT foreach json a shop as json
  • 如何在没有 DROP 数据库权限的情况下从命令行删除所有 MySQL 表? [复制]

    这个问题在这里已经有答案了 如何使用命令提示符删除 Windows MySQL 中的所有表 我想这样做的原因是我们的用户有权访问数据库删除 但无权重新创建数据库本身 因此我们必须手动删除表 有没有办法一次删除所有表 请记住 大多数表都与外键
  • 内连接 3 个表

    我正在使用 PHP 和 PDO 我需要重新收集连接 3 个表的信息 photos albums 相册照片 该表具有以下结构 photos photo id int path varchar nick varchar date timesta
  • 使用表白名单选项更新 Debezium MySQL 连接器

    我正在使用 Debezium 0 7 5 MySQL 连接器 并且我试图了解如果我想使用以下选项更新此配置 最好的方法是什么table whitelist 假设我创建了一个连接器 如下所示 curl i X POST H Accept ap
  • 解析时间字符串,如“1h 30min”

    任何人都知道 Java 库可以将 30min 或 2h 15min 或 2d 15h 30min 等时间字符串解析为毫秒 或某种 Duration 对象 Joda Time 可以做这样的事情吗 我有一个丑陋的长方法来维护它进行此类解析 并且
  • 如何删除MySQL中的所有事件

    如果我想删除某个事件 我需要查询类似的内容 DROP EVENT IF EXISTS eventname 但我找不到一次性删除所有事件的命令 必须一项一项地删除 有没有一次性删除所有事件的SQL DROP EVENT IF EXISTS S
  • 将非常大的Python列表输出保存到mysql表中

    我想将 python 生成的列表的输出保存在 mysql 数据库的表中 该表如下所示 mysql 中的 myapc8 表 https i stack imgur com 4B4Hz png这是Python代码 在此输入图像描述 https
  • Apache 子进程已退出,状态为 255

    经过大量的搜索 尝试 修复 等待和哭泣 在我放弃之前 我想为这个错误抓住最后的机会 我们正在奔跑Microsoft Windows Server 2012 Apache 2 4 6 Win64 OpenSSL 1 0 1e PHP 5 5
  • 批处理文件并与数据库比较

    目前我正在开发一个 Spring Boot 应用程序 该应用程序定期尝试处理包含用户数据的文件 其中每行都包含userId and departamentId隔开 例如123534 13 该文件将包含数百万条记录 我的要求是以这样的方式将此
  • mysql 查询选择当月的所有行?

    我有一个名为 startdate 的日期时间类型的列 我必须获取当前月份的开始日期和结束日期之间的所有行 即从 1 11 2014 到 30 11 2014 select from your table where year curdate

随机推荐