วิธีจัดเก็บวันที่ / เวลาและการประทับเวลาในเขตเวลา UTC ด้วย JPA และ Hibernate


106

ฉันจะกำหนดค่า JPA / Hibernate ให้จัดเก็บวันที่ / เวลาในฐานข้อมูลเป็นเขตเวลา UTC (GMT) ได้อย่างไร พิจารณาเอนทิตี JPA ที่มีคำอธิบายประกอบนี้:

public class Event {
    @Id
    public int id;

    @Temporal(TemporalType.TIMESTAMP)
    public java.util.Date date;
}

ถ้าเป็นวันที่ 2008-Feb-03 09:30 น. เวลามาตรฐานแปซิฟิก (PST) ฉันต้องการให้เวลา UTC ของปี 2008-Feb-03 17:30 น. เก็บไว้ในฐานข้อมูล ในทำนองเดียวกันเมื่อเรียกข้อมูลวันที่จากฐานข้อมูลฉันต้องการให้ตีความเป็น UTC ดังนั้นในกรณีนี้ 530pm คือ 530pm UTC เมื่อแสดงผลจะมีรูปแบบเป็น 9:30 น. PST


1
คำตอบของ Vlad Mihalceaให้คำตอบที่ปรับปรุงแล้ว (สำหรับ Hibernate 5.2+)
Dinei

คำตอบ:


74

ด้วย Hibernate 5.2 ตอนนี้คุณสามารถบังคับเขตเวลา UTC โดยใช้คุณสมบัติการกำหนดค่าต่อไปนี้:

<property name="hibernate.jdbc.time_zone" value="UTC"/>

สำหรับรายละเอียดเพิ่มเติมโปรดดูบทความนี้


19
ฉันเขียนข้อความนั้นด้วย: D ตอนนี้เดาว่าใครเพิ่มการรองรับฟีเจอร์นี้ใน Hibernate?
Vlad Mihalcea

โอ้ตอนนี้ฉันรู้แล้วว่าชื่อและรูปภาพเหมือนกันในโปรไฟล์ของคุณและบทความเหล่านั้น ... Good job Vlad :)
Dinei

@VladMihalcea ถ้าเป็น Mysql ต้องบอกให้ MySql ใช้เขตเวลาโดยใช้useTimezone=trueในสตริงการเชื่อมต่อ จากนั้นการตั้งค่าคุณสมบัติเท่านั้นจึงhibernate.jdbc.time_zoneจะทำงาน
TheCoder

ที่จริงคุณต้องตั้งค่าuseLegacyDatetimeCodeเป็นเท็จ
Vlad Mihalcea

2
hibernate.jdbc.time_zone ดูเหมือนจะถูกละเว้นหรือไม่มีผลเมื่อใช้กับ PostgreSQL
Alex R

48

เพื่อความรู้ที่ดีที่สุดของฉันคุณต้องใส่แอป Java ทั้งหมดของคุณในเขตเวลา UTC (เพื่อให้ Hibernate เก็บวันที่ใน UTC) และคุณจะต้องแปลงเป็นเขตเวลาที่ต้องการเมื่อคุณแสดงสิ่งต่างๆ (อย่างน้อยเราก็ทำ ทางนี้).

เมื่อเริ่มต้นเราทำ:

TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));

และตั้งค่าเขตเวลาที่ต้องการเป็น DateFormat:

fmt.setTimeZone(TimeZone.getTimeZone("Europe/Budapest"))

5
mitchnull โซลูชันของคุณจะไม่ทำงานในทุกกรณีเนื่องจาก Hibernate มอบหมายการตั้งค่าวันที่ให้กับไดรเวอร์ JDBC และไดรเวอร์ JDBC แต่ละตัวจัดการวันที่และโซนเวลาต่างกัน ดูstackoverflow.com/questions/4123534/… .
Derek Mahar

2
แต่ถ้าฉันเริ่มแอพของฉันโดยแจ้งไปยังคุณสมบัติ JVM "-Duser.timezone = + 00: 00" พฤติกรรมไม่เหมือนกันหรือไม่
rafa.ferreira

8
เท่าที่ฉันสามารถบอกได้มันจะทำงานในทุกกรณียกเว้นเมื่อ JVM และเซิร์ฟเวอร์ฐานข้อมูลอยู่ในเขตเวลาที่ต่างกัน
เชน

stevekuo และ @mitchnull ดูวิธีการแก้ปัญหา divestoclimb ด้านล่างซึ่งดีกว่ามากและการป้องกันผลข้างเคียงstackoverflow.com/a/3430957/233906
Cerber

HibernateJPA รองรับคำอธิบายประกอบ "@Factory" และ "@Externalizer" หรือไม่นี่เป็นวิธีจัดการ datetime utc ในไลบรารี OpenJPA stackoverflow.com/questions/10819862/…
ใคร

44

ไฮเบอร์เนตไม่รู้เรื่องเขตเวลาใน Dates (เพราะไม่มี) แต่จริงๆแล้วมันคือเลเยอร์ JDBC ที่ทำให้เกิดปัญหา ResultSet.getTimestampและPreparedStatement.setTimestampทั้งสองพูดในเอกสารว่าพวกเขาแปลงวันที่เป็น / จากเขตเวลา JVM ปัจจุบันตามค่าเริ่มต้นเมื่ออ่านและเขียนจาก / ไปยังฐานข้อมูล

ฉันคิดวิธีแก้ปัญหานี้ใน Hibernate 3.5 โดยคลาสย่อยorg.hibernate.type.TimestampTypeที่บังคับให้เมธอด JDBC เหล่านี้ใช้ UTC แทนโซนเวลาท้องถิ่น:

public class UtcTimestampType extends TimestampType {

    private static final long serialVersionUID = 8088663383676984635L;

    private static final TimeZone UTC = TimeZone.getTimeZone("UTC");

    @Override
    public Object get(ResultSet rs, String name) throws SQLException {
        return rs.getTimestamp(name, Calendar.getInstance(UTC));
    }

    @Override
    public void set(PreparedStatement st, Object value, int index) throws SQLException {
        Timestamp ts;
        if(value instanceof Timestamp) {
            ts = (Timestamp) value;
        } else {
            ts = new Timestamp(((java.util.Date) value).getTime());
        }
        st.setTimestamp(index, ts, Calendar.getInstance(UTC));
    }
}

สิ่งเดียวกันควรทำเพื่อแก้ไข TimeType และ DateType หากคุณใช้ประเภทเหล่านั้น ข้อเสียคือคุณจะต้องระบุด้วยตนเองว่าจะใช้ประเภทเหล่านี้แทนค่าเริ่มต้นในทุกฟิลด์วันที่ใน POJO ของคุณ (และยังทำลายความเข้ากันได้ของ JPA ที่แท้จริง) เว้นแต่จะมีใครรู้วิธีการแทนที่ทั่วไปมากกว่า

อัปเดต: ไฮเบอร์เนต 3.6 ได้เปลี่ยนประเภท API ใน 3.6 ฉันเขียนคลาส UtcTimestampTypeDescriptor เพื่อใช้สิ่งนี้

public class UtcTimestampTypeDescriptor extends TimestampTypeDescriptor {
    public static final UtcTimestampTypeDescriptor INSTANCE = new UtcTimestampTypeDescriptor();

    private static final TimeZone UTC = TimeZone.getTimeZone("UTC");

    public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicBinder<X>( javaTypeDescriptor, this ) {
            @Override
            protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
                st.setTimestamp( index, javaTypeDescriptor.unwrap( value, Timestamp.class, options ), Calendar.getInstance(UTC) );
            }
        };
    }

    public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicExtractor<X>( javaTypeDescriptor, this ) {
            @Override
            protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
                return javaTypeDescriptor.wrap( rs.getTimestamp( name, Calendar.getInstance(UTC) ), options );
            }
        };
    }
}

ตอนนี้เมื่อแอปเริ่มทำงานหากคุณตั้งค่า TimestampTypeDescriptor.INSTANCE เป็นอินสแตนซ์ของ UtcTimestampTypeDescriptor การประทับเวลาทั้งหมดจะถูกจัดเก็บและถือว่าเป็น UTC โดยไม่ต้องเปลี่ยนคำอธิบายประกอบบน POJO [ฉันยังไม่ได้ทดสอบ]


3
คุณบอกให้ไฮเบอร์เนตใช้แบบกำหนดเองของคุณได้UtcTimestampTypeอย่างไร?
Derek Mahar

divestoclimb กับ Hibernate เวอร์ชันใดที่UtcTimestampTypeเข้ากันได้
Derek Mahar

2
"ResultSet.getTimestamp และ PreparedStatement.setTimestamp ทั้งสองกล่าวในเอกสารว่าพวกเขาแปลงวันที่เป็น / จากเขตเวลา JVM ปัจจุบันตามค่าเริ่มต้นเมื่ออ่านและเขียนจาก / ไปยังฐานข้อมูล" คุณมีข้อมูลอ้างอิงหรือไม่? ฉันไม่เห็นการกล่าวถึงสิ่งนี้ใน Java 6 Javadocs สำหรับวิธีการเหล่านี้ ตามstackoverflow.com/questions/4123534/…วิธีการเหล่านี้ใช้โซนเวลากับไดรเวอร์ JDBC ที่กำหนดDateหรือTimestampขึ้นอยู่กับ
Derek Mahar

1
ฉันสาบานได้ว่าฉันอ่านเกี่ยวกับการใช้เขตเวลา JVM เมื่อปีที่แล้ว แต่ตอนนี้ฉันหาไม่เจอ ฉันอาจพบมันในเอกสารสำหรับไดรเวอร์ JDBC เฉพาะและเป็นแบบทั่วไป
divestoclimb

2
เพื่อให้ตัวอย่างเวอร์ชัน 3.6 ของคุณใช้งานได้ฉันต้องสร้างประเภทใหม่ซึ่งโดยพื้นฐานแล้วจะเป็น wrapper รอบ ๆ TimeStampType จากนั้นตั้งค่าประเภทนั้นบนฟิลด์
Shaun Stone

17

ด้วย Spring Boot JPA ให้ใช้รหัสด้านล่างในไฟล์ application.properties ของคุณและเห็นได้ชัดว่าคุณสามารถแก้ไขเขตเวลาตามที่คุณต้องการได้

spring.jpa.properties.hibernate.jdbc.time_zone = UTC

จากนั้นในไฟล์คลาสเอนทิตีของคุณ

@Column
private LocalDateTime created;

11

การเพิ่มคำตอบที่สมบูรณ์อยู่บนพื้นฐานและเป็นหนี้บุญคุณ divestoclimb กับคำใบ้จากฌอนหิน แค่อยากจะอธิบายโดยละเอียดเนื่องจากเป็นปัญหาที่พบบ่อยและวิธีแก้ปัญหาค่อนข้างสับสน

นี่คือการใช้ Hibernate 4.1.4 สุดท้ายแม้ว่าฉันจะสงสัยว่ามีอะไรหลังจาก 3.6 จะได้ผล

ขั้นแรกให้สร้าง UtcTimestampTypeDescriptor ของ divestoclimb

public class UtcTimestampTypeDescriptor extends TimestampTypeDescriptor {
    public static final UtcTimestampTypeDescriptor INSTANCE = new UtcTimestampTypeDescriptor();

    private static final TimeZone UTC = TimeZone.getTimeZone("UTC");

    public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicBinder<X>( javaTypeDescriptor, this ) {
            @Override
            protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
                st.setTimestamp( index, javaTypeDescriptor.unwrap( value, Timestamp.class, options ), Calendar.getInstance(UTC) );
            }
        };
    }

    public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicExtractor<X>( javaTypeDescriptor, this ) {
            @Override
            protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
                return javaTypeDescriptor.wrap( rs.getTimestamp( name, Calendar.getInstance(UTC) ), options );
            }
        };
    }
}

จากนั้นสร้าง UtcTimestampType ซึ่งใช้ UtcTimestampTypeDescriptor แทน TimestampTypeDescriptor เป็น SqlTypeDescriptor ในการเรียก super constructor แต่จะมอบหมายทุกอย่างให้กับ TimestampType:

public class UtcTimestampType
        extends AbstractSingleColumnStandardBasicType<Date>
        implements VersionType<Date>, LiteralType<Date> {
    public static final UtcTimestampType INSTANCE = new UtcTimestampType();

    public UtcTimestampType() {
        super( UtcTimestampTypeDescriptor.INSTANCE, JdbcTimestampTypeDescriptor.INSTANCE );
    }

    public String getName() {
        return TimestampType.INSTANCE.getName();
    }

    @Override
    public String[] getRegistrationKeys() {
        return TimestampType.INSTANCE.getRegistrationKeys();
    }

    public Date next(Date current, SessionImplementor session) {
        return TimestampType.INSTANCE.next(current, session);
    }

    public Date seed(SessionImplementor session) {
        return TimestampType.INSTANCE.seed(session);
    }

    public Comparator<Date> getComparator() {
        return TimestampType.INSTANCE.getComparator();        
    }

    public String objectToSQLString(Date value, Dialect dialect) throws Exception {
        return TimestampType.INSTANCE.objectToSQLString(value, dialect);
    }

    public Date fromStringValue(String xml) throws HibernateException {
        return TimestampType.INSTANCE.fromStringValue(xml);
    }
}

สุดท้ายเมื่อคุณเริ่มต้นการกำหนดค่าไฮเบอร์เนตของคุณให้ลงทะเบียน UtcTimestampType เป็นประเภทแทนที่:

configuration.registerTypeOverride(new UtcTimestampType());

ตอนนี้การประทับเวลาไม่ควรเกี่ยวข้องกับเขตเวลาของ JVM ระหว่างทางเข้าและออกจากฐานข้อมูล HTH.


6
จะเป็นการดีที่จะเห็นโซลูชันสำหรับ JPA และ Spring Configuration
Aubergine

2
หมายเหตุเกี่ยวกับการใช้แนวทางนี้กับการสืบค้นแบบเนทีฟภายใน Hibernate หากต้องการใช้ชนิดที่ถูกแทนที่เหล่านี้คุณต้องตั้งค่าด้วย query.setParameter (int pos, Object value) ไม่ใช่ query.setParameter (int pos, Date value, TemporalType temporalType) หากคุณใช้รุ่นหลัง Hibernate จะใช้การใช้งานประเภทดั้งเดิมเนื่องจากมีการเข้ารหัสอย่างหนัก
ไนเจล

ฉันควรเรียกคำสั่ง configuration.registerTypeOverride ที่ไหน (ใหม่ UtcTimestampType ()); เหรอ?
Stony

@Stony ทุกที่ที่คุณเริ่มต้นการกำหนดค่าไฮเบอร์เนตของคุณ หากคุณมี HibernateUtil (ส่วนใหญ่ทำ) มันจะอยู่ในนั้น
เชน

1
มันใช้งานได้ แต่หลังจากตรวจสอบแล้วฉันตระหนักว่าไม่จำเป็นสำหรับเซิร์ฟเวอร์ postgres ที่ทำงานในเขตเวลา = "UTC" และการประทับเวลาเริ่มต้นทุกประเภทเป็น "การประทับเวลาที่มีเขตเวลา" (จะดำเนินการโดยอัตโนมัติ) แต่รุ่นที่นี่ได้รับการแก้ไขสำหรับ Hibernate 4.3.5 GA เป็นชั้นเดียวที่สมบูรณ์และ overridding ถั่วโรงงานฤดูใบไม้ผลิpastebin.com/tT4ACXn6
Lukasz Frankowski

10

คุณคิดว่าปัญหาทั่วไปนี้จะได้รับการดูแลโดย Hibernate แต่ไม่ใช่! มี "แฮ็ก" เล็กน้อยเพื่อทำให้ถูกต้อง

สิ่งที่ฉันใช้คือการจัดเก็บ Date เป็น Long ไว้ในฐานข้อมูล ดังนั้นฉันมักจะทำงานกับมิลลิวินาทีหลังจาก 1/1/70 จากนั้นฉันก็มี getters และ setters ในชั้นเรียนของฉันที่ส่งคืน / รับเฉพาะ Dates เท่านั้น ดังนั้น API ยังคงเหมือนเดิม ข้อเสียคือฉันมีความปรารถนาในฐานข้อมูล ดังนั้นด้วย SQL ฉันทำได้แค่ <,>, = การเปรียบเทียบ - ไม่ใช่ตัวดำเนินการวันที่ที่หรูหรา

อีกวิธีหนึ่งคือการใช้ประเภทการแมปที่กำหนดเองตามที่อธิบายไว้ที่นี่: http://www.hibernate.org/100.html

ฉันคิดว่าวิธีที่ถูกต้องในการจัดการกับสิ่งนี้คือการใช้ปฏิทินแทนวันที่ ด้วยปฏิทินคุณสามารถตั้งค่าเขตเวลาก่อนที่จะดำเนินการต่อได้

หมายเหตุ: Silly stackoverflow จะไม่ให้ฉันแสดงความคิดเห็นดังนั้นนี่คือคำตอบของ david a.

หากคุณสร้างวัตถุนี้ในชิคาโก:

new Date(0);

ไฮเบอร์เนตยังคงเป็น "31/12/1969 18:00:00" วันที่ไม่ควรอยู่ในเขตเวลาดังนั้นฉันไม่แน่ใจว่าทำไมจึงต้องทำการปรับเปลี่ยน


1
น่าอายจริงๆเรา! คุณคิดถูกและลิงค์จากโพสต์ของคุณอธิบายได้ดี ตอนนี้ฉันเดาว่าคำตอบของฉันสมควรได้รับชื่อเสียงในแง่ลบ :)
david A.

1
ไม่ใช่เลย. คุณสนับสนุนให้ฉันโพสต์ตัวอย่างที่ชัดเจนมากว่าเหตุใดจึงเป็นปัญหา
codefinger

4
ฉันสามารถคงเวลาได้อย่างถูกต้องโดยใช้วัตถุปฏิทินเพื่อให้เก็บไว้ในฐานข้อมูลเป็น UTC ตามที่คุณแนะนำ อย่างไรก็ตามเมื่ออ่านเอนทิตีที่ยังคงอยู่กลับจากฐานข้อมูลไฮเบอร์เนตจะถือว่าอยู่ในเขตเวลาท้องถิ่นและวัตถุปฏิทินไม่ถูกต้อง!
John K

1
John K เพื่อแก้ไขปัญหาการCalendarอ่านนี้ฉันคิดว่า Hibernate หรือ JPA ควรมีวิธีการระบุสำหรับการทำแผนที่แต่ละครั้งเขตเวลาที่ Hibernate ควรแปลวันที่ที่อ่านและเขียนลงในTIMESTAMPคอลัมน์
Derek Mahar

joekutner หลังจากอ่านstackoverflow.com/questions/4123534/…ฉันมาแบ่งปันความคิดเห็นของคุณว่าเราควรจัดเก็บมิลลิวินาทีตั้งแต่ยุคในฐานข้อมูลแทนที่จะเป็นTimestampเพราะเราไม่จำเป็นต้องเชื่อถือไดรเวอร์ JDBC ในการจัดเก็บวันที่เป็น เราคาดหวัง
Derek Mahar

8

มีหลายเขตเวลาที่ใช้งานอยู่ที่นี่:

  1. คลาส Date ของ Java (util และ sql) ซึ่งมีเขตเวลาโดยปริยายของ UTC
  2. เขตเวลาที่ JVM ของคุณกำลังทำงานอยู่และ
  3. เขตเวลาเริ่มต้นของเซิร์ฟเวอร์ฐานข้อมูลของคุณ

สิ่งเหล่านี้อาจแตกต่างกัน Hibernate / JPA มีข้อบกพร่องในการออกแบบที่รุนแรงซึ่งผู้ใช้ไม่สามารถมั่นใจได้อย่างง่ายดายว่าข้อมูลเขตเวลาจะถูกเก็บรักษาไว้ในเซิร์ฟเวอร์ฐานข้อมูล (ซึ่งช่วยให้สามารถสร้างเวลาและวันที่ที่ถูกต้องใน JVM ใหม่ได้)

หากไม่มีความสามารถในการจัดเก็บเขตเวลา (อย่างง่ายดาย) โดยใช้ JPA / Hibernate ข้อมูลจะหายไปและเมื่อข้อมูลสูญหายจะมีราคาแพงในการสร้าง (ถ้าเป็นไปได้)

ฉันขอยืนยันว่าควรจัดเก็บข้อมูลเขตเวลาไว้เสมอ (ควรเป็นค่าเริ่มต้น) และผู้ใช้ควรมีความสามารถเสริมในการเพิ่มประสิทธิภาพเขตเวลาออกไป (แม้ว่าจะมีผลต่อการแสดงผลจริงๆ แต่ก็ยังมีเขตเวลาโดยนัยในวันที่ใด ๆ )

ขออภัยโพสต์นี้ไม่มีวิธีแก้ปัญหา (มีคำตอบที่อื่น) แต่เป็นการหาเหตุผลว่าทำไมการจัดเก็บข้อมูลเขตเวลารอบ ๆ จึงมีความสำคัญ น่าเสียดายที่ดูเหมือนว่านักวิทยาศาสตร์คอมพิวเตอร์และผู้ปฏิบัติงานด้านการเขียนโปรแกรมหลายคนโต้แย้งความต้องการเขตเวลาเพียงเพราะพวกเขาไม่เห็นคุณค่าของมุมมอง "การสูญเสียข้อมูล" และวิธีที่ทำให้สิ่งต่างๆเช่นการทำให้เป็นสากลเป็นเรื่องยากมากซึ่งเป็นสิ่งสำคัญมากในทุกวันนี้เมื่อมีเว็บไซต์ที่เข้าถึงได้โดย ลูกค้าและผู้คนในองค์กรของคุณขณะที่พวกเขาย้ายไปทั่วโลก


1
"Hibernate / JPA มีข้อบกพร่องด้านการออกแบบอย่างรุนแรง" ฉันจะบอกว่ามันเป็นข้อบกพร่องใน SQL ซึ่งโดยปกติแล้วจะอนุญาตให้มีการระบุเขตเวลาโดยปริยายและอาจเป็นอะไรก็ได้ SQL โง่ ๆ
Raedwald

3
จริงๆแล้วแทนที่จะเก็บเขตเวลาไว้เสมอคุณยังสามารถสร้างมาตรฐานในเขตเวลาเดียว (โดยปกติคือ UTC) และแปลงทุกอย่างเป็นเขตเวลานี้เมื่อยังคงอยู่ (และย้อนกลับไปเมื่ออ่าน) นี่คือสิ่งที่เรามักจะทำ อย่างไรก็ตาม JDBC ไม่สนับสนุนสิ่งนั้นโดยตรง: - /.
sleske

3

โปรดดูโครงการของฉันใน Sourceforge ซึ่งมีประเภทผู้ใช้สำหรับประเภทวันที่และเวลาของ SQL มาตรฐานเช่นเดียวกับ JSR 310 และ Joda Time ทุกประเภทพยายามแก้ไขปัญหาการหักล้าง ดูhttp://sourceforge.net/projects/usertype/

แก้ไข: เพื่อตอบคำถามของ Derek Mahar ที่แนบมากับความคิดเห็นนี้:

"คริสประเภทผู้ใช้ของคุณทำงานกับ Hibernate 3 ขึ้นไปได้หรือไม่ - Derek Mahar 7 พ.ย. 53 เวลา 12:30 น."

ใช่ประเภทเหล่านี้รองรับเวอร์ชัน Hibernate 3.x รวมถึง Hibernate 3.6


2

วันที่ไม่อยู่ในเขตเวลาใด ๆ (เป็นหน่วยงานมิลลิวินาทีจากช่วงเวลาที่กำหนดในเวลาเดียวกันสำหรับทุกคน) แต่ฐานข้อมูลพื้นฐาน (R) โดยทั่วไปจะจัดเก็บการประทับเวลาในรูปแบบการเมือง (ปีเดือนวันชั่วโมงนาทีวินาที) .. ) ที่มีความอ่อนไหวต่อเขตเวลา

เพื่อความจริงจัง Hibernate ต้องได้รับอนุญาตให้บอกภายในรูปแบบการทำแผนที่บางรูปแบบว่าวันที่ DB อยู่ในเขตเวลาดังกล่าวและเวลาดังกล่าวเพื่อที่เมื่อโหลดหรือจัดเก็บจะไม่ถือว่าเป็นของตัวเอง ...


1

ฉันพบปัญหาเดียวกันเมื่อฉันต้องการจัดเก็บวันที่ใน DB เป็น UTC และหลีกเลี่ยงการใช้varcharและโจ่งแจ้งString <-> java.util.Dateการแปลงหรือตั้งค่าแอป Java ทั้งหมดของฉันในเขตเวลา UTC (เนื่องจากอาจนำไปสู่ปัญหาที่ไม่คาดคิดอื่น ๆ หาก JVM เป็น ใช้ร่วมกันในหลายแอปพลิเคชัน)

ดังนั้นจึงมีโครงการโอเพ่นซอร์สDbAssistซึ่งช่วยให้คุณแก้ไขการอ่าน / เขียนเป็นวันที่ UTC จากฐานข้อมูลได้อย่างง่ายดาย เนื่องจากคุณใช้คำอธิบายประกอบ JPA เพื่อแมปฟิลด์ในเอนทิตีสิ่งที่คุณต้องทำคือรวมการอ้างอิงต่อไปนี้กับpomไฟล์Maven ของคุณ:

<dependency>
    <groupId>com.montrosesoftware</groupId>
    <artifactId>DbAssist-5.2.2</artifactId>
    <version>1.0-RELEASE</version>
</dependency>

จากนั้นคุณใช้การแก้ไข (สำหรับตัวอย่าง Hibernate + Spring Boot) โดยเพิ่ม@EnableAutoConfigurationคำอธิบายประกอบก่อนคลาสแอ็พพลิเคชัน Spring สำหรับคำแนะนำในการติดตั้งการตั้งค่าอื่น ๆ และตัวอย่างการใช้งานเพิ่มเติมโปรดดูที่githubของโปรเจ็กต์ต์

สิ่งที่ดีคือคุณไม่ต้องแก้ไขเอนทิตีเลย คุณสามารถออกจากjava.util.Dateสนามได้ตามที่เป็นอยู่

5.2.2จะต้องสอดคล้องกับเวอร์ชันไฮเบอร์เนตที่คุณใช้อยู่ ฉันไม่แน่ใจว่าคุณใช้เวอร์ชันใดในโปรเจ็กต์ของคุณ แต่รายการการแก้ไขที่ให้มาทั้งหมดมีอยู่ในหน้าวิกิของgithubของโปรเจ็กต์ สาเหตุที่การแก้ไขแตกต่างกันสำหรับเวอร์ชัน Hibernate ต่างๆเป็นเพราะผู้สร้าง Hibernate เปลี่ยน API สองสามครั้งระหว่างการเผยแพร่

ภายในการแก้ไขใช้คำแนะนำจาก divestoclimb เชนและแหล่งข้อมูลอื่น ๆ UtcDateTypeไม่กี่คนในเพื่อที่จะสร้างที่กำหนดเอง จากนั้นจะจับคู่มาตรฐานjava.util.DateกับแบบกำหนดเองUtcDateTypeซึ่งจัดการการจัดการโซนเวลาที่จำเป็นทั้งหมด การแมปประเภททำได้โดยใช้@Typedefคำอธิบายประกอบในpackage-info.javaไฟล์ที่ให้มา

@TypeDef(name = "UtcDateType", defaultForType = Date.class, typeClass = UtcDateType.class),
package com.montrosesoftware.dbassist.types;

คุณสามารถค้นหาบทความได้ที่นี่ซึ่งจะอธิบายว่าเหตุใดจึงเกิดการกะเวลาดังกล่าวและมีแนวทางในการแก้ไขอย่างไร


1

ไฮเบอร์เนตไม่อนุญาตให้ระบุเขตเวลาด้วยคำอธิบายประกอบหรือวิธีอื่นใด หากคุณใช้ปฏิทินแทนวันที่คุณสามารถใช้วิธีแก้ปัญหาโดยใช้คุณสมบัติ HIbernate AccessType และใช้การแมปด้วยตัวคุณเอง โซลูชันขั้นสูงกว่าคือการใช้ UserType ที่กำหนดเองเพื่อจับคู่วันที่หรือปฏิทินของคุณ โซลูชันทั้งสองมีอธิบายไว้ในบล็อกโพสต์ของฉันที่นี่: http://www.joobik.com/2010/11/mapping-dates-and-time-zones-with.html

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.