SPRING#1_4_JPA기초
어플리케이션이 데이터베이스를 직접 다룰 때의 문제점
1.훨씬 더 번거롭습니다.
1) 데이터베이스 테이블 만들기(db로 접속해서 sql 쿼리문을 내보내야한다.)
2) 어플리케이션에서 직접 쿼리 만들기
3) 쿼리를 jdbc api를 통해 직접 실행해줘야 합니다.
4) 쿼리 결과로 해당 객체도 직접 만들어줘야 합니다.
2. SQL 의존적이라 변경에 취약합니다.
유저의 몸무게 데이터를 새로 받아와야 한다면
1) 쿼리문도 직접 또 수정해야합니다.
2) 유저객체에 값 넣어주는 부분도 당연히 추가해줘야 합니다.
3. 객체지향 모델과 관계형 데이터베이스의 패러다임 불일치가 발생합니다.
1)패러다임 불일치
객체 | 릴레이션 | |
밀도 문제 | 다양하나 크기의 객체를 만들수 있음, 커스텀한 타입 만들기 쉬움 |
테이블, 기본 데이터 타입 |
서브타입 문제 | 상속, 다형성 구현 쉬움 | 상속 없음, 다형적인 관계 표현 불가 |
식별성 문제 | 레퍼런스 동일성, 인스턴스 동일성 | 오직 pk |
관계 문제 | 서로간의 객체 참조를 통해 표현, 다대다 가능, 방향O |
다대다 x, 다대다를 맺어주는 테이블로 처리, 외래키가 있어서 바로 조회 가능(방향X) |
데이터 네비게이션 문제 | 마음대로 레퍼런스타고 이동 가능 | 그러한 방식이 비효율적 (매번 join, 그리고 다 가져오면 성능문제) |
1. 데이터 베이스 데이터가 더 정형호되어있고 까다롭다.
2. 관계형 데이터 베이스에는 상속의 개념이 없습니다 하지만 상속은 객체의 역할과 구현을 분리해주기 때문에
객체 지향 프로그래밍에서 가장 핵심적인 기능 중 하나입니다.
3. 관계 문제와 데이터 네비게이션 문제는 설명이 조금 더 필요할 것 같습니다.
01 . JPA (Java Persistence API)
자바 ORM 기술에 대한 표준 명세
* ORM = 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑(연결)해주는 것
- 쿼리를 자동으로 만들어 줍니다.
- 어플리케이션 계층에서 sql 의존성을 줄여서 번거로운 작업이 단축됩니다.
- 패러다임의 불일치를 해결해줍니다.
- 특정한 상황을 제외하고는 성능도 높습니다. 최적화 되었습니다.
- h2 Database나 mySql,oracle등 무엇을 붙여도 코드의 변경이 없으며 관계형 db이자 표준을 준수한 sql을 지원한다면,
맞춰서 처리해줍니다.
02. JPA 샘플
1) Java 클래스
@Entity // DB 테이블 역할을 합니다.
public class User {
// ID가 자동으로 생성 및 증가합니다.
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// nullable: null 허용 여부
// unique: 중복 허용 여부 (false 일때 중복 허용)
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserRoleEnum role;
@Column(unique = true)
private Long kakaoId;
}
2)DB 테이블 및 컬럼
3) 테이블 형식
ID | KAKAO_ID | PASSWORD | ROLE | USERNAME | |
03. DB의 연관관계 이해
1) DB의 연관관계 체크사항
- JPA가 제공하는 연관관계는 결국 DB의 연관관계를 표현하기 위함입니다.
- 따라서 먼저 DB의 연관관계를 이해해야합니다.
- DB의 연관관계는 비즈니스 요구사항에 맞춰 이루어집니다.
2) 음식 주문앱 DB 설계 예제
1.일단 각 주체의 테이블 설계가 필요합니다.
고객(User) 테이블
음식(food)테이블
2. 연관 관계 고민
- 고객 테이블에 주문 정보를 넣을 시
- 음식 테이블에 주문 정보를 넣을 시
- 주문을 위한 테이블이 필요 -> Order 테이블 추가
- 회원 1명은 주문 N개를 할 수 있다.
회원 : 주문 = 1 : N 관계
- 음식 1개는 주문 N개에 포함될 수 있다.
음식 : 주문 = 1 : N 관계
- 결론
회원 : 음식 = N : N 관계
04. JPA 연관관계
1) JPA 연관관계 설정방법
- JPA의 경우는 Enitity 클래스의 필드 위에 연관관계 어노테이션(@)을 설정해 주는 것만으로 연관관계가 형성
<음식 배달 서버 예시>
Aa 관계 | 코드 선언 | Enitity | example |
1 : N | @OneToMany | Order(1) : Food(N) | 주문 1개에 음식 여러개 선택 가능 |
N : 1 | @ManyToOne | Owner(N) : Restaaurant(1) | 음식 주인 여러명이 하나의 음식점 소유 가능 |
1 : 1 | @OneToOne | Order(1) : Coupon(1) | 주문 1개에 할인 쿠폰 1개 적용 가능 |
N : N | @ManyToMany | User(N) : Restaurant(N) | 고객은 음식점 여러개 찜 가능 음식저은 고객 여러명에게 찜 가능 |
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class Timestamped {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column
private LocalDateTime modifiedAt;
}
앞으로 엔티티를 만들때 위와 같은 객체를 만들어두고,
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column
private LocalDateTime modifiedAt;
위와 같이 "extends Timestamped"로 해당 객체를 상속받는다면
상속 받는 엔티티 객체들은 항상 아래와 같은 칼럼들을 가지고 있게 됩니다.
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column
private LocalDateTime modifiedAt;
"extends Timestamped" 두 단어만으로 상속받는 객체들은 자동으로 생성, 수정시간을 데이터베이스에 입력하고, 필요하다면 해당값을 편하게 Get 할 수 있다.
@SpringBootApplication 이 있는 class 에 @EnableJpaAuditing 추가!
05. Spring Data JPA 이해
1) Spring Data JPA
- JPA를 편리하게 사용하기 위해, 스프링에서 JPA를 Wrapping
- 스프링 개발자들이 JPA를 사용할 때 필수적으로 생성해야 하나, 예상 가능하고 반복적인 코드들
Spring Data JPA가 대신 작성
- Repository 인터페이스만 작성하면, 필요한 구현은 스프링이 알아서 해줌
2) Spirng Data 기본 제공 기능 예제
- 상품 Enitity 선언
@Entity
public class Product extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long userId;
private String title;
private String image;
private String link;
private int lprice;
private int myprice;
}
- Spring Data JPA) 상품 Repository 생성
public interface ProductRepository extends JpaRepository<Product, Long> {
}
- Spring Data JPA) 기본 제공해 주는 기능
// 1. 상품 생성
Product product = new Product(...);
productRepository.save(product);
// 2. 상품 전체 조회
List<Product> products = productRepository.findAll();
// 3. 상품 전체 개수 조회
long count = productRepository.count();
// 4. 상품 삭제
productRepository.delete(product);
- ID 외의 필드에 대한 추가 기능은 interface만 선언해 주면, 구현은 Spring Data JPA가 대신해줌
public interface ProductRepository extends JpaRepository<Product, Long> {
// (1) 회원 ID 로 등록된 상품들 조회
List<Product> findAllByUserId(Long userId);
// (2) 상품명이 title 인 관심상품 1개 조회
Product findByTitle(String title);
// (3) 상품명에 word 가 포함된 모든 상품들 조회
List<Product> findAllByTitleContaining(String word);
// (4) 최저가가 fromPrice ~ toPrice 인 모든 상품들을 조회
List<Product> findAllByLpriceBetween(int fromPrice, int toPrice);
}