Temporal
Databases support temporal data types such as DATE (calendar date), TIME (clock time), and DATETIME (combined date and time).
Practice Exercise¶
MySQL also has
TIMESTAMP, which is Unix Epoch time (seconds since 01/01/1970).DATETIMEis by far the better choice.
Map these to the appropriate java.time classes such as LocalDate, LocalTime, and LocalDateTime.
@Column(name="create_date")
private LocalDateTime createDate;
Practice Exercise¶
JPA 2.1 was released before Java 8, so it didn’t support
java.time.LocalTimeorjava.time.LocalDate.The spec for JPA 2.2 was released August 2017 and included support for the Java8 time API. Hibernate 5.3 included full integration of JPA 2.2.
Prior to this, all database temporal data had to be mapped to
java.util.Dateor its subclassjava.sql.Date,java.util.Calendar, etc. and required you annotatejava.util.Dateorjava.util.Calendarfields with the@Temporalannotation, and specify a value from theTemporalTypeenumeration.
- The value can be
TIMESTAMP,TIMEorDATE.While you may see this in legacy code, all new entities can use@Entity @Table(name="CustomerTable") public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @Column(name="create_date") @Temporal(TemporalType.DATE) private Date createDate; // ... }java.timeclasses without further annotations.
Testing Temporal Values¶
The java.time classes have getters you can use to test their content.
@Test
void test_Rental_temporal_mapping() {
assertNotNull(rental);
assertNotNull(rental.getRentalDate());
assertEquals(2014, rental.getRentalDate().getYear());
assertEquals(9, rental.getRentalDate().getMonthValue());
assertEquals(24, rental.getRentalDate().getDayOfMonth());
}
-
getMonth()returns a special enumeration typeMonth, whilegetMonthValue()returns a 1 to 12 month number. -
Note that, while a month number might be displayed as
09, it's just the number9; in Java a literal with a leading0is considered to be octal, for which the values08and09are invalid.
This far more reliable than testing with the toString() value:
assertEquals("2014-09-24T22:04:30.0", rental.getRentalDate());
Practice Exercise¶
When using
LocalDateTime, teammates in different time zones may run into "off-by-a-day" errors for times near midnight.
Drill¶
- Add a new field called
createdAtof typejava.time.LocalDateTimeto yourCustomerentity.Here is the Customer for reference:
+-------------+----------------------+------+-----+-------------------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +-------------+----------------------+------+-----+-------------------+-----------------------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | store_id | smallint(5) unsigned | NO | MUL | NULL | | | first_name | varchar(45) | NO | | NULL | | | last_name | varchar(45) | NO | MUL | NULL | | | email | varchar(50) | YES | | NULL | | | address_id | int(10) unsigned | NO | MUL | NULL | | | active | tinyint(1) | NO | | 1 | | | create_date | datetime | NO | | NULL | | | last_update | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | +-------------+----------------------+------+-----+-------------------+-----------------------------+
Don’t forget to add get/set methods.
Write a test for the createdAt mapping.
Look at your
CustomerTestclass. In each of your tests you are likely using the methodem.findto retrieve a Customer entity - and likely the same Customer entity each time. This isn't very DRY... how could you refactor your test class to fix this?Look at how you are retrieving/assigning the EntityManagerFactory and EntityManager as fields at the class level... could you do something similar with the Customer entity?