blob: d48bd9ad304a9b80563b9d4bd13fb9606d6679a4 [file] [log] [blame]
/*
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// 02/01/2022: Tomas Kraus
// - Issue 1442: Implement New JPA API 3.1.0 Features
package org.eclipse.persistence.jpa.test.criteria;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.util.List;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.criteria.Root;
import org.eclipse.persistence.jpa.test.criteria.model.DateTimeEntity;
import org.eclipse.persistence.jpa.test.framework.DDLGen;
import org.eclipse.persistence.jpa.test.framework.Emf;
import org.eclipse.persistence.jpa.test.framework.EmfRunner;
import org.eclipse.persistence.jpa.test.framework.Property;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Test {@code LocalTime}/{@code LocalDate}/{@code LocalDateTime} functions in CriteriaBuilder.
* Added to JPA-API as PR #352
*/
@RunWith(EmfRunner.class)
public class TestDateTimeFunctions {
@Emf(createTables = DDLGen.DROP_CREATE,
classes = {
DateTimeEntity.class
},
properties = {
@Property(name = "eclipselink.cache.shared.default", value = "false"),
// This property remaps String from VARCHAR->NVARCHAR(or db equivalent)
@Property(name = "eclipselink.target-database-properties",
value = "UseNationalCharacterVaryingTypeForString=true"),
})
private EntityManagerFactory emf;
private final LocalDateTime[] TS = {
LocalDateTime.of(1970, 1, 1, 1, 11, 11),
LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0)
};
private final DateTimeEntity[] ENTITY = {
new DateTimeEntity(1, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]), // reserved for testCriteriaUpdateLocalTime
new DateTimeEntity(2, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]), // reserved for testCriteriaUpdateLocalDate
new DateTimeEntity(3, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]), // reserved for testCriteriaUpdateLocalDateTime
new DateTimeEntity(4, TS[1].toLocalTime(), TS[1].toLocalDate(), TS[1]) // reserved for testCriteriaQueryWhereLocalTime
// testCriteriaQueryWhereLocalTimeReturnsEmpty
// testCriteriaQueryWhereLocalDate
// testCriteriaQueryWhereLocalDateReturnsEmpty
// testCriteriaQueryWhereLocalDateTime
// testCriteriaQueryWhereLocalDateTimeReturnsEmpty
// testCriteriaQuerySelectLocalTime
// testCriteriaQuerySelectLocalDate
// testCriteriaQuerySelectLocalDateTime
};
@Before
public void setup() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
for (DateTimeEntity e : ENTITY) {
em.persist(e);
}
em.flush();
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
@After
public void cleanup() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
em.createQuery("DELETE FROM DateTimeEntity e").executeUpdate();
em.flush();
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaUpdate of localTime.
// Strategy: Update 1:11:11 to current LocalTime and verify that time in database
// differs in less than 30 seconds from current time.
// May fail when database time is not set properly.
@Test
public void testCriteriaUpdateLocalTime() {
final EntityManager em = emf.createEntityManager();
try {
// Run the update query: Set current time
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaUpdate<DateTimeEntity> cu = cb.createCriteriaUpdate(DateTimeEntity.class);
Root<DateTimeEntity> entity = cu.from(DateTimeEntity.class);
cu.set("time", cb.localTime());
cu.where(cb.equal(entity.get("id"), 1));
em.createQuery(cu).executeUpdate();
em.flush();
em.getTransaction().commit();
// Verify updated entity
DateTimeEntity data = em.find(DateTimeEntity.class, 1);
long diffMilis = Duration.between(data.getTime(), LocalTime.now()).toMillis();
// Positive value means that test did not pass midnight.
if (diffMilis > 0) {
MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
// Midnight pass correction.
} else {
MatcherAssert.assertThat(86400000L + diffMilis, Matchers.lessThan(30000L));
}
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaUpdate of localDate.
// Strategy: Update 1. 1. 1970 to current LocalDate and verify that time in database
// differs in less than two days from current date. One day difference
// may be caused by midnight being just passed.
// May fail when database time is not set properly.
@Test
public void testCriteriaUpdateLocalDate() {
final EntityManager em = emf.createEntityManager();
try {
// Run the update query: Set current time
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaUpdate<DateTimeEntity> cu = cb.createCriteriaUpdate(DateTimeEntity.class);
Root<DateTimeEntity> entity = cu.from(DateTimeEntity.class);
cu.set("date", cb.localDate());
cu.where(cb.equal(entity.get("id"), 2));
em.createQuery(cu).executeUpdate();
em.flush();
em.getTransaction().commit();
// Verify updated entity
DateTimeEntity data = em.find(DateTimeEntity.class, 2);
Period diff = Period.between(data.getDate(), LocalDate.now());
MatcherAssert.assertThat(diff.getYears(), Matchers.equalTo(0));
MatcherAssert.assertThat(diff.getMonths(), Matchers.equalTo(0));
MatcherAssert.assertThat(diff.getDays(), Matchers.lessThan(2));
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaUpdate of localDateTime.
// Strategy: Update 1. 1. 1970 1:11:11 to current LocalDateTime and verify that timestamp in database
// differs in less than 30 seconds from current timestamp.
// May fail when database time is not set properly.
@Test
public void testCriteriaUpdateLocalDateTime() {
final EntityManager em = emf.createEntityManager();
try {
// Run the update query: Set current time
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaUpdate<DateTimeEntity> cu = cb.createCriteriaUpdate(DateTimeEntity.class);
Root<DateTimeEntity> entity = cu.from(DateTimeEntity.class);
cu.set("datetime", cb.localDateTime());
cu.where(cb.equal(entity.get("id"), 3));
em.createQuery(cu).executeUpdate();
em.flush();
em.getTransaction().commit();
// Verify updated entity
DateTimeEntity data = em.find(DateTimeEntity.class, 3);
long diffMilis = Duration.between(data.getDatetime(), LocalDateTime.now()).toMillis();
MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaQuery with localTime in WHERE condition.
// Strategy: SELECT DateTimeEntity WHERE time column < localTime AND id = :id
// LocalTime in entity is set to 0:00:00.0.
@Test
public void testCriteriaQueryWhereLocalTime() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
cq.select(entity);
cq.where(cb.and(cb.lessThan(entity.get("time"), cb.localTime()), cb.equal(entity.get("id"), 4)));
em.createQuery(cq).getSingleResult();
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaQuery with localTime in WHERE condition with no record matching it.
// Strategy: SELECT DateTimeEntity WHERE time column > localTime AND id = :id
// localTime in entity is set to 0:00:00.0
// Such a record does not exist so empty result is expected.
@Test
public void testCriteriaQueryWhereLocalTimeReturnsEmpty() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
cq.select(entity);
cq.where(cb.and(cb.greaterThan(entity.get("time"), cb.localTime()), cb.equal(entity.get("id"), 4)));
List<DateTimeEntity> data = em.createQuery(cq).getResultList();
em.getTransaction().commit();
MatcherAssert.assertThat(data.size(), Matchers.equalTo(0));
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaQuery with localDate in WHERE condition.
// Strategy: SELECT DateTimeEntity WHERE time column < localTime AND id = :id
// LocalDate in entity is set to 1.1.1970.
@Test
public void testCriteriaQueryWhereLocalDate() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
cq.select(entity);
cq.where(cb.and(cb.lessThan(entity.get("date"), cb.localDate()), cb.equal(entity.get("id"), 4)));
em.createQuery(cq).getSingleResult();
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaQuery with localDate in WHERE condition with no record matching it.
// Strategy: SELECT DateTimeEntity WHERE time column > localTime AND id = :id
// LocalDate in entity is set to 1.1.1970.
// Such a record does not exist so empty result is expected
@Test
public void testCriteriaQueryWhereLocalDateReturnsEmpty() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
cq.select(entity);
cq.where(cb.and(cb.greaterThan(entity.get("date"), cb.localDate()), cb.equal(entity.get("id"), 4)));
List<DateTimeEntity> data = em.createQuery(cq).getResultList();
em.getTransaction().commit();
MatcherAssert.assertThat(data.size(), Matchers.equalTo(0));
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaQuery with localDateTime in WHERE condition.
// Strategy: SELECT DateTimeEntity WHERE time column < localDateTime AND id = :id
// LocalDate in entity is set to 1.1.1970 0:00:00.
@Test
public void testCriteriaQueryWhereLocalDateTime() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
cq.select(entity);
cq.where(cb.and(cb.lessThan(entity.get("datetime"), cb.localDateTime()), cb.equal(entity.get("id"), 4)));
em.createQuery(cq).getSingleResult();
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaQuery with localDateTime in WHERE condition with no record matching it.
// Strategy: SELECT DateTimeEntity WHERE time column > localDateTime AND id = :id
// LocalDate in entity is set to 1.1.1970 0:00:00.
// Such a record does not exist so empty result is expected
@Test
public void testCriteriaQueryWhereLocalDateTimeReturnsEmpty() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
cq.select(entity);
cq.where(cb.and(cb.greaterThan(entity.get("datetime"), cb.localDateTime()), cb.equal(entity.get("id"), 4)));
List<DateTimeEntity> data = em.createQuery(cq).getResultList();
em.getTransaction().commit();
MatcherAssert.assertThat(data.size(), Matchers.equalTo(0));
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaQuery with localTime in SELECT column list
@Test
public void testCriteriaQuerySelectLocalTime() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<LocalTime> cq = cb.createQuery(LocalTime.class);
Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
cq.select(cb.localTime());
cq.where(cb.equal(entity.get("id"), 4));
LocalTime time = em.createQuery(cq).getSingleResult();
em.getTransaction().commit();
long diffMilis = Duration.between(time, LocalTime.now()).toMillis();
// Positive value means that test did not pass midnight.
if (diffMilis > 0) {
MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
// Midnight pass correction.
} else {
MatcherAssert.assertThat(86400000L + diffMilis, Matchers.lessThan(30000L));
}
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaQuery with localDate in SELECT column list
@Test
public void testCriteriaQuerySelectLocalDate() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<LocalDate> cq = cb.createQuery(LocalDate.class);
Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
cq.select(cb.localDate());
cq.where(cb.equal(entity.get("id"), 4));
LocalDate date = em.createQuery(cq).getSingleResult();
em.getTransaction().commit();
Period diff = Period.between(date, LocalDate.now());
MatcherAssert.assertThat(diff.getYears(), Matchers.equalTo(0));
MatcherAssert.assertThat(diff.getMonths(), Matchers.equalTo(0));
MatcherAssert.assertThat(diff.getDays(), Matchers.lessThan(2));
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
// Test CriteriaQuery with localDateTime in SELECT column list
@Test
public void testCriteriaQuerySelectLocalDateTime() {
final EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<LocalDateTime> cq = cb.createQuery(LocalDateTime.class);
Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
cq.select(cb.localDateTime());
cq.where(cb.equal(entity.get("id"), 4));
LocalDateTime datetime = em.createQuery(cq).getSingleResult();
em.getTransaction().commit();
long diffMilis = Duration.between(datetime, LocalDateTime.now()).toMillis();
MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
}