JDBC # Oracle DB - Java: (JDBC 기초) 초기 세팅부터 멤버 관리 시스템 작성까지(update: 2020/06/29)

codepark_kr·2020년 6월 28일
0

JDBC

목록 보기
1/2

Introduction: JDBC(Java Database Connectivity)

JDBC란?

Java와 Database를 연결하기 위해 이에 접근할 수 있게 만들어 주는 Programming API이며, JDBC Driver라는 Middleware를 통해 Java와 Database를 연결할 수 있다. 이 JDBC로는 Oracle JDBC Driver, MYSQL JDBC Driver, Sybase JDBC Driver등이 제공된다. Java와 Oracle DB를 연결할 때에는 ojdbc.jar(JAR = Java Archive File)를 통해 이에 필요한 클래스들을 통합적으로 제공받을 수 있다. 이는 Oracle 공식 사이트에서 제공된다.


JDBC - Eclipse Set Up

0. Eclipse Workspace 분리 및 재설정을 권장한다.
0-1. window - Perspective - Select java
0-2. Show View - problems, Error Log Activate
0-3. Preference - General - Workspace - Text File Encoding - Select UTF-8
0-4. Preference - General - Editors - Text Editors - Spelling - Encoding - Default(UTF-8)

1. library jar 파일 먼저 등록.
1-0. ojdbc파일 다운로드.
(https://www.oracle.com/database/technologies/jdbcdriver-ucp-downloads.html)
1-1. lib 폴더 생성 후 ojdbc.jar 파일 넣은 후 alt+enter ->
1-2. library에서 해당 jar 파일 선택 후 apply
1-3. root Directiry에 Referenced Libraries 생성된 것 확인
2. vo 객체에 implements Serializable 선언
3. Driver Class 등록 (프로그램 시작시 한 번만 처리하면 된다.)

	public static void main(String[] args) 
		throws ClassNotFoundException{
		Class.forName("oracle.jdbc.OracleDriver");
		}
	}

Introduction: JDBC Process

package 구분에 관하여:

1. view: 사용자가 제공받을 메뉴, 화면 또는 그 틀을 제공
2. Controller: 명령을 내리고 제공하는 제어의 역할 수행
3. DAO(Database Access Object): DB를 왕래하며 쿼리 클래스에 데이터를 주고 받음
4. vo(Value Object): 데이터를 임시저장
5. Service: DAO를 제어하고 명령을 내림

JDBC Datatype 설정:

java의 Datatype과 DB의 Datatype은 일치하지 않는다. 때문에 상응하는 데이터 타입간 대략적으로 변환하는 과정이 필요하다. (e.g. String -> char, varchar2, int, float, double -> number, date -> date) 이 때, DB의 char(1)를 java의 char로 변환하는 경우 데이터를 담는 데에는 문제가 없으나, char 타입의 데이터를 직접적으로 사용하기 위한 메소드를 java에서 지원하지 않기 때문에 어차피 String으로 재변환하는 처리를 거쳐야 한다. 때문에 DB의 char(1) 역시 String 타입으로 변환해야 한다.


JDBC Process (DML)

1. DB Connection Object 생성 및 Auto-Commit Option deactivate
2. Connection Object로부터 Prepared Statement Object 생성
3. Prepared Statement에 미완성 Query 전달 및 값 대입
4. Query 실행, 결과값(int result = SQL%ROWCOUNT에 상응) 리턴받음
5. 자원 반납 (Close Statement)


JDBC Process (DQL)

1. DB Connection Object 생성
2. Connection Object로부터 Prepared Statement Object 생성
3. Prepared Statement에 미완성 Query 전달
4. Query 실행, Result Set(결과집합) 리턴받음
5. 복수의 결과인 경우 ResultSet -> vo -> List등으로 값 변환받음
6. 자원 반납 (Close Statement)


Remark


1. localhost의 PORT NUMBER는 127.0.0.1.이며, Oracle Port Number는 1521이다.
2. model, view, controller의 기본 구조로 패키지를 생성하는 것을 MVC Pattern(Model-View-Controller Pattern)이라고 한다.


코드 전문을 포함하고 있기 때문에 지금부터 후술될 내용이 좀 많다.
화면 오른쪽의 항목별 바로가기를 적극 활용하자.


(Oracle): Member DB Table

<<Member Management Table>>

create table member(
member_id varchar2(15),
password varchar2(15) not null,
member_name varchar2(30) not null,
gender char(1),
age number,
email varchar2(100),
phone char(11) not null,
address varchar2(300),
hobby varchar2(100),
enroll_date date default sysdate,
constraint pk_member primary key(member_id),
constraint ck_gender check(gender in ('M', 'F'))
);
commit;

별다를 것 없는 회원 관리 시스템용 테이블이다.
Constraint pk/ck 선언을 올바르게 하는 것을 잊지 말자.
(특히 unique 같은거 남발하면 나중에 귀찮아진다.)


(Java): vo.Member

<<Member Management - vo>>
Package Declaration/import

package member.vo;
import java.io.Serializable;
import java.sql.Date;

Field Variables Declaration

public class Member implements Serializable{
	private String memberId;
    private String password;
    private String memberName;
    private String gender;
    private int age;
    private String email;
    private String phone;
    private String address;
    private String hobby;
    private Date enrollDate;

Constructor by Superclass/ by Fields Declaration

    public Member() {
		super();
	}
	public Member(String memberId, String password, String memberName, 
    String gender, int age, String email, String phone, String address, 
    String hobby, Date enrollDate) {
		super();
		this.memberId = memberId;
		this.password = password;
		this.memberName = memberName;
		this.gender = gender;
		this.age = age;
		this.email = email;
		this.phone = phone;
		this.address = address;
		this.hobby = hobby;
		this.enrollDate = enrollDate;
	}
	public Member(String memberId, String password, 
    String email, String phone, String address) {
		super();
		this.memberId = memberId;
		this.password = password;
		this.email = email;
		this.phone = phone;
		this.address = address;
	}
	public Member(String memberId) {
		super();
		this.memberId = memberId;
	}
	public Member(String memberId, String password, int identifier) {
		super();
		this.memberId = memberId;
		this.password = password;
	}
	public Member(String memberId, String email, double identifier) {
		super();
		this.memberId = memberId;
		this.email = email;
	}
	public Member(String memberId, String phone, char identifier) {
		super();
		this.memberId = memberId;
		this.phone = phone;
	}
	public Member(String memberId, String address, Date identifier) {
		super();
		this.memberId = memberId;
		this.address = address;
	}

getter/setter

	public String getMemberId() {
		return memberId;
	}
	public void setMemberId(String memberId) {
		this.memberId = memberId;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getMemberName() {
		return memberName;
	}
	public void setMemberName(String memberName) {
		this.memberName = memberName;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getHobby() {
		return hobby;
	}
	public void setHobby(String hobby) {
		this.hobby = hobby;
	}
	public Date getEnrollDate() {
		return enrollDate;
	}
	public void setEnrollDate(Date enrollDate) {
		this.enrollDate = enrollDate;
	}

Override toString()

	@Override
	public String toString() {
		return memberId + "\t" + password + "\t" + memberName + "\t"
				+ gender + "\t" + age + "\t" + email + "\t" 
                + phone + "\t" + address+ "\t" + hobby + "\t" + enrollDate;
	}
}

상기했듯 DB의 대상 Table 내 각 Column의 Datatype을 고려하여 이에 대응하는 Field 변수를 선언하였고, 기본/파라미터 생성자 및 getter/setter 생성, toString() 메소드 오버라이딩까지 마친 상태이다. 이 vo 클래스에서 주목할 점은, 파라미터 생성자가 다양하게 선언되어 있으며 그 수가 많다는 점이다. 이는 view 클래스에서 각 컬럼별 Update DML 처리 후 오버로딩된 생성자를 통해 보다 쉽게 Member 객체를 리턴하기 위함이다. 예를 들어 password, email, phone, address 이 4가지 항목에 대해 각각 update 처리 후 Member 객체를 리턴해야 한다고 해보자. 이 4가지 경우 모두 파라미터 생성자를 통해 반환한다면 빠르고 편리하겠지만 생성자 오버로딩의 원칙상 파라미터명이 다르더라도 같은 갯수, 같은 타입의 파라미터를 갖는 생성자는 오버로딩이 불가하다.

//e.g.
public Member(String memberId, String password, int identifier) {
		super();
		this.memberId = memberId;
		this.password = password;
	}
public Member(String memberId, String email, double identifier) {
		super();
		this.memberId = memberId;
		this.email = email;
	}

때문에 이 4가지 경우에 맞는 파라미터 생성자를 우선 선언하고, 제 3의 파라미터로 각기 다른 타입의 매개변수를 받았다. (int/double/char/Date) 이들은 Member 객체를 생성하고 값을 대입할 때 각 생성자간 식별자 역할만을 수행할 뿐 Member 객체 생성에는 그 어떤 영향도 미치지 않는다. (꼼수 좀 부려본건데 이렇게 해도 되는건진 모르겠다 ㅋㅋ)


(Java): common.JDBCTemplate

<<Member Management - JDBCTemplate>>
Package Declaration/import

package member.common;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

(static)Registering JDBC Driver

public class JDBCTemplate {
	static {
		try {
			Class.forName("oracle.jdbc.OracleDriver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

Generate Connection Object

	public static Connection getConnection() {
		String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
		String user = "student";
		String password = "student";
		Connection conn = null;
		try {
			conn = DriverManager.getConnection
            (url, user, password);
			conn.setAutoCommit(false);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}

Conditional Method for commit/rollback

	public static void cORr(int result, Connection conn) {
		if (result > 0)
			try {
				if (conn != null && !conn.isClosed())
					conn.commit();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		else
			try {
				if (conn != null && !conn.isClosed())
					conn.rollback();
			} catch (SQLException e) {
				e.printStackTrace();
			}
	}

Mehods for Returning Resource(Connection/PreparedStatement/Resultset)

	public static void closeC(Connection conn) {
		try {
			if (conn != null && !conn.isClosed())
				conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public static void closeS(Statement stmt) {
		try {
			if (stmt != null && !stmt.isClosed())
				stmt.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public static void closeR(ResultSet rset) {
		try {
			if (rset != null && !rset.isClosed())
				rset.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

JDBCTemplate은 JDBC에서 필수적으로 수행해야 하는 과정들을 별도의 클래스와 메소드로 따로 빼 놓은 것이다. 코딩 시간 단축 및 코드 길이 절감에 큰 효과가 있다. 여기서의 구성 요소는:

1. (static) Registering JDBC Driver: JDBC의 경우 프로그램 실행시마다 JDBC Driver에 접속해야 하는데 이를 상수로 선언해 두어 필요할 때 코드 한 줄만 작성해주면 된다.
2. Generate Connection Object: DB와 연결하기 위한 Connection Object를 미리 선언해 두었다. 이제 url, user, password 입력을 매 때마다 하지 않아도 된다.
3. Conditional Method for commit/rollback: DML 처리 후 commit/rollback은 결과값으로 받아온 int(SQL%ROWCOUNT)값에 따라 분기가 갈리게 된다. int값이 0이면 처리된 내역이 없다는 뜻이므로 rollback, 0이상이면 해당 건수만큼 처리가 완료되었다는 뜻이므로 commit. 이 메소드는 인자로 Connection과 int result를 받아 해당 분기처리에 따라 commit 또는 rollback을 수행한다.
4. Returning Resource(Connection/PreparedStatement/Resultset): Connection, PreparedStatement, ResultSet 이 3가지 객체들의 조건절 자원 반납 메소드이다. 이는 중복된 처리 및 예외 상황을 대비해서 해당 객체가 null이 아니며 자원 반납이 이루어지지 않은 경우에만 반납하도록 작성되었다. 또한 각 자원 반납에 필수적으로 적용해야 하는 SQLException의 예외처리까지 미리 작성하여 보다 짧고 효율적인 코드 작성을 가능하게 한다.


(Java): service.MemberService

<<Member Management - Service>>
Package Declaration/import

package member.model.service;
import static member.common.JDBCTemplate.*;
import java.sql.Connection;
import java.util.List;
import member.common.JDBCTemplate;
import member.model.MemberDAO;
import member.vo.Member;

Field Variables Declaration

public class MemberService {
	private MemberDAO memberDAO = new MemberDAO();
	List<Member> list = null;
	Connection conn = null;
	int result = 0;
	String pwd = null;
	String ID = null;
	String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
	String user = "student";
	String password = "student";

Service Methods (DML): insert, update, delete

	public int insertMember(Member member) {
		conn = getConnection();
		result = memberDAO.insertMember(conn, member);
		cORr(result, conn);
		closeC(conn);
		return result;
	}
	public int deleteMember(Member member) {
		conn = getConnection();
		result = memberDAO.deleteMember(conn, member);
		cORr(result, conn);
		closeC(conn);
		return result;
	}
	public int updateMember(Member member, String whichOne) {
		conn = getConnection();
		result = memberDAO.updateMember(conn, member, whichOne);
		cORr(result, conn);
		closeC(conn);
		return result;
	}

Service Methods (DQL): select *, select by member_id, select by member_name

	public List<Member> selectMemberByName(String memberName) {
		conn = getConnection();
		list = memberDAO.selectMemberByName(conn, memberName);
		closeC(conn);
		return list;
	}
	public List<Member> selectMemberByID(String memberID) {
		conn = getConnection();
		list = memberDAO.selectMemberByID(conn, memberID);
		closeC(conn);
		return list;
	}
	public List<Member> selectAll() {
		conn = getConnection();
		list = memberDAO.selectAll(conn);
		closeC(conn);
		return list;
	}

get Matched Password/ID records

	public String getPassword(String memberID) {
		conn = getConnection();
		pwd = memberDAO.getPassword(conn, memberID);
		closeC(conn);
		return pwd;
	}
	public String getID(String memberID) {
		conn = getConnection();
		ID = memberDAO.getID(conn, memberID);
		closeC(conn);
		return ID;
	}
}

DAO(Database Access Object) 클래스에게 명령을 보내고 값을 받아오는 Service 클래스이다. DML/DQL 메소드를 한 눈에 비교해 보면 그 성질상의 차이점을 확실히 할 수 있다. 우선 각 메소드의 구성요소들을 한 번 살펴보자.

<DML Method>

//e.g.
public int insertMember(Member member) {
		conn = getConnection();
		result = memberDAO.insertMember(conn, member);
		cORr(result, conn);
		closeC(conn);
		return result;
	}

1. Member 객체를 인자로 받아 그 값을 기록하고 결과값으로 정수(Rowcount)를 리턴한다.
2. 이 결과값은 memberDAO의 insertMember();에서 DB 처리 후 반환된 값이다.
3. 결과값에 따라(result > 0) 조건절 분기하여 commit 또는 rollback이 이루어진다.
4. 사용한 Connection 객체를 자원 반납한다.

<DQL Method>

//e.g.
public List<Member> selectMemberByID(String memberID) {
		conn = getConnection();
		list = memberDAO.selectMemberByID(conn, memberID);
		closeC(conn);
		return list;
	}

1. 쿼리문의 조건절에 대입하기 위한 문자열 변수로 String 인자를 받아 List<Member>를 리턴한다.
2. 이 List는 memberDAO의 selectMemberByID();에서 DB 처리 후 반환된 값이다.
3. DQL, 즉 단순 조회/조건절 조회 구문이므로 별도의 commit/rollback이 필요하지 않다.
4. 사용한 Connection 객체를 자원 반납한다.

너무너무 중요한 부분이다. 사실 패키지와 클래스의 수가 많고 다양해지면 어느 클래스의 어느 메소드가 여기로 갔다가 저기로 갔다가 정신없고 그 흐름을 쫓기 힘든 경우가 많은데, 이렇게 놓고 보면 이해가 된다. 위의 내용을 요약해 보면 이러하다:

  1. DML은 DB의 해당 테이블에 값을 대입/갱신/삭제하므로 변동사항이 발생한다. 이를 위해 commit 또는 rollback 처리가 필요하며, DQL은 단순 조회문이므로 이 과정이 필요치 않다.
  2. DML은 처리된 행의 갯수를 세어 정수형으로 리턴하고, DQL은 조회된 결과 집합을 사용자가 명시한 형태에 빌어(위의 경우는 List) 리턴한다.
  3. 2의 과정에서 처리 결과가 없을 경우 DML은 0, DQL은 null을 리턴한다.
  4. DML/DQL구분과 상관없이 사용한 Connection 객체에 대한 자원반납이 이루어져야 한다.

또한 DML문 처리시 사용자가 입력한 ID/Password의 유효성 검사를 위해 별도의 메소드 getPassword(), getID(); 를 생성하였다. 이들의 구조는:

//e.g.
	public String getPassword(String memberID) {
		conn = getConnection();
		pwd = memberDAO.getPassword(conn, memberID);
		closeC(conn);
		return pwd;
	}
  1. 사용자가 기입한 String memberID를 인자로 받는다.
  2. memberDAO를 거쳐 쿼리문에 where 조건절 변수로 해당 인자를 대입한다.
  3. 인자로 받은 memberID에 해당하는 password가 있다면 이를 String으로 리턴한다.

위 과정을 거쳐 리턴받은 String password는 view의 메소드에서 유효성 검사(.contentEquals())를 실시하여 비밀번호가 일치하는 경우 DML 처리를 실행하도록 활용할 수 있다.


(Java): vo.MemberDAO

<<Member Management - DAO>>
Package Declaration/import

package member.model;
import static member.common.JDBCTemplate.closeR;
import static member.common.JDBCTemplate.closeS;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import member.vo.Member;

Field Variables Declaration

public class MemberDAO {
	List<Member> list = null;
	String pwd = null;
	String ID = null;
	int result = 0;

(DML) Insert new Member

	public int insertMember(Connection conn, Member member) {
		PreparedStatement pstmt = null;
		String sql = "insert into member values (?,?,?,?,?,?,?,?,?,default)";
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, member.getMemberId());
			pstmt.setString(2, member.getPassword());
			pstmt.setString(3, member.getMemberName());
			pstmt.setString(4, member.getGender());
			pstmt.setInt(5, member.getAge());
			pstmt.setString(6, member.getEmail());
			pstmt.setString(7, member.getPhone());
			pstmt.setString(8, member.getAddress());
			pstmt.setString(9, member.getHobby());
			result = pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			closeS(pstmt);
		}
		System.out.println("result@dao = " + result);
		return result;
	}

(DML) Update a Member password, email, phone, address

	public int updateMember(Connection conn, Member member, String whichOne) {
		PreparedStatement pstmt = null;
		String part1 = "update member set ";
		String part2 = whichOne;
		String part3 = " = ? where member_id = ?";
		String sql = part1 + part2 + part3;
		try {
			pstmt = conn.prepareStatement(sql);
			switch (whichOne) {
			case "password":
				pstmt.setString(1, member.getPassword());
				break;
			case "email":
				pstmt.setString(1, member.getEmail());
				break;
			case "phone":
				pstmt.setString(1, member.getPhone());
				break;
			case "address":
				pstmt.setString(1, member.getAddress());
				break;
			}
			pstmt.setString(2, member.getMemberId());
			result = pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			closeS(pstmt);
		}
		System.out.println("result@dao = " + result);
		return result;
	}

(DML) Delete a Member

	public int deleteMember(Connection conn, Member member) {
		PreparedStatement pstmt = null;
		String sql = "delete from member where member_id = ?";
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, member.getMemberId());
			result = pstmt.executeUpdate();
		} catch (SQLException sqle) {
			sqle.printStackTrace();
		} finally {
			closeS(pstmt);
		}
		return result;
	}

(DQL) select * from member where member_name = ?

	public List<Member> selectMemberByName(Connection conn, String memberName) {
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		String sql = "select * from member " + "where member_name like ?";
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, "%" + memberName + "%");
			rset = pstmt.executeQuery();
			list = new ArrayList<>();
			while (rset.next()) {
				Member member = new Member();
				member.setMemberId(rset.getString("member_id"));
				member.setPassword(rset.getString("password"));
				member.setMemberName(rset.getString("member_name"));
				member.setAge(rset.getInt("age"));
				member.setGender(rset.getString("gender"));
				member.setEmail(rset.getString("email"));
				member.setPhone(rset.getString("phone"));
				member.setAddress(rset.getString("address"));
				member.setHobby(rset.getString("hobby"));
				list.add(member);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			closeR(rset);
			closeS(pstmt);
		}
		return list;
	}

(DQL) select * from member where member_id = ?

	public List<Member> selectMemberByID(Connection conn, String memberID) {
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		String sql = "select * from member " + "where member_id like ?";
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, "%" + memberID + "%");
			rset = pstmt.executeQuery();
			list = new ArrayList<>();
			while (rset.next()) {
				Member member = new Member();
				member.setMemberId(rset.getString("member_id"));
				member.setPassword(rset.getString("password"));
				member.setMemberName(rset.getString("member_name"));
				member.setAge(rset.getInt("age"));
				member.setGender(rset.getString("gender"));
				member.setEmail(rset.getString("email"));
				member.setPhone(rset.getString("phone"));
				member.setAddress(rset.getString("address"));
				member.setHobby(rset.getString("hobby"));
				list.add(member);
			}
		} catch (SQLException sqle) {
			sqle.printStackTrace();
		} finally {
			closeR(rset);
			closeS(pstmt);
		}
		return list;
	}

(DQL) select * from member

	public List<Member> selectAll(Connection conn) {
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		String sql = "select * from member";
		try {
			pstmt = conn.prepareStatement(sql);
			rset = pstmt.executeQuery();
			list = new ArrayList<>();
			while (rset.next()) {
				Member member = new Member();
				member.setMemberId(rset.getString("member_id"));
				member.setPassword(rset.getString("password"));
				member.setMemberName(rset.getString("member_name"));
				member.setAge(rset.getInt("age"));
				member.setGender(rset.getString("gender"));
				member.setEmail(rset.getString("email"));
				member.setPhone(rset.getString("phone"));
				member.setAddress(rset.getString("address"));
				member.setHobby(rset.getString("hobby"));
				list.add(member);
			}
		} catch (SQLException sqle) {
			sqle.printStackTrace();
		} finally {
			closeR(rset);
			closeS(pstmt);
		}
		return list;
	}

get member Password/ID for verification

	public String getPassword(Connection conn, String memberID) {
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		String sql = "select password from member where member_id like ?";
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, "%" + memberID + "%");
			rset = pstmt.executeQuery();
			while (rset.next()) {
				pwd = rset.getString("password");
			}
		} catch (SQLException sqle) {
			sqle.printStackTrace();
		} finally {
			closeR(rset);
			closeS(pstmt);
		}
		return pwd;
	}
	public String getID(Connection conn, String memberID) {
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		String sql = "select member_id from member where member_id = ?";
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, memberID);
			rset = pstmt.executeQuery();
			while (rset.next()) {
				ID = rset.getString("member_id");
			}
		} catch (SQLException sqle) {
			sqle.printStackTrace();
		} finally {
			closeR(rset);
			closeS(pstmt);
		}
		return ID;
	}
 }

JDBC의 핵심인 DAO 클래스이다.
마찬가지로 DML/DQL 메소드 하나씩을 떼 와 그 구조를 비교하고 주요한 특징을 추려보기로 한다.

<<DML - Insert new Member>>

//e.g. DML
	public int insertMember(Connection conn, Member member) {
		PreparedStatement pstmt = null;
		String sql = "insert into member values (?,?,?,?,?,?,?,?,?,default)";
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, member.getMemberId());
			pstmt.setString(2, member.getPassword());
			pstmt.setString(3, member.getMemberName());
			pstmt.setString(4, member.getGender());
			pstmt.setInt(5, member.getAge());
			pstmt.setString(6, member.getEmail());
			pstmt.setString(7, member.getPhone());
			pstmt.setString(8, member.getAddress());
			pstmt.setString(9, member.getHobby());
			result = pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			closeS(pstmt);
		}
		System.out.println("result@dao = " + result);
		return result;
	}

1. 인자로 Connection객체와 Member 객체를 받아서 int result(Rowcount)를 리턴한다.
2. String으로 Query문을 작성하고 대입할 값 또는 변수를 ?로 표기한다.
3. PreparedStatement는 미완성 쿼리문에 값 전달을 위한 일종의 temp와 유사하다. 미완성 상태인 String sql문에 대입할 값을 받은 후, 실제 DB와 연결하는 객체인 Connection과의 다리를 놓는 중간 역할을 수행한다.
4. pstmt.setString();의 두 인자는 String sql에 대입될 순서(즉 ?의 번짓수), 대입될 값이다.
5. DML에서 완성된 pstmt의 값 전달 및 실행 구문은 "executeUpdate()" 즉 "업데이트 실행"이다.
6. 사용한 Preparedstatement(상위개체 - Statement)의 자원을 반납한다.

<<DQL - Conditional Select - by member_id>>

//e.g. DQL
	public List<Member> selectMemberByID(Connection conn, String memberID) {
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		String sql = "select * from member " + "where member_id like ?";
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, "%" + memberID + "%");
			rset = pstmt.executeQuery();
			list = new ArrayList<>();
			while (rset.next()) {
				Member member = new Member();
				member.setMemberId(rset.getString("member_id"));
				member.setPassword(rset.getString("password"));
				member.setMemberName(rset.getString("member_name"));
				member.setAge(rset.getInt("age"));
				member.setGender(rset.getString("gender"));
				member.setEmail(rset.getString("email"));
				member.setPhone(rset.getString("phone"));
				member.setAddress(rset.getString("address"));
				member.setHobby(rset.getString("hobby"));
				list.add(member);
			}
		} catch (SQLException sqle) {
			sqle.printStackTrace();
		} finally {
			closeR(rset);
			closeS(pstmt);
		}
		return list;
	}

1. 인자로 Connection객체와 String(조건절 목적값)을 받아 결과 집합을 list에 대입해 리턴한다.
2. pattern matching 조건절 검색을 위한 wild card의 대입 또한 필요하므로 pstmt.setString();의 2번째 인자로 양 와일드카드와 메소드 인자인 String memberID를 결합하여 선언한다. 이들의 결합된 값(String)이 미완성 쿼리의 ? 위치에 대입된다.
3. DQL에서 완성된 pstmt의 값 전달 및 실행 구문은 "executeQuery()" 즉 "질의 실행"이다.
4. 질의 결과는 Result Set(결과 집합)을 통해 도출되며, rset.getString("ColumnName");을 통해 결과 집합 내 특정 컬럼의 결과값을 받을 수 있다. 위의 코드에서는 이렇게 받아낸 결과값을 이에 상응하는 Member 객체의 각 변수에 대입하였다.
5. 결과 출력의 편의성을 위해 ResultSet으로부터 재생성한 Member 객체를 list에 담아 리턴한다.
6. 사용한 ResultSet, Preparedstatement(상위개체 - Statement)의 자원을 반납한다.

또한 DML 처리 이전 사용자의 ID/Password 유효성 검사를 위해 사용자가 입력한 ID와 대응되는 실 ID/실 Password를 받아오는 메소드를 별개로 작성하였다. 이의 구조는 다음과 같다:

<<DQL - Conditional Select - by member_id>>

//e.g. get Matching password (Conditional DQL)
	public String getPassword(Connection conn, String memberID) {
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		String sql = "select password from member where member_id = ?";
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, memberID);
			rset = pstmt.executeQuery();
			while (rset.next()) {
				pwd = rset.getString("password");
			}
		} catch (SQLException sqle) {
			sqle.printStackTrace();
		} finally {
			closeR(rset);
			closeS(pstmt);
		}
		return pwd;
	}

구조는 상기한 DQL문과 유사하다. 메소드의 인자로 조건절의 목적값인 String memberID를 사용자로부터 받아와 미완성 쿼리에 대입하고, DB에 이와 대응되는 memberID가 있다면 해당 ID의 password를 String으로 리턴한다.


(Java): view.MemberMenu

<<Member Management - view>>
Package Declaration/import

//e.g. DML
package member.view;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;
import member.controller.MemberController;
import member.vo.Member;

Field Variables Declaration

public class MemberMenu {
	Scanner sc = new Scanner(System.in);
	Member member = new Member();
	MemberController mc = new MemberController();
	List<Member> list = new ArrayList<>();
	List<Member> selectByNameL = new ArrayList<>();
	List<Member> selectByIDL = new ArrayList<>();
	String memberNameStr;
	String memberIDStr;
	String memberPW;
	int result = 0;
	String menuStr = "= Member Management Program 2 =\n" + "1. 회원 전체 조회\n" + "2. 회원 아이디 조회\n" + "3. 회원 이름 검색\n"
			+ "4. 회원 가입\n" + "5. 회원 정보 변경\n" + "6. 회원 탈퇴\n" + "9. 프로그램 종료\n" + "--------------------------------\n"
			+ "선택 > ";
	String subMenuStr = "****** 회원 정보 변경 메뉴******\r\n" + "1. 암호변경\r\n" + "2. 이메일변경\r\n" + "3. 전화번호변경\r\n"
			+ "4. 주소 변경\r\n" + "9. 메인메뉴 돌아가기";

View - Switch: DQL(select all, select by member_id, select by member_name)

public void mainMenu() throws NullPointerException {
	while (true) {
		System.out.println(menuStr);
		int choice = sc.nextInt();
		try {
			switch (choice) {
			case 1: // Select All (DQL) (F)
				list = mc.selectAll();
				displayMemberList(list);
				break;
			case 2: // Select by ID (DQL) (F)
				memberIDStr = inputMemberID();
				selectByIDL = mc.selectMemberByID(memberIDStr);
				displayMemberList(selectByIDL);
				break;
			case 3: // Select by Name (DQL) (F)
				memberNameStr = inputMemberName();
				selectByNameL = mc.selectMemberByName(memberNameStr);
				displayMemberList(selectByNameL);
				break;

View - Switch: DML(insert member, update, delete)

			case 4: // Sign Up (DML) (F)
				member = insertMember();
				result = mc.insertMember(member);
				displayMsg(result, "Sign Up");
				break;
			case 5: // Update Information(DML) (F)
				System.out.println(subMenuStr);
				int choice2 = sc.nextInt();
				try {
					switch (choice2) {
					case 1:
						member = updateMember("password");
						result = mc.updateMember(member, "password");
						displayMsg(result, "Update");
						break;
					case 2:
						member = updateMember("email");
						result = mc.updateMember(member, "email");
						displayMsg(result, "Update");
						break;
					case 3:
						member = updateMember("phone");
						result = mc.updateMember(member, "phone");
						displayMsg(result, "Update");
						break;
					case 4:
						member = updateMember("address");
						result = mc.updateMember(member, "address");
						displayMsg(result, "Update");
						break;
					case 9:
						System.out.println(menuStr);
						break;
					}
				} catch (NullPointerException npe) {
					printInvalid("ID or Password");
					break;
				}
				break;
				case 6: // Delete (DML) (F)
					member = deleteMember();
					int result = mc.deleteMember(member);
					displayMsg(result, "Delete");
					break;
				case 9:
					System.out.println("Do you really wanna Quit? (Y/N)");
					char yn = sc.next().toUpperCase().charAt(0);
					if (yn == 'Y')
						return;
					break;
				default:
					printInvalid("ID");
				}
			} catch (InputMismatchException imme) {
				printInvalid("Number");
			} catch (NullPointerException npe) {
				printInvalid("Password");
			}
		}
	}

Methods for DML

	private Member deleteMember() {
		System.out.println("Please Insert a ID for delete");
		System.out.print("ID? > ");
		String inputID = sc.next();
		String realID = mc.getID(inputID);
		String realPW = mc.getPassword(inputID);
		System.out.println("Existing password? > ");
		String inputPW = sc.next();
		if (inputID.contentEquals(realID) && inputPW.contentEquals(realPW)) {
			System.out.println(new Member(inputID).toString());
			return new Member(inputID);
		} else
			return null;
	}
	private Member updateMember(String whichOne) {
		System.out.println("Please Insert ID for update");
		System.out.print("ID? > ");
		String inputID = sc.next();
		String realID = mc.getID(inputID);
		String realPW = mc.getPassword(inputID);
		String newP = null;
		if (inputID.contentEquals(realID)) {
			selectByIDL = mc.selectMemberByID(inputID);
			displayMemberList(selectByIDL);
			System.out.println("Existing password? > ");
			String existingPassword = sc.next();
			if (existingPassword.contentEquals(realPW)) {
				System.out.println("New " + whichOne + "? > ");
				// Identifier for Overloaded Constructors:
				// int/double/char/Date
				switch (whichOne) {
				case "password":
					newP = sc.next();
					member = new Member(inputID, newP, 1);
					break;
				case "email":
					newP = sc.next();
					member = new Member(inputID, newP, 0.1);
					break;
				case "phone":
					newP = sc.next();
					member = new Member(inputID, newP, 'i');
					break;
				case "address":
					sc.nextLine(); // to empty buffer
					newP = sc.nextLine();
					member = new Member(inputID, newP, null);
					break;
				}
				System.out.println(menuStr);
			}
			return member;
		} else {
			printInvalid("number");
			System.out.println(menuStr);
			return null;
		}
	}

Input Data Section

	private String inputMemberName() {
		System.out.print("Name? > ");
		return sc.next();
	}
	private String inputMemberID() {
		System.out.print("ID? > ");
		return sc.next();
	}
	// Constructor from Superclass + setter
	private Member insertMember() {
		System.out.println("system: Please fill up the form");
		System.out.print("ID: ");
		member.setMemberId(sc.next());
		System.out.println("Password: ");
		member.setPassword(sc.next());
		System.out.println("Name: ");
		member.setMemberName(sc.next());
		System.out.println("Age: ");
		member.setAge(sc.nextInt());
		System.out.println("Gender(M/F): ");
		member.setGender(String.valueOf(sc.next().toUpperCase().charAt(0)));
		System.out.println("Email: ");
		member.setEmail(sc.next());
		System.out.println("Contact Number\n(numbers only): ");
		member.setPhone(sc.next());
		System.out.println("Address: ");
		sc.nextLine(); // to empty Buffer
		member.setAddress(sc.nextLine());
		System.out.println("Hobby: \n(without Puctation Marks/Space Characters) ");
		member.setHobby(sc.next());
		return member;
	}

Show Result List/system Messages Section

	private void displayMemberList(List<Member> list) {
		System.out.println("---------------------------------------");
		if (list == null || list.isEmpty()) {
			printInvalid("Data");
		} else {
			for (Member member : list) {
				System.out.println(member);
			}
			System.out.println("---------------------------------------");
		}
	}
	private void displayMsg(int result, String msg) {
		if (result > 0) 
			System.out.println("Process Success: successfully " + msg + "d");
		else
			System.out.println("Process Failed: " + msg + " failed");
	}
	private void printInvalid(String which) {
		System.out.println("system: Invalid " + which + "\nPlease check again\n");
	}
}

String으로 선언해 둔 메뉴를 출력하고, 사용자로부터 선택 번호를 받아 그에 해당하는 기능을 수행하고 결과를 콘솔창에 출력하는 구조로 view를 작성하였다. 선택 번호(int choice/choice2)에 따른 동작을 수행하기 위해 switch문을 기반으로 코드를 작성하였으며, 상기했던 대로 DML 작업을 위해 ID/Password를 사용자로부터 받고 그에 대응되는 값을 찾아 유효성 검사를 진행하였다. view 메뉴를 작성하며 특히 신경을 썼던 부분은 password, email, phone, address Column의 정보를 각각 수정하기 위해 단 하나의 메소드를 사용하도록 코드를 작성한 점이다.

private Member updateMember(String whichOne) {
		System.out.println("Please Insert ID for update");
		System.out.print("ID? > ");
		String inputID = sc.next();
		String realID = mc.getID(inputID);
		String realPW = mc.getPassword(inputID);
		String newP = null;
		if (inputID.contentEquals(realID)) {
			selectByIDL = mc.selectMemberByID(inputID);
			displayMemberList(selectByIDL);
			System.out.println("Existing password? > ");
			String existingPassword = sc.next();
			if (existingPassword.contentEquals(realPW)) {
				System.out.println("New " + whichOne + "? > ");
				// Identifier for Overloaded Constructors:
				// int/double/char/Date
				switch (whichOne) {
				case "password":
					newP = sc.next();
					member = new Member(inputID, newP, 1);
					break;
				case "email":
					newP = sc.next();
					member = new Member(inputID, newP, 0.1);
					break;
				case "phone":
					newP = sc.next();
					member = new Member(inputID, newP, 'i');
					break;
				case "address":
					sc.nextLine(); // to empty buffer
					newP = sc.nextLine();
					member = new Member(inputID, newP, null);
					break;
				}
				System.out.println(menuStr);
			}
			return member;
		} else {
			printInvalid("number");
			System.out.println(menuStr);
			return null;
		}
	}

본 메소드 하나로 4가지 Column에 대한 update(DML)작업을 각각 수행하기 위해, 인자로 String whichOne을 받아 각 항목에 대입하였다. DML/DQL 작업 모두 공통적으로 view -> Controller -> Service -> DAO(값 도출) -> Service(리턴된 값 전달) -> Controller (리턴된 값 전달) -> view (리턴된 값 받아서 후처리 후 최종 결과 출력)의 순서로 진행된다. 이 때, view에서 update 메소드의 인자로 받은 String whichOne을 값 도출 DAO 메소드까지 전달하여 미완성 쿼리문의 조건절 목적값으로 활용하고 있다. 4가지 Column의 정보를 각각 수정한다고 한들, 목적 컬럼만이 다를 뿐이지 쿼리문의 구조 자체는 같기 때문에 하나의 메소드 + String 메소드 인자 + 제 3의 인자를 식별자로 받은 생성자 오버로딩을 통해 4가지 결과 도출이 가능하다.


(Java): run.Run

<<Member Management - run>>
Package Declaration/import

package run;
import member.view.MemberMenu;

Main Method

public class Run {
	public static void main(String[] args) {
		new MemberMenu().mainMenu();
		System.out.println("==============FINISHED================");
	}
}

실행 클래스. view 클래스인 MemberMenu의 mainMenu 메소드를 호출하고, mainMenu의 swtich 문에서 사용자가 정상적으로 종료를 선택하여 프로그램이 종료되는 경우 종료 메세지를 출력한다.


profile
아! 응애에요!

0개의 댓글