2010-10-25 30 views
14

Tôi gặp vấn đề về quản lý ngày tháng bằng mili giây. Tôi hiểu nhu cầu sử dụng các dấu thời gian để lưu trữ mili giây:Làm thế nào để sử dụng java.sql.Timestamp như java.util.Date thực với JPA

@Temporal(TIMESTAMP) 
@Column(name="DATE_COLUMN", nullable = false) 
@Override public java.util.Date getDate() { return this.date; } 

Nhưng nếu tôi không thể so sánh ngày này sang một thể hiện của java.util.Date, trừ khi tôi chú ý đến thứ tự của equals() gọi , bởi vì this.date thể hiện là một java.sql.Timestamp. Làm thế nào để có được một java.util.Date từ JPA? Bởi vì ngày xuất phát từ JPA, ngay cả khi chữ ký phương thức là một java.util.Date thực sự là một cá thể của java.sql.Timestamp.

java.util.Date newDate = new Date(this.date.getTime()); 
this.date.equals(newDate) == false 
newDate.equals(this.date) == true 

Tôi đã cố gắng để thay đổi phương pháp của tôi trong lớp kiên trì:

@Override 
public Date getDate() { 
    return this.date == null ? null : new Date(this.date.getTime()); 
} 

Đó là làm việc, nhưng nó không hiệu quả với rất nhiều dữ liệu.

Có những lựa chọn khác:

  • tôi có thể thay đổi thiết kế của lớp kiên trì của tôi, sử dụng @PostLoad để tạo ra một java.util.Date từ ngày persited sau khi tôi lấy nó.

  • Tôi tự hỏi nếu tôi không thể nhận được kết quả bằng cách sử dụng ClassTransformer?

Bạn đã từng phải đối mặt với vấn đề này chưa? Những gì tôi không chính xác? Cách tốt nhất để xử lý vấn đề này là gì?

+0

Tôi vừa có một phiên bản rất khó chịu về vấn đề này, nơi điều này đã làm hỏng sự so sánh muối của Spring Security. Rõ ràng bằng cách sử dụng một phiên bản ngày sql.timestamp cho muối ám một kết quả khác nhau hơn bằng cách sử dụng một util.Date – Marc

Trả lời

7

java.sql.Timestamp override phương thức compareTo(Date), vì vậy nó nên có vấn đề sử dụng compareTo(..)

Nói tóm lại - java.util.Datejava.sql.Timestamp có thể so sánh lẫn nhau.

Hơn nữa, bạn luôn có thể so sánh date.getTime(), thay vì chính đối tượng.

Và thậm chí hơn nữa - bạn có thể sử dụng trường long để lưu trữ ngày. Hoặc thậm chí là DateTime (từ thời gian joda)

+0

Ngày xuất phát từ sự kiên trì được sử dụng bởi một khuôn khổ khác mà không thể sửa đổi (quá nhiều công trình). – chepseskaf

+0

Tôi vẫn không hiểu vấn đề là gì .. – Bozho

+0

Vì tôi không thể sửa đổi quá trình so sánh, tôi cần cung cấp java.util.Date cho hệ thống chính. Tôi muốn biết cách chuyển đổi một ngày hiệu quả? – chepseskaf

4

Theo kinh nghiệm của mình, bạn không muốn java.sql.Timestamp đưa vào logic của bạn - nó tạo ra rất nhiều lỗi lạ giống như bạn đã chỉ ra, và nó không nhận được bất kỳ tốt hơn nếu ứng dụng của bạn không tuần tự hóa.

Nếu nó hoạt động với ghi đè sẽ trả về một java.util.Date mới, sau đó đi cho một. Hoặc thậm chí tốt hơn, đi cho JodaTime. Bạn sẽ tìm thấy rất nhiều ví dụ trên mạng làm điều đó. Tôi sẽ không lo lắng về hiệu suất ở đây vì cơ sở dữ liệu của bạn có cường độ chậm hơn so với việc tạo ra một đối tượng java.util.Date mới.

EDIT: Tôi thấy rằng bạn đang sử dụng Hibernate. Nếu bạn sử dụng chú thích, bạn có thể làm:

@Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime") 
public DateTime getProvisionByTime() { 
    return provisionByTime; 
} 

Sau đó, bạn sẽ nhận được các đối tượng DateTime đẹp từ Jodatime trong các đối tượng liên tục của bạn.Nếu bạn chỉ muốn có một ngày, bạn có thể sử dụng LOCALDATE như thế này:

@Type(type = "org.joda.time.contrib.hibernate.PersistentLocalDate") 
public LocalDate getCloudExpireDate() { 
    return cloudExpireDate; 
} 

NẾU bạn sử dụng maven, sự phụ thuộc sau đây cần được điều này thiết lập cho bạn (bạn có thể cần phải cập nhật các phiên bản hibernate)

<dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate</artifactId> 
     <version>3.2.6.ga</version> 
    </dependency> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-annotations</artifactId> 
     <version>3.3.1.GA</version> 
    </dependency> 

    <dependency> 
     <groupId>joda-time</groupId> 
     <artifactId>joda-time-hibernate</artifactId> 
     <version>1.2</version> 
    </dependency> 
    <dependency> 
     <groupId>joda-time</groupId> 
     <artifactId>joda-time</artifactId> 
      <version>1.6.1</version> 
    </dependency> 
+0

Chính xác, java.sql.Timestamp được áp đặt bởi JPA. Tôi muốn tôi có thể làm mà không có, nhưng ánh xạ được cung cấp bởi JPA ... – chepseskaf

+0

Khi bạn đang sử dụng ngủ đông, tôi đoán bạn có thể thử các giải pháp tôi đã cung cấp ở trên. – Knubo

+0

Tôi sẽ thử kiểm tra, nhưng với giải pháp này, tôi sẽ không tuân thủ đầy đủ JPA ... – chepseskaf

1

JPA phải trả về java.util.Date cho thuộc tính loại java.util.Date, chú thích @Temporal (TIMESTAMP) chỉ ảnh hưởng đến cách ngày được lưu trữ. Bạn không nên lấy lại java.sql.Timestamp.

Nhà cung cấp JPA nào bạn đang sử dụng? Bạn đã thử điều này trong EclipseLink, thực hiện tham chiếu JPA chưa?

+0

Hibernate là nhà cung cấp JPA của tôi;) – chepseskaf

12

TBH, tôi không chắc chắn về tình trạng chính xác của điều này nhưng thực sự có thể có vấn đề với cách Hibernate (nhà cung cấp JPA của bạn, phải không?) Xử lý các cột TIMESTAMP.

Để ánh xạ một SQL TIMESTAMP thành java.util.Date, Hibernate sử dụng TimestampType sẽ thực sự gán một java.sql.Timestamp cho thuộc tính java.util.Date của bạn. Và trong khi điều này là "hợp pháp", vấn đề là Timestamp.equals(Object)không đối xứng (tại sao trên trái đất ?!) và điều này phá vỡ ngữ nghĩa của Date.equals(Object).

Kết quả là bạn không thể "mù quáng" được sử dụng myDate.equals(someRealJavaUtilDate) nếu myDate được ánh xạ tới SQL TIMESTAMP, tất nhiên là không thực sự chấp nhận được.

Nhưng mặc dù điều này đã được thảo luận rộng rãi trên các diễn đàn Hibernate, ví dụ: trong this threadthis one (đọc tất cả các trang), có vẻ như người dùng và nhà phát triển Hibernate chưa bao giờ đồng ý về vấn đề này (xem các vấn đề như HB-681) và tôi không hiểu tại sao.

Có lẽ đó chỉ là tôi, có lẽ tôi chỉ thiếu một cái gì đó đơn giản cho người khác, nhưng vấn đề trông hiển nhiên đối với tôi và trong khi tôi xem xét này ngu ngốc java.sql.Timestamp là thủ phạm, tôi vẫn nghĩ rằng Hibernate nên che chắn người dùng từ vấn đề này. Tôi không hiểu tại sao Gavin không đồng ý về điều này.

Đề xuất của tôi là tạo một trường hợp thử nghiệm chứng minh vấn đề (nên khá đơn giản) và báo cáo sự cố (một lần nữa) để xem bạn có nhận được phản hồi tích cực hơn từ nhóm hiện tại hay không.

Trong khi đó, bạn có thể sử dụng một loại tùy chỉnh để "sửa chữa" các vấn đề chính mình, sử dụng một cái gì đó như thế này (lấy từ diễn đàn này và dán như là):

public class TimeMillisType extends org.hibernate.type.TimestampType { 

    public Date get(ResultSet rs, String name) throws SQLException { 
     Timestamp timestamp = rs.getTimestamp(name); 
     if (timestamp == null) return null; 
     return 
      new Date(timestamp.getTime()+timestamp.getNanos()/1000000); 
    } 

} 
+1

Thật điên rồ. Tôi sẽ cố gắng liên quan đến Hibernate một lần nữa, như bạn đã khuyên tôi. – chepseskaf

+0

Tôi đã tạo một vấn đề mới khi tôi gặp phải vấn đề tương tự trong đơn đăng ký của mình: https://hibernate.onjira.com/browse/HHH-6873 – Martin

2

Vấn đề là quan trọng đối với các xét nghiệm DAO:

Employer employer1 = new Employer(); 
employer1.setName("namenamenamenamenamename"); 
employer1.setRegistered(new Date(1111111111)); // <- Date field 

entityManager.persist(employer1); 
assertNotNull(employer1.getId()); 

entityManager.flush(); 
entityManager.clear(); 

Employer employer2 = entityManager.find(Employer.class, employer1.getId()); 
assertNotNull(employer2); 
assertEquals(employer1, employer2); // <- works 
assertEquals(employer2, employer1); // <- fails !!! 

Vì vậy, kết quả thực sự đáng ngạc nhiên và các bài kiểm tra viết trở nên phức tạp.

Nhưng trong logic nghiệp vụ thực, bạn sẽ không bao giờ sử dụng thực thể như khóa tập hợp/bản đồ vì nó rất lớn và có thể thay đổi được. Và bạn sẽ không bao giờ so sánh giá trị thời gian bằng cách so sánh equal. Và so sánh toàn bộ các thực thể cũng nên tránh.

Kịch bản thông thường sử dụng ID thực thể không thể thay đổi cho khóa bản đồ/bộ và so sánh giá trị thời gian với phương pháp compareTo() hoặc chỉ sử dụng giá trị getTime().

Nhưng làm các xét nghiệm là một nỗi đau vì vậy tôi thực hiện kiểu xử lý của riêng tôi

http://pastebin.com/7TgtEd3x

http://pastebin.com/DMrxzUEV

Và tôi đã ghi đè các phương ngữ tôi sử dụng:

package xxx; 

import org.hibernate.dialect.HSQLDialect; 
import org.hibernate.type.AdaptedImmutableType; 
import xxx.DateTimestampType; 

import java.util.Date; 

public class CustomHSQLDialect extends HSQLDialect { 

    public CustomHSQLDialect() { 
     addTypeOverride(DateTimestampType.INSTANCE); 
     addTypeOverride(new AdaptedImmutableType<Date>(DateTimestampType.INSTANCE)); 
    } 
} 

tôi thiên đường' t đã quyết định chưa - tôi sẽ sử dụng phương pháp này cho cả thử nghiệm và sản xuất hay chỉ cho các thử nghiệm.

Các vấn đề liên quan