简体   繁体   中英

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

I have a very old legacy Java 6 app with hibernate3 and it connects to an AWS RDS MySQL 5.5 database.

It was working everything ok, but AWS now has an end of life for MySQL 5.5 and I'm upgrading to 5.6. As soon as I upgraded to 5.6 I started having this error:

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]

For the hibernate model I have:

<?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>

I debug what date value was being sent but I have a java date value and not an empty value.

Is there anything from 5.5 to 5.6 that could make the app crash here?

SHOW CREATE TABLE for history table

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

Update 23th March 2021

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"/>

Update 25th March 2021

Versions:

  • MySQL: 5.6.49
  • MySQL Connector: 5.1.49
  • Hibernate: 3.1.3

Update 26th March 2021

How Date is populated:

On the class that represents the object I have something like:

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

  // ...
}

And to set the date, I'm just using:

history.setDate(new Date());

Newer versions of mysql enable the STRICT_TRANS_TABLES mode, which essentially gives errors whenever what you try to insert is not what will end up stored in the table. Here, you have a date field that you are not setting, so it tries to insert '', which when cast to a date becomes 0000-00-00.

You should set sql_mode appropriately; do select @@sql_mode and see which of the new values you might want to keep, then set it in your aws parameter group.

If this date field is truly optional, I would encourage you to allow it to be NULL and use NULL as the not-present value, not 0000-00-00. I would also encourage you to upgrade to a current version (mysql 8 or mariadb 10.5); while this is likely to cause more upgrade issues now, it will save you from having to upgrade again soon, as well as providing you with a great number of new features.

You probably want java.sql.Timestamp , which gives you date and time. Be aware that there may be issues with timezones.

Meanwhile, please move away from MyISAM to InnoDB. (There are many reasons for this; it has been discussed repeatedly over the past decade.)

What is the logic in having set_value and set_value_id in the same table that is not a table with just those two columns ?

Similar problems to the one you indicated has been already reported in Stackoverflow.

The problems seems related with the different treatment provided when executing your DML sentences through Hibernate to the new version of MySQL.

In order to solve the problem, first, as suggested in the comments, please, consider use one of a recent mysql connector/j 5.1 version , it seems to be supported in Java 1.6 and is suitable for mysql 5.6 .

You are using a quite dated Hibernate version: please, consider to upgrade your Hibernate dependencies to a more recent version as well. I think that both the 3.x and 4.x branches can be appropriate for your problem.

As you indicated, using java.sql.Date instead of java.util.Date seems to solve the data truncation problem, but as a result you are loosing the time information.

As pointed out in the different comments to your question, link for possible duplicate , and later answer of @RickJames, when you use java.sql.Date you are loosing the time information. In order to preserve it, try to use java.sql.Timestamp instead.

For your comments, you have a lot of tables and the change will involve to convert all dates to timestamp before saving .

In order to avoid such a level of conversions, one thing you can try is to change your properties definition to use timestamp instead of date when the underlying column is of type DATETIME like in your example. For instance:

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

java.util.Date already contains the necessary time information, and Hibernate will by default map timestamp types to DATETIME when using MySQL.

Update

For your comments, it seems the actual problem is that if you use Timestamp in any way, no matter how, you face the truncation error.

One thing you can try is to perform a quick test: please, create a new table from scratch with DATETIME fields, and see if the problem continues when you insert data in the new table, maybe the problem could be related with the migration itself. I do not know how you actually migrated the database but as you can see in the upgrade guide there are important changes between how the actual information of time related types is stored in mysql 5.6.

To verify this last point, you can try to configure your application to run against a local database and perform some test, and see if the problem is still there. As I told you in my comments, I tried your setup, with the same library versions and the same MySQL database version, I created a table very similar to the one you provided, and just perform a quick test, and no matter if I use java.util.Date or type="timestamp" , it is always working properly. The only difference with your setup is the JDK version, I am unable to use JDK 1.6 and I tested the code with version 1.8.

My code is the following.

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>

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

history.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>

Test

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

I ran the database with docker, and the following command:

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

With this setup, with JDK 1.8, the test always run properly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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