
▶ 제 06장. 데이터베이스 연동
1. ORM (Object Relational Mapping)
- 객체와 RDB(Relational Database)의 테이블을 자동으로 매핑하는 방법
- 쿼리문 작성이 아닌 코드(메서드)로 데이터를 조작 할 수 있음
- 애플리케이션의 객체 관점과 DB의 관계 관점의 불일치
- 세분성 : 클래스가 테이블의 수보다 많아질 수 있음
- 상속성 : RDBMS에는 상속이라는 개념이 없음
- 식별성 : RDBMS는 기본키로 동일성을 정의하지만 자바는 두 객체의 값이 같아도 다르다고 판단 할 수 있음
- 연관성 : 객체지향에서는 객체 참조시 방향성 존재하지만 RDBMS에서 외래키를 삽입하는 것은 양방향의 관계를 갖음
- 탐색 : 자바에서는 값에 접근을 위해 그래프 형태(예. member.getOrganization().getAddress()와 같이 접근)의 접근법을 사용하지만 RDBMS에서는 쿼리를 최소화하고 JOIN을 통해 여러 테이블을 로드하고 값을 추출함
2. JPA (Java Persistence API)
- Java ORM 기술의 표준 인터페이스의 모음 -> 인터페이스이기 때문에 구현체는 존재하지 않음
- 내부적으로 JDBC를 사용하지만 개발자가 대신 SQL을 생성하고 DB를 조작해서 객체를 자동 매핑하는 역할을 수행
3. 하이버네이트 (Hibernate)
- 자바의 ORM 프레임워크로, JPA의 구현체
- Spring Data JPA : 하이버네이트의 기능을 더욱 편하게 사용하도록 모듈화 한 스프링 하위 프로젝트 중 하나
4. 영속성 컨텍스트 (Persistence Context)
- 엔티티 객체의 매핑 정보를 DB에 반영하는 작업을 수행 -> 엔티티와 테이블의 괴리를 해소, 객체 보관
5. 데이터 베이스 연동(application.yml파일 설정)
- ddl-auto(운영환경 : validate, none 사용 / 개발환경 : create, update 사용)
- create : 애플리케이션 가동 후 SessionFactory가 실행될 때마다 기존 테이블을 지우고 다시 생성
- create-drop : create와 동일한 기능을 수행하지만 애플리케이션 종료시 테이블 삭제
- update : SessionFactory가 실행될 때 객체의 변경이 감지된 스키마만을 갱신
- validate : update처럼 객체를 검사하지만 스키마는 건드리지 않고 DB의 테이블 정보와 객체 정보가 다르면 에러 발생
- none : ddl-auto기능을 사용하지 않음
6. 엔티티 설계
- Spring Data JPA를 사용하면 DB에 테이블 작성없이 엔티티 객체를 생성하고 실행을 돌리면 됨
- 엔티티 관련 기본 어노테이션
- @Entity
- 해당클래스가 엔티티임을 명시
- 클래스 자체가 테이블과 일대일로 매핑
- @Table
- 클래스명과 테이블 이름을 다르게 지정하고 싶을 때 사용
- 대체로 DB와 자바의 명명법이 다르기 때문에 거의 사용
- @Id
- 테이블의 기본값 역할로 사용
- 모든 엔티티는 @Id어노테이션이 필요
- @GeneratedValue
- @Id어노테이션과 함께 사용
- 해당 필드의 값을 어떤 방식으로 자동으로 생성할지 결정
- 사용하지 않는 방식
- 애플리케이션에서 자체적으로 고유한 기본값을 생성하는 경우
- 내부에 정해진 규칙에 의해 기본값을 생성하고 식별자로 사용
- AUTO
- @GeneratedValue의 기본 설정값
- 기본값을 사용하는 DB에 맞게 자동 생성
- IDENTITY
- 기본값 생성을 DB에 위임하는 방식
- DB의 AUTO_INCREMENT를 사용해 기본값을 생성
- SEQUENCE
- @SequenceGenerator 어노테이션으로 식별자 생성기를 설정하고 이를 통해 자동 주입 받음
- SequenceCenertor를 정의할 때는 name, SequenceName, allocationSize를 활용
- @GeneratedValue에 생성기를 설정
- TABLE
- 어떤 DBMS를 사용하더라도 동일하게 동작하기를 원할 경우 사용
- 식별자로 사용할 숫자의 보관 테이블을 별도로 생성해서 엔티티를 생성할 쌔마다 값을 생신하며 사용
- @TableGenerator 어노테이션으로 테이블 정보를 설정
- 사용하지 않는 방식
- @Column(자주 사용하는 것들)
- name : DB의 칼럼명을 설정
- nullable : 레코드를 생성할 때 칼럼 값에 null 처리가 가능한지를 명시
- length : DB에 저장하는 데이터의 최대 길이를 설정
- unique : 해당 칼럼을 유니크로 설정
- @Transient
- 엔티티 클래스에는 선언되어 있지는 필드 이지만 DB에서 필요 없는 경우
- @Entity
7. 리포지토리 인터페이스 설계
- JpaRepository를 상속 받아서 사용
- 예) public interface ProductRepository extends JpaRepository<Product, Long> {}
- 대상 엔티티와 기본값 타입을 지정
- 리포지토리 메서드의 생성 규칙
- FindBy : SQL문의 where절 역할을 수행하는 구문, findBy 뒤에 엔티티의 필드값을 입력해 사용 예)findByName(String name)
- AND, OR : 조건을 여러 개 설정하기 위해 사용 예) findByNameAndEmail(String name, String email)
- Like/NotLike : SQL문의 like와 동일한 기능을 수행하며 특정 문자를 포함하는지 여부를 조건으로 추가, 비슷한 키워드로 Containing, Contains, isContaing이 있음.
- StartsWith/StartngWith : 특정 키워드로 시작하는 문자열 조건을 설정
- EndsWith/EndingWith : 특정 키워드로 끝나는 문자열 조건을 설정
- IsNull/IsNotNull : 레코드 값이 Null이거나 Null이 아닌 값을 검색
- True/False : Boolean 타입의 레코드를 검색할 때 사용
- Before/After : 시간을 기준으로 값을 검색
- LessThan/GreaterThan : 특정 값(숫자)를 기준으로 대소 비교를 할 때 사용
- Between : 두 값(숫자) 사이의 데이터를 조회
- OrderBy : SQL문에서 order by와 동일한 기능을 수행 예)List<Product> findByNameOrderByPriceAsc(String name)
- countBy : SQL문의 count와 동일한 기능을 수행하며, 결과값의 개수(count)를 추출
8. DAO 설계
- DB에 접근하기 위한 로직을 관리하기 위한 객체(Spring Data API에서는 Repository가 대체)
- DAO 구현체 예제
- insert : productRepository.save(product);
- select
- productRepository.getById(number);
- EntityManager의 getReference()메서드를 호출 -> 프락시 객체를 리턴
- 실제로 쿼리는 프락시 객체를 통해 최초로 데이터에 접근하는 시점에 실행
- 객체 존재 하지 않을 경우 EntityNotFoundException발생
- findById보다 성능과 Optional객체에 대한 처리가 필요 없어 개발에 용이
- productRepository.findById(number);
- EntityManager의 find()메서드를 호출
- 영속성 컨텍스트의 캐시에서 값을 조회 -> 캐시에 없으면 DB 조회
- 객체 존재하지 않을 경우 리턴 값으로 Optional객체 전달
- Null값에 따른 처리를 하고 싶을 때 용이
- productRepository.getById(number);
- update : find()를 통해 해당 객체를 영속성 컨텍스트의 캐시에 추가하고 set()을 통해 객체에 변경을 주면 변경을 감지해서 값을 변경하고 다시 save()를 통해 값이 갱신됨.
- delete : update처럼 삭제하고자 하는 객체가 영속성 컨텍스트에 가져와야 하기 때문에 find()후에 delete()메서드롤 통하여 삭제