有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我有一个非常旧的带有hibernate3的传统Java 6应用程序,它连接到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模型,我有:

<?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日更新

通过将表hibernate配置文件上的date属性类型从java.util.Date更改为java.sql.Date,我可以使它部分工作,但这只保存了日期,在我还保存了时间之前:

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

2021年3月25日更新

版本:

  • MySQL:5.6.49
  • MySQL连接器:5.1.49
  • 休眠:3.1.3

2021年3月26日更新

如何填充日期:

在表示对象的类上,我有如下内容:

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

共 (3) 个答案

  1. # 1 楼答案

    Stackoverflow中出现了与您指出的问题类似的问题

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

    为了解决这个问题,首先,如注释中所建议的,请考虑使用一个最近的mysql connector/j 5.1 version,它看起来是supported in Java 1.6 and is suitable for mysql 5.6

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

    正如您所指出的,使用java.sql.Date而不是java.util.Date似乎可以解决数据截断问题,但结果是丢失了时间信息

    正如在对你的问题的不同评论中所指出的,possible duplicate的链接,以及@RickJames的后续答案,当你使用java.sql.Date时,你失去了时间信息。为了保存它,试着使用java.sql.Timestamp

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

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

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

    java.util.Date已经包含了必要的时间信息,在使用MySQL时,Hibernate默认将timestamp类型映射到DATETIME

    更新

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

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

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

    我的代码如下

    冬眠。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>
    

    简化类

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

    历史。哈佛商学院。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
    

    通过这种设置,使用JDK1.8,测试总是正常运行

  2. # 2 楼答案

    你可能需要java.sql.Timestamp,它会给你日期时间。请注意,时区可能存在问题

    同时,请从MyISAM搬到InnoDB。(原因有很多;在过去十年中,人们反复讨论过这一点。)

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

  3. # 3 楼答案

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

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

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