Tạo dấu thời gian và dấu thời gian cập nhật mới nhất với Hibernate và MySQL


244

Đối với một thực thể Hibernate nhất định, chúng tôi có yêu cầu lưu trữ thời gian tạo của nó và lần cuối cùng nó được cập nhật. Làm thế nào bạn sẽ thiết kế này?

  • Những loại dữ liệu nào bạn sẽ sử dụng trong cơ sở dữ liệu (giả sử MySQL, có thể ở múi giờ khác là JVM)? Các loại dữ liệu sẽ được nhận biết múi giờ?

  • Bạn sẽ sử dụng những loại dữ liệu trong Java ( Date, Calendar, long, ...)?

  • Ai sẽ chịu trách nhiệm thiết lập dấu thời gian cho cơ sở dữ liệu, khung ORM (Hibernate) hoặc lập trình viên ứng dụng?

  • Những chú thích nào bạn sẽ sử dụng cho ánh xạ (ví dụ @Temporal)?

Tôi không chỉ tìm kiếm một giải pháp làm việc, mà còn là một giải pháp an toàn và được thiết kế tốt.

Câu trả lời:


266

Nếu bạn đang sử dụng các chú thích JPA, bạn có thể sử dụng @PrePersistvà các @PreUpdatesự kiện hook làm điều này:

@Entity
@Table(name = "entities")    
public class Entity {
  ...

  private Date created;
  private Date updated;

  @PrePersist
  protected void onCreate() {
    created = new Date();
  }

  @PreUpdate
  protected void onUpdate() {
    updated = new Date();
  }
}

hoặc bạn có thể sử dụng @EntityListenerchú thích trên lớp và đặt mã sự kiện vào một lớp bên ngoài.


7
Hoạt động mà không có bất kỳ vấn đề nào trong J2SE, vì @PrePersist và @PerUpdate là các chú thích JPA.
Kdeveloper

2
@Kumar - Trong trường hợp bạn đang sử dụng phiên Hibernate đơn giản (thay vì JPA), bạn có thể thử trình nghe sự kiện ngủ đông, mặc dù điều đó không thanh lịch và nhỏ gọn so với chú thích JPA.
Shailendra

43
Trong Hibernate hiện tại với JPA, người ta có thể sử dụng "@CreationTimestamp" và "@UpdateTimestamp"
Florian Loch

@FlorianLoch có tương đương với Ngày thay vì Dấu thời gian không? Hay tôi sẽ phải tự tạo ra?
mike

150

Bạn chỉ có thể sử dụng @CreationTimestamp@UpdateTimestamp:

@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_date")
private Date createDate;

@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
private Date modifyDate;

3
cảm ơn bro như một điều nhỏ cần cập nhật dấu thời gian. Tôi không biết. bạn đã cứu ngày của tôi
Virendra Sagar

TemporalType.DATEtrong trường hợp đầu tiên và TemporalType.TIMESTAMPtrong trường hợp thứ hai.
v.ladynev

Bạn đang nói điều này cũng tự động thiết lập các giá trị? Đó không phải là kinh nghiệm của tôi; có vẻ như ngay cả với @CreationTimestamp@UpdateTimestampngười ta cần một số @Column(..., columnDefinition = "timestamp default current_timestamp")hoặc sử dụng @PrePersist@PreUpdate(sau này cũng đảm bảo khách hàng không thể đặt giá trị khác).
Arjan

2
Khi tôi cập nhật đối tượng và duy trì nó, bd đã mất creat_date ... tại sao?
Brenno Leal

1
Tôi bị xóa nullable=falsekhỏi trường hợp @Column(name = "create_date" , nullable=false)làm việc
Chaiarar Tupe

113

Lấy các tài nguyên trong bài đăng này cùng với thông tin được lấy từ trái và phải từ các nguồn khác nhau, tôi đã đưa ra giải pháp tao nhã này, tạo ra lớp trừu tượng sau

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@MappedSuperclass
public abstract class AbstractTimestampEntity {

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created", nullable = false)
    private Date created;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated", nullable = false)
    private Date updated;

    @PrePersist
    protected void onCreate() {
    updated = created = new Date();
    }

    @PreUpdate
    protected void onUpdate() {
    updated = new Date();
    }
}

và có tất cả các thực thể của bạn mở rộng nó, ví dụ:

@Entity
@Table(name = "campaign")
public class Campaign extends AbstractTimestampEntity implements Serializable {
...
}

5
điều này tốt cho đến khi bạn muốn thêm các hành vi độc quyền khác nhau vào các thực thể của mình (và bạn không thể mở rộng nhiều hơn một lớp cơ sở). afaik cách duy nhất để có được hiệu ứng tương tự mà không có lớp cơ sở là mặc dù khía cạnh người nghe hoặc người nghe sự kiện thấy @kieren dixon trả lời
gpilotino

3
Tôi sẽ làm điều này bằng cách sử dụng trình kích hoạt MySQL để ngay cả khi thực thể đầy đủ không được lưu hoặc được sửa đổi bởi bất kỳ ứng dụng bên ngoài hoặc truy vấn thủ công nào, nó vẫn sẽ cập nhật các trường này.
Webnet

3
bạn có thể cho tôi bất kỳ ví dụ làm việc nào vì tôi đang gặp ngoại lệnot-null property references a null or transient value: package.path.ClassName.created
Sumit Ramteke

@rishiAgar, Không, tôi không có. Nhưng bây giờ tôi đã gán ngày cho thuộc tính của mình từ hàm tạo mặc định. Sẽ cho bạn biết một khi tôi tìm thấy.
Sumit Ramteke

1
Thay đổi nó để @Column(name = "updated", nullable = false, insertable = false)làm cho nó hoạt động. Thật thú vị khi câu trả lời này nhận được rất nhiều sự ủng hộ ..
displayname

20

1. Loại cột cơ sở dữ liệu nào bạn nên sử dụng

Câu hỏi đầu tiên của bạn là:

Những loại dữ liệu nào bạn sẽ sử dụng trong cơ sở dữ liệu (giả sử MySQL, có thể ở múi giờ khác là JVM)? Các loại dữ liệu sẽ được nhận biết múi giờ?

Trong MySQL, TIMESTAMPkiểu cột thực hiện chuyển từ múi giờ cục bộ của trình điều khiển JDBC sang múi giờ của cơ sở dữ liệu, nhưng nó chỉ có thể lưu trữ dấu thời gian lên đến '2038-01-19 03:14:07.999999, vì vậy nó không phải là lựa chọn tốt nhất cho tương lai.

Vì vậy, sử dụng tốt hơn DATETIMEthay vào đó, không có giới hạn ranh giới trên. Tuy nhiên, DATETIMEkhông nhận thức được múi giờ. Vì vậy, vì lý do này, tốt nhất nên sử dụng UTC ở phía cơ sở dữ liệu và sử dụng thuộc tính hibernate.jdbc.time_zoneHibernate.

Để biết thêm chi tiết về hibernate.jdbc.time_zonecài đặt, hãy xem bài viết này .

2. Loại thuộc tính thực thể nào bạn nên sử dụng

Câu hỏi thứ hai của bạn là:

Những loại dữ liệu nào bạn sẽ sử dụng trong Java (Ngày, Lịch, dài, ...)?

Về phía Java, bạn có thể sử dụng Java 8 LocalDateTime. Bạn cũng có thể sử dụng di sản Date, nhưng các loại Ngày / Giờ của Java 8 tốt hơn vì chúng không thay đổi và không thực hiện chuyển múi giờ sang múi giờ địa phương khi đăng nhập chúng.

Để biết thêm chi tiết về các loại Ngày / Giờ Java 8 được Hibernate hỗ trợ, hãy xem bài viết này .

Bây giờ, chúng ta cũng có thể trả lời câu hỏi này:

Những chú thích nào bạn sẽ sử dụng cho ánh xạ (ví dụ @Temporal)?

Nếu bạn đang sử dụng LocalDateTimehoặc java.sql.Timestampđể ánh xạ một thuộc tính thực thể dấu thời gian, thì bạn không cần phải sử dụng@Temporal vì HIbernate đã biết rằng thuộc tính này sẽ được lưu dưới dạng Dấu thời gian JDBC.

Chỉ khi bạn đang sử dụng java.util.Date, bạn cần chỉ định @Temporalchú thích, như thế này:

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_on")
private Date createdOn;

Nhưng, sẽ tốt hơn nhiều nếu bạn lập bản đồ như thế này:

@Column(name = "created_on")
private LocalDateTime createdOn;

Cách tạo các giá trị cột kiểm toán

Câu hỏi thứ ba của bạn là:

Ai sẽ chịu trách nhiệm thiết lập dấu thời gian cho cơ sở dữ liệu, khung ORM (Hibernate) hoặc lập trình viên ứng dụng?

Những chú thích nào bạn sẽ sử dụng cho ánh xạ (ví dụ @Temporal)?

Có nhiều cách bạn có thể đạt được mục tiêu này. Bạn có thể cho phép cơ sở dữ liệu để làm điều đó ..

Đối với create_oncột, bạn có thể sử dụng DEFAULTràng buộc DDL, như:

ALTER TABLE post 
ADD CONSTRAINT created_on_default 
DEFAULT CURRENT_TIMESTAMP() FOR created_on;

Đối với updated_oncột, bạn có thể sử dụng trình kích hoạt DB để đặt giá trị cột vớiCURRENT_TIMESTAMP() mỗi lần sửa đổi một hàng nhất định.

Hoặc, sử dụng JPA hoặc Hibernate để đặt chúng.

Giả sử bạn có các bảng cơ sở dữ liệu sau:

Bảng cơ sở dữ liệu với các cột kiểm toán

Và, mỗi bảng có các cột như:

  • created_by
  • created_on
  • updated_by
  • updated_on

Sử dụng Hibernate @CreationTimestamp@UpdateTimestampchú thích

Hibernate cung cấp các chú thích @CreationTimestamp@UpdateTimestampchú thích có thể được sử dụng để ánh xạ các cột created_onupdated_oncột.

Bạn có thể sử dụng @MappedSuperclassđể định nghĩa một lớp cơ sở sẽ được mở rộng bởi tất cả các thực thể:

@MappedSuperclass
public class BaseEntity {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "created_on")
    @CreationTimestamp
    private LocalDateTime createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @Column(name = "updated_on")
    @UpdateTimestamp
    private LocalDateTime updatedOn;

    @Column(name = "updated_by")
    private String updatedBy;

    //Getters and setters omitted for brevity
}

Và, tất cả các thực thể sẽ mở rộng BaseEntity, như thế này:

@Entity(name = "Post")
@Table(name = "post")
public class Post extend BaseEntity {

    private String title;

    @OneToMany(
        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<PostComment> comments = new ArrayList<>();

    @OneToOne(
        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        fetch = FetchType.LAZY
    )
    private PostDetails details;

    @ManyToMany
    @JoinTable(
        name = "post_tag",
        joinColumns = @JoinColumn(
            name = "post_id"
        ),
        inverseJoinColumns = @JoinColumn(
            name = "tag_id"
        )
    )
    private List<Tag> tags = new ArrayList<>();

    //Getters and setters omitted for brevity
}

Để biết thêm chi tiết về việc sử dụng @MappedSuperclass, hãy xem bài viết này .

Tuy nhiên, ngay cả khi các thuộc tính createdOnupdateOnđược đặt theo chú thích @CreationTimestamp@UpdateTimestampchú thích cụ thể của Hibernate , thì createdByupdatedBy yêu cầu đăng ký một cuộc gọi lại ứng dụng, như được minh họa bởi giải pháp JPA sau đây.

Sử dụng JPA @EntityListeners

Bạn có thể gói các thuộc tính kiểm toán trong một nhúng:

@Embeddable
public class Audit {

    @Column(name = "created_on")
    private LocalDateTime createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @Column(name = "updated_on")
    private LocalDateTime updatedOn;

    @Column(name = "updated_by")
    private String updatedBy;

    //Getters and setters omitted for brevity
}

Và, tạo một AuditListenerđể thiết lập các thuộc tính kiểm toán:

public class AuditListener {

    @PrePersist
    public void setCreatedOn(Auditable auditable) {
        Audit audit = auditable.getAudit();

        if(audit == null) {
            audit = new Audit();
            auditable.setAudit(audit);
        }

        audit.setCreatedOn(LocalDateTime.now());
        audit.setCreatedBy(LoggedUser.get());
    }

    @PreUpdate
    public void setUpdatedOn(Auditable auditable) {
        Audit audit = auditable.getAudit();

        audit.setUpdatedOn(LocalDateTime.now());
        audit.setUpdatedBy(LoggedUser.get());
    }
}

Để đăng ký AuditListener, bạn có thể sử dụng @EntityListenerschú thích JPA:

@Entity(name = "Post")
@Table(name = "post")
@EntityListeners(AuditListener.class)
public class Post implements Auditable {

    @Id
    private Long id;

    @Embedded
    private Audit audit;

    private String title;

    @OneToMany(
        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<PostComment> comments = new ArrayList<>();

    @OneToOne(
        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        fetch = FetchType.LAZY
    )
    private PostDetails details;

    @ManyToMany
    @JoinTable(
        name = "post_tag",
        joinColumns = @JoinColumn(
            name = "post_id"
        ),
        inverseJoinColumns = @JoinColumn(
            name = "tag_id"
        )
    )
    private List<Tag> tags = new ArrayList<>();

    //Getters and setters omitted for brevity
}

Để biết thêm chi tiết về việc triển khai các thuộc tính kiểm toán với JPA @EntityListener, hãy xem bài viết này .


Câu trả lời rất kỹ lưỡng, cảm ơn. Tôi không đồng ý về việc thích datetimehơn timestamp. Bạn muốn cơ sở dữ liệu của bạn biết múi giờ của dấu thời gian của bạn. Điều này ngăn ngừa lỗi chuyển đổi múi giờ.
Ole VV

Các timestsmploại không lưu trữ thông tin múi giờ. Nó chỉ thực hiện một cuộc trò chuyện từ ứng dụng TZ đến DB TZ. Trong thực tế, bạn muốn lưu trữ TZ của máy khách một cách riêng biệt và thực hiện cuộc hội thoại trong ứng dụng trước khi kết xuất UI.
Vlad Mihalcea

Chính xác. MySQL timestampluôn ở trong UTC. MySQL chuyển đổi TIMESTAMPcác giá trị từ múi giờ hiện tại sang UTC để lưu trữ và quay lại từ UTC thành múi giờ hiện tại để truy xuất. Tài liệu về MySQL: Các loại DATE, DATETIME và TIMESTAMP
Ole VV

17

Bạn cũng có thể sử dụng một bộ chặn để đặt các giá trị

Tạo giao diện có tên TimeStamped mà thực thể của bạn triển khai

public interface TimeStamped {
    public Date getCreatedDate();
    public void setCreatedDate(Date createdDate);
    public Date getLastUpdated();
    public void setLastUpdated(Date lastUpdatedDate);
}

Xác định đánh chặn

public class TimeStampInterceptor extends EmptyInterceptor {

    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, 
            Object[] previousState, String[] propertyNames, Type[] types) {
        if (entity instanceof TimeStamped) {
            int indexOf = ArrayUtils.indexOf(propertyNames, "lastUpdated");
            currentState[indexOf] = new Date();
            return true;
        }
        return false;
    }

    public boolean onSave(Object entity, Serializable id, Object[] state, 
            String[] propertyNames, Type[] types) {
            if (entity instanceof TimeStamped) {
                int indexOf = ArrayUtils.indexOf(propertyNames, "createdDate");
                state[indexOf] = new Date();
                return true;
            }
            return false;
    }
}

Và đăng ký với nhà máy phiên



Đây là một giải pháp, nếu bạn làm việc với SessionFactory thay vì EntityManager!
olivmir

Chỉ dành cho những người gặp phải vấn đề tương tự như tôi đã làm trong bối cảnh này: nếu thực thể của bạn không tự xác định các trường bổ sung này (createdAt, ...) nhưng thừa hưởng nó từ lớp cha, thì lớp cha này phải được chú thích với @MappedSuperclass - nếu không thì Hibernate không tìm thấy các trường này.
olivmir

17

Với giải pháp của Olivier, trong các báo cáo cập nhật, bạn có thể gặp phải:

com.mysql.jdbc.exceptions.jdbc4.MyQueryIntegrityConstraintViolationException: Cột 'được tạo' không thể rỗng

Để giải quyết vấn đề này, hãy thêm updizable = false vào chú thích @Column của thuộc tính "created":

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created", nullable = false, updatable=false)
private Date created;

1
Chúng tôi đang sử dụng @Version. Khi một thực thể được đặt, hai cuộc gọi được thực hiện, một cuộc gọi sẽ được thực hiện và một cuộc gọi khác để cập nhật. Tôi đã phải đối mặt với cùng một vấn đề vì điều này. Khi tôi thêm @Column(updatable = false)nó đã giải quyết vấn đề của tôi.
Ganesh Satpute

12

Cảm ơn mọi người đã giúp đỡ. Sau khi tự mình thực hiện một số nghiên cứu (tôi là người đặt câu hỏi), đây là điều tôi thấy có ý nghĩa nhất:

  • Kiểu cột cơ sở dữ liệu: số lượng thời gian không xác định của mili giây kể từ năm 1970 được biểu thị decimal(20)vì 2 ^ 64 có 20 chữ số và dung lượng ổ đĩa rẻ; Hãy thẳng thắn. Ngoài ra, tôi sẽ không sử dụng DEFAULT CURRENT_TIMESTAMP, cũng không kích hoạt. Tôi muốn không có phép thuật trong DB.

  • Kiểu trường Java : long. Dấu thời gian Unix được hỗ trợ tốt trên nhiều longlib khác nhau, không có vấn đề về Y2038, số học dấu thời gian nhanh và dễ dàng (chủ yếu là toán tử <và toán tử +, giả sử không có ngày / tháng / năm liên quan đến tính toán). Và, quan trọng nhất, cả longs và s nguyên thủy java.lang.Longđều bất biến được truyền qua giá trị không giống như java.util.Dates; Tôi thực sự bực mình khi tìm thấy một cái gì đó giống như foo.getLastUpdate().setTime(System.currentTimeMillis())khi gỡ lỗi mã của người khác.

  • Khung ORM phải chịu trách nhiệm tự động điền dữ liệu.

  • Tôi chưa thử nghiệm điều này, nhưng chỉ nhìn vào các tài liệu mà tôi cho rằng @Temporalsẽ thực hiện công việc; không chắc chắn về việc tôi có thể sử dụng @Versioncho mục đích này hay không. @PrePersist@PreUpdatelà lựa chọn thay thế tốt để kiểm soát điều đó bằng tay. Thêm điều đó vào siêu lớp (lớp cơ sở chung) cho tất cả các thực thể, là một ý tưởng dễ thương với điều kiện bạn thực sự muốn đánh dấu thời gian cho tất cả các thực thể của mình.


Mặc dù dài và dài có thể là bất biến, nhưng điều đó sẽ không giúp bạn trong tình huống bạn mô tả. Họ vẫn có thể nói foo.setLastUpdate (Long mới (System.cienTimeMillis ());
Ian McLaird

2
Tốt rồi. Hibernate yêu cầu trình thiết lập bằng mọi cách (hoặc nó sẽ cố gắng truy cập trường trực tiếp thông qua sự phản chiếu). Tôi đã nói về khó khăn trong việc theo đuổi những người sửa đổi dấu thời gian từ mã ứng dụng của chúng tôi. Thật khó khăn khi bạn có thể làm điều đó bằng cách sử dụng một getter.
ngn

Tôi đồng ý với khiếu nại của bạn rằng khung ORM phải chịu trách nhiệm điền ngày tự động, nhưng tôi sẽ đi xa hơn một bước và nói rằng ngày đó nên được đặt từ đồng hồ của máy chủ cơ sở dữ liệu, thay vì máy khách. Tôi không rõ nếu điều này hoàn thành mục tiêu này. Trong sql, tôi có thể làm điều này bằng cách sử dụng hàm sysdate, nhưng tôi không biết làm thế nào trong Hibernate hoặc bất kỳ triển khai JPA nào.
MiguelMunoz

Tôi muốn không có phép thuật trong DB. Tôi hiểu ý của bạn, nhưng tôi muốn xem xét thực tế rằng cơ sở dữ liệu nên tự bảo vệ mình khỏi các nhà phát triển xấu / mới / không biết gì. Tính toàn vẹn dữ liệu rất quan trọng trong một công ty lớn, bạn không thể dựa vào người khác để chèn dữ liệu tốt. Các ràng buộc, mặc định và FK sẽ giúp đạt được điều đó.
Icegras

6

Trong trường hợp bạn đang sử dụng API phiên, các cuộc gọi lại PrePersist và PreUpdate sẽ không hoạt động theo câu trả lời này .

Tôi đang sử dụng phương thức contin () của Hibernate session trong mã của mình vì vậy cách duy nhất tôi có thể thực hiện công việc này là với mã bên dưới và theo dõi bài đăng trên blog này (cũng được đăng trong câu trả lời ).

@MappedSuperclass
public abstract class AbstractTimestampEntity {

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created")
    private Date created=new Date();

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated")
    @Version
    private Date updated;

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }
}

Nên trả lại các đối tượng được nhân bản như updated.clone()các thành phần khác có thể thao túng trạng thái bên trong (ngày)
1ambda


3

Mã sau làm việc cho tôi.

package com.my.backend.models;

import java.util.Date;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

import com.fasterxml.jackson.annotation.JsonIgnore;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import lombok.Getter;
import lombok.Setter;

@MappedSuperclass
@Getter @Setter
public class BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Integer id;

    @CreationTimestamp
    @ColumnDefault("CURRENT_TIMESTAMP")
    protected Date createdAt;

    @UpdateTimestamp
    @ColumnDefault("CURRENT_TIMESTAMP")
    protected Date updatedAt;
}

Xin chào, tại sao chúng ta cần protected Integer id;như protectedtrong lớp cha mẹ nói chung, vì tôi không thể sử dụng nó trong các trường hợp thử nghiệm của mình như.getId()
shareef

2

Một cách tiếp cận tốt là có một lớp cơ sở chung cho tất cả các thực thể của bạn. Trong lớp cơ sở này, bạn có thể có thuộc tính id nếu nó thường được đặt tên trong tất cả các thực thể của bạn (một thiết kế chung), sáng tạo và thuộc tính ngày cập nhật cuối cùng của bạn.

Đối với ngày tạo, bạn chỉ cần giữ một thuộc tính java.util.Date . Hãy chắc chắn, để luôn khởi tạo nó với Ngày mới () .

Đối với trường cập nhật mới nhất, bạn có thể sử dụng thuộc tính Dấu thời gian, bạn cần ánh xạ nó với @Version. Với chú thích này, tài sản sẽ được cập nhật tự động bởi Hibernate. Coi chừng Hibernate cũng sẽ áp dụng khóa lạc quan (đó là một điều tốt).


2
sử dụng cột dấu thời gian để khóa lạc quan là một ý tưởng tồi. Luôn sử dụng cột phiên bản số nguyên. Lý do là, 2 JVM có thể ở các thời điểm khác nhau và có thể không có độ chính xác đến mili giây. Thay vào đó, nếu bạn thực hiện chế độ ngủ đông, hãy sử dụng dấu thời gian DB, điều đó có nghĩa là các lựa chọn bổ sung từ DB. Thay vào đó chỉ sử dụng số phiên bản.
sethu

2

Chỉ để củng cố: java.util.Calenderkhông dành cho Dấu thời gian . java.util.Datelà trong một thời điểm, bất khả tri về những thứ trong khu vực như múi giờ. Hầu hết các cơ sở dữ liệu lưu trữ những thứ theo kiểu này (ngay cả khi chúng không xuất hiện; đây thường là cài đặt múi giờ trong phần mềm máy khách; dữ liệu tốt)


1

Là kiểu dữ liệu trong JAVA, tôi thực sự khuyên bạn nên sử dụng java.util.Date. Tôi gặp phải vấn đề múi giờ khá khó chịu khi sử dụng Lịch. Xem chủ đề này .

Để đặt dấu thời gian, tôi khuyên bạn nên sử dụng phương pháp AOP hoặc đơn giản là bạn có thể sử dụng Triggers trên bàn (thực ra đây là điều duy nhất tôi từng thấy việc sử dụng kích hoạt có thể chấp nhận được).


1

Bạn có thể xem xét việc lưu trữ thời gian dưới dạng DateTime và trong UTC. Tôi thường sử dụng DateTime thay vì Dấu thời gian vì thực tế là MySql chuyển đổi ngày thành UTC và quay lại giờ địa phương khi lưu trữ và truy xuất dữ liệu. Tôi muốn giữ bất kỳ loại logic nào ở một nơi (Lớp nghiệp vụ). Tôi chắc chắn có những tình huống khác khi sử dụng Timestamp là tốt hơn.


1

Chúng tôi đã có một tình huống tương tự. Chúng tôi đã sử dụng Mysql 5.7.

CREATE TABLE my_table (
        ...
      updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    );

Điều này làm việc cho chúng tôi.


Nó cũng hoạt động trong trường hợp khi dữ liệu được sửa đổi bằng truy vấn SQL trực tiếp trong cơ sở dữ liệu. @PrePersist@PrePersistkhông bao gồm một trường hợp như vậy.
pidabrow

1

Nếu chúng tôi đang sử dụng @Transactional trong các phương thức của mình, @CreationTimestamp và @UpdateTimestamp sẽ lưu giá trị trong DB nhưng sẽ trả về null sau khi sử dụng save (...).

Trong tình huống này, sử dụng saveAndFlush (...) đã thực hiện thủ thuật


0

Tôi nghĩ rằng nó gọn gàng hơn không làm điều này trong mã Java, bạn chỉ cần đặt giá trị mặc định của cột trong định nghĩa bảng MySql. nhập mô tả hình ảnh ở đây

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.