【问题标题】:How to map the PostgreSQL Interval column type in Hibernate?如何在 Hibernate 中映射 PostgreSQL Interval 列类型?
【发布时间】:2010-12-29 01:44:04
【问题描述】:

在PostgreSQL下,我使用PersistentDuration进行sql类型interval和duration之间的映射,但是不起作用。

另一位用户发现了同样的问题并使用了他自己的课程:

public void nullSafeSet(PreparedStatement statement, Object value, int index) 
        throws HibernateException, SQLException { 
    if (value == null) { 
        statement.setNull(index, Types.OTHER); 
    } else { 
        Long interval = ((Long) value).longValue(); 
        Long hours = interval / 3600; 
        Long minutes = (interval - (hours * 3600)) / 60; 
        Long secondes = interval - (hours * 3600) - minutes * 60; 
            statement.setString(index, "'"+ hours +":" 
                    + intervalFormat.format(minutes) + ":" 
                    + intervalFormat.format(secondes)+"'"); 

    } 
}

但它不适用于实际格式,因为它假设间隔模式只是 “hh:mm:ss”。事实并非如此:请参阅

这里有一些我需要从数据库中解析的真实例子:

1 天 00:29:42 00:29:42 1 周 00:29:42 1 周 2 天 00:29:42 1 个月 1 周 2 天 00:29:42 1 年 00:29:42 1个十年 00:29:42

http://www.postgresql.org/docs/current/interactive/datatype-datetime.html

你有干净的解决方案吗?

【问题讨论】:

    标签: postgresql hibernate intervals custom-type column-types


    【解决方案1】:

    您不必编写自己的 Hibernate 自定义类型来将 PostgreSQL interval 列映射到 Java Duration 对象。您需要做的就是使用Hibernate Ttypes 项目。

    所以,在添加适当的 Hibernate 依赖之后:

    <dependency>
        <groupId>com.vladmihalcea</groupId>
        <artifactId>hibernate-types-52</artifactId>
        <version>2.6.0</version>
    </dependency>
    

    你只需要使用@TypeDef注解来注册PostgreSQLIntervalType

    @Entity(name = "Book")
    @Table(name = "book")
    @TypeDef(
        typeClass = PostgreSQLIntervalType.class,
        defaultForType = Duration.class
    )
    @TypeDef(
        typeClass = YearMonthDateType.class,
        defaultForType = YearMonth.class
    )
    public class Book {
     
        @Id
        @GeneratedValue
        private Long id;
     
        @NaturalId
        private String isbn;
     
        private String title;
     
        @Column(
            name = "published_on",
            columnDefinition = "date"
        )
        private YearMonth publishedOn;
     
        @Column(
            name = "presale_period",
            columnDefinition = "interval"
        )
        private Duration presalePeriod;
     
        public Long getId() {
            return id;
        }
     
        public Book setId(Long id) {
            this.id = id;
            return this;
        }
     
        public String getIsbn() {
            return isbn;
        }
     
        public Book setIsbn(String isbn) {
            this.isbn = isbn;
            return this;
        }
     
        public String getTitle() {
            return title;
        }
     
        public Book setTitle(String title) {
            this.title = title;
            return this;
        }
     
        public YearMonth getPublishedOn() {
            return publishedOn;
        }
     
        public Book setPublishedOn(YearMonth publishedOn) {
            this.publishedOn = publishedOn;
            return this;
        }
     
        public Duration getPresalePeriod() {
            return presalePeriod;
        }
     
        public Book setPresalePeriod(Duration presalePeriod) {
            this.presalePeriod = presalePeriod;
            return this;
        }
    }
    

    现在,当持久化 Book 实体时:

    entityManager.persist(
        new Book()
            .setIsbn("978-9730228236")
            .setTitle("High-Performance Java Persistence")
            .setPublishedOn(YearMonth.of(2016, 10))
            .setPresalePeriod(
                Duration.between(
                    LocalDate
                        .of(2015, Month.NOVEMBER, 2)
                        .atStartOfDay(),
                    LocalDate
                        .of(2016, Month.AUGUST, 25)
                        .atStartOfDay()
                )
            )
    );
    

    Hibernate 将执行正确的 SQL INSERT 语句:

    INSERT INTO book (
        isbn,
        presale_period,
        published_on,
        title,
        id
    )
    VALUES (
        '978-9730228236',
        '0 years 0 mons 297 days 0 hours 0 mins 0.00 secs',
        '2016-10-01',
        'High-Performance Java Persistence',
        1
    )
    

    在获取Book实体时,我们可以看到Duration属性已从数据库中正确获取:

    Book book = entityManager
        .unwrap(Session.class)
        .bySimpleNaturalId(Book.class)
        .load("978-9730228236");
     
    assertEquals(
        Duration.between(
            LocalDate
                .of(2015, Month.NOVEMBER, 2)
                .atStartOfDay(),
            LocalDate
                .of(2016, Month.AUGUST, 25)
                .atStartOfDay()
        ),
        book.getPresalePeriod()
    );
    

    【讨论】:

      【解决方案2】:

      为什么不把它变成一个数字并映射到一个 Long 呢?

      SELECT EXTRACT(epoch FROM my_interval)
      

      【讨论】:

        【解决方案3】:

        这是适用于 JPA、Hibernate(带注释)的有效解决方案。

        这是实体类的开始(对于有间隔列的表):

        @Entity
        @Table(name="table_with_interval_col")
        @TypeDef(name="interval", typeClass = Interval.class)
        public class TableWithIntervalCol implements Serializable {
        

        这是间隔列:

        @Column(name = "interval_col",  nullable = false)
        @Type(type = "interval")    
        private Integer intervalCol;
        

        这是 Interval 类:

        package foo.bar.hibernate.type;
        
        import java.io.Serializable;
        import java.sql.PreparedStatement;
        import java.sql.ResultSet;
        import java.sql.SQLException;
        import java.sql.Types;
        import java.util.Date;
        
        import org.hibernate.HibernateException;
        import org.hibernate.usertype.UserType;
        import org.postgresql.util.PGInterval;
        
        
        /**
         * Postgres Interval type
         * 
         * @author bpgergo
         * 
         */
        public class Interval implements UserType {
            private static final int[] SQL_TYPES = { Types.OTHER };
        
            @Override
            public int[] sqlTypes() {
                return SQL_TYPES;
            }
        
            @SuppressWarnings("rawtypes")
            @Override
            public Class returnedClass() {
                return Integer.class;
            }
        
            @Override
            public boolean equals(Object x, Object y) throws HibernateException {
                return x.equals(y);
            }
        
            @Override
            public int hashCode(Object x) throws HibernateException {
                return x.hashCode();
            }
        
            @Override
            public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
                    throws HibernateException, SQLException {
                String interval = rs.getString(names[0]);
                if (rs.wasNull() || interval == null) {
                    return null;
                }
                PGInterval pgInterval = new PGInterval(interval);
                Date epoch = new Date(0l);
                pgInterval.add(epoch);
                return Integer.valueOf((int)epoch.getTime() / 1000);
            }
        
            public static String getInterval(int value){
                return new PGInterval(0, 0, 0, 0, 0, value).getValue();
            }
        
        
            @Override
            public void nullSafeSet(PreparedStatement st, Object value, int index)
                    throws HibernateException, SQLException {
                if (value == null) {
                    st.setNull(index, Types.VARCHAR);
                } else {
                    //this http://postgresql.1045698.n5.nabble.com/Inserting-Information-in-PostgreSQL-interval-td2175203.html#a2175205
                    st.setObject(index, getInterval(((Integer) value).intValue()), Types.OTHER);
                }
            }
        
            @Override
            public Object deepCopy(Object value) throws HibernateException {
                return value;
            }
        
            @Override
            public boolean isMutable() {
                return false;
            }
        
            @Override
            public Serializable disassemble(Object value) throws HibernateException {
                return (Serializable) value;
            }
        
            @Override
            public Object assemble(Serializable cached, Object owner)
                    throws HibernateException {
                return cached;
            }
        
            @Override
            public Object replace(Object original, Object target, Object owner)
                    throws HibernateException {
                return original;
            }
        
        }
        

        【讨论】:

        • @ankit,如果您create a new question 而不是在评论中提出您的问题,您将有更好的机会获得有用的答案
        • 在您的示例中,您要向intervalCol 发送什么数据?
        【解决方案4】:

        PostgreSQL 有一个 date_part / extract 函数,您可以使用它来返回不同的字段,epoch 就是其中之一。从间隔中提取纪元时,您会收到间隔中包含的秒数,然后您可以根据需要进行转换。我失去了使用 Hibernate 的经验,但你可以这样做:

        SELECT
            average_interval_between_airings
          , date_part('epoch', average_interval_between_airings) / 60 as minutes
          , date_part('epoch', average_interval_between_airings) as seconds
        FROM shows;
        

        【讨论】:

          【解决方案5】:

          根据链接,您应该有一天,然后是小时:分钟:秒。因此,假设您不需要超过 23 小时 59 分钟的时间间隔,请将代码更改为如下所示。

          statement.setString(index, "'0 "+ hours +":" 
          + intervalFormat.format(minutes) + ":" 
          + intervalFormat.format(secondes)+"'"); 
          

          我无法测试此代码,因为我没有安装 PostGreSql。有关同一问题的另一个讨论,请参见以下链接,尽管您必须修改提供的代码以处理秒数。不过这应该不是什么大问题。

          https://forum.hibernate.org/viewtopic.php?p=2348558&sid=95488ce561e7efec8a2950f76ae4741c

          【讨论】:

          • 不,必须有一天。而且不仅有关键工作天(年、月、周,.. 见上面的链接)这里有一些来自数据库的真实示例:-1 day 00 :29:42 -00:29:42 -1 周 00:29:42 1 周 2 天 00:29:42 1 个月 1 周 2 天 00:29:42 -1 年 00:29:42 1 个十年 00: 29:42
          猜你喜欢
          • 2023-03-24
          • 2012-12-23
          • 2015-03-09
          • 2011-06-26
          • 2011-02-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多