JDBCでDATETIME値0000-00-0000:00:00を処理する


85

やろうとすると例外が発生します(以下を参照)

resultset.getString("add_date");

DATETIME値0000-00-0000:00:00(DATETIMEの準NULL値)を含むMySQLデータベースへのJDBC接続の場合、値を文字列としてではなく文字列として取得しようとしていますが、オブジェクト。

私はこれを回避しました

SELECT CAST(add_date AS CHAR) as add_date

これは機能しますが、ばかげているようです...これを行うためのより良い方法はありますか?

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

注:ここで0000が登場します:( 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)

回答:


85

私は同じ問題を解決しようとしてこれに出くわしました。私が使用しているインストールでは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>

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


hibernate.cfg.xmlファイルに変更を加えると、私にとっては
うまくいき

124

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

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

編集:

出典:MySQLマニュアル

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

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

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

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


面白い。この情報はどこにありますか?
ジェイソンS

私の答えを更新しました!ただし、この場合、@ sarumontのURLの方が適している可能性があります(すべてのコネクタプロパティが一覧表示されます)。
Brian Clozel 2010

1
jdbcソフトウェアのバージョン5.1.16には、次のバグ修正が含まれています。-BUG#57808の修正-zeroDateTimeBehaviorはconvertToNullですが、getDate()の値が0000-00-00のDATEフィールドにwasNullが設定されていません。
アレクサンダーKjäll

うわー!情報をありがとう。あなたはそれを理解するのに苦労したと思います:
Brian Clozel 2011年

11

私のポイントは、生の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ステートメントまたはINSERTINTO ... SELECTステートメントの場合、MySQLServerは列データ型の暗黙的なデフォルト値を格納します。

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

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

[..]

デフォルトでは、MySQLは、範囲外の日付または時刻タイプの値を検出すると(このセクションの冒頭で説明したように)、そのタイプの値を「ゼロ」値に変換します。


6
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]


1
これは私のCAST(add_date AS CHAR)ソリューションと少し同じですが、これにより+1で、希望どおりに明示的にフォーマットできます。
ジェイソンS

5

行を追加した後の場合:

<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

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

SET sql_mode = 'NO_ZERO_DATE';

これをテーブルの説明に入れて、「0000-00-0000:00:00」の問題を解決しました。java.sql.Timestampとして表すことができません。


3

nullを使用してnull値を表すことをお勧めします。

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

ところで:

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

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


はい、しかし、遡及的にさえ、0と呼ばれる年はまだありません。1 AD / CEの前の年は
1BC

2
これが、MySQLが0000を無効な日付として使用する理由です。これは、既存の日付と競合しないためです。また、1999-00-00のような日付は、特定の日ではなく1999年を表すために使用されることがあります(私ではありません!)。
ジェイソンS

3

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

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


これは、すべてゼロのMySQLDATETIMEを有効な日付に変換する問題に対処するとは思いません。DATETIMEフィールドにnullを入力する方法を示しているだけですが、実際にはあまり役に立ちません。
Mark Chorley

2

他の回答に追加するには:0000-00-00文字列が必要な場合は、次を使用できますnoDatetimeStringSync=true(タイムゾーン変換を犠牲にすることに注意して)。

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

また、履歴の場合、JDBCNULL0000-00-00以前は日付を返していましたが、デフォルトで例外を返すようになりました。 ソース


2

jdbcのURLを次のように追加できます

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

これを利用して、SQLは「0000-00-0000:00:00」をnull値として変換します。

例えば:

jdbc:mysql:<host-name>/<db-name>?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.