Spring Boot + JPA_웹 애플리케이션 개발
스프링부트 starter를 이용하여 설치 진행
현재 maven에서 gradel로 넘어가는 추세이므로 gradle 사용
vscode
- Java Extension Pack 설치
- Spring boot Extension Pack 설치
- Lombok Annotations Support for VS Code 설치
Dependancy
- web
- thymeleaf
- JPA
- 데이터베이스 h2
- Lombok
- JUnit4 사용
testImplementation("org.junit.vintage:junit-vintage-engine") {
exclude group: "org.hamcrest", module: "hamcrest-core"
}
- 입력하지 않을 경우 JUnit5로 실행
Import
- vscode 실행 후 다운받은 스프링 부터 스타터 디렉토리 이동
- main에 있는 java 실행 후 localhost:8080 접근 가능한지 확인
- test.java 실행 후 문제 있는지 확인
lombok&test
- main에 Hello.java 생성
- test/java/*Application.java
라이브러리
gradle w dependencies : 의존 관계 리스트 출력
= VS code > Gradle > Dependencies > compileClasspath에 위치
Tymeleaf
- 스프링에서 호환이 잘되는 라이브러리
- jsp와 다르게 기본 마크업 문법을 따름
Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute ("data", "hello!!!!!");
return "hello";
}
}
model에 Key와 Value를 담아 view에 넘김
- return “hello” : resource > templates > hello.html 파일로 데이터 전송
정적 파일은 resource > static에 위치
랜더링 파일은 resource > template에 위치
수정한 파일을 바로 재시작하는 방법
- build.gradle > dependencies에 추가
implementation 'org.springframework.boot:spring-boot-devtools'
- application.properties에 옵션 추가
spring.devtools.livereload.enabled=true
spring.freemarker.cache=false
spring.thymeleaf.cache=false
또는
- application.yml에 옵션 추가
spring:
devtools:
livereload:
enabled: true
freemarker:
cache: false
thymeleaf:
cache: false
- 재시작 > 파일 수정 후 브라우저 새로고침
H2데이터베이스 설치
특징
- 개발 & 테스트 용도로 가벼운 DB
- 웹 환경, 웹 콘솔 환경 제공
환경 설정
- localhost로 변경후 웹 주소 이동
- url의 key값 유지
- JDBC URL은 프로젝트가 있는 루트까지의 위치
- 이후 JDBC URL 접속 시 jdbc:h2:tcp://localhost/FILE_PATH/jpashop 으로 접속
JPA와 DB 설정, 동작확인
- application.yml 옵션 추가
spring:
datasource:
url: jdbc:h2:tcp://localhost/Documents/basic_java/study_spring_jpa/jpasho;MVCC=TRUE
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
# show_sql: true
format_sql: true
logging:
level:
org.hibernate.SQL: debug
# SQL문의 파라미터 값을 알 수 있음
org.hibernate.type: trace
MVCC=TRUE
- 접근 할 때 더 빨리 처리
driver-class-name
- 데이터베이스 커넥션과 관련된 데이터 소스 설정 완료
- 위와 같이 설정하면 히카리 cpu를 사용하여 커넥션 풀이 스프링 부터에서 세팅을 완료
ddl-auto: create
- 자동으로 탭을 생성하는 모드
properties
- hibernate와 관련된 property 사용 가능
show_sql, format_sql
- 스프링 부트 매뉴얼에서 공부 가능
- 스프링 부트 > learn > 버전별 Ref.Doc
org.hibernate.SQL: debug
- hibernate에서 생성하는 SQL의 내용 모두 로깅으로 출력
- hibernate: show_sql의 차이점은 해당 내용은 시스템 아웃으로 출력
Repository
- 엔티티를 찾아주는 공간(=DAO)
- 해당 어노테이션은 Spring이 제공하는 기본 타입으로 컴포넌트 스캔의 대상이 되어 자동으로 스프링 빈에 등록되는 내용
@PersistenceContext
private EntityManager em;
- JPA를 사용하기 때문에 엔티티 매니저 필요
- 스프링 컨테이너에서 사용
- 스프링 부트가 @PersistenceContext 어노테이션을 읽어 주입을 해줌
김영한 팁
- 커맨드와 쿼리를 분류
Test코드
- 테스트 파일 생성(test 디렉토리 아래에 main에서 테스트 할 파일의 같은 위치)
@Autowired MemberRepository memberRepository;
- MemberRepository 내용 테스트를 위해 Autowired로 멤버 MemberRepository 인젝션
- 웬뎅 스타일
- Assertions.assertThat(찾을 내용의 아이디).isEqualTo(생성한 아이디)
- 엔티티 매니저를 통한 모든 데이터 변경은 항상 트랜잭션 안에서 이루어져야 함
Entity Class
- 실무에서는 가급적 Getter는 열어두고, Setter는 필요한 경우에만 사용
@GeneratedValue : 시퀀스 값 사용
@Column(name = "DB 테이블의 사용할 컬럼명")
- name을 사용 안할 경우 class 내부의 필드에 선언한 이름 그대로 사용
@Embeddable : JPA의 어떤 내장 타입이라는 것을 알려주기 위해 Embeddable 처리
- 어디에서든 내장이 될 수 있도록 도움
@Embedded : 임베디드 내장 타입을 포함했다는 표시
- Embeddable나 Embedded 둘 중 하나만 사용해도 되지만 그냥 두 개 다 적어도 무방
@Table(name = "DB 테이블에 사용할 테이블명")
- name을 사용 안할 경우 class에 선언한 이름 그대로 사용
@JoinColumn(name = "맵핑할 대상 테이블의 컬럼명(id)")
@OneToMany(mappedBy = "맵핑 대상자 테이블의 컬럼명(필드)")
- 양방향 연관 관계에서 주인을 정해 주어야 함
- 연관 관계에서 ‘을'일 경우 mappedBy의 누구에 의해 맵핑이 되었는지 작성
- 해당 annotation은 내가 매핑을 하는게 아닌, 위 대상자에 의해 매핑된 대상자라 표현(읽기 전용)
추상 클래스
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
- 상속관계의 전략을 지정할 때 부모 클래스에서 지정
- strategy에서 전략 방식을 지정
@DiscriminatorColumn(name = "dtype")
- 읽을 대상(자식 클래스)를 읽을 때 어떻게 읽을 것인지
@DiscriminatorValue(value = "A")
- 싱글 테이블에서 저장해둘 때 구분할 수 있도록 값을 지정
- 기본 값은 class명
@Enumerated(EnumType.타입 선택)
- ORDINAL : 숫자 형태
- STRING : 문자열 형태
엔티티 관계가 다대 다일 경우
@ManyToMany
엔티티와 엔티티 중간 테이블을 만들어 매핑이 필요
@JoinTable(name = "맵핑할 대상 테이블명",
joinColumns = @JoinColumn(name = "맵핑할 대상 테이블의 컬럼명"),
inverseJoinColumns = @JoinColumn(name = "item_id")
)
- inverseJoinColumns : 다대 다의 중간 테이블을 만들 때 상대쪽 엔티티의 매핑 컬럼
한쪽에서 위와 같은 작업을 끝낸 후 상대쪽 class에서는 아래와 같이 구성
@ManyToMany(mappedBy=”맵핑 대상자 테이블의 컬럼명(필드)”)
계층 트리
@ManyToOne
@JoinColumn(name = "parent_id")
private Category parent;
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
엔티티 설계시 주의
가급적 Setter 사용 x
- 변경 포인트가 많으면 유지보수에 어려움 발생. 리팩토링을 통해서라도 꼭 제거가 필요
모든 연관관계는 지연로딩(LAZY)으로 설정(중요)
즉시 로딩: 테이블을 조회할 때 연관된 내용을 한번에 조회
- 예측이 어렵다
- 어떤 SQL이 실행될지 추적이 어렵다
- 특히 JPQL을 실행할 때 N + 1문제가 자주 발생
연관된 엔티티를 함께 DB에서 조회
- fetch join 또는 엔티티 그래프 기능을 사용
@XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정
컬렉션은 필드에서 초기화
애플리케이션 구현
Repository
@Repository : 컴포넌트 스캔에 의해서 자동으로 Spring Bean으로 관리
@PersistenceContext : 스프링이 EntityManager를 만들어 주입
public void save(Member member) {
em.persist(member);
}
- persist를 하면 영속성 컨텍스트에 member Entity를 넣음
- 트랜잭션 commit되는 시점에 DB에 반영
JPQL
return em.createQuery("select m from Member m", Member.class)
.getResultList();
- SQL이랑 거의 똑같지만 JOIN 이랑 문법이 약간의 차이가 있다. JPQL은 결국 SQL로 번역됨
- SQL은 테이블을 대상으로 쿼리를 하지만, JPQL은 Entity 객체를 대상으로 쿼리
- 기본편 참고
Service
@Service : 컴포넌트 스캔에 의해서 자동으로 Spring Bean으로 관리
@Autowird
- 필드에 선언한 repository를 Injection
- 생성자가 하나일 경우 생략 가능
- Repository의 EntityManager는 PersistenceContext로만 인젝션이 가능했으나, 스프링부트 이후에는 해당 어노테이션을 사용하여 Repository에서 인젝션을 지원
@Transactional
- JPA의 모든 데이터 변경이나 로직들은 가급적 트랜잭션 안에서 실행되어야 함
- LAZY 로딩이 이루어짐
종류
- JavaX
- Spring
옵션
- readOnly = true
- 옵션은 해당 클래스에서 가장 많이 사용되는 내용을 전체로 묶어서 사용 후, 소수가 사용되는 옵션이 다를 경우에 다른 옵션을 재설정
- 필드(private)의 값을 선언할 경우 Setter로 할 경우
생성자
- 생성자 주입 방식을 권장
- 변경 불가능한 안전한 객체 생성 가능
@AllArgsConstructor
- 필드의 모든걸 가지고 생성자 생성
@RequiredArgsConstructor
- final 인 필드를 가지고 생성자 생성
Order Service의 save를 할 때, 연관되어있는 OrderItem과 Delivery는 CascadeType.ALL 로 묶여있기 때문에 Order가 persist할 경우 같이 persist 됨
숙제
스프링부트 3
JUnit5
Hibernate 6
위 버전에서 SQL Query 확인 불가
동적 쿼리
- JPQL : parameter 값이 있는지 없는지에 따라 Query를 붙이는 형태(복잡)
- JPA Criteria : 읽기가 힘들고 유지보수가 어렵기 때문에 사실상 실무에서 사용하기 어렵다.
- Query DSL
'끄적끄적 > Spring Boot' 카테고리의 다른 글
| 개인프로젝트_서버 구상 (0) | 2024.08.11 |
|---|---|
| Spring Boot Querydsl (0) | 2024.07.26 |
| Spring Boot Data JPA (0) | 2024.07.13 |
| Spring Boot 프로젝트 필기 (0) | 2024.06.29 |