web-dev-qa-db-ja.com

JDBCのDATETIME値0000-00-00 00:00:00の処理

実行しようとすると例外が発生します(以下を参照)

resultset.getString("add_date");

としてではなく、文字列として値を取得しようとしているにもかかわらず、0000-00-00 00:00:00(DATETIMEの準ヌル値)のDATETIME値を含むMySQLデータベースへのJDBC接続の場合オブジェクト。

私はこれを回避するために

SELECT CAST(add_date AS CHAR) as add_date

動作しますが、ばかげているようです...これを行うより良い方法はありますか?

私のポイントは、生のDATETIME文字列だけが必要なので、自分で解析できることですas

note:ここに0000が入ります:(from http://dev.mysql.com/doc/refman/5.0/en /datetime.html

無効なDATETIME、DATE、またはTIMESTAMP値は、適切なタイプの「ゼロ」値(「0000-00-00 00:00:00」または「0000-00-00」)に変換されます。

特定の例外はこれです:

SQLException: Cannot convert value '0000-00-00 00:00:00' from column 5 to TIMESTAMP.
SQLState: S1009
VendorError: 0
Java.sql.SQLException: Cannot convert value '0000-00-00 00:00:00' from column 5 to TIMESTAMP.
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.Java:1055)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.Java:956)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.Java:926)
    at com.mysql.jdbc.ResultSetImpl.getTimestampFromString(ResultSetImpl.Java:6343)
    at com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.Java:5670)
    at com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.Java:5491)
    at com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.Java:5531)
83
Jason S

同じ問題を解決しようとして、私はこれに出くわしました。私が作業しているインストールではJBOSSとHibernateを使用しているため、これを別の方法で行う必要がありました。基本的なケースでは、この 構成プロパティページ に従ってzeroDateTimeBehavior=convertToNullを接続URIに追加できるはずです。

休止状態の設定にそのパラメーターを配置することについて言及している他の提案を土地全体で見つけました:

hibernate.cfg.xml

<property name="hibernate.connection.zeroDateTimeBehavior">convertToNull</property>

hibernate.properties

hibernate.connection.zeroDateTimeBehavior=convertToNull

しかし、JBOSSのmysql-ds.xmlファイルに次のように配置する必要がありました。

<connection-property name="zeroDateTimeBehavior">convertToNull</connection-property>

これが誰かを助けることを願っています。 :)

81
sarumont

別の答えとして、このJDBC URLをデータソース構成で直接使用できます。

jdbc:mysql://yourserver:3306/yourdatabase?zeroDateTimeBehavior=convertToNull

編集:

ソース: MySQL Manual

すべてゼロのコンポーネントを持つ日付時刻(0000-00-00 ...)—これらの値は、Javaで確実に表すことができません。 Connector/J 3.0.xは、ResultSetから読み取られるときに、常にNULLに変換しました。

これらの値に遭遇すると、Connector/J 3.1はデフォルトで例外をスローします。これは、JDBCおよびSQL標準に従って最も正しい動作であるためです。この動作は、zeroDateTimeBehavior構成プロパティを使用して変更できます。許容値は次のとおりです。

  • exception(デフォルト)、S1009のSQLStateでSQLExceptionをスローします。
  • convertToNull。日付の代わりにNULLを返します。
  • round。日付を最も近い0001-01-01に丸めます。

更新:Alexanderは、その機能のmysql-connector-5.1.15に影響するバグを報告しました。 公式Webサイトの変更ログ を参照してください。

122
Brian Clozel

私のポイントは、生のDATETIME文字列が欲しいだけなので、そのまま自分で解析できるということです。

そのため、「回避策」は回避策ではなく、実際にデータベースからコードに値を取得する唯一の方法だと思います。

SELECT CAST(add_date AS CHAR) as add_date

ちなみに、MySQLのドキュメントからのいくつかのメモ:

MySQL 無効なデータの制約

MySQL 5.0.2より前では、MySQLは違法または不適切なデータ値を許容しており、データ入力の正当な値に強制します。 MySQL 5.0.2以降では、これがデフォルトの動作のままですが、サーバーのSQLモードを変更して、サーバーがそれらを拒否し、発生したステートメントを中止するように、より伝統的な不良値の処理を選択できます。

[..]

NULL値を受け取らない列にNULLを保存しようとすると、単一行のINSERTステートメントでエラーが発生します。複数行のINSERTステートメントまたはINSERT INTO ... SELECTステートメントの場合、MySQLサーバーは列データ型の暗黙のデフォルト値を保存します。

MySQL 5.x 日付と時刻のタイプ

MySQLでは、「ダミー日付」として「0000-00-00」を保存することもできます(NO_ZERO_DATE SQLモードを使用していない場合)。これは、場合によっては、NULL値を使用するよりも便利です(使用するデータとインデックススペースが少なくなります)。

[..]

デフォルトでは、MySQLは、範囲外のデータ型または時刻型の値を検出した場合(このセクションの冒頭で説明したように)、その型に対して無効です。値をその型の「ゼロ」値に変換します。

11
Arjan
DATE_FORMAT(column name, '%Y-%m-%d %T') as dtime

これを使用してエラーを回避してください。文字列形式で日付を返し、文字列として取得できます。

resultset.getString("dtime");

これは実際には機能しません。 getStringを呼び出しても。内部的には、mysqlはまだ最初にそれを日付に変換しようとします。

com.mysql.jdbc.ResultSetImpl.getDateFromString(ResultSetImpl.Java:2270)で

〜[mysql-connector-Java-5.1.15.jar:na] at com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.Java:5743)

〜[mysql-connector-Java-5.1.15.jar:na] at com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.Java:5576)

〜[mysql-connector-Java-5.1.15.jar:na]

6
Atul

行を追加した後:

<property
name="hibernate.connection.zeroDateTimeBehavior">convertToNull</property>

hibernate.connection.zeroDateTimeBehavior=convertToNull

<connection-property
name="zeroDateTimeBehavior">convertToNull</connection-property>

エラーのままです:

Illegal DATETIME, DATE, or TIMESTAMP values are converted to the “zero” value of the appropriate type ('0000-00-00 00:00:00' or '0000-00-00').

行を見つける:

1) resultSet.getTime("time"); // time = 00:00:00
2) resultSet.getTimestamp("timestamp"); // timestamp = 00000000000000
3) resultSet.getDate("date"); // date = 0000-00-00 00:00:00

それぞれ次の行に置き換えます。

1) Time.valueOf(resultSet.getString("time"));
2) Timestamp.valueOf(resultSet.getString("timestamp"));
3) Date.valueOf(resultSet.getString("date"));
5
Vadim

この問題に取り組み、上記の「convertToNull」ソリューションを実装しました。ローカルのMySqlインスタンスで動作しました。しかし、Play/ScalaアプリをHerokuにデプロイすると、機能しなくなりました。 Herokuは、ユーザーに提供するDB URLにいくつかの引数を連結します。また、Herokuは「?」の連結を使用するため、このソリューションも連結します。独自の引数セットの前では、機能しません。しかし、同じように機能する別のソリューションを見つけました。

SET sql_mode = 'NO_ZERO_DATE';

これをテーブルの説明に入れて、「0000-00-00 00:00:00」という問題がJava.sql.Timestampとして表せないという問題を解決しました

5
Aqume

Null値を表すためにnullを使用することをお勧めします。

あなたが得る例外は何ですか?

ところで:

0または0000と呼ばれる年はありません(一部の日付では今年が許可されます)

また、年の0月または月の0日はありません。 (問題の原因である可能性があります)

3
Peter Lawrey

「00-00 -....」が有効な日付ではないという問題を解決した後、SQL値の定義を変更して、NULL値を許可するように「NULL」式を追加しました。

SELECT "-- Tabla item_pedido";
CREATE TABLE item_pedido (
    id INTEGER AUTO_INCREMENT PRIMARY KEY,
    id_pedido INTEGER,
    id_item_carta INTEGER,
    observacion VARCHAR(64),
    fecha_estimada TIMESTAMP,
    fecha_entrega TIMESTAMP NULL, // HERE IS!!.. NULL = DELIVERY DATE NOT SET YET
    CONSTRAINT fk_item_pedido_id_pedido FOREIGN KEY (id_pedido)
        REFERENCES pedido(id),...

次に、NULL値を挿入できるようにする必要があります。つまり、「そのタイムスタンプをまだ登録していません」ということです。

SELECT "++ INSERT item_pedido";
INSERT INTO item_pedido VALUES
(01, 01, 01, 'Ninguna', ADDDATE(@HOY, INTERVAL 5 MINUTE), NULL),
(02, 01, 02, 'Ninguna', ADDDATE(@HOY, INTERVAL 3 MINUTE), NULL),...

テーブルは次のように見えます。

mysql> select * from item_pedido;
+----+-----------+---------------+-------------+---------------------+---------------------+
| id | id_pedido | id_item_carta | observacion | fecha_estimada      | fecha_entrega       |
+----+-----------+---------------+-------------+---------------------+---------------------+
|  1 |         1 |             1 | Ninguna     | 2013-05-19 15:09:48 | NULL                |
|  2 |         1 |             2 | Ninguna     | 2013-05-19 15:07:48 | NULL                |
|  3 |         1 |             3 | Ninguna     | 2013-05-19 15:24:48 | NULL                |
|  4 |         1 |             6 | Ninguna     | 2013-05-19 15:06:48 | NULL                |
|  5 |         2 |             4 | Suave       | 2013-05-19 15:07:48 | 2013-05-19 15:09:48 |
|  6 |         2 |             5 | Seco        | 2013-05-19 15:07:48 | 2013-05-19 15:12:48 |
|  7 |         3 |             5 | Con Mayo    | 2013-05-19 14:54:48 | NULL                |
|  8 |         3 |             6 | Bilz        | 2013-05-19 14:57:48 | NULL                |
+----+-----------+---------------+-------------+---------------------+---------------------+
8 rows in set (0.00 sec)

最後に:JPAの動作:

@Stateless
@LocalBean
public class PedidosServices {
    @PersistenceContext(unitName="vagonpubPU")
    private EntityManager em;

    private Logger log = Logger.getLogger(PedidosServices.class.getName());

    @SuppressWarnings("unchecked")
    public List<ItemPedido> obtenerPedidosRetrasados() {
        log.info("Obteniendo listado de pedidos retrasados");
        Query qry = em.createQuery("SELECT ip FROM ItemPedido ip, Pedido p WHERE" +
                " ip.fechaEntrega=NULL" +
                " AND ip.idPedido=p.id" +
                " AND ip.fechaEstimada < :arg3" +
                " AND (p.idTipoEstado=:arg0 OR p.idTipoEstado=:arg1 OR p.idTipoEstado=:arg2)");
        qry.setParameter("arg0", Tipo.ESTADO_BOUCHER_ESPERA_PAGO);
        qry.setParameter("arg1", Tipo.ESTADO_BOUCHER_EN_SERVICIO);
        qry.setParameter("arg2", Tipo.ESTADO_BOUCHER_RECIBIDO);
        qry.setParameter("arg3", new Date());

        return qry.getResultList();
    }

ついにそのすべての仕事。それがお役に立てば幸いです。

3
EdU

他の回答に追加するには:0000-00-00文字列が必要な場合は、noDatetimeStringSync=trueを使用できます(タイムゾーン変換を犠牲にするという警告があります)。

MySQLの公式バグ: https://bugs.mysql.com/bug.php?id=47108

また、履歴については、JDBCは0000-00-00日付に対してNULLを返していましたが、デフォルトでは例外を返します。 ソース

2
damio

jdbc urlを追加できます

?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8

これを使用して、sqlが '0000-00-00 00:00:00'をヌル値として変換します。

例えば:

jdbc:mysql:<Host-name>/<db-name>?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8
2
Mukul Aggarwal