프로젝트

Cinephile 스프링 프로젝트 시작(DAO 패턴)5

전설의개발자 2020. 9. 13. 21:50

DAO(Data Access Object) 패턴

  • DAO 인터페이스
  • DAO 인터페이스를 구현한 클래스
  • 데이터 전송 객체(Beans에 대한 객체)

비지니스 로직?

DB에서 Insert, Update, Select, Delete 한것을 갖다가 어떻게 프로그램 적으로 활용할 것이냐?

 

DAO는 비지니스 로직과 데이터베이스 사이에서 구현되는 인터페이스 같다.

(beaniejoy.tistory.com/22<< 의 글을 참고해 보았을때 DAO는 JDBC가 사용되는 여러 방법중 하나 인것 같다.

일종의 JDBC라면 DAO도 SQL문을 실행하기 위한 인터페이스!)

 

그니까!! DAO패턴은 JDBC인데

JDBC에서 DBHelper.java의 도움을 받아 Main00.java에서 모든 작업을 처리 했다면 

DAO는 Main00.java에서 SQL처리를 따로 분리시킨것을 말하는것 같다.

 

DAO 작성 패턴 과정

  • 처리할 데이터가 저장되기 위한 데이터베이스 테이블의 구조를 Java Beans로 표현
  • 데이터의 입력, 수정, 삭제, 조회(단일, 다중)의 기능을 명시한 Interface를 정의
  • Interface를 상속받는 클래스를 준비하고, Interface에서 명시하고 있는 메서드들을 재정의
  • 재정의 된 메서드의 기능구현

위의 과정을 정리하자면 DAO에서는 자바 빈즈를 만들고 SQL기능을 수행할 인터페이스를 만들고 이 인터페이스를 상속받는 클래스에 재정의해서 써라.

 

1.먼저 빈즈를 만들기 위해서 대상 테이블의 구조를 확인한다.

테이블 Department의 구조이다.

이 구조를 확인했다면 이를 바탕으로 자바 Beans를 만든다.

 

study.java.daoex.model.Department.java

package study.java.daoex.model;
/**
 * @File Department.java
 * @Discription JavaBeans 클래스 만들기
 * @Author 장혁준 newjhj31@gmail.com
 */

/**department 테이블 구조에 맞춘 Java Beans 생성*/
public class Department {
	private int deptno;
	private String dname;
	private String loc;
	
	public Department(int deptno, String dname, String loc) {
		super();
		this.deptno = deptno;
		this.dname = dname;
		this.loc = loc;
	}
	
	public int getDeptno() { return deptno; }
	public void setDeptno(int deptno) { this.deptno = deptno; }
	public String getDname() { return dname; }
	public void setDname(String dname) { this.dname = dname; }
	public String getLoc() { return loc; }
	public void setLoc(String loc) { this.loc = loc; }
	
	@Override
	public String toString() {
		return "Department [deptno=" + deptno + ", dname=" + dname + ", loc=" + loc + "]";
	}
	
}

 

2. 구현해야 하는 SQL기능의 설계를 위한 인터페이스를 추가한다.

study.java.daoex.dao.DepartmentDao.java

package study.java.daoex.dao;

import java.util.List;

import study.java.daoex.model.Department;

/**
 * @File DepartmentDao.java
 * @Discription Dao가 구현해야하는 기능 정의
 * @Author 장혁준 newjhj31@gmail.com
 */
public interface DepartmentDao {
	/**
	 * 데이터를 저장한다. (INSERT 구문을 실행)
	 * @param params	저장할 값을 담고 있는 Department 클래스의 객체
	 * @return int 	 저장된 행의 Primary Key 값
	 */
	public int insert(Department params);
	
	/**
	 * 데이터를 삭제한다. (DELETE 구문을 실행)
	 * @param params	WHERE절 조건값으로 사용할 deptno
	 * @return int 삭제된 데이터의 수
	 */
	public int delete(int params);
	
	/**
	 * 데이터를 갱싱한다. (UPDATE 구문을 실행)
	 * @param params Department 클래스의 객체
	 * @return	int 수정된 데이터의 수
	 */
	public int update(Department params);
	
	/**
	 * 데이터 한 건을 조회한다. (WHERE절을 사요한 SELECT 문을 실행)
	 * @param params	WHERE절 조건값으로 사용할 deptno
	 * @return Department 조회된 데이터를 포함한 객체
	 */
	public Department selectOne(int params);
	
	/**
	 * 데이터 목록을 조회한다. (SELECT를 사용한 전체 데이터 조회)
	 * @return Lst 조회된 데이터를 포함한 컬렉션
	 */
	public List<Department> select();
	

}

위의 코드를 잘 보면 SQL문에서 쓰이는 INSERT, UPDATE, DELETE, SELECT로 함수이름을 만들었다.

INSERT, UPDATE, DELETE는 SQL을 수행했을때 따로 조회된 데이터를 보는것이 아니라 '1개의 데이터가 입력되었습니다.' 이런식의 결과가 나오기 때문에 반환형이 int고 SELECT는 조회된 데이터를 보여주어야 하기때문에 리턴타입이 Department(위에서 만들었던 Beans) 형태다.

 

이렇게 인터페이스를 만들어 놓고

 

3.이 인터페이스를 상속받아 기능 구현을 할 자바 클래스를 만든다.

study.java.daoex.dao.impl.DepartmentDaoImpl.java

* @File DepartmentDaoImpl.java
 * @Discription DepartmentDao 상속 클래스와 재정의
 * @Author 장혁준 newjhj31@gmail.com
 */

package study.java.daoex.dao.impl;

import java.util.ArrayList;
import java.util.List;
//모든 메서드 안에서 Connection 객체를 사용해야 한다.
//이 객체는 Main 클래스에서 DB접속을 수행하여 생성자를 통해 전달된다.
import java.sql.Connection;

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

import study.java.daoex.dao.DepartmentDao;
import study.java.daoex.model.Department;

//!!!!! 인터페이스 상속할때 제일먼저 할 것은 리턴할 변수부터 만드는 것이다 !!!!!!!!
public class DepartmentDaoImpl implements DepartmentDao {

	/** 데이터베이스 접속 객체 */
	private Connection conn;
	//↑↑ mains 에서 helper를 통해서 접속한 sql데이터를 이어받기 위해
	

	/** 생성자를 통해서 데이터베이스 접속 객체를 전달 받기 */
	public DepartmentDaoImpl(Connection conn) {
		super();
		this.conn = conn;
	}

	/**
	 * 이 메서드틑 department 테이블에 대한 INSERT 구문을 실행한다. 저장할 데이터는 JAVA BEANS로 묶여 파라미터로
	 * 전달된다. 퍼리완효 후 Primary Key 값을 리턴한다.
	 */
	@Override
	public int insert(Department params) {
		int result = 0;
		// 실행할 SQL구문 정의
		String sql = "INSERT INTO department (dname, loc) VALUES (?,?)";

		// SQL 구문 실행하기 위한 객체
		// --> import java.sql.PraparedStatement;
		PreparedStatement pstmt = null;
		// --> import java.sql.ResultSet;
		ResultSet rs = null;
		// <-- Auto_Increment 값을 알기 위해 필요

		// SQL구문 처리하기
		// pstmt 객체 할당
		try {
			pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

			// 템플릿에 데이터 설정
			pstmt.setString(1, params.getDname());
			pstmt.setString(2, params.getLoc());

			// SQL문 실행하기 --> 결과 행의 수를 리턴할 변수에 대입함
			pstmt.executeUpdate();

			// Primary Key 받기
			rs = pstmt.getGeneratedKeys();
			rs.next();
			result = rs.getInt(1);

		} catch (SQLException e) {
			System.out.println("MySQL SQL Fail: " + e.getMessage());
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (Exception e) {
				}
			}
			if (pstmt != null) {
				// 객체 닫기
				try {
					pstmt.close();
				} catch (SQLException e) {
				}
			}
		}
		return result;
	}

	/**
	 * 데이터를 삭제하기 위한 SQL구문 정의하기 삭제를 위한 조건값은 파라미터로 전달된다. 일반적으로 데이터 삭제에는 Primary Key를
	 * 조건값으로 사욯한다.
	 */
	@Override
	public int delete(int params) {
		int result = 0;
		// 실행할 SQL구문 정의
		String sql = "DELETE FROM department WHERE deptno=?";

		// SQL 구문 실행하기 위한 객체
		// --> import java.sql.PraparedStatement;
		PreparedStatement pstmt = null;

		// SQL구문 처리하기
		// pstmt 객체 할당
		try {
			pstmt = conn.prepareStatement(sql);

			// 템플릿에 데이터 설정
			pstmt.setInt(1, params);

			// SQL문 실행하기 --> 결과 행의 수를 리턴할 변수에 대입함
			result = pstmt.executeUpdate();

		} catch (SQLException e) {
			System.out.println("MySQL SQL Fail: " + e.getMessage());
		} finally {
			if (pstmt != null) {
				// 객체 닫기
				try {
					pstmt.close();
				} catch (SQLException e) {
				}
			}
		}

		return result;
	}

	@Override
	public int update(Department params) {
		int result = 0;
		// 실행할 SQL구문 정의
		String sql = "UPDATE department SET dname=?, loc=? WHERE deptno=?";

		// SQL 구문 실행하기 위한 객체
		// --> import java.sql.PraparedStatement;
		PreparedStatement pstmt = null;

		// SQL구문 처리하기
		// pstmt 객체 할당
		try {
			pstmt = conn.prepareStatement(sql);

			// 템플릿에 데이터 설정
			pstmt.setString(1, params.getDname());
			pstmt.setString(2, params.getLoc());
			pstmt.setInt(3, params.getDeptno());

			// SQL문 실행하기 --> 결과 행의 수를 리턴할 변수에 대입함
			result = pstmt.executeUpdate();

		} catch (SQLException e) {
			System.out.println("MySQL SQL Fail: " + e.getMessage());
		} finally {
			if (pstmt != null) {
				// 객체 닫기
				try {
					pstmt.close();
				} catch (SQLException e) {
				}
			}
		}
		return result;
	}

	@Override
	public Department selectOne(int params) {
		Department result = null;
		// 실행할 SQL구문 정의
		String sql = "SELECT deptno, dname, loc FROM department WHERE deptno=?";

		// SQL 구문 실행하기 위한 객체
		// --> import java.sql.PraparedStatement;
		PreparedStatement pstmt = null;
		// --> import java.sql.ResultSet;
		ResultSet rs = null;

		// SQL구문 처리하기

		try {
			// pstmt 객체 할당
			pstmt = conn.prepareStatement(sql);

			// 템플릿에 데이터 설정
			pstmt.setInt(1, params);

			// SQL문 실행하기 --> 결과 행의 수를 리턴할 변수에 대입함
			rs = pstmt.executeQuery();
			boolean first = rs.next();
			if (first) {
				// SELECT절에 명시한 컬럼이름을 사용하여 데이터 추출
				int deptno = rs.getInt("deptno");
				String dname = rs.getString("dname");
				String loc = rs.getString("loc");
//자바 빈즈에서 가져오는 것이 아니라 조회된값에서 가져오는것임↑↑↑
				
				

				// 리턴할 객체에 조회한 값을 사용하여 객체를 할당한다.
				result = new Department(deptno, dname, loc);
			} else {
				System.out.println("조회 결과가 없습니다.");
			}

		} catch (SQLException e) {
			System.out.println("MySQL SQL Fail: " + e.getMessage());
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
				}
			}
			if (pstmt != null) {
				// 객체 닫기
				try {
					pstmt.close();
				} catch (SQLException e) {
				}
			}
		}
		return result;
	}

	@Override
	public List<Department> select() {
		List<Department> result = null;
		// department 테이블에 데이터를 갱신하기 위한 SQL의 템플릿
		String sql = "SELECT deptno, dname, loc FROM department";

		// SQL 구문 실행하기 위한 객체
		// --> import java.sql.PraparedStatement;
		PreparedStatement pstmt = null;
		// --> import java.sql.ResultSet;
		ResultSet rs = null;

		// SQL구문 처리하기

		try {
			// pstmt 객체 할당
			pstmt = conn.prepareStatement(sql);

			// SELECT구문을 실행한 후, 결과셋을 리턴받는다.
			rs= pstmt.executeQuery(); //<-- 괄호안에 sql을 넣지 안도록 주의 여기선 상관 없지만 '?' 를 받는 코드에선 꼬임 
			//**SQL결과를 컬렉션에 할당*/
			//SQL이 실행되므로 컬렉션을 할당한다.
			result = new ArrayList<Department>();
			
			//한 줄씩 스캔하는 반복문 구성
			while(rs.next()) {
				int deptno = rs.getInt("deptno");
				String dname = rs.getString("dname");
				String loc = rs.getString("loc");
				
				Department item = new Department(deptno, dname, loc);
				result.add(item);
			}
			

		} catch (SQLException e) {
			System.out.println("MySQL SQL Fail: " + e.getMessage());
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
				}
			}
			if (pstmt != null) {
				// 객체 닫기
				try {
					pstmt.close();
				} catch (SQLException e) {
				}
			}
		}
		return result;
	}

}

인터페이스 구현 클래스에 데이터베이스 접속 객체를 선언하고 생성자를 통해 접속 객체를 전달 받는다.

 

다음 각각의 기능들이 어떻게 구현 되었는지 잘 보면 PrepareStatement에서 했던것이다. 

  1. 저장할 데이터를 따로 String의 객체로 만들어 놓고 /SQL 문을 String의 객체 sql로 써 놓고 저장될 데이터가 들어가야 하는 자리는 '?'로 대신한다.
  2. DBHelper를 통해 데이터베이스에 접속하고(CMD에서 SQL문을 쓰기 직전의 상태같음)
  3. PrepareStatement로 구문을 실행하기 위해 객체 pstmt를 만들고 실행 결과를 받을 ResultSet의 객체를 만든다.
    -insert를 수행하면 자동으로 생성되는 Primary Key(PK)값을 확인하기 위해 데이터 입력시에는 ResultSet객체를 만든다.
  4. 입력 결과를 담을 result 객체를 만들고 자동으로 생성된 PK값을 확인하기 위해 autoGeneratedID 객체를 만든다. 
  5. SQL구문을 실행한다.
    -pstmt는 CMD에서 입력을 위한 sql문을 써 놓는 리턴받을 PK값이 있다고 설정하는것 같다.
    -pstmt.setString에서는 준비한 sql에 '?'로 처리한 곳에 입력할 데이터를 넣는 작업을 진행한다.
    -pstmt.executeUpdate() 위에서 준비된 SQL 문을 실행하는 것이고
    -pstmt.getGeneratedKeys()에서는 INSERT 했기때문에 새로 생성된 PK값을 조회해 autoGeneratedID 객체에 세팅
  6. 데이터베이스 접속을 해제한다.

SELECT 기능을 보면 JDBC와 PrepareStatement에서는 데이터 조회를 받기위해 조회한 테이블의 항목들을 일일이 String으로 객체를 만들어 주고 sysou로 출력을 했으나 여기서는 같은 경우를 위해서 sysou대신 Beans를 사용한다.

 

준비된 DAO패턴을 가지고 JDBC에서 해보았던 자바에서 데이터베이스에 접속해 SQL기능을 수행해보자

 

값 입력하기

Main01.java

import java.sql.Connection;

import study.java.daoex.dao.DepartmentDao;
import study.java.daoex.dao.impl.DepartmentDaoImpl;
import study.java.daoex.model.Department;
import study.java.helper.DBHelper;

/**
 * @File Main01.java
 * @Discription DAO를 사용하기 위한 프로그램 구성
 * @Author 장혁준 newjhj31@gmail.com
 */
public class Main01 {

	public static void main(String[] args) {
		//1) 데이터베이스 접속
		//--> import java.sql.Connection;
		//--> import study.java.helper.DBHelper;
		Connection conn = DBHelper.getInstance().open();
		
		if (conn ==null) {
			System.out.println("데이터베이스 접속 실패");
			return;
		}
		
		
		//2) INSERT 수행할 데이터 생성
		//-->사용되지 않는 값(deptno)는 0(int)이나 null(String)로 지정한다.
		Department model = new Department(0,"인터넷통신", "공학관");
		//↑↑↑ 빈즈에 (생성자로 저장된 값)저장된 값
		
		//3) 데이터 저장
		DepartmentDao dao = new DepartmentDaoImpl(conn); //바통 DepartmentDaoImpl로 넘김
		//↑↑객체(conn)을 파라미터로 넘기면 참조이다. 그니까 19번쨰 줄에서 킨 것을 이어받은 것이다.
		int result = dao.insert(model);
		
		//4) 결과 판별
		System.out.println(result + "번 데이터 저장됨");
		
		
		//5) DB접속 해제
		
		DBHelper.getInstance().close();
		
	}

}

1. DBHelper로 데이터 베이스에 접속

2. Department 자바 빈즈 객체를 만들어 입력할 값을 파라미터로 전달

3. DepartmentDaoImpl 인터페이스 구현체 에서 입력 기능을 수행하고 결과를 리턴 받는다.

4. 수행 결과를 확인하고 DB접속을 해제한다.

 

값 삭제하기

Main02.java(더보기)

더보기
import java.sql.Connection;

import study.java.daoex.dao.DepartmentDao;
import study.java.daoex.dao.impl.DepartmentDaoImpl;

import study.java.helper.DBHelper;

/**
 * @File Main02.java
 * @Discription DAO를 활용하여 데이터를 삭제하기 위한 프로그램 구성
 * @Author 장혁준 newjhj31@gmail.com
 */
public class Main02 {

	public static void main(String[] args) {
		// 1) 데이터베이스 접속
		// --> import java.sql.Connection;
		// --> import study.java.helper.DBHelper;
		Connection conn = DBHelper.getInstance().open();

		if (conn == null) {
			System.out.println("데이터베이스 접속 실패");
			return;
		}

		// 2) 삭제할 데이터 생성
		// -->Main01에서 출력된 값을 입력하시오
		int target = 316;

		// 3) 데이터 삭제
		DepartmentDao dao = new DepartmentDaoImpl(conn);
		int result = dao.delete(target);

		// 4) 결과 판별
		System.out.println(result + "개의 데이터 삭제됨");

		// 5) DB접속 해제

		DBHelper.getInstance().close();

	}

}

 

1. DBHelper로 데이터 베이스에 접속

2. 삭제하고 싶은 데이터의 PK값을 target 객체에 저장 

3. DepartmentDaoImpl 인터페이스 구현체 에서 삭제 기능을 수행하고 결과를 리턴 받는다.

4. 수행 결과를 확인하고 DB접속을 해제한다.


 

값 수정하기

Main03.java(더보기)

더보기
import java.sql.Connection;

import study.java.daoex.dao.DepartmentDao;
import study.java.daoex.dao.impl.DepartmentDaoImpl;
import study.java.daoex.model.Department;
import study.java.helper.DBHelper;

/**
 * @File Main03.java
 * @Discription 데이터 수정하기
 * @Author 장혁준 newjhj31@gmail.com
 */
public class Main03 {

	public static void main(String[] args) {
		// 1) 데이터베이스 접속
		// --> import java.sql.Connection;
		// --> import study.java.helper.DBHelper;
		Connection conn = DBHelper.getInstance().open();

		if (conn == null) {
			System.out.println("데이터베이스 접속 실패");
			return;
		}

		// 2) UPDATE를 수행할 데이터 생성
		Department model = new Department(101, "인터넷통신학과", "7호관");

		// 3) 데이터 수정
		DepartmentDao dao = new DepartmentDaoImpl(conn);
		int result = dao.update(model);

		// 4) 결과 판별
		System.out.println(result + "개의 데이터 수정됨");

		// 5) DB접속 해제

		DBHelper.getInstance().close();
	}

}

 

1. DBHelper로 데이터 베이스에 접속

2. Department 자바 빈즈 객체를 만들어 수정할 값을 파라미터로 전달

3. DepartmentDaoImpl 인터페이스 구현체 에서 수정 기능을 수행하고 결과를 리턴 받는다.

4. 수행 결과를 확인하고 DB접속을 해제한다.


 

단일행 데이터 조회하기

Main04.java(더보기)

더보기
import java.sql.Connection;

import study.java.daoex.dao.DepartmentDao;
import study.java.daoex.dao.impl.DepartmentDaoImpl;
import study.java.daoex.model.Department;
import study.java.helper.DBHelper;

/**
 * @File Main04.java
 * @Discription 데이터 조회하기
 * @Author 장혁준 newjhj31@gmail.com
 */
public class Main04 {

	public static void main(String[] args) {
		// 1) 데이터베이스 접속
		// --> import java.sql.Connection;
		// --> import study.java.helper.DBHelper;
		Connection conn = DBHelper.getInstance().open();

		if (conn == null) {
			System.out.println("데이터베이스 접속 실패");
			return;
		}

		// 2) 조회할  데이터
		int target = 101; //primary key 값이 101인 데이터를 조회하고 싶다
		
		// 3) 데이터 조회
		DepartmentDao dao = new DepartmentDaoImpl(conn);
		Department result = dao.selectOne(target); //리턴값이 빈즈형식임

		// 4) 결과 판별
		if (result ==null) {
			System.out.println("조회결과 없음");
		}else {
			System.out.println(result.toString());
		}

		// 5) DB접속 해제

		DBHelper.getInstance().close();

	}

}

 

1. DBHelper로 데이터 베이스에 접속

2. 조회하고 싶은 데이터의 PK값을 target 객체에 저장 

3. DepartmentDaoImpl 인터페이스 구현체 에서 조회 기능을 수행하고 결과를 리턴 받는다.

4. 수행 결과를 확인하고 DB접속을 해제한다.


 

다중행 데이터 조회하기

Main05.java(더보기)

더보기
import java.sql.Connection;
import java.util.List;

import study.java.daoex.dao.DepartmentDao;
import study.java.daoex.dao.impl.DepartmentDaoImpl;
import study.java.daoex.model.Department;
import study.java.helper.DBHelper;

/**
 * @File Main05.java
 * @Discription 목록조회 결과는 LIST형태로 리턴되므로, 반복문을 사용해 출력해야 한다.
 * @Author 장혁준 newjhj31@gmail.com
 */
public class Main05 {

	public static void main(String[] args) {
		// 1) 데이터베이스 접속
		// --> import java.sql.Connection;
		// --> import study.java.helper.DBHelper;
		Connection conn = DBHelper.getInstance().open();

		if (conn == null) {
			System.out.println("데이터베이스 접속 실패");
			return;
		}

		// 2) 데이터 목록 조회
		DepartmentDao dao = new DepartmentDaoImpl(conn);
		List<Department> result = dao.select();

		// 3) 결과 판별
		
		if (result == null) {
			System.out.println("조회결과 없음");
		} else {
			for (int i =0; i< result.size(); i++) {
				Department item = result.get(i);
				System.out.println(item.toString());
			}
		}

		// 4) DB접속 해제

		DBHelper.getInstance().close();

	}

}

 

1. DBHelper로 데이터 베이스에 접속

2. DepartmentDaoImpl 인터페이스 구현체 에서 조회 기능을 수행하고 결과를 리턴 받는다.

3. 리턴받는 결과가 여러 줄이기 때문에 List<Department>로 결과를 받았다.

4. 여러행의 결과를 확인하기 위해서 반복을 돌며 결과값을 출력한다.

5. 수행 결과를 확인하고 DB접속을 해제한다.


 

아무튼 DAO는 JDBC인데 DAO패턴을 사용해서 비지니스 로직(Main)과 저수준의 데이터 엑세스를 분리하면 그냥 JDBC로 작업을 수행할 때보다 유지보수가 더 쉬우니까 분리를 시켜놨다. 

이제 쪼금 뭔지 알것같다.