繁体   English   中英

在 AWS 上从 MySQL 5.5 升级到 5.6 时 Hibernate 和日期的问题

[英]Problems with Hibernate and dates when upgrading from MySQL 5.5 to 5.6 on AWS

我有一个非常古老的 Java 6 应用程序,带有 hibernate3,它连接到 AWS RDS MySQL 5.5 数据库。

一切正常,但 AWS 现在 MySQL 5.5 的生命周期结束,我正在升级到 5.6。 升级到 5.6 后,我开始出现此错误:

ERROR org.hibernate.util.JDBCExceptionReporter - Data truncation: Incorrect datetime value: '' for column 'date' at row 1
org.hibernate.exception.DataException: could not insert: [thebusiness.core.history.History]

对于 hibernate model 我有:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
    
<hibernate-mapping>
<class name="thebusiness.core.history.History" table="history">
    <id name="id" type="int" column="id">
        <generator class="native" />
    </id>
    <property name="objectType" type="int" column="object_type" not-null="true" length="3"/>
    <property name="recordId" type="int" column="record_id" not-null="true" length="7"/>
    <property name="initialValue" type="java.lang.String" column="initial_value" not-null="true" />
    <property name="setValue" type="java.lang.String" column="set_value" not-null="true" />
    <property name="initialValueId" type="int" column="initial_value_id" not-null="true" />
    <property name="setValueId" type="int" column="set_value_id" not-null="true" />
    <property name="date" type="java.util.Date" column="date" not-null="true"/>
    <property name="fieldName" type="java.lang.String" column="field_name" length="255"/>
    <property name="fieldNameArg0" type="java.lang.String" column="field_name_arg0" length="255"/>
    <property name="comment" type="java.lang.String" column="comment" />    
    <property name="finalComment" type="java.lang.String" column="final_comment" />    
    <many-to-one name="contact" class="thebusiness.core.contact.Contact" not-null="false" lazy="false">
        <column name="contact_id"/>
    </many-to-one>
</class>
</hibernate-mapping>

我调试发送的日期值,但我有一个 java 日期值而不是空值。

从 5.5 到 5.6 有什么东西可以让应用程序在这里崩溃吗?

为历史表显示创建表

CREATE TABLE `history` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `object_type` tinyint(3) NOT NULL DEFAULT '0',
  `record_id` int(11) NOT NULL DEFAULT '0',
  `initial_value` varchar(255) NOT NULL,
  `initial_value_id` int(11) NOT NULL DEFAULT '0',
  `set_value` varchar(255) NOT NULL,
  `set_value_id` int(11) NOT NULL DEFAULT '0',
  `field_name` char(255) DEFAULT NULL,
  `date` datetime DEFAULT NULL,
  `contact_id` int(11) NOT NULL DEFAULT '0',
  `comment` text,
  `field_name_arg0` char(255) DEFAULT NULL,
  `final_comment` text,
  PRIMARY KEY (`id`),
  KEY `IX_history_record_id` (`record_id`,`field_name`,`object_type`),
  KEY `IX_date` (`date`),
  KEY `IX_object_type` (`object_type`)
) ENGINE=MyISAM AUTO_INCREMENT=82991415 DEFAULT CHARSET=utf8

2021 年 3 月 23 日更新

I was able to make it work partially by changing the date property type on the table hibernate configuration file from java.util.Date to java.sql.Date but this only saves the Date and before I was also saving the time:

<property name="date" type="java.sql.Date" column="date" not-null="true"/>

2021 年 3 月 25 日更新

版本:

  • MySQL:5.6.49
  • MySQL 连接器:5.1.49
  • Hibernate:3.1.3

2021 年 3 月 26 日更新

如何填充日期:

在代表 object 的 class 上,我有类似的东西:

public class History extends ModelBase implements java.io.Serializable {
  // ...
  private Date date;
  // ...

  public Date getDate() {
    return this.date;
  }

  public void setDate(Date date) {
    this.date = date;
  }

  // ...
}

并设置日期,我只是使用:

history.setDate(new Date());

较新版本的 mysql 启用 STRICT_TRANS_TABLES 模式,当您尝试插入的内容不是最终存储在表中的内容时,该模式本质上会出错。 在这里,您有一个未设置的日期字段,因此它尝试插入“”,当转换为日期时变为 0000-00-00。

您应该适当地设置 sql_mode; 执行select @@sql_mode并查看您可能想要保留哪些新值,然后在您的 aws 参数组中设置它。

如果此日期字段确实是可选的,我鼓励您允许它为 NULL 并使用 NULL 作为不存在的值,而不是 0000-00-00。 我还鼓励您升级到当前版本(mysql 8 或 mariadb 10.5); 虽然现在这可能会导致更多升级问题,但它将使您不必很快再次升级,并为您提供大量新功能。

您可能想要java.sql.Timestamp ,它为您提供日期时间。 请注意,时区可能存在问题。

同时,请远离 MyISAM 到 InnoDB。 (造成这种情况的原因有很多;在过去的十年中已经反复讨论过。)

在同一个表中设置set_valueset_value_id而不是只有这两列的表的逻辑是什么?

Stackoverflow 中已经报告了与您指出的问题类似的问题。

这些问题似乎与通过 Hibernate 到新版本的 MySQL 执行 DML 语句时提供的不同处理有关。

为了解决这个问题,首先,按照评论中的建议,请考虑使用最近的mysql 连接器/j 5.1 版本之一,它似乎在 Java 1.6 中受支持,适用于 Z81C3B050DAD78.5372FE .

您使用的是相当过时的 Hibernate 版本:请考虑将您的 Hibernate 依赖项也升级到更新的版本。 我认为3.x4.x分支都适合您的问题。

正如您所指出的,使用java.sql.Date而不是java.util.Date似乎可以解决数据截断问题,但结果是您正在浪费时间。

正如在对您的问题的不同评论中指出的那样,当您使用java.sql.Date时,可能会出现重复的链接以及@RickJames 的答案,您正在失去时间信息。 为了保存它,请尝试使用java.sql.Timestamp代替。

对于您的评论,您有很多表格,更改将涉及在保存之前将所有日期转换为时间戳

为了避免这种级别的转换,您可以尝试的一件事是在基础列的类型为DATETIME时更改属性定义以使用timestamp而不是date ,就像您的示例中一样。 例如:

<property name="date" type="timestamp" column="date" not-null="true"/>

java.util.Date已经包含了必要的时间信息,而 Hibernate 将默认 map timestamp类型为DATETIME时。

更新

对于您的评论,实际问题似乎是,如果您以任何方式使用Timestamp ,无论如何,您都会面临截断错误。

您可以尝试的一件事是执行快速测试:请从头开始使用DATETIME字段创建一个新表,并查看在新表中插入数据时问题是否仍然存在,也许问题可能与迁移本身有关。 我不知道您实际上是如何迁移数据库的,但正如您在升级指南中看到的那样,时间相关类型的实际信息在 mysql 5.6 中的存储方式之间存在重要变化。

要验证最后一点,您可以尝试将应用程序配置为针对本地数据库运行并执行一些测试,看看问题是否仍然存在。 正如我在评论中告诉你的那样,我尝试了你的设置,使用相同的库版本和相同的 MySQL 数据库版本,我创建了一个与你提供的表非常相似的表,然后执行快速测试,无论我是否使用java.util.Datetype="timestamp" ,它总是工作正常。 与您的设置的唯一区别是 JDK 版本,我无法使用 JDK 1.6,我用 1.8 版测试了代码。

我的代码如下。

hibernate.cfg.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.username">test</property>
        <property name="hibernate.connection.password">test</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test?nullNamePatternMatchesAll=true</property>
        <property name="show_sql">true</property>
        <property name="format_sql">false</property>
        <mapping resource="history.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

简化History class

package thebusiness.core.history;

import java.util.Date;

public class History {

  private Integer id;
  private String fieldName;
  private Date date;

  public History() {
  }

  public History(Integer id, String fieldName, Date date) {
    this.id = id;
    this.fieldName = fieldName;
    this.date = date;
  }

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getFieldName() {
    return fieldName;
  }

  public void setFieldName(String fieldName) {
    this.fieldName = fieldName;
  }

  public Date getDate() {
    return date;
  }

  public void setDate(Date date) {
    this.date = date;
  }
}

历史.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping>
    <class name="thebusiness.core.history.History" table="history2">
        <id name="id" type="int" column="id">
            <generator class="native" />
        </id>
        <property name="date" type="java.util.Date" column="date" not-null="true"/>
        <property name="fieldName" type="java.lang.String" column="field_name" length="255"/>
    </class>
</hibernate-mapping>

测试

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class Main {

  public static void main(String[] args) throws ParseException {

    SessionFactory factory;

    try {
      factory = new Configuration().configure().buildSessionFactory();
    } catch (Throwable ex) {
      System.err.println("Failed to create sessionFactory object." + ex);
      throw new ExceptionInInitializerError(ex);
    }

    Session session = factory.openSession();
    Transaction tx = null;
    Integer id = null;

    //SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //Date date = format.parse("2021-03-26 18:34:23");
    // Following your example
    Date date = new Date();

    try {
      tx = session.beginTransaction();
      History history = new History(1, "name", date);
      id = (Integer) session.save(history);
      tx.commit();
    } catch (HibernateException e) {
      if (tx!=null) tx.rollback();
      e.printStackTrace();
    } finally {
      session.close();
    }
  }
}

我使用 docker 和以下命令运行数据库:

docker run --name local-mysql -e MYSQL_ROOT_PASSWORD='root' -e MYSQL_USER='test' -e MYSQL_PASSWORD='test' -e MYSQL_DATABASE='test' -d -p 3306:3306 mysql/mysql-server:5.6.49

使用此设置和 JDK 1.8,测试始终可以正常运行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM