Maksim Rybalkin
Maksim Rybalkin

Reputation: 277

JPA and searching by several dates

This is my entity class

@Data
@NoArgsConstructor
@Builder
@AllArgsConstructor
@Entity(name = "words")
public class Word {

    @Id
    private Long id;

    @ManyToOne
    private Group group;

    @ManyToOne
    private User user;

    @NotBlank
    private String englishWord;

    @NotBlank
    private String russianWord;

    private LocalDate created = LocalDate.now();

    private final LocalDate plusOneDay = created.plusDays(1);

    private final LocalDate plusTwoDays = created.plusDays(2);

    private final LocalDate plusFiveDays = created.plusDays(5);

    private final LocalDate plusTenDays = created.plusDays(10);

    private final LocalDate plusTwoWeeks = created.plusWeeks(2);

    private final LocalDate plusSixWeeks = created.plusWeeks(6);

    private final LocalDate plusThreeMonths = created.plusMonths(3);

    private final LocalDate plusSixMonths = created.plusMonths(6);

    private final LocalDate plusOneYear = created.plusYears(1);
}

As you see I have several LocalDate fields.

I need to check, is created, OR plusOneDay, OR plusTwoWeek, etc matches with today's day. If it matches - it must be got from a database. I can write something like that:

Set<Word> findAllByCreatedOrByPlusOneDayOrByPlusTwoDays(LocalDate date);

But the request will be too long, and it doesn't work. Is there another way to check several fields by one date?

Upvotes: 0

Views: 756

Answers (2)

sgtcortez
sgtcortez

Reputation: 427

I think that you may use Specification. Doc: https://docs.spring.io/spring-data/jpa/docs/1.7.0.RELEASE/reference/html/#specifications

I made some code, that may help.

import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.LocalDate;

public class WordSpecs {

        public static Specification<Word> isOneDateEqual(final LocalDate date){
            return new Specification<Word>() {
                @Override
                public Predicate toPredicate(Root<Word> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    final Predicate created = criteriaBuilder.equal(root.get("created"), date);
                    final Predicate plusOneDay = criteriaBuilder.equal(root.get("plusOneDay"), date);
                    return criteriaBuilder.or(created, plusOneDay);
                }
            };

        }
    }

Repository Class:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

@org.springframework.stereotype.Repository
public interface WordRepository extends JpaRepository<Word, Long>, JpaSpecificationExecutor {


}

Service class

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

@Service
public class WordService {

    @Autowired
    private WordRepository repository;

    public List<Word> findMatched(final LocalDate date){
        return repository.findAll(WordSpecs.isOneDateEqual(date));

    }

}

Edit:

Much easier, you may like:

@Query("SELECT word FROM Word word WHERE 1 = 1 AND word.user.userId = :userId AND ( word.created = :date OR word.plus1 = :date ) " )
List<Word> findMatched(@Param("date") final LocalDate date, @Param("userId") final Long userId); 

Upvotes: 1

Andronicus
Andronicus

Reputation: 26026

Yes, you can pass a list of possible dates:

Set<Word> findAllByCreatedIn(List<LocalDate> dates);

and then invoke it as:

repo.findAllByCreatedIn(List.of(date, date.minusDays(1), date.minusDays(2)));

Upvotes: 1

Related Questions