source

JPA 및 최대 절전 모드로 UTC 시간대에 날짜/시간 및 타임스탬프를 저장하는 방법

factcode 2023. 1. 19. 21:00
반응형

JPA 및 최대 절전 모드로 UTC 시간대에 날짜/시간 및 타임스탬프를 저장하는 방법

데이터베이스에 날짜/시간을 UTC(GMT) 시간대로 저장하도록 JPA/Hibernate를 설정하려면 어떻게 해야 합니까?주석이 달린 다음 JPA 엔티티를 고려합니다.

public class Event {
    @Id
    public int id;

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

날짜가 2008년 2월 3일 오전 9시 30분 태평양 표준시(PST)인 경우 UTC 시간 2008년 2월 3일 오후 5시 30분을 데이터베이스에 저장합니다.마찬가지로 데이터베이스에서 날짜를 검색할 때 UTC로 해석해 주셨으면 합니다.이 경우 530pm은 530pm UTC입니다.이 화면이 표시되면 PST 기준으로 오전 9시 30분 포맷됩니다.

부터, 을 「」5.2 「」, 「Da」, 「UTC」에 추가하는 것으로, 으로 설정할 수 .properties.xmlJPA:

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

Boot 를 하고 있는는, 이을 Spring Boot 에 합니다.application.properties 삭제:

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

내가 아는 한, Java 앱 전체를 UTC 시간대에 배치하고(Hibernate가 UTC로 날짜를 저장하도록), 원하는 시간대로 변환해야 합니다(적어도 이 방법으로 표시).

기동시에, 다음의 조작을 실시합니다.

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

원하는 시간대를 DateFormat으로 설정합니다.

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

휴지상태는 날짜의 시간대 정보를 인식하지 못하지만(날짜가 없기 때문에), 실제로는 JDBC 계층이 문제를 일으킵니다. ResultSet.getTimestamp ★★★★★★★★★★★★★★★★★」PreparedStatement.setTimestamp두 문서 모두 데이터베이스에서 읽고 쓸 때 기본적으로 현재 JVM 시간대로 또는 현재 JVM 시간대로 날짜를 변환한다고 합니다.

3.에서 Hibernate 3.5로 하위 하여 해결 방법을 .org.hibernate.type.TimestampType로컬 타임존이 아닌 UTC를 사용하도록 다음 JDBC 메서드를 강제합니다.

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의 모든 날짜 필드에 기본값 대신 이러한 유형을 사용하도록 수동으로 지정해야 한다는 것입니다.

업데이트: Hibernate 3.6에서는 API 유형이 변경되었습니다.3.6에서 UtcTimestamp 클래스를 작성했습니다.이를 구현하려면 Descriptor를 입력합니다.

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

이제 앱이 시작될 때 타임스탬프를 설정하면TypeDescriptor를 입력합니다.UtcTimestamp 인스턴스에 대한 인스턴스TypeDescriptor. 모든 타임스탬프가 저장되고 POJO의 주석을 변경할 필요 없이 UTC에 있는 것으로 취급됩니다.[아직 테스트하지 않았습니다]

Spring Boot JPA에서는 application.properties 파일에서 다음 코드를 사용하여 원하는 시간대를 변경할 수 있습니다.

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

그런 다음 엔티티 클래스 파일에서

@Column
private LocalDateTime created;

Shaun Stone에서 힌트를 얻어 완전히 disestoclib에 기반을 두고 빚진 답변을 추가한다.일반적인 문제이고 해결 방법이 좀 복잡하기 때문에 자세히 설명하려고 합니다.

이것은 Hibernate 4.1.4를 사용하고 있습니다.마지막으로, 3.6 이후로는 아무 것도 작동되지 않을 것 같습니다.

먼저 disestoclimb의 UtcTimestamp를 만듭니다.Type Descriptor

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

그런 다음 UtcTimestamp를 만듭니다.UtcTimestamp를 사용하는 유형타임스탬프 대신 TypeDescriptor슈퍼 컨스트럭터 호출에서 SQLTypeDescriptor로 TypeDescriptor를 입력하지만 그렇지 않으면 모든 것을 타임스탬프에 위임합니다.유형:

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

마지막으로 휴지 상태 구성을 초기화할 때 UtcTimestamp를 등록합니다.유형 재정의로 입력:

configuration.registerTypeOverride(new UtcTimestampType());

이제 타임스탬프는 데이터베이스로 송수신되는 JVM의 시간대와 관련이 없습니다.HTH

이 일반적인 문제는 Hibernate에 의해 해결된다고 생각할 수 있습니다.하지만 그렇지 않아요!제대로 하려면 몇 가지 "핵"이 있습니다.

제가 사용하는 것은 데이터베이스에 날짜를 긴 날짜로 저장하는 것입니다.따라서 항상 1/1/70 이후 밀리초 단위로 작업합니다.그런 다음 수업 시간에 날짜만 반환/수락하는 getter와 setter가 있습니다.따라서 API는 그대로 유지됩니다.단점은 데이터베이스에 longs가 있다는 것입니다.따라서 SQL에서는 <,>= 비교만 할 수 있으며, 복잡한 날짜 연산자는 할 수 없습니다.

또 다른 접근방식은 http://www.hibernate.org/100.html에서 설명하는 커스텀매핑 타입을 사용하는 것입니다.

이 문제를 해결하는 올바른 방법은 날짜 대신 달력을 사용하는 것이라고 생각합니다.Calendar를 사용하여 지속하기 전에 TimeZone을 설정할 수 있습니다.

메모: 어리석은 스택 오버플로우에서는 코멘트를 할 수 없습니다.따라서 여기 david a에 대한 답변이 있습니다.

시카고에서 이 개체를 만드는 경우:

new Date(0);

휴지 상태에서는, 「12/31/1969 18:00:00」라고 표시됩니다.날짜에는 시간대가 없어야 하는데, 왜 조정이 이루어지는지 모르겠습니다.

여기에서는, 몇개의 타임 존이 동작하고 있습니다.

  1. UTC의 암묵적인 시간대를 가진 Java의 날짜 클래스(util 및 sql)
  2. JVM이 실행되고 있는 시간대 및
  3. 데이터베이스 서버의 기본 표준 시간대입니다.

이 모든 것이 다를 수 있습니다.휴지 상태/JPA에는 심각한 설계상의 결함이 있습니다.사용자는 시간대 정보를 데이터베이스 서버에 쉽게 보존할 수 없습니다(이를 통해 JVM에서 정확한 시간과 날짜를 재구축할 수 있습니다).

JPA/Hibernate를 사용하여 시간대를 쉽게 저장할 수 없으면 정보가 손실되고 정보가 손실되면 구축 비용이 많이 듭니다(가능한 경우).

항상 시간대 정보를 저장하는 것이 좋습니다(기본값이 되어야 함). 그러면 사용자가 시간대를 최적화할 수 있는 옵션 기능을 가질 수 있습니다(표시에는 실제로 영향을 줄 뿐이지만 어떤 날짜에도 암묵적인 시간대가 존재합니다).

죄송합니다. 이 게시물에서는 회피책(다른 곳에서 답변)은 제시되지 않았지만, 왜 항상 시간대 정보를 주변에 저장하는 것이 중요한지에 대한 합리화입니다.불행히도 많은 컴퓨터 사이언티스트와 프로그래밍 실무자들은 단순히 '정보의 손실'이라는 관점을 이해하지 못하기 때문에 타임존의 필요성에 대해 반대하고 있는 것 같습니다.또, 오늘날에는, 클라이언트나 종업원이 액세스 할 수 있는 Web 사이트에 의해서, 국제화등의 일이 매우 어려워지고 있습니다.조직을 조직화하는 데 도움이 됩니다.

표준 SQL 날짜 및 시간 유형과 JSR 310 및 Joda Time 사용자 유형을 가진 Sourceforge 프로젝트를 살펴보시기 바랍니다.모든 유형이 오프셋 문제를 해결하려고 합니다.http://sourceforge.net/projects/usertype/ 를 참조해 주세요.

편집: 이 코멘트에 첨부된 Derek Mahar의 질문에 대한 답변:

Chris, 당신의 사용자 유형은 Hibernate 3 이상에서 작동합니까?– Derek Mahar 10년 11월 7일 12시 30분

네, 이 타입들은 Hibernate 3.6을 포함한 Hibernate 3.x 버전을 지원합니다.

날짜는 어느 시간대에도 없습니다(모든 사람이 같은 시간에 정의된 시간에서 밀리초 단위 사무실). 그러나 기본(R) DB는 일반적으로 시간대에 민감한 정치적 형식(년, 월, 일, 시간, 분, 초 등)으로 타임스탬프를 저장합니다.

진지하게 말하면, 최대 절전 모드는 DB 날짜가 특정 시간대에 있음을 매핑의 어떤 형태로든 알 수 있도록 허용해야 합니다. 따라서 DB 날짜는 로드 또는 저장될 때 자체 시간대로 간주되지 않습니다.

사용을 했습니다.varchar 명시적 ""String <-> java.util.Date변환 또는 Java 앱 전체를 UTC 시간대로 설정합니다(JVM이 여러 응용 프로그램에서 공유될 경우 예기치 않은 다른 문제가 발생할 수 있기 때문입니다).

.DbAssist이를 통해 데이터베이스에서 읽기/쓰기를 UTC 날짜로 쉽게 수정할 수 있습니다. JPA 의 Maven 를 Maven에 .pom 삭제:

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

다음 이+ 예제 이 수정은 "Dibernate + Spring Boot"를 합니다.@EnableAutoConfigurationSpring 응용 프로그램클래스 앞에 주석을 붙입니다.기타 설치 지침 및 기타 사용 예는 프로젝트의 github를 참조하십시오.

를 전혀가 없다는 입니다. 엔티티는 그대로 둘 수 .을 사용하다java.util.Date들판을 그대로 두어라.

5.2.2는, 사용중의 휴지 상태에 대응하고 있을 필요가 있습니다.프로젝트에서 어떤 버전을 사용하고 있는지 모르겠지만 제공된 수정의 전체 목록은 프로젝트 기트허브의 위키 페이지에서 확인할 수 있습니다.Hibernate 버전에 따라 수정이 다른 이유는 Hibernate 크리에이터가 출시 사이에 API를 여러 번 변경했기 때문입니다.

이 및 몇 disestoclimb, Shane을 .UtcDateType그런 다음 표준을 매핑합니다.java.util.DateUtcDateType이치노유형 매핑은 다음 방법을 사용하여 수행됩니다.@Typedef 기재되어 있는 package-info.javafilename을 클릭합니다.

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

이러한 시간 이동이 발생하는 이유와 이를 해결하기 위한 접근법에 대해 설명하는 기사는 여기에서 찾을 수 있습니다.

휴지 상태에서는 주석이나 다른 방법으로 시간대를 지정할 수 없습니다.날짜 대신 달력을 사용하는 경우 HIbernate 속성 액세스를 사용하여 해결 방법을 구현할 수 있습니다.직접 매핑을 입력하고 구현합니다.보다 고급 솔루션은 날짜 또는 달력을 매핑하는 사용자 정의 UserType을(사용자 정의 유형)을 구현하는 것입니다.두 솔루션 모두 블로그 투고에 설명되어 있습니다.http://www.joobik.com/2010/11/mapping-dates-and-time-zones-with.html

언급URL : https://stackoverflow.com/questions/508019/how-to-store-date-time-and-timestamps-in-utc-time-zone-with-jpa-and-hibernate

반응형