일반적으로 시간을 다루는 연산은 그 종류가 많고, 다소 복잡합니다. 예를 들어, 시간대, 시간 타입 (DATE 인지 DATETIME 인지 Timestamp 인지, 혹은.. String인지 등), 단위 연산( [초, 분, 시, 날, 월, 년] * [덧셈, 뺄셈] ) 등 처리할 것이 많습니다. 기본적으로 sql 은 dsl 이므로 완전 선언적 인 언어입니다. 선언적인 언어의 약점은 아주 잘 정의하지 않으면 이런 복잡한 여러 case 들을 처리하기 번거로울 수 있다는 것이죠. 때문에 어플리케이션 영역에서 시간 객체를 조건에 알맞게 수정해서 sql 에서는 간단한 비교만 가능하게 하는 것이 좋습니다. jpql 도 sql 과 맥락이 같기 때문에, jpql 내부에서 기본타입이 아니거나 시간처럼 복잡한 연산을 하는 것은 피하는 것이 필요합니다.
Spring Data JPA
를 사용하고, Foo
Entity와 timelog
(시작시간) 속성을 가정했습니다.
어플리케이션 level 에서 시간을 조절하기 위해, 시간 대소를 비하는 메서드를 선언하고, facade pattern 으로 현재시간으로부터 10분 전까지 select 하는 method 를 추가선언했습니다.
@Repository
public interface FooRepository extends JpaRepository<Foo, Long> {
@Query("select f from Foo f where f.timelog < :threshold ")
List<Foo> findByTimelogLessThan(@Param("threshold") LocalDateTime threshold);
default List<Foo> findOlds() {
long minutesGap = 10; // 허용 갭이 10분이라 가정
return findByTimelogLessThan(LocalDateTime.now().minusMinutes(minutesGap));
}
}
위에서는 JPQL
을 쓰는 것을 가정했지만, 만약 시간만 비교하는 것이라면, Spring Data JPA
의 장점을 사용해, 아래처럼 @Query
없이 메서드 시그지처만으로도 같은 쿼리가 가능합니다. findBy속성명+조건
List<Product> findByTimelogLessThan(LocalDateTime threshold);
다음 test는 pass합니다.
import static org.junit.Assert.assertEquals;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class FooRepositoryTests {
@Autowired
private FooRepository fooRepository;
@Before
public void setUp() {
Foo f1 = new Foo();
f1.setTimelog(LocalDateTime.now().minusMinutes(12)); //12분 전 => select 되어야 함.
Foo f2 = new Foo();
f2.setTimelog(LocalDateTime.now().minusMinutes(8)); //8분 전 => select 되지 말아야 함.
fooRepository.saveAll(Arrays.asList(f1, f2));
}
@Test
public void test(){
List<Foo> foos = fooRepository.findOlds();
//::setUp 에서 12분 전으로 설정하고 저장한 record 만 select 된다.
assertEquals(1, foos.size());
}
}