2021 11 21
2021-11-21¶
JPA OneToOne¶
- 참고 1: https://www.baeldung.com/jpa-one-to-one
- 참고 2: https://ict-nroo.tistory.com/126
- 참고 3: http://wonwoo.ml/index.php/post/1530
- 참고 4: https://kwonnam.pe.kr/wiki/java/jpa/one-to-one
- 일대일 관계 - 주 테이블이나 대상 테이블 중에서 FK를 넣을 테이블 선택 가능 - JPA 다대일 관계와 어노테이션만 다르고 거의 유사함 - 외래키가 있는 곳이 "외래키 관리자" (연관관계의 주인)
- 예시
- 한 유저당 <-> 하나의 메일 주소
- [User]
- Address에
@OneToOne-@JoinColumn을 통해서 User 테이블에 Address의 PK를 어찌 저장할지 명명할 수 있음 - 이를 명명함으로써 FK의 관리자로 명시할 수 있음!@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "address_id", referencedColumnName = "id") private Address address; // ... getters and setters }- [Address] - 여기에도
@OneToOne통해서 양방향 매핑
- 일대일 관계는 Shared PK를 가질 수 있다!
- 어차피 너<->나 일대일 매칭이면 PK를 공유할 수 있겠지?
- 굳이 다른 칼럼으로 FK로 매칭하지 않고 PK를 공유하자!
- 예시
@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) @PrimaryKeyJoinColumn // <= User의 PK를 Address의 FK로 연관짓겠다 private Address address; //... getters and setters } @Entity @Table(name = "address") public class Address { @Id @Column(name = "user_id") // <= Generated 말고 이걸로! private Long id; //... @OneToOne @MapsId // <= PK가 User 엔티티로 복사되겠다 @JoinColumn(name = "user_id") private User user; //... getters and setters }
- 주의할 점! - 안타깝게도 Lazy로딩이 적용되지 않음! 1. null값이 가능한 OneToOne은 프록시로 못 감쌈 2. 만약 null 값이 가능한 OneToOne에 프록시 객체 넣으면, 그 순간 null이 또 아니게 됨 3. 기본적으로 JPA 구현체는 OneToOne 관계에 Lazy 허용 안 함!
@JoinColumn 을 안쓴다면 네이밍 규칙¶
- 참고: https://docs.jboss.org/hibernate/jpa/2.2/api/javax/persistence/JoinColumn.html
@JoinColumn뜻 - 엔티티를 조인하도록 하는 칼럼의 명세를 정의합니다.
- name 속성
- FK 칼럼의 네이밍을 지정한다
- 규칙
-
@OneToOne혹은@ManyToOne이라면, FK는 Source 엔티티에 붙음 - 단방향@OneToMany라면, FK는 Target 엔티티에 붙음 -@ManyToMany||@OneToOne|| 양방향@ManyToOne/@OneToMany라면 FK는 Join Table에 붙음 - name이 없다면! - "참조엔티티_PK칼럼" 으로 FK 네이밍이 결정됨!
Spring 톰캣 쓰레드¶
- 참고: https://velog.io/@sihyung92/how-does-springboot-handle-multiple-requests
- 다중 요청 처리 과정 1. 스프링부트는 내장 서블릿 컨테이너인 Tomcat 사용 2. 톰캣은 다중 요청을 처리하기 위해 Thread Pool 생성 3. 유저의 요청 (HttpServletRequest) 가 들어오면 Thread Pool 에서 하나씩 Thread를 할당 - Dispatcher Servlet을 거쳐 유저 요청 처리 4. 작업 모두 수행하고 나면, 쓰레드풀로 반환됨
- Thread Pool의 기본 Flow
- 적절한 수로 쓰레드 유지하자!!
1. 첫 작업이 들어오면, "core size" 만큼 쓰레드를 생성
2. 유저 요청 (Connection/Server Socker에서 accept한 소켓 객체) 들어오면 작업 큐에 담아둠
3. Core Size의 스레드 중 idle 쓰레드가 있다면 작업 큐에서 꺼내 쓰레드에 작업 할당하여 작업 처리
- idle 없다면, 작업 큐에서 대기
- 작업 큐가 꽉 차면, 쓰레드 새로 생성
- 쓰레드 최대 사이즈 도달하고, 작업큐도 꽉차면, 추가 요청에 대해 connection-refused
4 태스크 완료되면 쓰레드 다시 idle로!
- 작업큐가 비어있고, core size 이상의 쓰레드가 생성되어있다면 쓰레드 파괴!
- 톰캣 설정
- 스프링부트에서는 내장 Tomcat을 활용
- application.yml / application.properties로 간편하게 톰캣 설정 바꿀 수 있음
server: tomcat: threads: max: 200 # 생성할 수 있는 쓰레드의 총 갯수 min-spare: 10 # 항상 활성화 되어있는 쓰레드의 갯수 accept-count: 100 # 작업 큐의 사이즈- 부트에서는 기본적으로 accept-count 안주면 "무한 대기열 전략" 으로 Integer.MAX 만큼 설정
- BIO Connector & NIO Connector - Connector - 소켓 연결을 수립하고, 데이터 획득하여, HttpServletRequest 객체로 변환하여, Servlet 객체에 전달 1. Acceptor에서 while 문으로 대기하며, 포트에서 Listening 하면서 Socket Connection 얻음 2. Socket Connection으로 부터 데이터 획득. 데이터 파싱해서 HttpServletRequest 생성 3. Servlet Container에 해당 요청 객체 전달 - ServletContainer 알맞은 서블릿을 찾아 처리 - BIO Connector - Java의 기본적 I/O 기술 - Connection 닫힐 때까지, 하나의 쓰레드는 특정 Connection에 할당 - 동시 사용되는 쓰레드 수 == 동시 접속 가능 사용자 수 - 문제: 쓰레드들이 idle로 너무 많이 오래 남아있어 - NIO Connector - Poller라는 별도의 스레드가 커넥션 처리! - Socket들을 캐시로 들고 있다가, 해당 Socket에서 처리 가능한 순간에만 쓰레드 할당! - serverSocket.accept()로 소켓 커넥션 얻음 - Selector에 다수의 채널이 등록되어, 데이터 읽을 소켓 얻음 - Poller는 "Max Connection" 까지 연결 수락하고, 셀렉터를 통해 채널 관리해서 작업 큐 사이즈와 관계없이 받아둘 수 있음!
JAVA NIO¶
- 참고: https://brunch.co.kr/@myner/47
- 개요 - IO 성능이 안좋은 자바... 개선이 필요해! - NIO == New Input/Output
- 자바 IO vs 자바 NIO - [스트림 vs 채널] - IO는 스트림 기반 - 데이터 읽기 위해 입력 스트림, 출력하기 위해 출력 스트림 생성해야함 - NIO는 채널 기반 - 채널은 스트림과 달리 양방향으로 입출력 가능 - [넌버퍼 vs 버퍼] - IO는 넌버퍼 - 기본적으로는 1바이트씩 읽는 넌버퍼방식 - BufferedInputStream, BufferedOutputStream 등으로 개선! - NIO는 버퍼 - 기본적으로 버퍼 사용! - [블로킹 vs 넌블로킹] - IO는 블로킹 - 입력 스트림의 read(), 출력 스트림의 write() 호출시 블로킹 - NIO는 블로킹, 넌블로킹 둘다!