Skip to content

Commit

Permalink
Add support for mapping the Java Period object to a PostgreSQL interv…
Browse files Browse the repository at this point in the history
…al type #128

In PostgreSQL, an interval may comprise years, months, days, hours,
minutes and seconds. In Java, an interval is distinguished between
a temporal interval (Duration) and a quantity in terms of days, months
and years (Period).

For example `age(timestamp, timestamp)` returns an interval between two
timestamps in days, months and years. The result of this function should
as such be mapped to a Java Period instead of an Duration.

On the contrary, `timestamp - timestamp` returns an interval between two
timestamps in days, hours, minutes and seconds, for which the existing
Duration-based type is appropiate.
  • Loading branch information
jwgmeligmeyling authored and vladmihalcea committed Sep 14, 2019
1 parent 956747d commit 589e08d
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
2 changes: 2 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Version 2.7.0 - IN PROGRESS

Add support for JSON column values for Oracle #131

Add support for mapping the Java Period object to a PostgreSQL interval type #128

Add YearMonthTimestampType #127

Ability to use PostgreSQLEnumType and EnumArrayType with TypedParameterValue #125
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.vladmihalcea.hibernate.type.interval;

import com.vladmihalcea.hibernate.type.ImmutableType;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.postgresql.util.PGInterval;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.time.Duration;
import java.time.Period;

/**
* Maps a Java {@link Duration} object to a PostgreSQL Interval column type.
*
* @author Jan-Willem Gmelig Meyling
* @author Vlad Mihalcea
* @since 2.6.2
*/
public class PostgreSQLPeriodType extends ImmutableType<Period> {

public static final PostgreSQLPeriodType INSTANCE = new PostgreSQLPeriodType();

public PostgreSQLPeriodType() {
super(Period.class);
}

@Override
protected Period get(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws SQLException {
final PGInterval interval = (PGInterval) rs.getObject(names[0]);

if (interval == null) {
return null;
}

final int years = interval.getYears();
final int months = interval.getMonths();
final int days = interval.getDays();

return Period.ofYears(years)
.plusMonths(months)
.plusDays(days);
}

@Override
protected void set(PreparedStatement st, Period value, int index, SharedSessionContractImplementor session) throws SQLException {
if (value == null) {
st.setNull(index, Types.OTHER);
} else {
final int days = value.getDays();
final int months = value.getMonths();
final int years = value.getYears();
st.setObject(index, new PGInterval(years, months, days, 0, 0, 0));
}
}

@Override
public int[] sqlTypes() {
return new int[]{Types.OTHER};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.vladmihalcea.hibernate.type.interval;

import com.vladmihalcea.hibernate.type.model.BaseEntity;
import com.vladmihalcea.hibernate.type.util.AbstractPostgreSQLIntegrationTest;
import org.hibernate.annotations.TypeDef;
import org.junit.Test;

import javax.persistence.Column;
import javax.persistence.Entity;
import java.time.Period;

import static org.junit.Assert.assertEquals;

/**
* Tests for {@see PostgreSQLIntervalType} Hibernate type.
*
* @author Jan-Willem Gmelig Meyling
*/
public class PostgreSQLPeriodTypeTest extends AbstractPostgreSQLIntegrationTest {

@Override
protected Class<?>[] entities() {
return new Class[]{WorkShift.class};
}

@Test
public void test() {
Period duration = Period.of(1, 2, 3);

doInJPA(entityManager -> {
WorkShift intervalEntity = new WorkShift();
intervalEntity.setId(1L);
intervalEntity.setDuration(duration);

entityManager.persist(intervalEntity);
});

doInJPA(entityManager -> {
WorkShift result = entityManager.find(WorkShift.class, 1L);
assertEquals(duration, result.getDuration());
});
}

@Entity(name = "WorkShift")
@TypeDef(typeClass = PostgreSQLPeriodType.class, defaultForType = Period.class)
public static class WorkShift extends BaseEntity {

@Column(columnDefinition = "interval")
private Period duration;

public Period getDuration() {
return duration;
}

public void setDuration(Period duration) {
this.duration = duration;
}
}
}

0 comments on commit 589e08d

Please sign in to comment.