이번 유닛에서는 java 환경에서 SQL을 다룰 수 있는 기술인 JPA에 대해서 학습과 실습을 진행했다. Spring과 함께 데이터베이스를 java로 다루는 연습을 했는데 꽤 좋은 것 같으면서도 어려웠었던 것 같다. 학습을 하면서 배웠던 개념들을 정리를 해보겠다.
JPA는 JJava Persistence API
의 약자로, java에서의 ORM 기술 표준으로, 인터페이스의 모음이다.
JPA를 구현한 대표적인 오픈소스가 Hibernate이다.
JPA를 통해 데이터베이스의 CRUD 작업을 수행할 수 있다.
JPA를 사용하기 위해서는 EntityManager
를 생성해야 한다. 이는 JPA에서 가장 핵심이 되는 객체로 스프링에 컨테이너 객체와 동일한 위치라고 생각할 수 있다.
생성자 주입을 통해 EntityManager
를 주입 받을 수 있으며, EntityManager
는 스프링에서 자동으로 넣어준다.
EntityManager
를 통해 데이터베이스와 데이터를 주고 받는 메소드 혹은 클래스에서는 @Transactional
어노테이션을 무조건 선언해주어야 하며, 이는 DB 작업에서 생성되는 트랜잭션을 자동으로 관리해준다.
entityManager.createQuery(쿼리문)
은 SQL을 직접 작성할 수 있게 해주는 메소드이며, 첫 인자로 SQL문이 들어오며, 두번째 인자는 리턴 되는 Entity 객체 타입이 된다. 그리고 첫 인자로 들어오는 SQL문은 일반적인 SQL문이 아니라 JPQL이다.저장 : entityManager.persist(orders(객체))
조회 : Users findUser = entityManager.find(Users.class, id);
수정 : 해당 객체의 Setter를 이용한다.
삭제 : entityManager.remove(객체);
엔티티 클래스는 DB의 실제 테이블과 매핑되는 객체이다. 엔티티는 DB와 1:1로 대응되며, 이를 기준으로 생성되기 때문에 테이블이 가지지 않는 컬럼을 필드로 가지고 있으면 안된다. 또한 다른 클래스를 상속받을 수 없고, 인터페이스의 구현체여서도 안된다.
엔티티 클래스는 @Entity
어노테이션을 붙여서 엔티티임을 명시해 주어야 하고, 내부의 필드에서는 @Column
, @Id
어노테이션 등을 사용해야 한다.
아래와 같이 사용한다.
위 코드를 보면 연관관계 매핑이 존재한다.
@ManyToOne
@JoinColumn
양방향 연관관계 매핑을 위해서는 한 쪽에서 연관관계가 매핑됐으면 대응되면 쪽에서도 매핑을 해야 한다. @ManyToOne으로 매핑을 해왔다면, 대응되는 쪽에서는 @OneToMany를 통해서 1 : N 관계를 가진다는 것을 명시해야 한다. 그리고 여기에서는 읽기만 가능하며, 옵션으로 mappedBy가 오게 되는데 들어가는 값은 매핑을 해온 필드의 필드명이 된다.
실습은 전에 React를 통해서 만들어봤던 마켓 앱을 사용해 해당 데이터를 데이터베이스로 관리하는 방법을 실습했다. 두 개의 레포지토리를 각 기능에 맞게 구현을 해주면 됐다.
1) 데이터베이스 구조
2) ItemRepository
마켓을 열었을 때 상품들이 보이는 화면에서의 데이터 관리 구현이다. 화면에서는 상품들을 모두 보여주면 되기 때문에 엔티티매니저를 통한 JPQL을 통해서 쉽게 구현할 수 있었다.
엔티티매니저를 사용하기 때문에 메소드위에 @Transactional
어노테이션을 사용했다.
3) OrderRepository
1. 주문 내역 만들기
우선 데이터베이스 구조를 보면 OrderItems를 통해 Orders와 Items에 접근할 수 있으므로 양방향 매핑 관계를 만들어 줘야 한다. 그러기 위해서는 엔티티 클래스에서 @ManyToOne, @OneToMany를 통해서 매핑을 해주어야 한다.
JPQL을 통해서 OrderItems와 Items, Orders를 조인하고 Orders는 Users와 연관관계가 있으므로 Orders에서 user_id가 인자로 들어온 id와 같다면 SELECT를 해서 원하는 데이터를 가져올 수 있다.
또한 JPQL에서는 데이터베이스에서의 컬럼명이 아닌 엔티티클래스에서 선언한 필드명으로 매핑을 해주어야 한다. 이렇게 해주어도 JPA에서 변환을 해주어 데이트베이스에 올라가게 된다.
리스트를 리턴하는 것을 보면 자료형이 OrdersResDTO라는 것을 알 수 있는데 이는 엔티티가 아니다. 그리고 query.getResultList()를 통해 데이터가 저장되는 것을 보면 각 컬럼들이 Object의 배열로 이루어져있는 것을 알 수 있다. 각각은 자료형을 가지고 있지만 이를 변환을 못해준 것 같다. 그래서 리스트를 돌면서 setter를 통해 OrdersResDTO를 설정해주고 결과 리스트에 추가해주는 작업을 해주었다. 사실 쿼리를 통해서 OrdersResDTO의 형태로 뽑아주면 좋겠지만 이 방법은 더 찾아봐야 할 것 같다.
4) 느낀점