ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • jinq
    카테고리 없음 2023. 4. 21. 15:27
    오늘은 jinq라는 아이에 대해 살펴보자. jinq는 c#의 linq의 영감을 얻어 linq 스타일을 그대로 따라 만들었다. jinq는 그냥 평범한 자바 코드로 DB의 데이터를 조회 할 수 있다. QueryDsl과 비슷한 역할을 하는 아이이다. QueryDsl 보다 좋은점은 Q 클래스를 같은 클래스를 만들지 않아도 된다는게 가장 좋은 장점이라고 생각한다. 또한 jpa도 지원하며 구현체로는 Hibernate 와 EclipseLink를 지원하고 scala와 jooq라는 프레임워크도 같이 사용할 수 있다고 한다. jooq도 잠깐 봤었는데 그냥 그렇다. 하지만 java8 의 Stream api와 비슷하기 때문에 java8을 써야 한다. 람다를 써야 더 깔끔하고 보기 좋기 때문에 java8을 써서 하자. 오늘은 간단하게 살펴보자. 필자도 공부하는 중이라..
    @Entity
    @Data
    public class Member {
    
      @Id
      @GeneratedValue
      private Long id;
    
      private String name;
    
      private int age;
    
      @ManyToOne(fetch = FetchType.LAZY)
      @JoinColumn(name = "TEAM_ID")
      private Team team;
    }
    
    @Entity
    @Data
    @ToString(exclude = "members")
    public class Team {
    
      @Id
      @GeneratedValue
      private Long id;
    
      private String name;
    
      @OneToMany(mappedBy = "team")
      private List<Member> members = new ArrayList<>();
    
    }
    
    
    저런 엔티티가 있다고 가정하자. 그리고 jinq를 사용하기 위한 설정을 해야된다. 설정이라기 보다 디펜더시와 사용하기 위한 setup정도이다.
    <dependency>
        <groupId>org.jinq</groupId>
        <artifactId>jinq-jpa</artifactId>
        <version>1.8.13</version>
    </dependency>
    
    우리는 jpa를 사용하기 때문에 jinq-jpa를 디펜더시 받으면 된다.
      private EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("default");
    
      @Test
      public void jinqTest() {
        JinqJPAStreamProvider streams = new JinqJPAStreamProvider(entityManagerFactory);
        EntityManager em = entityManagerFactory.createEntityManager();
       //... 
      }
    }
    
    위와 같이 EntityManagerFactory 를 생성후에 JinqJPAStreamProvider에 생성자로 entityManagerFactory를 넣으면 된다. 그리고 그 다음 부터는 jpa 의 기본 코드이다. EntityManagerFactory 통해 EntityManager를 가져오면 된다. 이제 간단하게 문법을 살펴보자. java8을 사용한다면 좀더 쉽게 느껴질 수 있지만 java8의 Stream api와 lambda 를 잘 모른다면 이해가 되지 않을 수 도 있다. 만약 모른다면 그냥 이런게 있다고만 알면 된다.
    final Member member = streams
      .streamAll(em, Member.class)
      .where(m -> m.getName().equals("wonwoo"))
      .getOnlyValue();
    System.out.println(member);
    
    딱봐도 Stream api와 많이 닮았다. where 절에 보면 member의 name이 wonwoo와 같은 아이를 찾는 것이다. 실제로 JPQL을 보면 아래와 같다.
    SELECT
        A 
    FROM
        Member A 
    WHERE
        A.name = wonwoo 
    
    getOnlyValue 함수 경우에는 결과 값이 두개 이상이면 에러를 내뱉는다. 만약 두개 이상이 나왔을 때는 첫번째 결과가 나오게 하고 싶다면 아래와 같이 하면된다.
    final Member member = streams
      .streamAll(em, Member.class)
      .where(m -> m.getName().equals("wonwoo"))
      .findFirst().get();
    System.out.println(member);
    
    findFirst 는 Optional를 리턴한다. 물론 get() 함수를 바로 쓰면 안되는건 다 알고 있을 것이다. orElseThrow나 orElse, orElseGet을 쓰길 바란다. get은 되도록이면 쓰지 않는 것이 좋다. 아니면 isPresent 를 사용해서 먼저 검사를 하는 게 좋다.
    final List<Member> members = streams
      .streamAll(em, Member.class)
      .where(m -> m.getAge() < 20)
      .toList();
    System.out.println(members);
    
    QueryDsl과는 달리 number 같은 경우에는 우리가 흔히 쓰던 부등호를 사용할 수 있다. 위의 쿼리는 나이가 20보다 작은 Member를 불러워는 쿼리가 된다. 실제 JPQL은 다음과 같다.
    SELECT
        A 
    FROM
        Member A 
    WHERE
        A.age < 20
    
    만약 특정 필드만 출력하고 싶다면 select를 이용하면 된다.
    final String member = streams
      .streamAll(em, Member.class)
      .where(m -> m.getName().equals("wonwoo"))
      .select(Member::getName)
      .findFirst().get();
    System.out.println(member);
    
    JPQL을 보면 다음과 같다.
    SELECT
        A.name 
    FROM
        Member A 
    WHERE
        A.name = wonwoo
    
    또한 동적 쿼리도 가능하다.
    String name = "wonwoo";
    int age = 20;
    
    JPAJinqStream<Member> stream = streams
      .streamAll(em, Member.class);
    if (name != null)
      stream = stream.where( p -> p.getName().equals(name) );
    
    if (age != 0) {
      stream = stream.where( p -> p.getAge() < age );
    }
    System.out.println(stream.toList());
    
    QueryDsl과 비슷한 방법으로 해당 필드가 있으면 where 넣고 그렇지 않으면 무시한다. 실제 JPQL은 어떤지 보자.
    SELECT
        A 
    FROM
        Member A 
    WHERE
        A.name = :param0 
        AND A.age < :param1
    
    위와 같이 파라미터를 바인딩 한다. 물론 join도 가능하다.
    List<Team> teams =
      streams.streamAll(em, Team.class)
        .joinFetchList(Team::getMembers)
        .toList();
    System.out.println(teams);
    
    위는 fetch 조인을 사용한 코드이다. 물론 inner join, outer join 등 있을껀 다 있다. 위의 코드는 JPQL이 어떻게 나오는지 보자.
    SELECT
        A 
    FROM
        Team A 
    JOIN
        FETCH A.members B
    
    조인은 나중에 필자도 공부좀하고 다시 살펴보도록 하자.
    List<Team> teams =
      streams.streamAll(em, Team.class)
        .where(i -> i.getId().equals(1L) || i.getId().equals(2L))
        .joinFetchList(Team::getMembers)
        .toList();
    System.out.println(teams);
    
    만약 조건절에 and말고 or을 사용하고 싶다면 위와 같이 하면 된다. 흠... 조금 아쉽긴하다. 이렇게 jinq에 대해 살짝 알아봤다. 필자도 공부좀 하고 난뒤에 join도 살펴보고 기타 jinq에서 지원하는 함수들을 살펴보자.

    댓글

Designed by Tistory.