어떤 클래스가 최초 한번만 메모리를 할당하고(Static) 그 메모리에 객체를 만들어 사용하는 디자인 패턴을 의미
장점
단점
package com.test.run;
import com.test.member.view.MemberView;
public class TestMain {
public static void main(String[] args) {
new MemberView().start();
}
}
package com.test.member.model.vo;
import java.sql.Date;
public class Member {
private int memberNo;
private String memberId;
private String memberPwd;
private String memberName;
private char gender;
private int age;
private String email;
private String phone;
private String address;
private String hobby;
private Date enrollDate;
private char withdrawYN;
public Member() {}
public Member(String memberId, String memberPwd, String memberName, char gender, int age, String email,
String phone, String address, String hobby) {
super();
this.memberId = memberId;
this.memberPwd = memberPwd;
this.memberName = memberName;
this.gender = gender;
this.age = age;
this.email = email;
this.phone = phone;
this.address = address;
this.hobby = hobby;
}
public int getMemberNo() {
return memberNo;
}
public void setMemberNo(int memberNo) {
this.memberNo = memberNo;
}
public String getMemberId() {
return memberId;
}
public void setMemberId(String memberId) {
this.memberId = memberId;
}
public String getMemberPwd() {
return memberPwd;
}
public void setMemberPwd(String memberPwd) {
this.memberPwd = memberPwd;
}
public String getMemberName() {
return memberName;
}
public void setMemberName(String memberName) {
this.memberName = memberName;
}
public char getGender() {
return gender;
}
public void setGender(char 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;
}
public char getWithdrawYN() {
return withdrawYN;
}
public void setWithdrawYN(char withdrawYN) {
this.withdrawYN = withdrawYN;
}
@Override
public String toString() {
return memberNo + "/" + memberId + "/" + memberPwd + "/" + memberName + "/" + gender + "/" + age + "/" + email
+ "/" + phone + "/" + address + "/" + hobby + "/" + enrollDate + "/" + withdrawYN;
}
}
View 클래스는 사용자에게 보여지는 화면
Web 으로 표현하자면 Front-End 파트가 View Class 가 하는 역할
사용자는 View 에서 보여주는 메뉴를 가지고 선택할 수 있게 되고, 선택된것에 따라 View는 Controller에게 요청 함
현재 회원관리 프로그램에서는 다음과 같은 메뉴를 가지고 있다.
package com.test.member.view;
import java.util.ArrayList;
import java.util.Scanner;
import com.test.member.controller.MemberController;
import com.test.member.model.vo.Member;
public class MemberView {
private Scanner sc = new Scanner(System.in);
private MemberController mCon = new MemberController();
// 가장 먼저 시작하는 메소드
public void start() {
while (true) {
System.out.println("---------- 회원 관리 프로그램 ver 1.0 ----------");
System.out.println("1. 전체 회원 정보 조회");
System.out.println("2. ID로 회원 조회");
System.out.println("3. 이름으로 회원 조회");
System.out.println("4. 회원 정보 추가");
System.out.println("5. 회원 정보 수정");
System.out.println("6. 회원 정보 삭제");
System.out.println("0. 프로그램 종료");
System.out.print("선택 : ");
int select = sc.nextInt();
if (select == 0) {
System.out.println("프로그램을 종료합니다. 사용해주셔서 감사합니다. (--) (_ _) 꾸벅");
return;
}
switch (select) {
case 1:
selectAll();
break;
case 2:
selectOneId();
break;
case 3:
selectName();
break;
case 4:
insertMember();
break;
case 5:
updateMember();
break;
case 6:
deleteMember();
break;
}
}
}
public void selectAll() {
ArrayList<Member> list = mCon.selectAll();
System.out.println("============= 회원정보 출력 =============");
for (Member m : list) {
System.out.println(m);
}
}
public void selectOneId() {
// 사용자에게 찾으려고하는 ID 를 입력하라고 해야함
System.out.print("검색하려는 회원ID 입력 : ");
String memberId = sc.next();
Member m = mCon.selectOneId(memberId);
if (m != null) {
System.out.println("------------- " + memberId + " 회원정보-------------");
System.out.println("이름 : " + m.getMemberName());
System.out.println("나이 : " + m.getAge());
System.out.println("주소 : " + m.getAddress());
System.out.println("성별 : " + m.getGender());
System.out.println("폰번호 : " + m.getPhone());
System.out.println("이메일 : " + m.getEmail());
System.out.println("취미 : " + m.getHobby());
System.out.println("가입일 : " + m.getEnrollDate());
} else {
System.out.println(memberId + "를(을) 가진 회원을 검색하지 못하였습니다.");
}
}
public void selectName() {
System.out.print("검색하려는 회원 이름 입력 : ");
String memberName = sc.next();
ArrayList<Member> list = mCon.selectName(memberName);
// 결과 2가지 상황
// 1. 찾았다면 -> 리스트 안에 Member 객체 있다.
// 2. 못찾았으면 -> 리스트 안에 Member 객체 없다.
if (!list.isEmpty()) {
for (Member m : list) {
System.out.println(m);
}
} else {
System.out.println(memberName + "이라는 이름을 가진 사람을 검색하지 못하였습니다.");
}
}
public void insertMember() {
System.out.println("----------- 추가될 회원 정보 입력 -----------");
System.out.print("회원ID : ");
String memberId = sc.next();
System.out.print("회원 PWD : ");
String memberPwd = sc.next();
System.out.print("회원 이름 : ");
String memberName = sc.next();
System.out.print("회원성별(1.남/2.여) : ");
int gender = sc.nextInt();
System.out.print("회원 나이 : ");
int age = sc.nextInt();
System.out.print("회원 이메일 : ");
String email = sc.next();
System.out.print("회원 폰번호 : ");
String phone = sc.next();
System.out.print("회원 주소 : ");
sc.nextLine();
String address = sc.nextLine();
System.out.print("회원 취미(여러개인경우,(콤마)로 구분) : ");
String hobby = sc.next();
char genderChar;
if (gender == 1) {
genderChar = 'M';
} else {
genderChar = 'F';
}
Member m = new Member(memberId, memberPwd, memberName, genderChar, age, email, phone, address, hobby);
boolean result = mCon.insertMember(m);
if (result) {
System.out.println(m.getMemberName() + "님의 회원정보가 추가 되었습니다.");
} else {
System.out.println("회원정보 추가를 실패하였습니다. - 지속적인 문제 발생시 관리자에게 문의해주세요 -");
}
}
public void updateMember() {
System.out.print("수정하려는 회원의 ID를 입력 : ");
String memberId = sc.next();
Member m = mCon.selectOneId(memberId);
if (m != null) {
while (true) {
System.out.println("------------ 검색된 " + m.getMemberName() + " 회원 정보 ------------");
System.out.println("ID : " + m.getMemberId());
System.out.println("이름 : " + m.getMemberName());
System.out.println("성별 : " + m.getGender());
System.out.println("나이 : " + m.getAge());
System.out.println("주소 : " + m.getAddress());
System.out.println("이메일 : " + m.getEmail());
System.out.println("폰번호 : " + m.getPhone());
System.out.println("취미 : " + m.getHobby());
System.out.println("가입일 : " + m.getEnrollDate());
System.out.println("삭제 여부: " + m.getWithdrawYN());
System.out.println("-------------------------------------");
System.out.println("1. 주소 수정");
System.out.println("2. 이메일 수정");
System.out.println("3. 폰번호 수정");
System.out.println("4. 취미 수정");
System.out.println("0. 수정 취소");
System.out.print("선택 : ");
int select = sc.nextInt();
if (select == 0) {
break;
}
switch (select) {
case 1:
System.out.print("수정될 주소 입력 : ");
sc.nextLine();
String address = sc.nextLine();
m.setAddress(address);
break;
case 2:
System.out.print("수정될 이메일 입력 :");
String email = sc.next();
m.setEmail(email);
break;
case 3:
System.out.print("수정될 폰번호 입력 : ");
String phone = sc.next();
m.setPhone(phone);
break;
case 4:
System.out.print("수정될 취미 입력 (여러개인 경우 ,(콤마)로 구분) : ");
String hobby = sc.next();
m.setHobby(hobby);
break;
}
// 수정된 데이터가 DB에 반영되기 위해서 view에서 controller로 보내주어야한다.
boolean result = mCon.updateMember(m);
if (result) {
System.out.println("회원 정보가 수정되었습니다.");
} else {
System.out.println("회원 정보 수정에 실패하였습니다. - 지속적인 문제 발생시 관라자에게 문의해주세요 -");
}
}
} else {
System.out.println(memberId + "(을)를 가진 회원을 검색하지 못하였습니다.");
}
}
public void deleteMember() {
System.out.print("삭제하려는 회원의 ID를 입력 : ");
String memberId = sc.next();
Member m = mCon.selectOneId(memberId);
if(m!=null) {
System.out.print(m.getMemberId()+"("+m.getMemberName()+")회원을 정말로 삭제하시겠습니까? (Y,N) : ");
char select = sc.next().toUpperCase().charAt(0);
if(select=='Y') {
boolean result = mCon.deleteMember(memberId);
if(result) {
System.out.println(memberId + " 회원을 정상적으로 삭제하였습니다.");
}else {
System.out.println("회원삭제를 처리하지 못하였습니다. - 지속적인 문제 발생시 관리자에게 문의해주세요 -");
}
}else {
System.out.println("삭제를 취소하였습니다.");
}
}else
System.out.println(memberId+"(을)를 가진 회원이 없습니다.");
}
}
STEP1 : Controller -> DAO 호출
STEP2 : Controller-> Service-> DAO 호출
JAVA의 코드만으로는 Controller가 필요 없을 수 있음
실제 컨트롤러
package com.test.member.controller;
import java.util.ArrayList;
import com.test.member.model.service.MemberService;
import com.test.member.model.vo.Member;
public class MemberController {
private MemberService mService = new MemberService();
public ArrayList<Member> selectAll() {
// cotroller의 역할은 요청을 처리할 수 있는 Service를 연결하는 역할
ArrayList<Member> list = mService.selectAll();
return list;
}
public Member selectOneId(String memberId) {
Member m = mService.selectOneId(memberId);
return m;
}
public ArrayList<Member> selectName(String memberName) {
ArrayList <Member> list = mService.selectName(memberName);
return list;
}
public boolean insertMember(Member m) {
int resultRow = mService.insertMember(m);
if(resultRow>0) {
return true;
}else {
return false;
}
}
public boolean updateMember(Member m) {
int resultRow = mService.updateMember(m);
if(resultRow>0) {
return true;
}else {
return false;
}
}
public boolean deleteMember(String memberId) {
int result = mService.deleteMember(memberId);
if(result >0) {
return true;
}else {
return false;
}
}
}
package com.test.member.model.service;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
// Service의 역할
//1. 드라이버 등록
//2. DBMS 연동
//3. DAO 호출 / 호출할때 DBMS 연결한 Connection 보내주어야 한다.
//4. Connection close 해주는 역할
//5. 필요한 경우에는 commit과 rollback 처리하는 역할
import java.util.ArrayList;
import com.test.common.JDBCTemplate;
import com.test.member.model.dao.MemberDAO;
import com.test.member.model.vo.Member;
public class MemberService {
private MemberDAO mDAO = new MemberDAO();
public ArrayList<Member> selectAll() {
Connection conn = JDBCTemplate.getConnection();
ArrayList<Member> list = mDAO.selectAll(conn);
JDBCTemplate.close(conn);
return list;
}
public Member selectOneId(String memberId) {
Connection conn = JDBCTemplate.getConnection();
Member m = mDAO.selectOneId(memberId, conn);
JDBCTemplate.close(conn);
return m;
}
public ArrayList<Member> selectName(String memberName) {
Connection conn = JDBCTemplate.getConnection();
ArrayList<Member> list = mDAO.selectName(memberName, conn);
JDBCTemplate.close(conn);
return list;
}
public int insertMember(Member m) {
Connection conn = JDBCTemplate.getConnection();
int result = 0;
result = mDAO.insertMember(m, conn);
if (result > 0) {
JDBCTemplate.commit(conn);
} else {
JDBCTemplate.rollback(conn);
}
JDBCTemplate.close(conn);
return result;
}
public int updateMember(Member m) {
Connection conn = JDBCTemplate.getConnection();
int result = mDAO.updateMember(m, conn);
if (result > 0) {
JDBCTemplate.commit(conn);
} else {
JDBCTemplate.rollback(conn);
}
JDBCTemplate.close(conn);
return result;
}
public int deleteMember(String memberId) {
Connection conn = JDBCTemplate.getConnection();
int result = 0;
int deletMemberDelWriteResult = mDAO.deleteMemberDelWrite(memberId, conn);
int deleteMemberResult = mDAO.deleteMember(memberId, conn);
if (deletMemberDelWriteResult > 0 && deleteMemberResult > 0) {
JDBCTemplate.commit(conn);
result = 1;
} else {
JDBCTemplate.rollback(conn);
result = 0;
}
JDBCTemplate.close(conn);
return result;
}
}
기존 Service 에서 처리 되었던 중복 코드들 Common에서 처리
구성
싱글톤 패턴이 적용되기 위해서는 각 메소드마다 static 키워드가 붙게 된다.
static 키워드가 붙게 되면 정적 이라는 의미, 메모리상에 이미 올라간 상태의 메소드를 의미한다.
static 키워드가 붙은 메소드는 interface에서 정의할 수 없다는 점 참고
또한, 이미 메모리에 올라간 상태 이므로 Service나 DAO에서는 이제 JDBCTemplate를 new를 통해서 만들 필요가 없어진다.
package com.test.common;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCTemplate {
public static Connection getConnection() {
Connection conn = null;
Properties prop = new Properties();
try {
// 적혀 있는 경로에 있는 properties 파일을 로드(읽어 오겠다는 의미)
prop.load(new FileReader("resources/DBMS.properties"));
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String dbId = prop.getProperty("dbId");
String dbPw = prop.getProperty("dbPw");
Class.forName(driver);
conn = DriverManager.getConnection(url,dbId,dbPw);
conn.setAutoCommit(false);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
public static void close(Connection conn) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void close(ResultSet rset) {
try {
rset.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void close(Statement stmt) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void commit(Connection conn) {
try {
conn.commit();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void rollback(Connection conn) {
try {
conn.rollback();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
# DBMS.properties
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:xe
dbId=member
dbPw=1234
package com.test.member.model.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import com.test.common.JDBCTemplate;
import com.test.member.model.vo.Member;
public class MemberDAO {
private JDBCTemplate jdbc = new JDBCTemplate ();
public ArrayList<Member> selectAll(Connection conn) {
// DAO는 DBMS와 연결을 하여 SQL을 전송하고 결과를 받아오는 객체
// 여기서 필요한 코딩이 JDBC
Statement stmt = null;
ResultSet rset = null;
// list도 추후 리턴을 위해서 객체를 만들어 주고 try 에서 사용할수 있도록
ArrayList<Member> list = new ArrayList<Member>();
try {
// 3. Statement 객체 생성(SQL을 전송하기 위한 객체)
stmt = conn.createStatement();
// 4. SQL 전송
String sql = "select * from member";
rset = stmt.executeQuery(sql);
// rset에 있는 cursor는 처음에는 전체 결과를 가르키고 있다.
// 5. 결과 처리
// rset.next() 메소드를 호출하게 되면, 그때부터는 하나의 행을 가르키게 된다.
// System.out.println(rset.getString("member_name")); // member_no 컬럼 데이터를 가져와서
// 출력
// 정보를 가져와서 저장
while (rset.next()) {
Member m = new Member();
m.setMemberNo(rset.getInt("member_no"));
m.setMemberId(rset.getString("member_id"));
m.setMemberPwd(rset.getString("member_pwd"));
m.setMemberName(rset.getString("member_name"));
m.setGender(rset.getString("gender").charAt(0));
m.setAge(rset.getInt("age"));
m.setEmail(rset.getString("email"));
m.setPhone(rset.getString("phone"));
m.setAddress(rset.getString("address"));
m.setHobby(rset.getString("hobby"));
m.setEnrollDate(rset.getDate("enroll_date"));
m.setWithdrawYN(rset.getString("withdraw_YN").charAt(0));
list.add(m);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6. close 처리
JDBCTemplate.close(rset);
JDBCTemplate.close(stmt);
}
return list;
}
public Member selectOneId(String memberId, Connection conn) {
PreparedStatement pstmt =null;
ResultSet rset = null;
Member m = null;
try {
// 기존 SQL
//String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID= '"+memberId+ "'";
String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID= ?";
pstmt= conn.prepareStatement(sql);
pstmt.setString(1, memberId);
//4. SQL 전송 ->PreparedStatement 방식은 이미 생성할때 sql을 담고 있기 때문에 또 넣어줄 필요 없다.
rset = pstmt.executeQuery();
// 5. 결과 처리
if(rset.next()) {
m = new Member();
m.setMemberNo(rset.getInt("member_no"));
m.setMemberId(rset.getString("member_id"));
m.setMemberPwd(rset.getString("member_pwd"));
m.setMemberName(rset.getString("member_name"));
m.setGender(rset.getString("gender").charAt(0));
m.setAge(rset.getInt("age"));
m.setEmail(rset.getString("email"));
m.setPhone(rset.getString("phone"));
m.setAddress(rset.getString("address"));
m.setHobby(rset.getString("hobby"));
m.setEnrollDate(rset.getDate("enroll_date"));
m.setWithdrawYN(rset.getString("withdraw_YN").charAt(0));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//6. close
JDBCTemplate.close(rset);
JDBCTemplate.close(pstmt);
}
// 7. 리턴
return m;
}
public ArrayList<Member> selectName(String memberName, Connection conn) {
PreparedStatement pstmt= null;
ResultSet rset = null;
ArrayList<Member> list = new ArrayList<Member>();
try {
String sql = "SELECT * FROM MEMBER WHERE MEMBER_NAME LIKE ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "%"+memberName+"%");
rset = pstmt.executeQuery();
while(rset.next()) {
Member m = new Member();
m.setMemberNo(rset.getInt("member_no"));
m.setMemberId(rset.getString("member_id"));
m.setMemberPwd(rset.getString("member_pwd"));
m.setMemberName(rset.getString("member_name"));
m.setGender(rset.getString("gender").charAt(0));
m.setAge(rset.getInt("age"));
m.setEmail(rset.getString("email"));
m.setPhone(rset.getString("phone"));
m.setAddress(rset.getString("address"));
m.setHobby(rset.getString("hobby"));
m.setEnrollDate(rset.getDate("enroll_date"));
m.setWithdrawYN(rset.getString("withdraw_YN").charAt(0));
list.add(m);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
JDBCTemplate.close(rset);
JDBCTemplate.close(pstmt);
}
return list;
}
public int insertMember(Member m, Connection conn) {
PreparedStatement pstmt = null;
int resultRow = 0;
try {
// 3. PreparedStatement 객체 생성(SQL 처리)
String sql = "INSERT INTO MEMBER VALUES(SEQ_M_NO.NEXTVAL,?,?,?,?,?,?,?,?,?, SYSDATE, 'N')";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, m.getMemberId());
pstmt.setString(2, m.getMemberPwd());
pstmt.setString(3, m.getMemberName());
pstmt.setString(4, String.valueOf(m.getGender()));
pstmt.setInt(5, m.getAge());
pstmt.setString(6, m.getEmail());
pstmt.setString(7, m.getPhone());
pstmt.setString(8, m.getAddress());
pstmt.setString(9, m.getHobby());
// 4. SQL 전송
// INSERT, UPDATE, DELETE를 처리하기 위한 메소드
resultRow = pstmt.executeUpdate();
// 검증
// System.out.println("처리된 행의 개수: " +resultRow);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTemplate.close(pstmt);
}
return resultRow;
}
public int updateMember(Member m, Connection conn) {
PreparedStatement pstmt = null;
int resultRow = 0;
try {
String sql = "UPDATE MEMBER SET ADDRESS=?, EMAIL=?, PHONE=? , HOBBY=? WHERE MEMBER_ID = ?";
pstmt= conn.prepareStatement(sql);
pstmt.setString(1, m.getAddress());
pstmt.setString(2, m.getEmail());
pstmt.setString(3, m.getPhone());
pstmt.setString(4, m.getHobby());
pstmt.setString(5, m.getMemberId());
resultRow = pstmt.executeUpdate();
}catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCTemplate.close(pstmt);
}
return resultRow;
}
public int deleteMemberDelWrite(String memberId, Connection conn) {
PreparedStatement pstmt = null;
int result = 0;
try {
String sql = "INSERT INTO DEL_MEMBER (" +
"SELECT SEQ_DEL_MEMBER_NO.NEXTVAL, " +
"MEMBER_ID, " +
"MEMBER_NAME," +
"SYSDATE " +
"FROM MEMBER " +
"WHERE MEMBER_ID = ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, memberId);
result = pstmt.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCTemplate.close(pstmt);
}
return result;
}
public int deleteMember(String memberId, Connection conn) {
PreparedStatement pstmt = null;
int result = 0;
try {
String sql = "UPDATE MEMBER SET " +
"MEMBER_PWD='DELETE-DATA', " +
"MEMBER_NAME='DELETE-DATA', " +
"GENDER= '-', " +
"AGE=0, " +
"EMAIL='DELETE-DATA', " +
"PHONE='DELETE-DATA', " +
"ADDRESS='DELETE-DATA', " +
"HOBBY='DELETE-DATA', " +
"WITHDRAW_YN='Y' " +
"WHERE MEMBER_ID= ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, memberId);
result = pstmt.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCTemplate.close(pstmt);
}
return result;
}
}