국비교육(22-23)

23일차(1)/java(34) : JDBC, DAO

서리/Seori 2022. 11. 7. 14:31

23일차(1)/java(34) : JDBC, DAO

 

MainClass11

- DELETE 메소드 분리하기

package test.main;

import java.sql.Connection;
import java.sql.PreparedStatement;

import test.dto.MemberDto;
import test.util.DBConnect;

public class MainClass11 {
	public static void main(String[] args) {
		//삭제할 회원의 번호라고 하자
		int num=3;		
		delete(num);
	}
	
	//인자로 전달한 번호에 해당하는 회원 한명의 정보를 삭제하는 메소드
	public static void delete(int num) {
		
		//DELETE 작업을 위해서 필요한 객체의 참조값을 담을 지역변수 미리 만들기
		Connection conn=null;
		PreparedStatement pstmt=null;
		try {
			//connection 객체의 참조값 얻어오기
	  	  	conn=new DBConnect().getConn();
	  	  	//실행할 SQL문
	  	  	String sql="DELETE FROM member"
	  	  			+ " WHERE num=?";  	  	
	  	  	//PreparedStatement 객체의 참조값 가져오기
	  	  	pstmt=conn.prepareStatement(sql);
	  	  	//?에 값 바인딩하기			  	  	
	  	  	pstmt.setInt(1, num);
	  	  	pstmt.executeUpdate();
	  	  	System.out.println("회원 정보를 삭제했습니다.");				
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close();
			}catch(Exception e) {}
		};
	}
}

- update, insert, delete문은 전부 형태가 같다. 실행할 SQL문과 바인딩할 ?의 값만 잘 수정하면 된다.
- .setInt 에 숫자 바인딩, setString에 문자열 바인딩

- 삭제하는 메소드가 미리 존재한다면 해당 메소드를 사용해서 삭제하기만 하면 되어서 사용이 편리하다.

- 메소드를 미리 준비해놓고 활용하는 연습!

 


 

MainClass12

- SELECT문 메소드 분리! (전체 회원의 정보 리턴)

package test.main;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import test.dto.MemberDto;
import test.util.DBConnect;

public class MainClass12 {
	public static void main(String[] args) {
		//여기서 전체 회원의 목록이 필요하면
		//getList() 메소드를 호출하는 것만으로 전체 회원목록을 얻어낼 수 있다.
		List<MemberDto> list=getList();
		for(MemberDto tmp:list) {
			System.out.println(tmp.getNum()+" | "+tmp.getName()+" | "+tmp.getAddr());
		}		
	}
	
	//전체 회원의 목록을 리턴해주는 메소드
	public static List<MemberDto> getList() {
		//회원 전체 목록을 담을 ArrayList 객체를 생성해서 참조값을 List 인터페이스 type으로 담기
		List<MemberDto> list=new ArrayList<>();
				
		Connection conn=null;
		PreparedStatement pstmt=null;
		ResultSet rs=null;
		try {
			//connection 객체의 참조값 얻어오기
	  	  	conn=new DBConnect().getConn();
	  	  	//실행할 SQL문
	  	  	String sql="SELECT num, name, addr"
	  	  				+ " FROM member"
	  	  				+ " ORDER BY num ASC";	  	  			  	  	
	  	  	//PreparedStatement 객체의 참조값 가져오기
	  	  	pstmt=conn.prepareStatement(sql);
	  	  	//?에 값 바인딩하기	  	  		  	  	
	  	  	rs=pstmt.executeQuery();
	  	  	while(rs.next()) {
	  	  		//커서가 위치한 곳의 회원정보를 MemberDto 객체에 담기
	  	  		MemberDto dto=new MemberDto();
	  	  		dto.setNum(rs.getInt("num"));
	  	  		dto.setName(rs.getString("name"));
	  	  		dto.setAddr(rs.getString("addr"));
	  	  		//회원 한명의 정보가 담긴 MemberDto 객체의 참조값을 ArrayList 객체에 누적시키기
	  	  		list.add(dto);
	  	  	}	  	  					
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(rs!=null)rs.close();
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close();
			}catch(Exception e) {}
		}
		//모든 회원의 정보가 누적된 ArrayList 객체이 참조값 리턴해주기
		return list;
	}
}

 

- 전체 회원목록의 리턴타입은?
멤버 한명 : MemberDto 타입
멤버 여러명 : List<MemberDto> 타입
→ List<MemberDto> 타입으로 받는 이유는? 회원정보를 목록(리스트) 형태로 받기 위해서!

 

- 결과를 받는 변수는 ResultSet (rs) 타입을 사용한다.
- while문을 돌면서 값을 불러와서 결과를 resultSet에 넣는다.
 select 된 결과값 테이블이 resultSet 안에 들어있게 되는 것!

- insert, delete, update에서 사용하던 pstmt.executeUpdate(); 대신에 
 pstmt.executeQuery(); 를 쓴다. executeQuery는 쿼리에서 resultSet을 반환하는 메소드이다.
- ResultSet 타입의 결과값을 가져와야 하기 때문에!

- ResultSet에서는 행마다 커서를 하나씩 내리면서 추출한다!
 커서가 하나씩 내려오면서 값을 가져온다. → 반복 for문 돌기

- new MemberDto(); 로 담아야 하는데, row 하나 당 MemberDto 값 하나가 나온다!

 → ArrayList에 담어서 리턴한다.

 

- getList 라는 완전한 형태의 메소드가 미리 만들어져 있다면,
사용자(메인메소드) 입장에서는 getList만 사용하면 된다. 어떻게 사용할지만 고려하면 된다.

 

 

- MemberDto의 참조값을 내보낼 수 있도록 리스트에 추가해준다!

 

- getList 메소드의 작동방식.

 dto.setNum/setName/setAddr 로 가져온 값(참조값)을 ArrayList객체에 담아서 마지막에 리턴하도록 한다.

 

- 디버그모드. while문을 한바퀴씩 돌때마다 각각의 방에 값이 들어가는 것을 확인할 수 있다.

- .next() 메소드를 사용해서 커서를 하나씩 내리며 다음에 값이 있는지 없는지 확인!

 

- Main메소드의 for문을 돌 때마다 List에 값이 들어간다.

- 사물함 안에 만들어둔 ArrayList를 넣어두고, 마지막에 return list; 로 넣어둔 59번 사물함 키를 리턴해주는 것.

 

- 디버그모드에서 F5 누르면 메소드 안으로 추적해서 들어갈 수 있다.

 


 

MainClass13

- SELECT문 메소드 분리! (특정 회원의 정보 리턴)

package test.main;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import test.dto.MemberDto;
import test.util.DBConnect;

public class MainClass13 {
	public static void main(String[] args) {
		//메소드를 호출해서 인자로 전달한 번호에 해당하는 회원정보를 얻어오기
		MemberDto dto=getData(2);
		if(dto==null) {
			System.out.println("해당 회원은 존재하지 않습니다.");
		}else {
			System.out.println("번호: "+dto.getNum()+" 이름: "+dto.getName()+" 주소:"+dto.getAddr());
		}
	}
	
	//인자로 전달된 번호에 해당하는 회원 한명 한명의 정보를 리턴하는 메소드
	//번호에 해당하는 회원의 정보가 없으면 null을 리턴
	public static MemberDto getData(int num) {
		//MemberDto 객체의 참조값을 담을 지역변수 미리 만들기
		MemberDto dto=null;
		
		Connection conn=null;
		PreparedStatement pstmt=null;
		ResultSet rs=null;
		try {
			//connection 객체의 참조값 얻어오기
	  	  	conn=new DBConnect().getConn();
	  	  	//실행할 미완성의 SQL문
	  	  	String sql="SELECT num, name, addr"
	  	  				+ " FROM member"
	  	  				+ " WHERE num=?";	  	  			  	  	
	  	  	//PreparedStatement 객체의 참조값 가져오기
	  	  	pstmt=conn.prepareStatement(sql);
	  	  	//?에 값 바인딩하기	  	  		  	  	
	  	  	pstmt.setInt(1, num);
	  	  	rs=pstmt.executeQuery();
	  	  	//만일 cursor를 한칸 내릴 수 있다면(select 된 row가 있다면)
	  	  	if(rs.next()) {
	  	  		//MemberDto 객체를 생성해서 참조값을 미리만들어진 지역변수 dto에 담기
	  	  		dto=new MemberDto();
	  	  		//cursor가 위치한곳의 회원정보를 MemberDto 객체에 담기
	  	  		dto.setNum(rs.getInt("num"));
	  	  		dto.setName(rs.getString("name"));
	  	  		dto.setAddr(rs.getString("addr"));
	  	  	}	  	  					
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(rs!=null)rs.close();
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close();
			}catch(Exception e) {}
		}
		//회원 한명의 정보를 담고 있는 MemberDto 객체 리턴해주기
		return dto;
	}
}

 

- 안에서 지역변수를 만들면 나중에 null이 반환될 수 없으므로, MemberDto dto=null; 이라고 위에서 선언해둔다.

- 여기서 하는 select는 row가 하나(선택된 회원정보만 보는 것이라서) 나오므로

  정렬은 따로 필요없고 대신 where 문을 사용!

- resultset에서 커서를 내려서 읽는것도 한번만 하면 된다.


- select문에서도 ? 사용이 가능하다. ? 자리에 똑같이 바인딩해주면 된다.

 

dto=new MemberDto(); (값 대입)
- 위에서 변수를 만들어두었기 때문에 변수를 새로 만들 필요는 없고, 그대로 사용한다.


- ArrayList나 List 형태는 필요없기 때문에 dto 객체를 사용하고,

 값을 얻어내기 위해 여러번 반복해 도는 것이 아니므로 while대신에 if를 사용한다.

 


 

test.dao 패키지 / MemberDao 클래스 생성

- insert, update, delete, getList, getData 메소드 분리

package test.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import test.dto.MemberDto;
import test.util.DBConnect;

/*
 * Data Access Object 만들어보기
 * - DB에 insert, update, delete, select 작업을 대신해주는 객체를 생성할 클래스 설계하기
 */
public class MemberDao {

	//회원 한명의 정보를 저장하고 작업의 성공 여부를 리턴해주는 메소드
	public boolean insert(MemberDto dto) {
		Connection conn=null;
		PreparedStatement pstmt=null;
		//수정된 row의 갯수를 담을 지역변수를 미리 만들고 초기값 0 대입하기
		int rowCount=0;
		try {
			//connection 객체의 참조값 얻어오기
	  	  	conn=new DBConnect().getConn();
	  	  	//실행할 SQL문
	  	  	String sql="INSERT INTO member"
	  	  			+ " (num, name, addr)"
	  	  			+ " VALUES(member_seq.NEXTVAL, ?, ?)";  	  	
	  	  	//PreparedStatement 객체의 참조값 가져오기
	  	  	pstmt=conn.prepareStatement(sql);
	  	  	//?에 값 바인딩하기			  	  	
	  	  	pstmt.setString(1, dto.getName());
	  	  	pstmt.setString(2, dto.getAddr());
	  	  	//sql 문 실행하고 변화된(추가,수정,삭제) row의 개수를 리턴받기
	  	  	rowCount=pstmt.executeUpdate();
	  	  	
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close();
			}catch(Exception e) {}
		}
		//변화된 rowCount의 값을 확인해서 작업의 성공여부를 리턴해준다.
		if (rowCount>0) {
  	  		return true;	  	  		
  	  	}else{
  	  		return false;
  	  	}		
	}
	
	//회원 한명의 정보를 수정하고 작업의 성공 여부를 리턴해주는 메소드
	public boolean update(MemberDto dto) {
		Connection conn=null;
		PreparedStatement pstmt=null;
		//수정된 row의 갯수를 담을 지역변수를 미리 만들고 초기값 0 대입하기
		int rowCount=0;
		try {
			//connection 객체의 참조값 얻어오기
	  	  	conn=new DBConnect().getConn();
	  	  	//실행할 SQL문
	  	  	String sql="UPDATE member"
	  	  			+ " SET name=?, addr=?"
	  	  			+ " WHERE num=?";  	  	
	  	  	//PreparedStatement 객체의 참조값 가져오기
	  	  	pstmt=conn.prepareStatement(sql);
	  	  	//?에 값 바인딩하기			  	  
	  	  	pstmt.setString(1, dto.getName());
	  	  	pstmt.setString(2, dto.getAddr());
	  	  	pstmt.setInt(3, dto.getNum());	  	  	
	  	  	//sql 문 실행하고 변화된(추가,수정,삭제) row의 개수를 리턴받기
	  	  	rowCount=pstmt.executeUpdate();
	  	  	
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close();
			}catch(Exception e) {}
		}
		//변화된 rowCount의 값을 확인해서 작업의 성공여부를 리턴해준다.
		if (rowCount>0) {
  	  		return true;	  	  		
  	  	}else{
  	  		return false;
  	  	}		
	}
	
	//회원 한명의 정보를 삭제하고 작업의 성공 여부를 리턴해주는 메소드
	public boolean delete(int num) {
		Connection conn=null;
		PreparedStatement pstmt=null;
		//수정된 row의 갯수를 담을 지역변수를 미리 만들고 초기값 0 대입하기
		int rowCount=0;
		try {
			//connection 객체의 참조값 얻어오기
	  	  	conn=new DBConnect().getConn();
	  	  	//실행할 SQL문
	  	  	String sql="DELETE FROM member"
	  	  			+ " WHERE num=?";
	  	  	//PreparedStatement 객체의 참조값 가져오기
	  	  	pstmt=conn.prepareStatement(sql);
	  	  	//?에 값 바인딩하기			  	  	
	  	  	pstmt.setInt(1, num);	  	  	
	  	  	//sql 문 실행하고 변화된(추가,수정,삭제) row의 개수를 리턴받기
	  	  	rowCount=pstmt.executeUpdate();
	  	  	
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close();
			}catch(Exception e) {}
		}
		//변화된 rowCount의 값을 확인해서 작업의 성공여부를 리턴해준다.
		return rowCount>0 ? true:false;  	  	
	}
	
	//전체 회원의 목록을 리턴해주는 메소드
	public List<MemberDto> getList() {
		//회원 전체 목록을 담을 ArrayList 객체를 생성해서 참조값을 List 인터페이스 type으로 담기
		List<MemberDto> list=new ArrayList<>();
				
		Connection conn=null;
		PreparedStatement pstmt=null;
		ResultSet rs=null;
		try {
			//connection 객체의 참조값 얻어오기
	  	  	conn=new DBConnect().getConn();
	  	  	//실행할 SQL문
	  	  	String sql="SELECT num, name, addr"
	  	  				+ " FROM member"
	  	  				+ " ORDER BY num ASC";	  	  			  	  	
	  	  	//PreparedStatement 객체의 참조값 가져오기
	  	  	pstmt=conn.prepareStatement(sql);
	  	  	//?에 값 바인딩하기	  	  		  	  	
	  	  	rs=pstmt.executeQuery();
	  	  	while(rs.next()) {
	  	  		//커서가 위치한 곳의 회원정보를 MemberDto 객체에 담기
	  	  		MemberDto dto=new MemberDto();
	  	  		dto.setNum(rs.getInt("num"));
	  	  		dto.setName(rs.getString("name"));
	  	  		dto.setAddr(rs.getString("addr"));
	  	  		//회원 한명의 정보가 담긴 MemberDto 객체의 참조값을 ArrayList 객체에 누적시키기
	  	  		list.add(dto);
	  	  	}	  	  					
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(rs!=null)rs.close();
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close();
			}catch(Exception e) {}
		}
		//모든 회원의 정보가 누적된 ArrayList 객체이 참조값 리턴해주기
			return list;
	}
	
	//인자로 전달된 번호에 해당하는 회원 한명 한명의 정보를 리턴하는 메소드
	//번호에 해당하는 회원의 정보가 없으면 null을 리턴
	public MemberDto getData(int num) {
		//MemberDto 객체의 참조값을 담을 지역변수 미리 만들기
		MemberDto dto=null;
		
		Connection conn=null;
		PreparedStatement pstmt=null;
		ResultSet rs=null;
		try {
			//connection 객체의 참조값 얻어오기
	  	  	conn=new DBConnect().getConn();
	  	  	//실행할 미완성의 SQL문
	  	  	String sql="SELECT num, name, addr"
	  	  				+ " FROM member"
	  	  				+ " WHERE num=?";	  	  			  	  	
	  	  	//PreparedStatement 객체의 참조값 가져오기
	  	  	pstmt=conn.prepareStatement(sql);
	  	  	//?에 값 바인딩하기	  	  		  	  	
	  	  	pstmt.setInt(1, num);
	  	  	rs=pstmt.executeQuery();
	  	  	//만일 cursor를 한칸 내릴 수 있다면(select 된 row가 있다면)
	  	  	if(rs.next()) {
	  	  		//MemberDto 객체를 생성해서 참조값을 미리만들어진 지역변수 dto에 담기
	  	  		dto=new MemberDto();
	  	  		//cursor가 위치한곳의 회원정보를 MemberDto 객체에 담기
	  	  		dto.setNum(rs.getInt("num"));
	  	  		dto.setName(rs.getString("name"));
	  	  		dto.setAddr(rs.getString("addr"));
	  	  	}	  	  					
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(rs!=null)rs.close();
				if(pstmt!=null)pstmt.close();
				if(conn!=null)conn.close();
			}catch(Exception e) {}
		}
		//회원 한명의 정보를 담고 있는 MemberDto 객체 리턴해주기
		return dto;
	}
}

 

DTO : Data Transfer Object
- DAO : Data Access Object 

 

public boolean insert(MemberDto dto) {}
- public void 는 작업 성공여부와 상관없이 메소드만 호출하는 것
- public boolean 은 작업을 하고, 성공여부에 따라 t/f를 반환하는 것

 

- insert, update, delete라는 메소드 3개를 위와 같은 틀, 형태로 만들어볼 예정!

 

 

.executeUpdate : DML의 영향을 받은 row의 개수를 리턴해준다.

- 입력, 수정, 삭제된 행의 숫자를 리턴하는 것.

- 리턴해주는 숫자가 0보다 크면 작업이 성공이라는 뜻. 0이면 실패했다는 뜻!

 

- 성공 여부를 판별하여 리턴하는 데에 사용하기!

int rowCount=pstmt.executeUpdate(); executeUpdate의 숫자를 변수에 담아서 if 로 분기하기

 

- 하지만 이 위치에 if~else문이 만들어지면 아래로 내려가지 못한다!

- try절에서 리턴값이 이미 나와버리므로 마무리작업(finally절)을 할 수 없다.

 

- finally절까지 종료한 후에 하단에서 if절을 활용해야 한다.

 

- 그런데 if절을 아래로 빼면 지역변수 rowCount를 참조할 수 없다.

 

 

- 따라서 rowCount라는 변수도 위에서 미리 선언해준다.(0 상태로)

- 이렇게 변수를 먼저 선언한 후 나중에 값을 대입하는 구조는 자주 쓰이니까 잘 기억하기!

 

- Update, Delete 도 똑같이 수정!

 

if (rowCount>0) {
	return true;      
}else{
	return false;
}
//
return rowCount>0 ? true:false;

- 같은 식!  if~else 문은 위와 같이 3항연산자 형태로 쓸 수도 있다.

 

- 저번에 만든 메소드들을 이 클래스로 가져와서 정리하기.

 


 

MainClass14

- DAO 활용방법(INSERT)

package test.main;

import test.dao.MemberDao;
import test.dto.MemberDto;

public class MainClass14 {
	public static void main(String[] args) {
		//추가할 회원의 정보라고 가정
		String name="복숭아";
		String addr="대전";		
		
		/*
		 * 회원의 정보를 DB에 저장하는 기능을 가지고 있는
		 * MemberDao객체를 생성해서 참조값을 dao라는 지역변수에 담기
		 */
		MemberDao dao=new MemberDao();
		
		//insert() 메소드에 전달할 MemberDto 객체 생성
		MemberDto dto=new MemberDto();
		//추가할 회원의 정보를 담고
		dto.setName(name);
		dto.setAddr(addr);
		//MemberDao 객체를 이용해서 회원 정보를 추가하고 성공여부를 리턴받는다.
		boolean isSuccess=dao.insert(dto);
		//성공 여부에 따라 다른 동작을 한다.
		if(isSuccess) {
			System.out.println(name+"의 정보를 추가했습니다.");
		}else {
			System.out.println("회원정보 추가 실패!");
		}
	}
}

 

- 메소드가 다른 곳에 미리 만들어져 있다면 이렇게 간단하게 메인메소드만 작성 가능하다.

- 데이터베이스 접속방법 등 다른 것들을 신경쓰지 않아도 메소드 활용 가능! (부분 프로그래밍)

 

- 잘 추가되어 있다.

 

 


 

- UPDATE 연습해본것

package test.main;

import test.dao.MemberDao;
import test.dto.MemberDto;

public class MainTest {
	public static void main(String[] args) {
		//추가할 회원의 정보라고 가정
		int num=2;
		String name="사과";
		String addr="문경";
				
		/*
		 * MemberDao객체를 생성해서 참조값을 dao라는 지역변수에 담기
		 */
		MemberDao dao=new MemberDao();
		
		MemberDto dto=new MemberDto();
		dto.setNum(num);
		dto.setName(name);
		dto.setAddr(addr);
				
		boolean isSuccess=dao.update(dto);
		
		if(isSuccess) {
			System.out.println(name+"의 정보를 수정했습니다.");
		}else {
			System.out.println("회원정보 수정 실패!");
		}
	}
}

 

- 수정할 정보를 dto에 담아서 MemberDao 클래스 안에 있는 insert 메소드에 전달하면 알아서 저장해준다.

 

 

- 다른 클래스에 있는 update 메소드를 불러와서 사용하기. 잘 수정된다!

 

 


 

참고) SQL의 시퀀스 값은 일정 개수는 미리 만들어져 있다. 언제든 불러내어 사용할 수 있도록!

그런데 그 미리 얻어놓은 시퀀스들은 재부팅하면 사라진다.(캐시 데이터)
시퀀스의 숫자는 '겹치지 않는 숫자'라는 것이 중요하고, 연속성이 필수인 것은 아니다. 값이 중간에 빠져있을 수도 있다!