diff --git a/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/Event.java b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/Event.java new file mode 100644 index 000000000000..ec17e4c6afe4 --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/Event.java @@ -0,0 +1,70 @@ +package com.baeldung.jpa.localdatetimequery; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "events") +public class Event { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public Event() {} + + public Event(String name, LocalDateTime createdAt) { + this.name = name; + this.createdAt = createdAt; + } + + @PreUpdate + protected void onUpdate() { + updatedAt = LocalDateTime.now(); + } + + // Getters and setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } + + @Override + public String toString() { + return "Event{" + + "id=" + id + + ", name='" + name + '\'' + + ", createdAt=" + createdAt + + ", updatedAt=" + updatedAt + + '}'; + } +} \ No newline at end of file diff --git a/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/EventCriteriaRepository.java b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/EventCriteriaRepository.java new file mode 100644 index 000000000000..41ad224aa2a0 --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/EventCriteriaRepository.java @@ -0,0 +1,35 @@ +package com.baeldung.jpa.localdatetimequery; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.stereotype.Repository; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; + +@Repository +public class EventCriteriaRepository { + + @PersistenceContext + private EntityManager entityManager; + + public List findByCreatedDate(LocalDate date) { + LocalDateTime startOfDay = date.atStartOfDay(); + LocalDateTime endOfDay = date.plusDays(1).atStartOfDay(); + + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Event.class); + Root root = cq.from(Event.class); + + cq.select(root).where( + cb.between(root.get("createdAt"), startOfDay, endOfDay) + ); + + return entityManager.createQuery(cq).getResultList(); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/EventRepository.java b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/EventRepository.java new file mode 100644 index 000000000000..f130d8ab106e --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/EventRepository.java @@ -0,0 +1,33 @@ +package com.baeldung.jpa.localdatetimequery; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +public interface EventRepository extends JpaRepository { + + List findByCreatedAtBetween(LocalDateTime start, LocalDateTime end); + + List findByCreatedAtGreaterThanEqualAndCreatedAtLessThan( + LocalDateTime start, + LocalDateTime end + ); + + + @Query("SELECT e FROM Event e WHERE FUNCTION('DATE', e.createdAt) = :date") + List findByDate(@Param("date") LocalDate date); + + @Query( + value = "SELECT * FROM events " + + "WHERE created_at >= :startOfDay " + + "AND created_at < :endOfDay", + nativeQuery = true + ) + List findByDateRangeNative( + @Param("startOfDay") LocalDateTime startOfDay, + @Param("endOfDay") LocalDateTime endOfDay + ); +} \ No newline at end of file diff --git a/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/LocalDateTimeQueryApplication.java b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/LocalDateTimeQueryApplication.java new file mode 100644 index 000000000000..ac26b803cabf --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/jpa/localdatetimequery/LocalDateTimeQueryApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.jpa.localdatetimequery; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LocalDateTimeQueryApplication { + + public static void main(String[] args) { + SpringApplication.run(LocalDateTimeQueryApplication.class, args); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-jpa-3/src/main/resources/application.properties b/persistence-modules/spring-jpa-3/src/main/resources/application.properties new file mode 100644 index 000000000000..362db88cccfb --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= + +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=false \ No newline at end of file diff --git a/persistence-modules/spring-jpa-3/src/test/java/com/baeldung/jpa/localdatetimequery/EventRepositoryUnitTest.java b/persistence-modules/spring-jpa-3/src/test/java/com/baeldung/jpa/localdatetimequery/EventRepositoryUnitTest.java new file mode 100644 index 000000000000..76a05be906ba --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/test/java/com/baeldung/jpa/localdatetimequery/EventRepositoryUnitTest.java @@ -0,0 +1,122 @@ +package com.baeldung.jpa.localdatetimequery; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +@DataJpaTest +@Import(EventCriteriaRepository.class) +public class EventRepositoryUnitTest { + @Autowired + private EventRepository eventRepository; + + @Autowired + private EventCriteriaRepository eventCriteriaRepository; + + private LocalDate testDate; + + @BeforeEach + public void setUp() { + testDate = LocalDate.of(2025, 10, 12); + + eventRepository.deleteAll(); + + eventRepository.save(new Event("Morning Meeting", LocalDateTime.of(2025, 10, 12, 9, 0, 0))); + eventRepository.save(new Event("Lunch Discussion", LocalDateTime.of(2025, 10, 12, 12, 30, 0))); + eventRepository.save(new Event("Evening Review", LocalDateTime.of(2025, 10, 12, 18, 45, 0))); + eventRepository.save(new Event("Next Day Planning", LocalDateTime.of(2025, 10, 13, 10, 0, 0))); + } + @Test + public void givenLocalDateAndEventsWithTimestamps_whenQueryUsingBetween_thenReturnAllEventsForThatDay() { + LocalDateTime startOfDay = testDate.atStartOfDay(); + LocalDateTime endOfDay = testDate.plusDays(1).atStartOfDay(); + + List results = eventRepository.findByCreatedAtBetween(startOfDay, endOfDay); + + assertEquals(3, results.size()); + assertEquals("Morning Meeting", results.get(0).getName()); + assertEquals("Lunch Discussion", results.get(1).getName()); + assertEquals("Evening Review", results.get(2).getName()); + } + + @Test + public void givenLocalDateAndEventsWithTimestamps_whenQueryUsingExplicitBoundaries_thenReturnAllEventsForThatDay() { + LocalDateTime startOfDay = testDate.atStartOfDay(); + LocalDateTime endOfDay = testDate.plusDays(1).atStartOfDay(); + + List results = eventRepository.findByCreatedAtGreaterThanEqualAndCreatedAtLessThan(startOfDay, endOfDay); + + assertEquals(3, results.size()); + assertEquals("Morning Meeting", results.get(0).getName()); + assertEquals("Lunch Discussion", results.get(1).getName()); + assertEquals("Evening Review", results.get(2).getName()); + } + +// @Test +// public void givenLocalDateAndEventsWithTimestamps_whenQueryUsingJpqlDateFunction_thenReturnAllEventsForThatDay() { +// LocalDate queryDate = LocalDate.of(2025, 10, 12); +// +// List results = eventRepository.findByDate(queryDate); +// +// assertEquals(3, results.size()); +// assertEquals("Morning Meeting", results.get(0).getName()); +// assertEquals("Lunch Discussion", results.get(1).getName()); +// assertEquals("Evening Review", results.get(2).getName()); +// } + + @Test + public void givenLocalDateAndEventsWithTimestamps_whenQueryUsingCriteriaApi_thenReturnAllEventsForThatDay() { + LocalDate queryDate = LocalDate.of(2025, 10, 12); + + List results = eventCriteriaRepository.findByCreatedDate(queryDate); + + assertEquals(3, results.size()); + assertEquals("Morning Meeting", results.get(0).getName()); + assertEquals("Lunch Discussion", results.get(1).getName()); + assertEquals("Evening Review", results.get(2).getName()); + } + + @Test + public void givenLocalDateAndEventsWithTimestamps_whenQueryUsingNativeSql_thenReturnAllEventsForThatDay() { + LocalDateTime startOfDay = testDate.atStartOfDay(); + LocalDateTime endOfDay = testDate.plusDays(1).atStartOfDay(); + + List results = eventRepository.findByDateRangeNative(startOfDay, endOfDay); + + assertEquals(3, results.size()); + assertEquals("Morning Meeting", results.get(0).getName()); + assertEquals("Lunch Discussion", results.get(1).getName()); + assertEquals("Evening Review", results.get(2).getName()); + } + + @Test + public void givenLocalDateForDifferentDay_whenQueryUsingBetween_thenReturnOnlyEventForThatDay() { + LocalDate differentDate = LocalDate.of(2025, 10, 13); + LocalDateTime startOfDay = differentDate.atStartOfDay(); + LocalDateTime endOfDay = differentDate.plusDays(1).atStartOfDay(); + + List results = eventRepository.findByCreatedAtBetween(startOfDay, endOfDay); + + assertEquals(1, results.size()); + assertEquals("Next Day Planning", results.get(0).getName()); + } + + @Test + public void givenNoEventsForDate_whenQueryUsingBetween_thenReturnEmptyList() { + LocalDate emptyDate = LocalDate.of(2025, 10, 14); + LocalDateTime startOfDay = emptyDate.atStartOfDay(); + LocalDateTime endOfDay = emptyDate.plusDays(1).atStartOfDay(); + + List results = eventRepository.findByCreatedAtBetween(startOfDay, endOfDay); + + assertEquals(0, results.size()); + } +}