카테고리 없음

53일차 공부 DB연동

이즈라핀 2020. 6. 22. 12:39

** 데이터 베이스 연동 준비

=> 데이터베이스에 접속

=> 프로그래밍 언어와 연동에 필요한 드라이버

 

1. Dynamic Web Project 생성

=> 프로젝트 설정 파일인 web.xml 파일이 포함되도록 생성

=> servlet-api.jar 파일과 jstl.jar 파일을 WebContet/WEB-INF/lib 디렉토리에 복사

servler-api.jar : JDK SE 버전을 설치한 상태에서 HttpServlet 클래스를 사용하기 위해서 

jstl.jar : jsp 페이지에서 if 나 for 를 java 코드를 이용하지 않고 사용하기 위해서

 

2, JDBC

=> Java를 이용해서 데이터베이스에 접속하는 방식

 

3. JDBC를 사용하는 방법

1) JDK가 제공하는 API를 이용하는 방법

=> Connection, Statement(PreparedStatement, CallableStatement), ResultSet을 이용

 

2) 프레임워크를 이용하는 방법

=> JPA(Hibernate - SI를 제외한 전분야에서 이용), MyBatis(공공기관 SI에서 주로 이용)

Spring의 JDBC프레임워크(공부할 때만 이용)

 

4. 연동 방법

1) 데이터베이스 드라이버를 애플리케이션에 사용할 수 있도록 복사

=> 일반 Application의 경우는 build path에 복사

=> Web Application의 경우는 WebContent/WEB-INF/lib 디렉토리에 복사

=> maven의 경우는 porm.xml에 작성 : Spring

=> gradle의 경우는 json 파일에 작성 : Android

 

2) Driver Class를 로드

=> 한번만 수행 - 일반 Application에서는 하지 않아도 됨

Class.forName(String driverClassName)

=> 데이터베이스 종류마다 다름

MySQL : com.mysql.jdbc.Driver

Oracle : oracle.jdbc.driver.OracleDriver

 

3) 데이터베이스 연결

Connection 연결변수명 = DriverManager.getConnection(String url, String account, String password);

=> url은 데이터베이스 종류마다 다르게 설정

MySQL : jdbc:mysql://HOST:PORT/DBNAME   (대문자는 바뀌는 부분)

Oracle : jdbc:oracle:thin:@HOST:PORT:SID - Oracle 11g 까지 기본

              jdbc:oracle:thin:@HOST:PORT:SERVICENAME - Oracle 12g 이후 기본

 

4) 데이터베이스 사용

=> Statement를 이용해서 SQL을 실행

=> int 나 ResultSet으로 실행 결과를 받아서 사용

 

5) 사용한 자원을 반납 - close()

 

 

CREATE table Item(
	code int PRIMARY KEY auto_increment,
	title char(50) NOT NULL,
	category varchar(50),
	description text
)engine=innodb DEFAULT charset=utf8;

 

 

--샘플 데이터 작성
insert into Item values(1, 'Java', 'language','오픈소스 라이브러리가 많은 범용 프로그래밍 언어');
insert into Item values(2, 'Eclipse', 'IDE', '프로그래밍을 편리하게 할 수 있도록 해주는 오픈 소스 프로그램');
insert into Item values(3, 'Tomcat', 'Web Application Server','apache web server를 이용하는 오픈 소스 프로그램');
insert into Item values(4, 'Oracle', 'DataBase', '대기업이나 공공기관이 주로 사용하는 관계형 DBMS');
insert into Item values(5, 'MySQL', 'DataBase', '오픈 소스 기반의 관계형 DBMS - Maria DB와 거의 동일');
insert into Item values(6, 'MongoDB', 'DataBase', '가장 많이 언급되는 NoSQL');
insert into Item values(7, 'DBeaver', 'DB Tool', '데이터베이스 사용을 쉽게 해주는 프로그램');
insert into Item values(8, 'Spring', 'Framework', '자바 프레임워크');
insert into Item values(9, 'Android', 'Mobile OS', 'OHA 컨소시엄이 만든 Linux 기반의 모바일 운영체제');
insert into Item values(10, 'Android Studio', 'IDE', 'Android 용 프로그램을 개발할 수 있도록 해주는 프로그램');
insert into Item values(11, 'iOS', 'Mobile OS', 'Apple이 만든 unix 기반의 모바일 운영체제');
insert into Item values(12, 'Xcode', 'IDE', 'iOS 및 Mac 용 프로그램을 개발할 수 있도록 해주는 프로그램');
commit;
select * from Item;

 

 

6. 데이터베이스 드라이버를 프로젝트에 복사

=> WebContent/WEB-INF/lib 디렉토리에 복사

 

7. 연동할 데이터베이스 테이블을 표현할 DTO 클래스를 생성

=> domain.ITEM

package domain;

 

package javaweb0622;

public class Domain {
	//클라이언트용 ㅡ프로그램이면 속성 앞의 접근 지정자를 public으로 하고 
	//생성자만 만들고 접근자 메소드는 만들지 않아도 된다.
	private int code;
	private String title;
	private String category;
	private String description;
	
	public Domain() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Domain(int code, String title, String category, String description) {
		super();
		this.code = code;
		this.title = title;
		this.category = category;
		this.description = description;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getCategory() {
		return category;
	}

	public void setCategory(String category) {
		this.category = category;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	@Override
	public String toString() {
		return "Domain [code=" + code + ", title=" + title + ", category=" + category + ", description=" + description
				+ "]";
	}

}

 

 

 

 

8. Item 테이블과 연동할 Dao 클래스를 생성하고 필요한 변수와 연결 메소드와 해제 메소드를 생성 - 서버에서 사용할 거면 싱글톤 패턴으로 디자인

=> dao.

=> Connection, PreparedStatement, ResultSet 이 필요

 

package dao;

import java.sql.DriverManager;
import java.sql.ResultSet;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;

public class ItemDao {
	
	// 데이터베이스 연동에 필요한 변
	private Connection con;
	private PreparedStatement pstmt;
	private ResultSet rs;
	
	private ItemDao() {
		// 드라이버 클래스 로드
		// 한번만 수행하면 되기 때문에 생성자에 작성
		try {
			Class.forName("");
		}catch(Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}		
	}
	
	private static ItemDao itemDao;
	
	public static ItemDao sharedInstance() {
		if(itemDao ==null) {
			itemDao = new ItemDao();
		}
		return itemDao;
	}
	//연결메소드와 해제메소드
	//연결과 해제는 모든 곳에서 사용이 되는 부분이므로 중복해서 코딩하지 않을려고 별도의 메소드로 생성
	//이 메소드는 코드의 중복을 회피할려고 만든 메소드이므로 private 으로 생성해서 외부에서 호출하지 못하도록 생성
	private void connect() {
		try {
			con = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/sample?useUnicode=true&characterEncoding=utf8",
					"root","password");
		}catch(Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
	}
	
	private void close() {
		try {
			if(rs != null) {
				rs.close();				
			}
			if(pstmt !=null) {
				pstmt.close();
			}				
			if(con != null) {
				con.close();
			}
		}catch(Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
	}
}

 

 

 

9. Service 인터페이스와 ServiceImpl 구현

=> 사용자의 요청마다 호출되는 형태라서 템플릿 메소드 패턴을 적용하고 사용자의 요청 1개에 메소드 1개씩 매핑되는 것이 좋다.

 

1) Service 인터페이스를 생성

=> service.ItemService

 

package service;

public interface ItemService {

}

 

 

2) ServiceImpl 클래스를 생성

=> service.ItemServiceImpl

=> Service 인터페이스를 implements

=> Dao 클래스를 주입받아야 한다.

 

 

package service;

import dao.ItemDao;

public class ItemServiceImpl implements ItemService {
	private ItemDao itemDao;
	
	private ItemServiceImpl() {
		//Dao 인스턴스를 생성
		itemDao = ItemDao.sharedInstance();
	}
	private static ItemService itemService;
	
	public static ItemService sharedInstance() {
		if(itemService == null) {
			itemService = new ItemServiceImpl();
		}
		return itemService;
	}
}

 

 

10. Controller 클래스를 생성

=> HttpServlet 으로 부터 상속

=> service를 주입받아야 한다.

=> 싱글톤을 설정하지 않아도 WAS가 싱글톤으로 처리를 한다

=> url 패턴을 설정해야 하는데 이전에는 확장자 패턴을 많이 사용했고 최근에는 디렉토리 패턴에 작업을 기재하는 형태로 많이 작성

=> iteminsert.do 이런식이었는데 item/insert 형태로 많이 작성

=> item 디렉토리 패턴을 사용

=> controller.ItemController

 

 

package controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import service.ItemService;
import service.ItemServiceImpl;


@WebServlet({ "/", "/item/*" })
public class itemController extends HttpServlet {
	private static final long serialVersionUID = 1L;
      
	// 서비스 인스턴스 참조 변수
    private ItemService itemService;
    public itemController() {
        super();
        itemService = ItemServiceImpl.sharedInstance();
    }

	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//공통된 부분을 제거하는 주소를 만든다
		String contextPath = request.getContextPath();
		String reqeustURI = request.getRequestURI();
		String command = requestURI.substring(contextPath.length());
		//전송방식을 저장
		String method = request.getMethod();
	}

	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		doGet(request, response);
	}

}

 

 

11. 시작페이지 출력 작업

=> WebContent 디렉토리에 index.jsp 파일로 생성

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Item CRUD</title>
</head>
<body>
	<h3>ITEM CRUD 작업</h3>
</body>
</html>

 

 

2) Controller 클래스의 doGet 메소드에서 시작 요청을 처리하는 코드를 작성

=> 시작 요청이 오면 WebContent/index.jsp로 포워딩 하도록 설정

=> 단순한 페이지 이동은 포워딩으로 처리

		//시작 요청이 온 경우 index.jsp 페이지로 포워
		if(command.contentEquals("/")) {
			RequestDispatcher dispatcher = request.getRequestDispatcher("index.jsp");
			dispatcher.forward(request, response);
		}

 

 

3) 404에러가 난 경우

=> Controller의 가장 상단에 어노테이션에서 처리하지 않는 요청이 아닌지 확인

=> doGet 메소드의 비교하는 문장에서 요청하는 처리르 맞게 했는지

=> 포워딩할 주소와 실제 페이지의 위치가 같은지 확인

=> 이클립스와 톰캣의 경우 가끔 html 페이지이면 못읽고 jsp 페이지이면 읽어내는 경우가 발생한다.

 

 

12. 조회 작업

=> 전체 데이터 조회를 한다던가 데이터 개수를 파악하는 일들을 할 때 파라미터가 없다.

여러 데이터 중에서 하나의 데이터를 선택해서 조회하는 경우는 파라미터로 기본키의 값을 주어야 한다.

조건을 주고 데이터를 조회하는 경우는 파라미터로 조건과 값을 주어야 한다.

페이징 처리를 할 때는 현재 페이지 번호와 데이터개수를 추가로 넘겨주어야 한다.

조회를 할 때 매개변수가 많은 경우는 4개까지가 된다.

현재 페이지 번호, 페이지 당 데이터 개수, 검색에 사용할 필드명, 검색에 사용할 값이다.

 

=> 리턴 타입은 4가지

Scala Type : 데이터 개수를 찾아오는 경우, ID 나 Nickname 존재 여부

Map 이나 DTO : 기본키를 가지고 데이터 1개를 조회하는 경우(상세보기 나 로그인)

 

List<Scala> : id 목록을 가져온다던가 하는 경우

List<Map 이나 DTO> : 기본키가 아닌 항목을 가지고 데이터를 조회하는 경우

 

13. 전체 데이터 조회

1) 요청을 생성

 

<body>
	<h3>ITEM CRUD 작업</h3>
	<ul>
		<li><a href="item/list">전체 데이터 조회</a></li>
	</ul>
</body>

 

2) DAO 작업

=> 메소드 모양 : public List<Item> list();

=> sql 작성 : item 테이블의 전체 데이터를 가져오는 SQL - select * from item;

데이터가 2개 이상이면 order by를 이용해서 정렬하는 것이 좋은데 기본키로 오름차순정렬해서 출력하고자 하는 경우는 order by를 사용할 필요가 없다.

관계형 데이터베이스는 order by 가 없으면 기본키로 오름차순 정렬해서 가져온다.

 

3) Service 작업

=> ServiceImpl

 

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface ItemService {
	//Item 테이블의 모든 데이터를 읽어오는 메소
	public void list (HttpServletRequest request, HttpServletResponse response);
}

 

 

 

 

		@Override
		public void list(HttpServletRequest request, HttpServletResponse response) {
			// 1. 파라미터 읽기
			
			// 2. 파라미터 변환 작업이나 알고리즘 처리
			
			// 3. 호출할 Dao 메소드의 매개변수를 생성
			
			// 4. Dao의 메소드를 호출해서 결과를 저장
			List<Item> list = itemDao.list();
			
			// 5. Dao 메소드 호출 결과를 View로 전달하기 위해서 request 나 session에 저장
			
			// 포워딩 할 거면 request
			// 리다이렉트 할거면 session
			request.setAttribute("list", list);			
		}

 

 

 

 

4) Controller 작업

=> 요청에 필요한 Service 메소드를 호출하고 그 결과를 확인해서 필요한 View페이지로 이동하도록 작성

=> 조회는 필요한 메소드를 호출하고 결과 페이지로 포워딩 시키면 된다.

 

		}else if(command.contentEquals("/item/list")) {
			//전체 데이터를 가져오는 서비스 메소드를 호출
			itemService.list(request, response);
			// 결과 페이지로 이동
			// 현재 요청이 /item/list이므로 ../view/list.jsp이면
			// WebContent/view/list.jsp가 된다.
			RequestDispatcher dispatcher = 
					request.getRequestDispatcher(
						"../view/list.jsp");
			dispatcher.forward(request, response);
		}

 

 

 

5) View 작업 - Designer가 있는 경우에는 데이터의 모양을 설명하고 이 작업을 동시에 진행

=> WebContent 디렉토리에 view 디렉토리를 생성하고 list.jsp 파일을 만들어서 출력

 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- 제어문 사용을 위한 태그 라이브러리를 설정 -->
<%@ taglib prefix="c" 
	uri="http://java.sun.com/jsp/jstl/core" %>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>목록보기</title>
</head>
<body>

	<h3 align="center">데이터 목록 보기</h3>
	<table align="center" border="1">
		<tr>
			<th>코드</th>
			<th>카테고리</th>
			<th>이름</th>
		</tr>
		<c:forEach var="item" items="${list}">
			<tr>
				<td>&nbsp;${item.code}</td>
				<td>&nbsp;${item.category}</td>
				<td>&nbsp;${item.title}</td>
			</tr>	
		</c:forEach>	
	</table>
</body>
</html>

 

 

6) 처리 과정

=> index.jsp 에서 전체 데이터 조회를 클릭하면 ItemController의 doGet 메소드로 가야한다.

여기서 출력이 안되면 URL을 확인해야 한다.

 

=> ItemController에서 ItemServiceImpl의 메소드를 호출한다.

여기서 출력이 안되면 메소드를 잘못 호출

 

=> ItemServiceImpl 클래스에서 ItemDao의 메소드를 호출한다.

여기서 출력이 안되면 메소드를 잘못 호출

 

=> ItemDao 클래스의 마지막에서 리턴할 데이터를 출력한다.

여기서 출력이 잘못되었으면 sql과 매개변수를 확인

매개변수가 잘못된 경우 ItemServiceImpl에서 잘못 넘겨주었을 수도 있다.

 

=> ItemServiceImpl 에서 ItemDao 메소드의 매개변수를 출력

 

=> ItemController의 doGet 메소드로 와서 결과 페이지이름과 출력페이지이름이 정확한지 확인 

이 부분이 잘못되면 404 에러가 나던지 화면에 아무것도 보이지 않는다.

 

=> list.jsp 파일을 다시 확인

ItemServiceImpl에서 저장한 attribute 이름과 출력 내용이 일치하는지 확인

 

13. 데이터 삽입

=> 과정 : 삽입요청 -> 입력 페이지로 이동 -> 삽입 요청 -> 실제 데이터 삽입 요청 -> Controller -> 입력 페이지로 이동하고 사용자의 입력 -> Controller -> Service -> Dao -> Repository -> Dao -> ServiceImpl -> Controller -> 결과 페이지

=> 최근에는 입려 페이지로 이동하고 처리하는 형태의 1개의 URL만 사용하고 입력페이지로 이동할 대는 GET방식을 이용하고 처리하는 부분은 POST방식으로 처리

 

=> 기본키

기본키 나 unique 속성의 데이터를 입력하는 경우는 중복검사를 해줘야 한다.

기본키를 자동설정하는 경우는 sequence 나 auto_increment를 이용하는 경우는 삽입하는 SQL에서 처리하고 가장 큰 번호를 찾아서 +1을 하는 경우에는 Dao에 메소드를 추가해야 한다.

 

가장 큰 번호를 찾아서 +1 을 하는 방식은 Oracle 이나 MySQL이나 동일하기 때문에 데이터베이스를 변경해도 그대로 두면 된다.

하나의 SQL구문을 별도로 실행해야 한다는 점은 단점이다.

 

1) index.jsp 파일에 삽입 요청을 생성

<li><a href="item/insert">데이터삽입</a></li>

 

2) ItemController 클래스에 위의 요청을 처리하는 코드를 doGet에 삽입

 

		else if(command.equals("/item/insert") && method.equals("GET")) {
			//입력페이지로 이동
			RequestDispatcher dispatcher = 
					request.getRequestDispatcher(
						"../view/insert.jsp");
			dispatcher.forward(request, response);

 

3) WebContent/view/insert.jsp 파일을 추가하고 화면 디자인

 

 

4) ItemDao 클래스에 가장 큰 code를 찾아오는 메소드와 데이터를 삽입하는 메소드를 생성

=> 가장 큰 code를 찾아오는 메소드 - select max(code) form item

public int maxCode(){

 

}

	//가장 큰 글번호 찾아오는 메소드
	public int maxCode(){
		int result = 0;
		connect();
		try {
			//SQL을 생성
			pstmt = con.prepareStatement(
				"select max(code) from item");
			//SQL을 실행
			rs = pstmt.executeQuery();
			if(rs.next()) {
				result = rs.getInt("max(code)");
			}
		}catch(Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
		close();
		return result;
	}

 

 

=> 데이터를 삽입하는 메소드

데이터 삽입과 갱신은 DTO 나 Map을 매개변수로 받고 정수를 리턴
데이터 삭제는 일반적으로 Primary Key를 매개변수로 받고 정수를 리턴
insert into 테이블이름(컬럼이름 나열) values(?,?....)

고정된 값은 ? 대신에 직접 입력(오라클에서 오늘 날짜는 sysdate, 일련번호는 시퀀스이름.nextval 등, MySQL에서는 Auto_Increment는 생략)

 

	public int insert(Item item) {
		//-1로 초기화해서 -1이 리턴되면 작업 실패
		int result = -1;
		connect();
		try {
			pstmt = con.prepareStatement(
				"insert into item(code, category, title, description) "
				+ "values(?,?,?,?)");
			//?에 값을 바인딩
			pstmt.setInt(1, item.getCode());
			pstmt.setString(2,  item.getCategory());
			pstmt.setString(3, item.getTitle());
			pstmt.setString(4, item.getDescription());
			
			//SQL 실행
			result = pstmt.executeUpdate();
			
		}catch(Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
		close();
		return result;
	}

 

 

 

 

5) ItemService 클래스에 데이터 삽입 처리를 위한 메소드를 생성

=> ItemService 인터페이스에 데이터 삽입 처리를 위한 메소드를 선언

 

	// Item 테이블에 데이터를 저장하는 메소드
	public void insert (
		HttpServletRequest request, 
		HttpServletResponse response);

 

 

=> ItemServiceImpl 클래스에 데이터 삽입 처리를 위한 메소드를 구현

		@Override
		public void insert(HttpServletRequest request, HttpServletResponse response) {
			// 1. 파라미터 읽기
			String category = request.getParameter("category");
			String title = request.getParameter("title");
			String description = request.getParameter("description");
			// 2. 파라터를 가지고 필요한 작업 수행
			// 가장 큰 code를 찾아서 +1을 해서 챙ㄷ에 대이
			int code = itemDao.maxCode() +1;
					
			// 3. 호출할 DAO 메소드의 매개변수를 생성
			Item item = new Item();
			item.setCode(code);
			item.setCategory(category);
			item.setTitle(title);
			item.setDescription(description);
			
			// 4. DAO 메소드를 호출
			int result = itemDao.insert(item);
			// 5. 결과를 저장
			request.getSession().setAttribute("result", result);
			}

 

 

 

6) ItemController 클래스의 doGet 메소드에 삽입요청을 처리하는 코드를 추가

 

		}else if(command.equals("/item/insert") 
				&& method.equals("POST")) {
			//삽입을 처리
			itemService.insert(request, response);
			//삽입하고 결과 페이지로 이동
			//작업을 수행했으므로 목록보기로 이동
			response.sendRedirect("list");