2020년 11월 17일
업데이트:
JDBCRun.java
import com.kh.jdbc.view.JDBCView;
public class JDBCRun {
public static void main(String[] args) {
new JDBCView().displayMain();
}
}
JDBCView.java
import java.util.InputMismatchException;
import java.util.Scanner;
import com.kh.jdbc.member.mode.vo.Member;
import com.kh.jdbc.member.model.service.MemberService;
/** JDBCProject View
* @author 이한솔
*
*/
public class JDBCView {
private Scanner sc = new Scanner(System.in);
private MemberService mService = new MemberService();
private Member loginMember = null; // 로그인된 회원의 정보를 저장
// alt + shift + j
/**
* 메인 메뉴 View
* 작성자 : 이한솔
*/
public void displayMain() {
int sel = 0;
do {
try {
if(loginMember == null) { // 로그인이 되어있지 않은 경우
System.out.println("★☆★☆★☆★☆★☆ JDBC PROJECT ★☆★☆★☆★☆★☆");
System.out.println("====================================");
System.out.println("1. 로그인");
System.out.println("2. 회원가입");
System.out.println("0. 프로그램 종료");
System.out.println("====================================");
System.out.print("메뉴 선택 >> ");
sel = sc.nextInt();
sc.nextLine();
System.out.println();
switch(sel) {
case 1 : login(); break;
case 2 : signUp(); break;
case 0 : System.out.println("프로그램 종료."); break;
default : System.out.println("잘못 입력하셨습니다.");
}
} else {
System.out.println("====================================");
System.out.println("[메인 메뉴]");
System.out.println("1. 회원 기능");
System.out.println("9. 로그아웃");
System.out.println("0. 프로그램 종료");
System.out.println("====================================");
System.out.print("메뉴 선택 >> ");
sel = sc.nextInt();
sc.nextLine();
System.out.println();
switch(sel) {
case 1 : break;
case 9 : loginMember = null; // 회원 정보를 없앰
System.out.println("로그아웃 되었습니다.");
break;
case 0 : System.out.println("프로그램 종료."); break;
default : System.out.println("잘못 입력하셨습니다.");
}
}
} catch (InputMismatchException e) {
System.out.println("숫자만 입력해주세요.");
sel = -1;
sc.nextLine(); // 버퍼에 남아있는 문자열 제거
}
}while(sel != 0);
}
/**
* 회원가입용 View
*/
private void signUp() {
System.out.println("[회원 가입]");
System.out.print("아이디 : ");
String memId = sc.nextLine();
System.out.print("비밀번호 : ");
String memPw = sc.nextLine();
System.out.print("이름 : ");
String memNm = sc.nextLine();
System.out.print("전화번호 : ");
String phone = sc.nextLine();
System.out.print("성별 : ");
char gender = sc.nextLine().toUpperCase().charAt(0);
// 입력받은 값을 하나의 Member 객체에 저장
Member newMember = new Member(memId, memPw, memNm, phone, gender);
// 입력받은 회원 정보를 DB에 삽입하기 위한 service 호출
try {
System.out.println();
int result = mService.signUp(newMember);
if(result > 0) {
System.out.println("회원 가입 성공 !!!!!!!!!!!!!!");
}else {
System.out.println("회원 가입 실패.......");
}
} catch (Exception e) {
System.out.println("회원 가입 과정에서 오류 발생");
e.printStackTrace();
}
}
/**
* 로그인용 View
*/
private void login() {
System.out.println("[로그인]");
System.out.print("아이디 : ");
String memId = sc.nextLine();
System.out.print("비밀번호 : ");
String memPw = sc.nextLine();
// 아이디, 비밀번호를 하나의 Member 객체에 저장
Member member = new Member();
member.setMemId(memId);
member.setMemPw(memPw);
//입력받은 ID, PW를 이용하여 로그인 서비스 수행
// -> 반환 받은 결과를 loginMember에 저장
try {
loginMember = mService.login(member);
// 로그인이 성공한 경우
if(loginMember != null) {
System.out.println("***** " + loginMember.getMemNm() + "님 환영합니다. *****");
} else {
System.out.println("아이디 또는 비밀번호가 일치하지 않거나, 탈퇴한 회원입니다.");
}
} catch (Exception e) {
System.out.println("로그인 과정에서 오류가 발생했습니다.");
e.printStackTrace();
}
}
}
MemberService.java
//static import : 특정 static 필드, 메소드 호출 시 클래스명을 생략할 수 있게 하는 구문
import static com.kh.jdbc.common.JDBCTemplate.*;
import java.sql.Connection;
import com.kh.jdbc.common.JDBCTemplate;
import com.kh.jdbc.member.mode.vo.Member;
import com.kh.jdbc.member.model.dao.MemberDAO;
//Service
// 1. 요청, 응답 데이터의 가공 처리
// 2. 여러 DAO 메소드를 호출하여 DML 진행 후 수행된 DML을 하나의 트랜잭션으로 묶어 처리하는 역할
// ***** 트랜잭션 처리를 위해 Connection 객체의 생성과 반환을 Service에서 진행해야 함 *****
public class MemberService {
private MemberDAO mDAO = new MemberDAO();
public int signUp(Member newMember) throws Exception {
// Connection 생성 == JDBCTemplate에서 커넥션 얻어오기
Connection conn = getConnection();
// 요청을 처리할 수 있는 알맞은 DAO 메소드를 호출하여
// 커넥션과 매개변수 전달하고 결과를 반환 받음
int result = mDAO.signUp(conn, newMember);
// result 값에 따른 트랜잭션 처리
if(result > 0) commit(conn);
else rollback(conn);
// conn 반환
close(conn);
return result;
}
public Member login(Member member) throws Exception{
// 커넥션 객체 얻어오기
Connection conn = getConnection();
// 얻어온 커넥션과 매개변수를 DAO 메소드로 전달.
Member loginMember = mDAO.login(conn, member);
// Select는 트랜잭션 처리가 필요없으므로 바로 커넥션 반환
close(conn);
// DB 조회 결과인 loginMember 반환
return loginMember;
}
}
Member.java
import java.sql.Date;
public class Member {
private int memNo; // 회원번호
private String memId; //아이디
private String memPw; // 비밀번호
private String memNm; // 이름
private String phone; //전화번호
private char gender; //성별
private Date hireDt; // 가입일
private char scsnFl; //탈퇴여부
public Member() { } // 기본생성자
public Member(String memId, String memPw, String memNm, String phone, char gender) {
super();
this.memId = memId;
this.memPw = memPw;
this.memNm = memNm;
this.phone = phone;
this.gender = gender;
}
// 로그인용 생성자
public Member(int memNo, String memId, String memNm, String phone, char gender, Date hireDt) {
super();
this.memNo = memNo;
this.memId = memId;
this.memNm = memNm;
this.phone = phone;
this.gender = gender;
this.hireDt = hireDt;
}
public Member(int memNo, String memId, String memPw, String memNm, String phone, char gender, Date hireDt,
char scsnFl) {
super();
this.memNo = memNo;
this.memId = memId;
this.memPw = memPw;
this.memNm = memNm;
this.phone = phone;
this.gender = gender;
this.hireDt = hireDt;
this.scsnFl = scsnFl;
}
public int getMemNo() {
return memNo;
}
public void setMemNo(int memNo) {
this.memNo = memNo;
}
public String getMemId() {
return memId;
}
public void setMemId(String memId) {
this.memId = memId;
}
public String getMemPw() {
return memPw;
}
public void setMemPw(String memPw) {
this.memPw = memPw;
}
public String getMemNm() {
return memNm;
}
public void setMemNm(String memNm) {
this.memNm = memNm;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public Date getHireDt() {
return hireDt;
}
public void setHireDt(Date hireDt) {
this.hireDt = hireDt;
}
public char getScsnFl() {
return scsnFl;
}
public void setScsnFl(char scsnFl) {
this.scsnFl = scsnFl;
}
@Override
public String toString() {
return "Member [memNo=" + memNo + ", memId=" + memId + ", memPw=" + memPw + ", memNm=" + memNm + ", phone="
+ phone + ", gender=" + gender + ", hireDt=" + hireDt + ", scsnFl=" + scsnFl + "]";
}
}
MemberDAO.java
import static com.kh.jdbc.common.JDBCTemplate.close;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import com.kh.jdbc.member.mode.vo.Member;
public class MemberDAO {
// DAO에서 자주 사용하는 JDBC 객체 참조 변수를 멤버 변수로 선언
private Statement stmt = null;
private PreparedStatement pstmt = null;
private ResultSet rset = null;
// 외부 xml 파일에 저장된 SQL 구문을 읽어들일 Properties 변수 선언
private Properties prop = null;
public MemberDAO() {
// 기본 생성자를 통한 객체 생성 시 xml 파일을 읽어오게 함.
try {
prop = new Properties();
prop.loadFromXML(new FileInputStream("member-query.xml"));
} catch (Exception e) {
e.printStackTrace();
}
}
/** 회원가입용 DAO
* @param conn
* @param newMember
* @return result
* @throws Exception
*/
public int signUp(Connection conn, Member newMember) throws Exception{
// 예외를 view에서 처리하기위해 던져줌
/*
* 이전 DAO에서 하던 역할
* 1) JDBC 드라이버 등록
* 2) DB 연결 커넥션 생성
* 3) SQL 수행
* 4) 트랜잭션 처리
* 5) JDBC 객체 반환
* 6) sql 수행 결과 반환
*
* --> 지금은 3, 5, 6번 수행
*/
int result = 0; //DML 수행 결과 저장용 변수
try {
String query = prop.getProperty("signUp");
// SQL 구문을 DBdp 전달할 준비
pstmt = conn.prepareStatement(query);
pstmt.setString(1, newMember.getMemId());
pstmt.setString(2, newMember.getMemPw());
pstmt.setString(3, newMember.getMemNm());
pstmt.setString(4, newMember.getPhone());
pstmt.setString(5, newMember.getGender() + "");
// char 데이터에 빈문자열("")을 더하여 String 형태로 형변환
// SQL 수행 수 결과를 반환받아 result에 저장
result = pstmt.executeUpdate();
} finally {
// catch가 없어도 finally 작성 가능
// DB 자원 반환
close(pstmt);
}
return result;
}
public Member login(Connection conn, Member member) throws Exception {
Member loginMember = null; // 로그인된 회원 정보를 저장할 임시변수
try {
// member-query.xml 파일에서 키값이 "login"인 태그의 값을 얻어옴
String query = prop.getProperty("login");
// DB 전달을 위한 pstmt 생성
pstmt = conn.prepareStatement(query);
// SQL 구문의 위치홀더에 알맞는 값을 배치
pstmt.setString(1, member.getMemId());
pstmt.setString(2, member.getMemPw());
// SQL 수행 후 결과를 ResultSet으로 반환 받음
rset = pstmt.executeQuery();
// 조회 결과가 있는지 확인하는 if문 작성
if(rset.next()) {
// 조회 결과가 있을 경우 Member 객체를 만들어 로그인 정보를 저장
loginMember = new Member(rset.getInt("MEM_NO"),
rset.getString("MEM_ID"),
rset.getString("MEM_NM"),
rset.getString("PHONE"),
rset.getString("GENDER").charAt(0),
rset.getDate("HIRE_DT"));
}
}finally {
// DB 자원 반환
close(rset);
close(pstmt);
}
// 조회 결과가 담긴 loginMember 반환
return loginMember;
}
}
JDBCTemplate.java
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
public class JDBCTemplate {
// 1. 반복되는 Connection 객체의 생성을 간소화
// 2. 트랜잭션 처리, close() 처리의 간소화
// ** 싱글톤(singleTon) 패턴
// 프로그램 구동 시 메모리 상에 딱 하나의 객체만 기록되게 하는 디자인 패턴
// 대표적인 예시로 java.lang.Math가 있음.
// 모든 필드, 메소드를 static으로 선언하여 프로그램 구동 시 static 메모리 영역에
// 모든 클래스 내용을 로드하여 하나의 객체 모양을 띄게 함.
// 하나의 공용 커넥션 참조변수 선언
// public으로 접근제한자를 설정하면 null 값을 가진 커넥션을 참조할 수도 있으므로 private으로 제한을 건다.
private static Connection conn = null;
//private 선언 이유 : 직접 접근 시 null 값이나 닫혀진 커넥션 객체를 가져갈 수 있는 확률이 있어서 이를 미연에 차단한다
// 해당 클래스의 내용은 모두 static에서 객체의 모양을 이루기 때문에
// 추가적인 객체 생성을 막기 위해 private을 사용
private JDBCTemplate() { }
// DB 연결을 위한 Connection 객체를 간접적으로 얻어가는 메소드 생성
public static Connection getConnection() {
try {
if(conn == null || conn.isClosed()) { // 커넥션이 없거나, 닫혀있는 경우
// -> 새로운 커넥션을 생성하여 반환
/* DB 연결을 위한 Driver 정보나
* DB URL, 계정 정보는 상황에 따라 언제든지 바뀔 가능성이 높음.
* --> 코드를 직접 수정하게 되는 경우 유지보수에 좋지 않음
* --> 외부파일에 해당 정보들을 기입하여 읽어오는 형태로 변환
* --> 내부 코드에 변화가 없으므로 재컴파일이 필요없음
* --> 유지보수성 향상
*/
Properties prop = new Properties();
// driver.xml 파일에 작성된 entry를 prop에 저장함
prop.loadFromXML(new FileInputStream("driver.xml"));
// JDBC 드라이버 등록 및 커넥션 얻어오기
Class.forName(prop.getProperty("driver"));
conn = DriverManager.getConnection(prop.getProperty("url"),
prop.getProperty("user"),
prop.getProperty("password"));
// 자동 commit 비활성화
conn.setAutoCommit(false);
}
}catch (Exception e) {
e.printStackTrace();
}
return conn;
}
// 트랜잭션 처리(commit, rollback)도 공통적으로 사용함
// static으로 미리 선언으로 코드 길이 감소 효과 + 재사용성
public static void commit(Connection conn) {
try {
if (conn != null && !conn.isClosed()) {
// 참조하고 있는 커넥션이 닫혀있지 않은 경우
conn.commit();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void rollback(Connection conn) {
try {
if (conn != null && !conn.isClosed()) {
// 참조하고 있는 커넥션이 닫혀있지 않은 경우
conn.rollback();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// DB 연결 자원 반환 구문도 static으로 작성
public static void close(Connection conn) {
try {
if (conn != null && !conn.isClosed()) {
// 참조하고 있는 커넥션이 닫혀있지 않은 경우
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(ResultSet rset) {
try {
if (rset != null && !rset.isClosed()) {
// 참조하고 있는 커넥션이 닫혀있지 않은 경우
rset.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Statement, PreparedStatement 두 객체를 반환하는 메소드
// 어떻게? PreparedStatement는 Statement의 자식이므로
// 매개변수 stmt에 다형성이 적용되어서 자식 객체인 PreparedStatement를 참조 가능
public static void close(Statement stmt) {
try {
if (stmt != null && !stmt.isClosed()) {
// 참조하고 있는 커넥션이 닫혀있지 않은 경우
stmt.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
TestProperties.java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Properties;
public class TestProperties {
public static void main(String[] args) {
// Map<K, V> --> 키와 값에 모든 자료형이 가능함
// Map<String, String> == Properties
// Properties 클래스
// 키와 값이 모두 String인 컬렉션
// + 외부 파일 입출력이 간단하게 구현되어 있음.
Properties prop = new Properties();
/*
// 값 세팅
prop.setProperty("driver", "oracle.jdbc.driver.OracleDriver");
prop.setProperty("url", "jdbc:oracle:thin:@localhost:1521:xe");
prop.setProperty("user", "jdbc");
prop.setProperty("password", "jdbc");
// 꺼내 쓰는 법
//System.out.println(prop.getProperty("driver"));
// 세팅한 Properties 객체를 외부파일(XML)로 출력
// XML의 장점 : 모든 프로그래밍 언어에서 입출력 가능한 파일
try {
prop.storeToXML(new FileOutputStream("driver.xml"), "driver information");
}catch (Exception e) {
e.printStackTrace();
}
*/
// 외부 xml 파일 읽어오기
try {
prop.loadFromXML(new FileInputStream("driver.xml"));
System.out.println(prop.getProperty("driver"));
System.out.println(prop.getProperty("url"));
System.out.println(prop.getProperty("user"));
System.out.println(prop.getProperty("password"));
}catch (Exception e) {
e.printStackTrace();
}
}
}
driver.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>driver information</comment>
<entry key="user">jdbc</entry>
<entry key="password">jdbc</entry>
<entry key="url">jdbc:oracle:thin:@localhost:1521:xe</entry>
<entry key="driver">oracle.jdbc.driver.OracleDriver</entry>
</properties>
member-query.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<!-- member 관련 SQL 구문을 작성하는 xml 파일 -->
<entry key="signUp">
INSERT INTO TB_MEMBER
VALUES(SEQ_MNO.NEXTVAL, ?, ?, ?, ?, ?, DEFAULT, DEFAULT)
</entry>
<entry key="login">
SELECT MEM_NO, MEM_ID, MEM_NM, PHONE, GENDER, HIRE_DT
FROM TB_MEMBER WHERE MEM_ID = ? AND MEM_PW = ?
AND SCSN_FL = 'N'
</entry>
</properties>
댓글남기기