국비학원_75일차(Api, myBatis)

써니·2022년 11월 8일
0

spring

목록 보기
17/23

API

🔧성능최적화

❌N+1 문제

연관 관계에서 발생하는 이슈로 연관 관계가 설정된 엔티티를 조회할 경우 조회된 데이터 갯수만큼 연관관계 조회 쿼리가 추가로 발생하여 데이터를 읽어오게 된다. 이를 N+1 라 한다


💡 N+1 문제해결

join Fetch

하나의 자식에만 적용가능


dafault_batch_fetch_size -> 여러자식이 있을 때

batch_size 옵션은 하위 엔티티를 로딩할 때 한번에 상위 엔티티 ID를 지정한 숫자 만큼 IN Query로 로딩해준다. 예를 들어 batch-size:500으로 되어있으면, 상위 엔티티인 store의 id 1000개를 In Query로 Product와 Employee를 조회하게 된다.

default_batch_fetch_size: 500

Custom JpaPagingItemReader

Batch에서는 위에 옵션이 적용이 안된다.
-> PaginItemReader의 경우 트랜잭션을 Chunk에 맡긴다.
-> 하지만 JpaPagingItemReader의 트랜잭션을 Reader에서 진행한다.
그래서 트랜잭션 안에서만 작동하는 dafault_batch_fetch_size가 단일객체에서만 발동




Mybatis

🔻oBootMybatis01

⚙ 초기 세팅


  • application.yml
server:
  port : 8391
  
# Oracle Connect
spring:
  datasource:
    url: jdbc:oracle:thin:@localhost:1521/xe
    username: scott
    password: tiger
    driver-class-name: oracle.jdbc.driver.OracleDriver
    
  # Jpa Setting
  jpa:
    hibernate: 
      ddl-auto: create # none create update
    properties: 
      hibernate:
        default_batch_fetch_size: 500
      show_sql: true      # System.out에 하이버네이트 실행 SQL
      format_sql: true
  
  # view Resolver
  mvc: 
    view: 
      prefix: /WEB-INF/views/
      suffix: .jsp
      
logging.level:
  org.hibernate.SQL: debug   # logger를 통해 하이버네이트 실행 SQL

view를 위한 폴더 생성




💡 Entity, Dto 개념과 차이점(+VO)

  • Entity
    Entity 클래스는 실제 DB 테이블과 매핑되는 핵심 클래스로 데이터베이스의 테이블에 존재하는 컬럼들을 필드로 가지는 객체이다.
    (DB의 테이블과 1:1로 매핑되며, 테이블이 가지지 않는 칼럼을 필드로 가져서는 안됩니다.)
    Entity는 데이터베이스 영속성의 목적으로 사용되는 객체이며, 요청이나 응답값을 전달하는 클래스로 사용하는 것은 좋지 않음
    -> 그렇기 때문에 setter 메서드의 사용을 지양한다. 이유는 변경되지 않는 인스턴스에 대해서 setter로 접근이 가능해지기 때문에 객체의 일관성, 안전성을 보장하기 힘들어진다. 그 대신 Constructor(생성자) 또는 Builder를 사용하게 됨

  • DTO
    DTO는 계층 간 데이터 교환이 이루어질 수 있도록 하는 객체로 Json serialization과 같은 직렬화에도 사용되는 객체이다. DTO는 원래 DAO 패턴에서 유래된 단어로 DAO에서 DB 처리 로직을 숨기고 DTO라는 결과값을 내보내는 용도로 활옹됨. Controller 같은 클라이언트 단과 직접 마주하는 계층에서는 Entity 대신 Dto라는 결과값을 내보내는 용도로 활용. 주로 View와 Controller 사이에서 데이터를 주고 받을 때 활용성이 높음. DTO는 getter, setter 메서드를 포함하며, 이 외의 비즈니스 로직을 포함하지 않음 참고



[com.oracle.oBootMybatis01.model]

  • Dept.java
package com.oracle.oBootMybatis01.model;

import lombok.Data;

@Data
public class Dept {
	private int detpno;
	private String dname;
	private String loc;
}
  • Emp.java
package com.oracle.oBootMybatis01.model;

import lombok.Data;

@Data
public class Emp {
	private int empno;			private String ename;
	private String job;			private int mgr;
	private String hiredate; 	private int sal;
	private int comm;			private int detpno;
	
	// 조회용
	private String search;		private String keyword;
	private String pageNum;
	private int start;			private int end;
}

[com.oracle.oBootMybatis01.dao]

  • EmpDao.java( interface )
package com.oracle.oBootMybatis01.dao;

public interface EmpDao {
	int			totalEmp();
}
  • EmpDaoImpl.java( +interface )
package com.oracle.oBootMybatis01.dao;

import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Repository
@Slf4j
@RequiredArgsConstructor
public class EmpDaoImpl implements EmpDao {
	
	private final SqlSession session;

	@Override
	public int totalEmp() {
		int tot = 0;
		log.info("totalEmp Start...");
		return 0;
	}
}

[com.oracle.oBootMybatis01.service]

  • EmpService.java( interface )
package com.oracle.oBootMybatis01.service;

public interface EmpService {
	int			totalEmp();
}
  • EmpServiceImpl.java( +interface )
package com.oracle.oBootMybatis01.service;

import org.springframework.stereotype.Service;

import com.oracle.oBootMybatis01.dao.EmpDao;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
@RequiredArgsConstructor
public class EmpServiceImpl implements EmpService {
	
	private final EmpDao ed;
	
	@Override
	public int totalEmp() {
		log.info("totalEmp Start...");
		return 0;
	}
}

[com.oracle.oBootMybatis01.controller]

  • EmpController.java
package com.oracle.oBootMybatis01.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.service.EmpService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
@RequiredArgsConstructor
public class EmpController {
	
	private final EmpService  es;
	
	@RequestMapping(value = "listEmp")
	public String empList(Emp emp, String currentPage, Model model) {
		log.info("listEmp Start...");
		
		return "list";
	}
}




  • build.gradle( dependencies 추가 )
    -> gradle -> refresh
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'javax.servlet:jstl'

[com.oracle.oBootMybatis01.configuration]

  • SecurityConfig.java

security 건너뛰기

package com.oracle.oBootMybatis01.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration //IoC 빈(bean)을 등록
@EnableWebSecurity // 필터 체인 관리 시작 어노테이션
public class SecurityConfig { // 환경작업
	
	// 암호화 빈 설정
	@Bean
	public BCryptPasswordEncoder encodePwd() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.csrf().disable();
		http.authorizeRequests()
			.anyRequest()
			.permitAll(); // 요청이 들어오면 다 permit
		return http.build();
	}
}

📂 views

  • list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>list.jsp 입성 성공</h1>
</body>
</html>




⚙ Mybatis 세팅

  • application.yml
server:
  port : 8391
  
# Oracle Connect
spring:
  datasource:
    url: jdbc:oracle:thin:@localhost:1521/xe
    username: scott
    password: tiger
    driver-class-name: oracle.jdbc.driver.OracleDriver
    
  # Jpa Setting
  jpa:
    hibernate: 
      ddl-auto: create # none create update
    properties: 
      hibernate:
        default_batch_fetch_size: 500
      show_sql: true      # System.out에 하이버네이트 실행 SQL
      format_sql: true
  
  # view Resolver
  mvc: 
    view: 
      prefix: /WEB-INF/views/
      suffix: .jsp

# Mybatis
mybatis:
  config-location: classpath:configuration.xml
  mapper-locations: classpath:mappers/*.xml
  
 
logging.level:
  org.hibernate.SQL: debug   # logger를 통해 하이버네이트 실행 SQL

📂 src/main/resources

  • configuration.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<typeAliases>
		<typeAlias alias="Emp" type="com.oracle.oBootMybatis01.model.Emp"/>
		<typeAlias alias="Dept" type="com.oracle.oBootMybatis01.model.Dept" />
	</typeAliases>
</configuration>

## 회원수 ### 📂 mappers - Emp.xml ```xml SELECT Count(*) FROM emp ```

[com.oracle.oBootMybatis01.controller]

  • EmpController.java
package com.oracle.oBootMybatis01.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.service.EmpService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
@RequiredArgsConstructor
public class EmpController {
	
	private final EmpService  es;
	
	@RequestMapping(value = "listEmp")
	public String empList(Emp emp, String currentPage, Model model) {
		log.info("empList Start...");
		int totalEmp = es.totalEmp();
		log.info("empList total=>{}", totalEmp);
		model.addAttribute("total", totalEmp);
		return "list";
	}
}

[com.oracle.oBootMybatis01.Service]

  • EmpServiceImpl.java
package com.oracle.oBootMybatis01.service;

import org.springframework.stereotype.Service;

import com.oracle.oBootMybatis01.dao.EmpDao;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
@RequiredArgsConstructor
public class EmpServiceImpl implements EmpService {
	
	private final EmpDao ed;
	
	@Override
	public int totalEmp() {
		log.info("totalEmp Start...");
		int totEmpCnt = ed.totalEmp();
		log.info("totalEmp totEmpCnt=>{}",totEmpCnt);
		return totEmpCnt;
	}
}

[com.oracle.oBootMybatis01.dao]

  • EmpDaoImpl.java
package com.oracle.oBootMybatis01.dao;

import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Repository
@Slf4j
@RequiredArgsConstructor
public class EmpDaoImpl implements EmpDao {
	
	// Mybatis DB 연결 -> JPA EntityManager
	private final SqlSession session;

	@Override
	public int totalEmp() {
		int totEmpCount = 0;
		log.info("totalEmp Start...");
		
		try {
			totEmpCount = session.selectOne("empTotal");
			log.info("totalEmp totEmpCount->{}", totEmpCount);
		} catch (Exception e) {
			log.info("totalEmp Exception->{}",e.getMessage());
		}
		return totEmpCount;
	}
}

📂 views

  • list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>list.jsp 입성 성공</h1>
	<h5>사원 수 : ${totalEmp}</h5> 
</body>
</html>




회원목록

[com.oracle.oBootMybatis01.controller]

  • EmpController.java
package com.oracle.oBootMybatis01.controller;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.oracle.oBootMybatis01.model.Emp;
import com.oracle.oBootMybatis01.service.EmpService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
@RequiredArgsConstructor
public class EmpController {
	
	private final EmpService  es;
	
	@RequestMapping(value = "listEmp")
	public String empList(Emp emp, String currentPage, Model model) {
		log.info("empList Start...");
		int totalEmp = es.totalEmp();
		log.info("empList total=>{}", totalEmp);
		
		// Parameter emp --> page만 추가 Setting(페이징)
		emp.setStart(1);
		emp.setEnd(10);
		
		List<Emp> listEmp = es.listEmp(emp);
		log.info("listEmp.size()->{}",listEmp.size());
		
		model.addAttribute("totalEmp", totalEmp);
		model.addAttribute("listEmp", listEmp);
		return "list";
	}
}

[com.oracle.oBootMybatis01.Service]

  • EmpService.java
package com.oracle.oBootMybatis01.service;

import java.util.List;

import com.oracle.oBootMybatis01.model.Emp;

public interface EmpService {
	int			totalEmp();

	List<Emp> listEmp(Emp emp);
}
  • EmpServiceImpl.java
package com.oracle.oBootMybatis01.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.oracle.oBootMybatis01.dao.EmpDao;
import com.oracle.oBootMybatis01.model.Emp;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
@RequiredArgsConstructor
public class EmpServiceImpl implements EmpService {
	
	private final EmpDao ed;
	
	@Override
	public int totalEmp() {
		log.info("totalEmp Start...");
		int totEmpCnt = ed.totalEmp();
		log.info("totalEmp totEmpCnt=>{}",totEmpCnt);
		return totEmpCnt;
	}

	@Override
	public List<Emp> listEmp(Emp emp) {
		List<Emp> empList = null;
		log.info("listEmp Start...");
		empList = ed.listEmp(emp);
		log.info("listEmp empList.size()->{}",empList.size());
		return empList;
	}
}

[com.oracle.oBootMybatis01.dao]

  • EmpDao.java
package com.oracle.oBootMybatis01.dao;

import java.util.List;

import com.oracle.oBootMybatis01.model.Emp;

public interface EmpDao {
	int			totalEmp();

	List<Emp> listEmp(Emp emp);
}
  • EmpDaoImpl.java
package com.oracle.oBootMybatis01.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;

import com.oracle.oBootMybatis01.model.Emp;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Repository
@Slf4j
@RequiredArgsConstructor
public class EmpDaoImpl implements EmpDao {
	
	// Mybatis DB 연결 -> JPA EntityManager
	private final SqlSession session;

	@Override
	public int totalEmp() {
		int totEmpCount = 0;
		log.info("totalEmp Start...");
		
		try {
			totEmpCount = session.selectOne("empTotal");
			log.info("totalEmp totEmpCount->{}", totEmpCount);
		} catch (Exception e) {
			log.info("totalEmp Exception->{}",e.getMessage());
		}
		return totEmpCount;
	}

	@Override
	public List<Emp> listEmp(Emp emp) {
		List<Emp> empList = null;
		log.info("listEmp Start...");
		//								Map ID		parameter
		try {
			empList = session.selectList("tkEmpListAll", emp);
			log.info("empList.size()->{}",empList.size());
		} catch (Exception e) {
			log.info("empList Exception->{}",e.getMessage());
		}
		return empList;
	}

}

📂 mappers

  • Emp.xml
<?xml version="1.0" encoding="UTF-8" ?>

<!-- ======= mapper 기본설정 ======= -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- ==== 루트 엘리먼트 & 네임스페이스 설정(프로젝트 전체 내에서 유일해야 한다.) ==== -->
<mapper namespace="com.oracle.oBootMybatis01.EmpMapper">
	<select id="empTotal" resultType="int">
		SELECT Count(*) FROM emp
	</select>
	
	<select id="tkEmpListAll" parameterType="Emp" resultType="Emp">
		select *
		from 
		(
		        select rownum rn, a.*
		        from
		        (select * from emp order by empno) a
		)
		where rn BETWEEN #{start} and #{end}
	</select>
</mapper>

📂 views

static -> css / js (정적파일)

  • header.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<link href="css/board.css" rel="stylesheet" type="text/css">
  • list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file="header.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>list.jsp 입성 성공</h1>
	<h5>사원 수 : ${totalEmp}</h5>
	
	<table>
		<tr>
			<th>번호</th>
			<th>사번</th>
			<th>이름</th>
			<th>업무</th>
			<th>급여</th>
		</tr>
		<c:forEach var="emp" items="${listEmp}">
			<tr>
				<td>${num}</td>
				<td>${emp.empno}</td>
				<td><a href="detail?empno=${emp.empno}">${emp.ename}</a></td>
				<td>${emp.job}</td>
				<td>${emp.sal}</td>
			</tr>
			<c:set var="num" value="${num - 1}"></c:set>
		</c:forEach>
	</table>
</body>
</html>

0개의 댓글