xử lý các giá trị DATETIME 0000-00-00 00:00:00 trong JDBC


85

Tôi nhận được một ngoại lệ (xem bên dưới) nếu tôi cố gắng làm

resultset.getString("add_date");

cho kết nối JDBC với cơ sở dữ liệu MySQL chứa giá trị DATETIME là 0000-00-00 00:00:00 (giá trị gần như null cho DATETIME), mặc dù tôi chỉ đang cố gắng lấy giá trị dưới dạng chuỗi, không phải dưới dạng vật.

Tôi đã giải quyết vấn đề này bằng cách làm

SELECT CAST(add_date AS CHAR) as add_date

cách nào hoạt động, nhưng có vẻ ngớ ngẩn ... có cách nào tốt hơn để làm điều này không?

Quan điểm của tôi là tôi chỉ muốn chuỗi DATETIME thô, vì vậy tôi có thể tự phân tích cú pháp như vậy .

lưu ý: đây là nơi xuất hiện 0000: (từ http://dev.mysql.com/doc/refman/5.0/en/datetime.html )

Các giá trị DATETIME, DATE hoặc TIMESTAMP không hợp lệ được chuyển đổi thành giá trị “không” của loại thích hợp ('0000-00-00 00:00:00' hoặc '0000-00-00').

Ngoại lệ cụ thể là cái này:

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)

Câu trả lời:


85

Tôi tình cờ thấy điều này đang cố gắng giải quyết vấn đề tương tự. Bản cài đặt tôi đang làm việc sử dụng JBOSS và Hibernate, vì vậy tôi phải thực hiện việc này theo cách khác. Đối với trường hợp cơ bản, bạn có thể thêm zeroDateTimeBehavior=convertToNullvào URI kết nối của mình theo trang thuộc tính cấu hình này .

Tôi đã tìm thấy các đề xuất khác trên khắp vùng đất đề cập đến việc đặt thông số đó trong cấu hình ngủ đông của bạn:

Trong hibernate.cfg.xml :

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

Trong hibernate.properties :

hibernate.connection.zeroDateTimeBehavior=convertToNull

Nhưng tôi phải đặt nó vào tệp mysql-ds.xml của mình cho JBOSS như:

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

Hy vọng điều này sẽ giúp ai đó. :)


Thực hiện thay đổi trên tệp hibernate.cfg.xml giúp tôi giải quyết vấn đề này, cảm ơn bạn, vì tôi hầu như không có kiến ​​thức về Java
Gendrith

124

Câu trả lời khác, bạn có thể sử dụng URL JDBC này trực tiếp trong cấu hình nguồn dữ liệu của mình:

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

Biên tập:

Nguồn: MySQL Manual

Datetimes với các thành phần hoàn toàn không (0000-00-00 ...) - Những giá trị này không thể được biểu diễn một cách đáng tin cậy trong Java. Connector / J 3.0.x luôn chuyển đổi chúng thành NULL khi được đọc từ Tập kết quả.

Connector / J 3.1 ném một ngoại lệ theo mặc định khi các giá trị này gặp phải vì đây là hành vi đúng nhất theo các tiêu chuẩn JDBC và SQL. Hành vi này có thể được sửa đổi bằng cách sử dụng thuộc tính cấu hình zeroDateTimeBehavior. Các giá trị cho phép là:

  • ngoại lệ (mặc định), ném một SQLException với SQLState của S1009.
  • convertToNull , trả về NULL thay vì ngày tháng.
  • vòng , làm tròn ngày đến giá trị gần nhất là 0001-01-01.

Cập nhật: Alexander đã báo cáo một lỗi ảnh hưởng đến mysql-connector-5.1.15 trên tính năng đó. Xem CHANGELOGS trên trang web chính thức .


hấp dẫn. bạn tìm thấy thông tin này ở đâu?
Jason S

vừa cập nhật câu trả lời của tôi! Nhưng URL của @sarumont có thể phù hợp hơn trong trường hợp này (nó liệt kê tất cả các thuộc tính trình kết nối).
Brian Clozel

1
phiên bản 5.1.16 của phần mềm jdbc có chứa bản sửa lỗi này: - Sửa lỗi cho BUG # 57808 - wasNull không được đặt cho trường DATE có giá trị 0000-00-00 trong getDate () mặc dù zeroDateTimeBehavior là convertToNull.
Alexander Kjäll

Chà! Cảm ơn bạn về thông tin. Tôi đoán bạn đã có một thời gian khó khăn để hình dung ra điều đó :-(
Brian Clozel

URL .. Cuộc sống. Đã lưu!
CodingInCircles

11

Quan điểm của tôi là tôi chỉ muốn chuỗi DATETIME thô, vì vậy tôi có thể tự phân tích cú pháp như vậy.

Điều đó khiến tôi nghĩ rằng "cách giải quyết" của bạn không phải là một giải pháp thay thế, mà trên thực tế là cách duy nhất để lấy giá trị từ cơ sở dữ liệu vào mã của bạn:

SELECT CAST(add_date AS CHAR) as add_date

Nhân tiện, một số ghi chú khác từ tài liệu MySQL:

Các ràng buộc của MySQL về dữ liệu không hợp lệ :

Trước MySQL 5.0.2, MySQL đã tha thứ cho các giá trị dữ liệu bất hợp pháp hoặc không phù hợp và buộc chúng phải tuân theo các giá trị pháp lý để nhập dữ liệu. Trong MySQL 5.0.2 trở lên, đó vẫn là hành vi mặc định, nhưng bạn có thể thay đổi chế độ SQL của máy chủ để chọn cách xử lý truyền thống hơn đối với các giá trị xấu để máy chủ từ chối chúng và hủy bỏ câu lệnh mà chúng xuất hiện.

[..]

Nếu bạn cố gắng lưu trữ NULL vào một cột không nhận giá trị NULL, thì sẽ xảy ra lỗi đối với câu lệnh INSERT một hàng. Đối với câu lệnh INSERT nhiều hàng hoặc câu lệnh INSERT INTO ... SELECT, MySQL Server lưu trữ giá trị mặc định ngầm định cho kiểu dữ liệu cột.

Các loại ngày và giờ trong MySQL 5.x :

MySQL cũng cho phép bạn lưu trữ '0000-00-00' dưới dạng "ngày giả" (nếu bạn không sử dụng chế độ SQL NO_ZERO_DATE). Điều này trong một số trường hợp thuận tiện hơn (và sử dụng ít dữ liệu và không gian chỉ mục hơn) so với việc sử dụng giá trị NULL.

[..]

Theo mặc định, khi MySQL gặp một giá trị cho loại ngày hoặc giờ nằm ​​ngoài phạm vi hoặc không hợp pháp đối với loại (như được mô tả ở đầu phần này), nó sẽ chuyển đổi giá trị thành giá trị “không” cho loại đó.


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

Sử dụng điều này để tránh lỗi. Nó trả về ngày ở định dạng chuỗi và sau đó bạn có thể lấy nó dưới dạng chuỗi.

resultset.getString("dtime");

Điều này thực sự KHÔNG hoạt động. Mặc dù bạn gọi getString. Nội bộ mysql vẫn cố gắng chuyển đổi nó sang ngày đầu tiên.

tại com.mysql.jdbc.ResultSetImpl.getDateFromString (ResultSetImpl.java:2270)

~ [mysql-connector-java-5.1.15.jar: na] tại com.mysql.jdbc.ResultSetImpl.getStringInternal (ResultSetImpl.java:5743)

~ [mysql-connector-java-5.1.15.jar: na] tại com.mysql.jdbc.ResultSetImpl.getString (ResultSetImpl.java:5576)

~ [mysql-connector-java-5.1.15.jar: na]


1
Điều đó tương tự như giải pháp CAST (add_date AS CHAR) của tôi, nhưng +1 vì điều này cho phép bạn định dạng nó một cách rõ ràng theo cách bạn muốn.
Jason S

5

Nếu, sau khi thêm dòng:

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

hibernate.connection.zeroDateTimeBehavior=convertToNull

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

tiếp tục là một lỗi:

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').

tìm dòng:

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

thay thế bằng các dòng sau, tương ứng:

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

5

Tôi đã vật lộn với vấn đề này và thực hiện các giải pháp 'convertToNull' được thảo luận ở trên. Nó hoạt động trong phiên bản MySql cục bộ của tôi. Nhưng khi tôi triển khai ứng dụng Play / Scala của mình cho Heroku, nó sẽ không hoạt động nữa. Heroku cũng nối một số args với URL DB mà họ cung cấp cho người dùng và giải pháp này, do Heroku sử dụng cách ghép nối của "?" trước khi bộ args của riêng họ, sẽ không hoạt động. Tuy nhiên, tôi đã tìm thấy một giải pháp khác có vẻ hoạt động tốt như nhau.

SET sql_mode = 'NO_ZERO_DATE';

Tôi đặt điều này trong mô tả bảng của mình và nó đã giải quyết được vấn đề '0000-00-00 00:00:00' không thể được biểu diễn dưới dạng java.sql.Timestamp.


3

Tôi khuyên bạn nên sử dụng null để biểu thị giá trị null.

Ngoại lệ bạn nhận được là gì?

BTW:

Không có năm nào được gọi là 0 hoặc 0000. (Mặc dù một số ngày cho phép năm nay)

Và không có 0 tháng trong năm hoặc 0 ngày trong tháng. (Đó có thể là nguyên nhân gây ra sự cố của bạn)


có, nhưng vẫn không có năm nào gọi là 0, thậm chí là truy thu. Năm trước 1 AD / CE là 1 BC / BCE
Peter Lawrey

2
Đó là toàn bộ lý do MySQL sử dụng 0000 làm ngày không hợp lệ, vì nó không xung đột với các ngày hiện có. Ngoài ra, các ngày như 1999-00-00 đôi khi được sử dụng (không phải bởi tôi!) Để đại diện cho năm 1999 thay vì một ngày cụ thể.
Jason S

3

Tôi đã giải quyết vấn đề do '00 -00 -.... 'không phải là ngày hợp lệ, sau đó, tôi đã thay đổi định nghĩa cột SQL của mình bằng cách thêm giải pháp "NULL" để cho phép giá trị 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),...

Sau đó, tôi có thể chèn giá trị NULL, có nghĩa là "Tôi chưa đăng ký dấu thời gian đó" ...

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),...

Bảng trông rằng:

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)

Cuối cùng: JPA đang hoạt động:

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

Cuối cùng thì tất cả công việc của nó. Tôi hy vọng rằng sẽ giúp bạn.


Tôi không nghĩ rằng điều này giải quyết vấn đề chuyển đổi MySQL DATETIME của tất cả các số không thành một ngày hợp lệ. Nó chỉ cho thấy cách nhập null vào trường DATETIME mà không thực sự giúp ích được nhiều.
Mark Chorley

2

Để thêm vào các câu trả lời khác: Nếu bạn muốn 0000-00-00chuỗi, bạn có thể sử dụng noDatetimeStringSync=true(với lưu ý là hy sinh chuyển đổi múi giờ).

Lỗi chính thức của MySQL: https://bugs.mysql.com/bug.php?id=47108 .

Ngoài ra, đối với lịch sử, JDBC sử dụng để trả NULLcho 0000-00-00số ngày nhưng bây giờ quay trở lại là một ngoại lệ theo mặc định. Nguồn


2

bạn có thể nối url jdbc với

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

Với sự trợ giúp của điều này, sql chuyển đổi '0000-00-00 00:00:00' thành giá trị null.

ví dụ:

jdbc:mysql:<host-name>/<db-name>?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.