Temporal information is very precious for all marketing purposes, as birthday e-mailing or several other reminders. JPA has specific annotation to handle this type - @Temporal.
Data Engineering Design Patterns
Looking for a book that defines and solves most common data engineering problems? I'm currently writing
one on that topic and the first chapters are already available in π
Early Release on the O'Reilly platform
I also help solve your data engineering problems π contact@waitingforcode.com π©
Through this article we'll explore 3 temporal types supported by JPA specification: DATE, TIME and TIMESTAMP. In the first part, we'll explain them before writing some examples in the second part.
@Temporal in JPA
@Temporal is an annotation destined to be used to represent datetime fields in JPA entities. Depending on database field type, @Temporal can be created as one of three available entries in javax.persistence.TemporalType enumeration :
- DATE: is destined to map as java.sql.Date, ie. as a simple date, without information about hour or minutes. So, even if value stored in database has supplementary information about time, with TemporalType.DATE, only date will be set to mapped Java field. Time values will be replaced by 0.
- TIME: maps database fields of time type as java.sql.Time. This mapping is translated by the absence of date informations of produced object. According to the Javadoc, date values shouldn't be accessed and manipulated because they're set to "zero epoch" - January 1, 1970. Only information about the time is real.
- TIMESTAMP: this type is the most informative because it contains as well date as the time. Even more, object mapped by this temporal type, java.sql.Timestamp, can hold the information about nanoseconds. This information is displayed only if it's saved in database. Otherwise 0 is returned.
Example of TemporalType.DATE
We'll begin our tests by DATE fields, mapped as below in JPA entity (only DATE-related fields and methods are shown):
@Entity @Table(name = "shopping_cart") public class ShoppingCart { private Date creationDate; @Temporal(DATE) @Column(name="create_date") public Date getCreationDate() { return this.creationDate; } public void setCreationDate(Date creationDate) { this.creationDate = creationDate; } }
And the test illustrating TemporalType.DATE specificities:
/** * Test cases for {@link javax.persistence.TemporalType} annotation. * * Expected tables before the tests : * <pre> * mysql> select * from `order`; * +----+------------------+---------+-----------+---------------------+ * | id | shopping_cart_id | user_id | state | updated_date | * +----+------------------+---------+-----------+---------------------+ * | 1 | 1 | 1 | CREATED | 2014-10-28 19:00:00 | * | 2 | 2 | 3 | CONFIRMED | 2014-10-28 19:15:00 | * +----+------------------+---------+-----------+---------------------+ * 2 rows in set (0.00 sec) * * mysql> select id, create_date, last_used_date, last_update_date from shopping_cart; * +----+---------------------+---------------------+---------------------+ * | id | create_date | last_used_date | last_update_date | * +----+---------------------+---------------------+---------------------+ * | 1 | 2014-10-28 10:43:31 | 2014-11-11 11:11:11 | 2014-10-28 10:43:31 | * | 2 | 2014-10-28 10:43:31 | 2014-11-11 11:11:11 | 2014-10-28 10:43:31 | * +----+---------------------+---------------------+---------------------+ * 2 rows in set (0.00 sec) * </pre> * * @author Bartosz Konieczny */ public class TemporalTypeTest extends AbstractJpaTester { @Test public void testDateTemporal() { Query query = entityManager.createQuery("SELECT sc FROM ShoppingCart sc WHERE id = :id"); query.setParameter("id", 1l); ShoppingCart shoppingCart = (ShoppingCart) query.getSingleResult(); assertEquals("Creation date was bad formatted", "2014-10-28", shoppingCart.getCreationDate().toString()); Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("CET")); cal.setTime(shoppingCart.getCreationDate()); assertTrue("TemporalType.DATE shouldn't contain time information", cal.getTime().toString().contains("00:00:00 CET")); } }
Example of TemporalType.DATETIME
TemporalType.DATETIME is also represented by ShoppingCart fields:
private Date lastUpdateDate; private Date lastUseDate; // represented as DATETIME column in database @Temporal(TIMESTAMP) @Column(name="last_update_date") public Date getLastUpdateDate() { return this.lastUpdateDate; } // represented as TIMESTAMP column in database @Temporal(TIMESTAMP) @Column(name="last_used_date") public Date getLastUseDate() { return this.lastUseDate; } public void setLastUpdateDate(Date lastUpdateDate) { this.lastUpdateDate = lastUpdateDate; } public void setLastUseDate(Date lastUseDate) { this.lastUseDate = lastUseDate; }
And some veryfications to illustrate DATETIME features are defined as:
@Test public void testDateTimeTemporal() { Query query = entityManager.createQuery("SELECT sc FROM ShoppingCart sc WHERE id = :id"); query.setParameter("id", 1l); ShoppingCart shoppingCart = (ShoppingCart) query.getSingleResult(); // result should be formatted in the same way for both, TIMESTAMP and DATETIME types assertEquals("Last updated date was bad formatted", "2014-10-28 10:43:31.0", shoppingCart.getLastUpdateDate().toString()); assertEquals("Last used date was bad formatted", "2014-11-11 11:11:11.0", shoppingCart.getLastUseDate().toString()); }
Example of TemporalType.TIME
The last examined type is TemporalType.TIME. It's used in OrderCrated entity to represent the time of order creation:
@Entity @Table(name = "`order`") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "state") public abstract class Order { protected Date lastUpdated; @Temporal(TemporalType.TIME) @Column(name = "updated_date") public Date getLastUpdated() { return this.lastUpdated; } public void setLastUpdated(Date lastUpdated) { this.lastUpdated = lastUpdated; } } @Entity @DiscriminatorValue("CREATED") public class OrderCreated extends Order { }
Tests for this case look like:
@Test public void testTimeTemporal() { Query query = entityManager.createQuery("SELECT oc FROM OrderCreated oc WHERE id = :id"); query.setParameter("id", 1l); OrderCreated order = (OrderCreated) query.getSingleResult(); assertEquals("Order last update time was bad formatted", "19:00:00", order.getLastUpdated().toString()); Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("CET")); cal.setTime(order.getLastUpdated()); assertEquals("TemporalType.TIME should be set by default to 'zero epoch'", "Thu Jan 01 19:00:00 CET 1970", cal.getTime().toString()); }
This time we described a little bit the world of datetime dimension in JPA-database relation. We saw that all of 3 available TemporalType fields reflect well data stored in database layer - when table stores only a time, mapped Java object will represent only a time. The same dependency is applied for date type. For more complete case, we can use DATETIME type which holds as well date as the time.