본문 바로가기
코딩도전기/DB(mariaDB)

CODO Day37_DB_JDBC(Java Data Base Connectivity)

by 코도꼬마 2023. 3. 27.

JDBC(Java Data Base Connectivity)

Java & Data Base 연결에 필요한 프로그래밍

  • JAVA 와 DB 연결 프로그래밍
  • DB 쿼리 실행 프로그래밍
  • DB 쿼리 반환 값 추출 프로그래밍

# but 라이브러리를 활용하면 간단하게 가능

  • 아래 파일을 다운받은 후 WEB-INF.lib에 copy

mariadb-java-client-2.7.2.jar
0.60MB

 

직접 연결 방법

  • STS 사용 시 더이상 MVC 패턴을 사용하지 않아, Model 대신 Service로 사용하나 비슷한 역할
  • Service : Controller에서 받은 작업 중 DB와 연결해야하는 작업을 DAO로 넘김
  • Package : Controller / DAO / Service

 

DAO & DTO

DAO DB 연결 및 쿼리문 실행
DTO DAO에서 주고 받는 데이터를 저장하기 위한 데이터 타입(Bean 역할)

 

  • DBcontroller
package kr.co.web.controller;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
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 kr.co.web.service.DBservice;

@WebServlet("/")
public class DBcontroller extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		//model 대신 service 객체화
		DBservice service = new DBservice();	
		//db와 연결하기 위해 dbConnection 메서드 실행
		String msg = service.dbConnection();
		req.setAttribute("msg", msg);
		RequestDispatcher dis = req.getRequestDispatcher("result.jsp");
		dis.forward(req, resp);		
	}
}

 

  • DBservice
package kr.co.web.service;

import kr.co.web.dao.HomeDAO;

public class DBservice {
	
	public String dbConnection() {
		System.out.println("DB 접속요청");
		HomeDAO dao = new HomeDAO();
		return dao.dbConnection();
	}
}

 

  • HomeDAO
    • 연결순서
      1. DB 접속 정보를 준비
      2. 추가한 라이브러리 드라이버를 불러옴
      3. 드라이버 매니저를 불러 접속 정보 넣어줌
      4. 드라이버 매니저가 가져온 커넥션을 받음
      5. Connection을 이용하여 데이터를 처리하고 닫기
package kr.co.web.dao;

import java.sql.Connection;
import java.sql.DriverManager;

public class HomeDAO {

	//DB 접속을 시도해 성공/실패 여부를 반환
	public String dbConnection() {
		
		System.out.println("DB 접속시도");
		
		//1. 사용하는 드라이버 라이브러리(mariadb)
		String driver = "org.mariadb.jdbc.Driver";
		
		//2. 접속정보 받기
		//mariadb 시작할 때 설정했던 host & localhost
		String host = "jdbc:mariadb://localhost:3306";
		//mariadb에 생성했던 username
		String username = "web_user";
		//user의 password
		String password = "pass";
        
		//아래에서 연결에 실패했을 경우 다음 msg 반환
		String msg = "DB접속에 실패하였습니다.";
		
		try {
			//3. 드라이버 매니저 부르기(위의 정보를 제공해야함)
			Class.forName(driver);
            
			//매개변수로 위의 정보를 넣어주고 DriverManager의 Connection을 가져옴
			Connection conn = DriverManager.getConnection(host, username, password);
			//DB와 연결되었으면 값을 반환하고, 연결되지 않으면 null을 반환
			System.out.println(conn);
            
			if(conn != null) {
				msg = "DB접속에 성공했습니다.";
			}		
			//자원을 반납하지 않으면 점점 느려짐
			//4. 자원 반납
			conn.close();
		} catch (Exception e) {
			e.printStackTrace();
		}		
		return msg;
	}
}

 

 

Connection Pool을 활용하여 연결

  • DB연결이 필요할 때 마다 만들었다 버리는 것은 비효율적이고 자원 낭비임
  • Connection Pool을 활용하면 Resource를 재사용 할 수 있음
  • Servers/Tomcat/context.xml에 사용할 Resource 저장 후 불러오는 방식

 

# common-dbcp 라이브러리 추가

commons-dbcp2-2.1.1.jar
0.17MB

 

# 프로젝트 생성 후 가장 먼저 context.xml에 사용할 Resource 추가

 

  • context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
    
    
    <Resource
    	name="jdbc/MariaDB"
    	auth="Container"
    	type="javax.sql.DataSource"
    	driverClassName="org.mariadb.jdbc.Driver"
    	url="jdbc:mariadb://localhost:3306/mydb"
    	username="web_user"
    	password="pass"
    />
    
    
</Context>

 

  • PoolController
package kr.co.web.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 kr.co.web.service.PoolService;

@WebServlet("/")
public class PoolController extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		PoolService service = new PoolService();
		String msg = service.dbConnection();		
		System.out.println(msg);
	}
}

 

  • PoolService
package kr.co.web.service;

import kr.co.web.dao.PoolDAO;

public class PoolService {
	
	public String dbConnection() {
		
		PoolDAO dao = new PoolDAO();
		
		return dao.dbConnection();
	}	
}

 

  • PoolDAO
package kr.co.web.dao;

import java.sql.Connection;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class PoolDAO {
	
	public String dbConnection() { 
		
		String msg = "DB 접속에 실패하였습니다.";
		
		//conext.xml에서 데이터 꺼내오는 방법
        
		//1. context.xml을 자바객체화 시킴
        //Context는 interface이기 때문에 InitialContext로 객체화
		try {
			Context ctx = new InitialContext();
			//2. Resource 찾아옴(name을 사용) 
			//3. 찾아온 내용을 DataSource객체로 변환
            //java 컴파일 환경에서 이름이 'jdbc/MariaDB'인 리소스 찾아오기
            //리소스는 Object 객체이기 때문에 DataSource 타입으로 형변환
			DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MariaDB");
            
			//4. Connection 받아오기
            //ds에서 Connection 받아와 conn에 담기
			Connection conn = ds.getConnection();
            
            //conn에 값이 담기면 db와 연결성공/null이면 연결실패
			if(conn != null) {
				msg = "DB 접속에 성공하였습니다.";
                
				//5. 자원반납
				conn.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return msg;
	}	
}

 

 

 

DB에 쿼리문 넣기

 

Query문 실행

  1. Statement 쿼리문으로 실행
    • 한번 실행 후 다시 실행되지 않으면(예: 최초 table 생성) Statement가 효율적임
  2. Prepared Statement 쿼리문으로 실행(VALUES()에 값이 아닌 ?를 넣어 다양한 값 대응)
    • Prepared Statement는 문장을 컴파일 한 후 재사용
    • 문장이 여러번 반복될 때 유리 / 성능&속도 우수

 

Query문 반환

  • executeQurey()
    • 반환되는 데이터가 있을 경우 사용 ex) SELECT
    • 결과값을 ResultSet 객체로 반환
    • ResultSet 객체에 대한 MetaData를 뽑아 낼 수 있음
close() ResultSet 객체를 반환
getTYPE(int ColumnIndex) 레코드 값을 지정한 타입으로 가져옴(컬럼 인덱스 지정)
getTYPE(String ColumnIndex) 레코드 값을 지정한 타입으로 가져옴(컬럼명 지정)
next() 다음행으로 커서 이동(데이터 존재 여부를 boolean 값으로 반환)

 

  • executeUpdate()
    • DB에 변화를 줄 경우 사용 ex) UPDATE, DELETE, INSERT
    • 사용 시 반환되는 int는 적용에 성공한 데이터 행(row)의 개수

 

  • HomeController
    • 들어오는 요청 관리
package kr.co.web.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 kr.co.web.service.HomeService;

@WebServlet(urlPatterns = {"/","/stmt","/pstmt","/resultSet"})
public class HomeController extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		
		String uri = req.getRequestURI();
		String ctx = req.getContextPath();
		String addr = uri.substring(ctx.length());
		
		HomeService service = new HomeService(req,resp);
		
		switch (addr) {
		case "/":
			service.home();
			break;

		case "/stmt":
			service.stmt();
			break;
			
		case "/pstmt":
			service.pstmt();
			break;
			
		case "/resultSet":
			service.resultSet();
			break;
		}
	}
}

 

  • HomeService
package kr.co.web.service;

import java.io.IOException;

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

import kr.co.web.dao.HomeDAO;

public class HomeService {

	HttpServletRequest req;
	HttpServletResponse resp;
	RequestDispatcher dis;
    
    //DB 처리가 필요할 경우 사용하기 위해 DAO 객체화
    HomeDAO dao = new HomeDAO();
	
	public HomeService(HttpServletRequest req, HttpServletResponse resp) {
		this.req = req;
		this.resp = resp;
	}

	public void home() throws ServletException, IOException {
		dis = req.getRequestDispatcher("main.jsp");
		dis.forward(req, resp);
	}

	public void stmt() throws ServletException, IOException {		
		try {
			int row = dao.stmt();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			req.setAttribute("msg", "테이블 생성 완료");
			dis = req.getRequestDispatcher("result.jsp");
			dis.forward(req, resp);
		}		
	}

	public void pstmt() throws ServletException, IOException {
		String msg = "데이터 수정에 실패했습니다.";
		try {
			int row = dao.pstmt();
            //row가 1일 경우 1개의 데이터를 update 했다는 것으로 원하는 작업 성공
            //0 : 데이터 update 작업이 수행되지 않음
            //row>1 : 여러개의 데이터가 수정된 것으로 의도와 다른 데이터들도 수정됨
			if(row == 1) {
				msg = "데이터가 1개 삽입되었습니다.";
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			req.setAttribute("msg", msg);
			dis = req.getRequestDispatcher("result.jsp");
			dis.forward(req, resp);
		}
		
	}

	public void resultSet() throws ServletException, IOException {
		String msg = "데이터를 가져오는데 실패했습니다.";
		try {
			if(dao.resultSet()) {
				msg = "데이터를 성공적으로 가져왔습니다.";
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			req.setAttribute("msg", msg);
			dis = req.getRequestDispatcher("result.jsp");
			dis.forward(req, resp);
		}		
	}	
}

 

  • HomeDAO
package kr.co.web.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class HomeDAO {

	Connection conn = null;
	
	public HomeDAO() { //생성자 - 객체화 되자마자 DB에 접속할 수 있도록 		
		try { //1. context.xml 객체화
			Context ctx = new InitialContext();
			//2. Resource를 name으로 찾고 DataSource로 변환
			DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MariaDB");
			//3. Connection 얻어옴
			conn = ds.getConnection();
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

	public int stmt() throws Exception {
		int row = -1;
		//1. 쿼리문 준비(테이블 생성 쿼리문)
		String sql = "create table member(" + 
				"id varchar(50) primary key" + 
				",pw varchar(100)" + 
				",name varchar(50)" + 
				",age int(3)" + 
				",gender varchar(4)" + 
				",email varchar(100)" + 
				")";
                
		//2. Statement(한번 사용할 경우) or PreparedStatement(여러번 사용할 경우 ex)insert,delete..)
		//쿼리문 사용 빈도에 따라 선택(spring 사용 시 자동 선택해줌)
		//쿼리문 담아주기
        Statement stmt = conn.createStatement();
        
		//3. 쿼리문 실행(executeQuery(select문) or executeUpdate(insert,delete..))
		//executeQuery : select문으로 가져온 데이터 반환
		//executeUpdate : update 된 데이터 수 반환
        
		//4. 결과값 반환(row에 담기)
		row = stmt.executeUpdate(sql);		
        
		//5. 자원 반납		
		stmt.close();
		conn.close();
		return row;
	}

	public int pstmt() throws SQLException {
		// 1.쿼리문 준비
		// 여러값이 들어올 경우 ?로 작성
		String sql = "INSERT INTO member(id,pw,name,age,gender,email)VALUES(?,?,?,?,?,?)";
        
		//2.Statement or PreparedStatement
		PreparedStatement pstmt = conn.prepareStatement(sql);		
        
		//2-2. ? 대응
		//pstmt에 ? 순서대로 값을 넣어줌
		pstmt.setString(1, "admin");
		pstmt.setString(2, "pass");
		pstmt.setString(3, "홍길동");
		pstmt.setInt(4, 22);
		pstmt.setString(5, "남자");
		pstmt.setString(6, "admin@naver.com");
        
		// 3.쿼리문 실행(executeQuery or executeUpdate)
		// 4.결과값 반환
		// update 된 데이터 수 반환
		int row = pstmt.executeUpdate();	
        
		// 5.자원반납
		pstmt.close();
		conn.close();
		return row;
	}

	public boolean resultSet() {
		boolean success = true;
        
		//1.쿼리문 준비
		String sql = "SELECT * FROM member"; // * 사용 지양(보안성 떨어지고 가져오는 데이터 확인 어려움)
		//2. Statement or PreparedStatement
		try {
			PreparedStatement ps = conn.prepareStatement(sql);
            
			//3. 쿼리문 실행(excuteQuery or executeUpdate)
			// select문으로 가져온 데이터 반환
			// data를 Set형태로 담아오기 때문에 ResultSet에 담아줌
			ResultSet rs = ps.executeQuery();
            
			//4. 결과값 반환
			//next() : 테이블의 각 행들을 선택해 원하는 값이 있으면 가져와서 반환
			//Array에서 값을 꺼내오는 것과는 조금 다름
			while(rs.next()) {
				//rs.get[데이터 타입](컬럼명 or 순서);
				System.out.println("id : "+rs.getString("id"));
				System.out.println("pw : "+rs.getString(2)); //비추(어떤 데이터가 나올지 모름)
				System.out.println("name : "+rs.getString("name"));
				System.out.println("age : "+rs.getInt("age"));
				System.out.println("gender : "+rs.getString("gender"));
				System.out.println("email : "+rs.getString("email"));
			}
            
			// 5. 자원반납
			rs.close();
		} catch (SQLException e) {
			success = false;
			e.printStackTrace();
		}
		return success;
	}
}

 

  • main
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
<style>
	table, th, td{
		border: 1px solid black;
		border-collapse: collapse;
		padding: 5px 10px;
	}
</style>
</head>
<body>
	<h3>다음의 속성을 갖는 member 테이블을 생성하는 쿼리를 작성하시오</h3>
	
	<table>
		<tr>
			<th>FIELD</th>
			<th>TYPE</th>
			<th>SIZE</th>
		</tr>
		<tr>
			<td><ins>id</ins></td>
			<td>VARCHAR</td>
			<td>50</td>
		</tr>
		<tr>
			<td>pw</td>
			<td>VARCHAR</td>
			<td>100</td>
		</tr>
		<tr>
			<td>name</td>
			<td>VARCHAR</td>
			<td>50</td>
		</tr>
		<tr>
			<td>age</td>
			<td>INT</td>
			<td>3</td>
		</tr>
		<tr>
			<td>gender</td>
			<td>VARCHAR</td>
			<td>4</td>
		</tr>
		<tr>
			<td>email</td>
			<td>VARCHAR</td>
			<td>100</td>
		</tr>
	</table>
	<p><a href="stmt">테이블 생성(Statement)</a></p>
	<p><a href="pstmt">테이블 삽입(PreparedStatement)</a></p>
	<p><a href="resultSet">테이블 요청(PreparedStatement)</a></p>
</body>
<script></script>
</html>

 

  • result
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
<style></style>
</head>
<body>
	${msg}
	<p>
		<a href="./">메인으로 돌아가기</a>
	</p>
</body>
<script></script>
</html>