객체지향 쿼리 언어1 - 기본 문법

born_a·2022년 9월 22일
0

소개

jpql을 짜면 sql로 번연이 되어서 실행된다.

select m : 멤버 자체를 조회해와 라는 뜻

jpql은 동적 쿼리를 해결하기가 쉽지 않다.
-> criteria
-> 너무 복잡.

->QueryDSL

JDBC 직접 사용, SpringJdbcTemplate 등

  • JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나, 스프링 JdbcTemplate, 마이바티스등을 함께 사용 가능
  • 단 영속성 컨텍스트를 적절한 시점에 강제로 플러시 필요
    예) JPA를 우회해서 SQL을 실행하기 직전에 영속성 컨텍스트 수동 플러시
    ex)
    JpaMain.java
public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            Member member = new Member();
            member.setUsername("member1");
            em.persist(member);

            //flush는 commit하기 직전에도 호출되지만,
            //entity Manager를 통해서 쿼리가 날라갈 때도 flush가 된다.
            List<Member> resultList = em.createNativeQuery("select MEMBER_ID, city, street, zipcode, USERNAME from Member" , Member.class)
                    .getResultList();

            for (Member member1 : resultList) {
                System.out.println("member1 = " + member1);
            }

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        }finally {
            em.close();
        }

        emf.close();
    }
}


수동으로 flush를 해주지 않았지만 member가 제대로 db에 저장이 되어 출력됨을 확인할 수 있다.
그 이유는 flush는 commit하기 직전에도 호출되지만,
entity Manager를 통해서 쿼리가 날라갈 때도 flush가 되기 때문이다.

엔티티 매니저를 사용하는 방식이 아닌, jpa와 아무 상관 없는 db 커넥션을 얻어오는 경우엔 수동으로 flush 해줘야 한다.

ex)
JpaMain.java

try {
            //1.얘네는 아직 jpa안에 있음. 영속성 컨텍스트 내에 있고, db로 날라가지 않음
            Member member = new Member();
            member.setUsername("member1");
            em.persist(member);
            
            //3.강제로 플러쉬 해줘야 함.
            em.flush();

            //dbconn.executeQuery("select * from member");

            tx.commit(); //2.커밋하고 플러시됨
        }

따라서 jpql을 열심히 공부하고 QueryDSL을 선택하자!

기본 문법과 쿼리 API

TypeQuery, Query

JpaMain.java

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            em.persist(member);

            TypedQuery<Member> query1 = em.createQuery("select m from Member  m", Member.class);// 응답 클래스. 타입정보
            TypedQuery<String> query2 = em.createQuery("select m.username from Member m", String.class);
            Query query3 = em.createQuery("select m.username, m.age from Member m");

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        }finally {
            em.close();
        }

        emf.close();
    }
}
try {
            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            em.persist(member);

            Member result = em.createQuery("select m from Member  m where m.username = :username", Member.class)// 응답 클래스. 타입정보
                    .setParameter("username", "member1")
                    .getSingleResult();
            
            System.out.println("singleResult = " + result.getUsername());


            tx.commit();
        }
try {
            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            em.persist(member);

            Member result = em.createQuery("select m from Member  m where m.username = :username", Member.class)// 응답 클래스. 타입정보
                    .setParameter("username", "member1")
                    .getSingleResult();
            
            System.out.println("singleResult = " + result.getUsername());


            tx.commit();
        }

위치 기준 쓰지 말고 이름 기준을 쓰자.

프로젝션

JpaMain.java

try {
            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            em.persist(member);

            em.flush();
            em.clear();

            List<Member> result = em.createQuery("select m from Member m", Member.class)
                    .getResultList();

            Member findMember = result.get(0);
            findMember.setAge(20);
            
            tx.commit();
        } 


20으로 바뀌어 있는 것을 확인 가능.

엔티티 프로젝션을 하면, 영속성 컨텍스트에서 다 관리 된다.
따라서 정상적으로 반영된다.

프로젝션 - 여러 값 조회

SELECT m.username, m.age FROM Member m

• 1. Query 타입으로 조회

• 2. Object[] 타입으로 조회

1번째 방법
JpaMain.java

try {
            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            em.persist(member);

            em.flush();
            em.clear();

            List resultList = em.createQuery("select m.username, m.age from Member m")
                    .getResultList();

            Object o = resultList.get(0);
            Object[] result = (Object[]) o;
            System.out.println("username = " + result[0]);
            System.out.println("age = " + result[1]);

            tx.commit();
        }

2번째 방법 : 제네릭 이용
JpaMain.java

try {
            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            em.persist(member);

            em.flush();
            em.clear();

            List<Object[]> resultList = em.createQuery("select m.username, m.age from Member m")
                    .getResultList();

            Object[] result = resultList.get(0);
            System.out.println("username = " + result[0]);
            System.out.println("age = " + result[1]);

            tx.commit();
        }

• 3. new 명령어로 조회

JpaMain.java

try {
            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            em.persist(member);

            em.flush();
            em.clear();

            List<MemberDto> result = em.createQuery("select new jpql.MemberDto(m.username, m.age) from Member m", MemberDto.class)
                    .getResultList();

            MemberDto memberDto = result.get(0);
            System.out.println("username = " + memberDto.getUsername());
            System.out.println("age = " + memberDto.getAge());

            tx.commit();
        }

페이징

orderby 를 해야 페이징이 제대로 돌아가는지 확인할 수 있다
JpaMain.java

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            for (int i = 0; i < 100; i++) {
                Member member = new Member();
                member.setUsername("member" + i);
                member.setAge(i);
                em.persist(member);
            }


            em.flush();
            em.clear();

            List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
                    .setFirstResult(1)
                    .setMaxResults(10)
                    .getResultList();
            System.out.println("result.size = " +result.size());

            for (Member member1 : result) {
                System.out.println("member1 = " + member1);
            }


            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        }finally {
            em.close();
        }

        emf.close();
    }
}

.setFirstResult(1) : 몇번째 인덱스부터 채울건지
, .setMaxResults(10) : 몇번째 인덱스까지 채울건지
만 맞게 넣어주면 쿼리 LIMIT ? , ? 부분을 알아서 채워준다.

조인

외부 조인은 팀이 없어도 팀 데이터가 다 널이고, 멤버 데이터 조회는 된다.
세타 조인 : 연관 관계 없는 애들을 비교해보고 싶을때 사용

서브쿼리

메인쿼리와 서브쿼리는 관계 없다.

JPQL 타입 표현과 기타식

문자는 작은 따옴표 ' ' 안에 넣으면 되고, ' '안에 '표현하고 싶으면
'She''s ' 이런 식으로 '' 두번 적는다.

Enum은 자바 패키지명을 포함시켜야한다.

Member.java

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String username;
    private int age;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    
	@Enumerated(EnumType.STRING)
    private MemberType memberType;

    //연관관계 편의 메소드
    public void changeTeam(Team team) {
        this.team = team;
        team.getMembers().add(this);
    }
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

JpaMain.java

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            Team team = new Team();
            team.setName("teamA");
            em.persist(team);

            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            em.persist(member);

            em.flush();
            em.clear();

            String query = "select m.username, 'HELLO', TRUE from Member m";
            List<Object[]> result = em.createQuery(query)
                    .getResultList();

            for (Object[] objects : result) {
                System.out.println("objects = " + objects[0]);
                System.out.println("objects = " + objects[1]);
                System.out.println("objects = " + objects[2]);
            }



            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        }finally {
            em.close();
        }

        emf.close();
    }
}

JpaMain.java

try {
            Team team = new Team();
            team.setName("teamA");
            em.persist(team);

            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            member.setType(MemberType.ADMIN);
            em.persist(member);

            em.flush();
            em.clear();

            String query = "select m.username, 'HELLO', TRUE from Member m "
                            + "where m.type = jpql.MemberType.ADMIN";

            List<Object[]> result = em.createQuery(query)
                    .getResultList();

            for (Object[] objects : result) {
                System.out.println("objects = " + objects[0]);
                System.out.println("objects = " + objects[1]);
                System.out.println("objects = " + objects[2]);
            }



            tx.commit();
        }
        

@Enumerated(EnumType.STRING)
private MemberType memberType;

추가해주어야 memberType = 'ADMIN'으로 잘 들어간다.

파라미터 바인딩으로 조회하는 법

JpaMain.java

try {
            Team team = new Team();
            team.setName("teamA");
            em.persist(team);

            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            member.setType(MemberType.ADMIN);
            em.persist(member);

            em.flush();
            em.clear();

            String query = "select m.username, 'HELLO', TRUE from Member m "
                            + "where m.type = :userType";

            List<Object[]> result = em.createQuery(query)
                    .setParameter("userType", MemberType.ADMIN)
                    .getResultList();

            for (Object[] objects : result) {
                System.out.println("objects = " + objects[0]);
                System.out.println("objects = " + objects[1]);
                System.out.println("objects = " + objects[2]);
            }



            tx.commit();
        }

쿼리 안에 하드 코딩 해야한다 -> 패키지명을 다 넣어야한다.

엔티티 타입: TYPE(m) = Member (상속 관계에서 사용)

예를 들어 bookㅇ item을 상속 받을때,
em.createQuery("select i from Item i where type(i) = Book",Item.class);이런식으로 쓴다.

조건식

JpaMain.java

try {
            Team team = new Team();
            team.setName("teamA");
            em.persist(team);

            Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            member.setType(MemberType.ADMIN);
            em.persist(member);

            em.flush();
            em.clear();

            String query = "select " +
                    "case when m.age <= 10 then '학생요금' " +
                    "     when m.age >= 60 then '경로요금' " +
                    "     else '일반요금' " +
                    " end " +
                    "from Member m";

            List<String> result = em.createQuery(query, String.class)
                    .getResultList();

            for (String s : result) {
                System.out.println("s = " + s);
            }

            tx.commit();
        }

JPQL 함수

JPQL 기본함수는 데이터베이스에 관계없이 그냥 쓰면 된다.
CONCAT : 문자를 더하는것.

String query = "select concat( 'a' , ' b') from Member m";

substring : ex) 2번째부터 3개를 잘라내라
"String query = "select substring(m.username, 2, 3) From Member m"

TRIM : 공백 제거

Locate : 해당 문자 위치 찾아
ex)
"String query = "select locate('de', 'abcdef') From Member m";
숫자 4가 반환됨

SIZE : 컬렉션의 크기를 돌려줌
String query = "select size(t.members) From Team t";

사용자 정의 함수 등록하기

MyH2Dialect.java

public class MyH2Dialect extends H2Dialect {
    public MyH2Dialect(){
        registerFunction("group_concat",new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
    }
}

persistence.xml 에서
<property name="hibernate.dialect" value="dialect.MyH2Dialect"/>
를 수정한다.

JpaMain.java

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            Member member1 = new Member();
            member1.setUsername("관리자1");
            em.persist(member1);

            Member member2 = new Member();
            member2.setUsername("관리자2");
            em.persist(member2);

            em.flush();
            em.clear();

            String query = "select function('group_concat',m.username) from Member m";

            List<String> result = em.createQuery(query, String.class)
                    .getResultList();

            for (String s : result) {
                System.out.println("s = " + s);
            }

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        }finally {
            em.close();
        }

        emf.close();
    }
}

두 줄이 출력되어야 하는데, 관리자 1,2가 한꺼번에 출력되었다.

오류가 나면 hql을 추가해준다.

0개의 댓글

관련 채용 정보