// 1. db접속 및 Connection 생성
public static Connection getConnection() {
/*
* JDBC Driver, 접속할 URL, 계정명, 비밀번호를 자바 소스코드내에
* 명시적 작성하는 경우 보안상 위험할 수 있다.
*
* DB와 관려된 정보들을 별도로 보관하는 외부파일을 생성하여 보안성을 향상
* */
Properties prop = new Properties();
Connection conn = null;
try {
prop.load( new FileInputStream("resources/driver.properties"));
// 1) Driver 등록
Class.forName(prop.getProperty("driver"));
// 2) Connection 생성
conn = DriverManager.getConnection(
prop.getProperty("url")
,prop.getProperty("username")
,prop.getProperty("password"));
// 3) autocommit설정
conn.setAutoCommit(false);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return conn;
}
// 2. jdbc용 객체들을 대신 반납해주는 메소드들.
public static void close(Connection conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(Statement stmt) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(ResultSet rset) {
try {
if(rset != null && !rset.isClosed())
rset.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 3. 전달받은 Connection을 통해 대신 트랜잭션을 수행하는 메소드.
public static void commit(Connection conn) {
try {
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void rollback(Connection conn) {
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}}
*/
public class MemberView {
private Scanner sc = new Scanner(System.in);
private MemberController mc = new MemberController();
public void mainMenu() {
while(true) {
System.out.println("#### 회원 프로그램 ####");
System.out.println("1. 회원 추가");
System.out.println("2. 회원 전체 조회");
System.out.println("3. 회원 아이디로 검색");
System.out.println("4. 회원 이름 검색");
System.out.println("5. 회원 정보 변경");
System.out.println("6. 회원 탈퇴 기능");
System.out.print("이용할 메뉴 선택 : " );
int menu = Integer.parseInt(sc.nextLine());
switch(menu) {
case 1: insertMember(); break;
case 2: selectAll(); break;
case 3: selectByUserId(); break;
case 4: selectByUserName(); break;
case 5: updateMember(); break;
case 6: deleteMember(); break;
case 9: return;
}
}
}
/**
* 사용자에게 탈퇴할 회원의 아이디를 입력받은 후 삭제 요청하는 메소드
* */
private void deleteMember() {
System.out.print("삭제할 회원 아이디 : ");
String id = sc.nextLine();
mc.deleteMember(id);
}
/**
* 사용자에게 변경할 회원의 아이디, 변경할 정보들(비번, 이메일, 휴대폰번호, 주소)를
* 입력 받은 후 변경요청하는 메소드
* */
private void updateMember() {
System.out.print("정보를 변경할 회원의 아이디 : ");
String id = sc.nextLine();
System.out.print("변경할 비밀번호 : ");
String pwd = sc.nextLine();
System.out.print("변경할 이메일 : ");
String email = sc.nextLine();
System.out.print("변경할 핸드폰번호 : ");
String phone = sc.nextLine();
System.out.print("변경할 주소 : ");
String address = sc.nextLine();
mc.updateMember(id, pwd, email, phone, address);
}
/**
* 사용자에게 검색할 회원의 "이름"을 입력 받은 후 조회 요청하는 메소드
* + 이름에 입력값이 포함된 경우 모두 조회
* */
private void selectByUserName() {
System.out.print("검색할 회원 이름을 입력하세요 : ");
String name = sc.nextLine();
List<Member> list = mc.selectByUserName(name);
for(Member m : list) {
System.out.println(m);
}
}
/**
* 사용자에게 검색할 회원의 아이디를 입력 받은 후 조회 요청하는 메소드.
* */
private void selectByUserId() {
// view -> controller -> service -> dao -> sql
System.out.print("검색할 회원 아이디 : ");
String id = sc.nextLine();
System.out.println(mc.selectByUserId(id));
}
private void selectAll() {
System.out.println("----- 회원 전체 조회 -----");
// 회원 정보 조회 요청
List<Member> list = mc.selectAll();
list.stream()
.forEach( m -> System.out.println(m) );
}
/**
* 회원 추가용 화면(view)
* 추가하고자 하는 회원의 정보를 입력받아 controller에 회원 추가 요청을 할 수 있는 화면.
* */
private void insertMember() {
System.out.println("---- 회원 추가 ----");
System.out.print("아이디 : ");
String memberId = sc.nextLine();
System.out.print("비밀번호 : ");
String memberPwd = sc.nextLine();
System.out.print("이름 : ");
String memberName = sc.nextLine();
// 입력받은 정보를 통해 회원가입을 할 수 있도록, controller에게 넘긴다.
mc.insertMember(memberId, memberPwd, memberName);
}
public void displaySuccess(String string) {
System.out.println("서비스 요청 결과 : "+string);
}
public void displayFail(String string) {
System.out.println("서비스 요청 결과 : "+string);
}
}
처리 결과값을 반환시켜주는 객체. *
*/
public class MemberDao {
private Properties prop = new Properties();
{
try {
prop.loadFromXML(new FileInputStream("resources/query.xml"));
} catch (InvalidPropertiesFormatException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public int insertMember(Connection conn, Member m) {
// 1) 변수들 초기화
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("insertMember");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, m.getMemberId());
pstmt.setString(2, m.getMemberPwd());
pstmt.setString(3, m.getMemberName());
result = pstmt.executeUpdate();
//pstmt = conn.prepareStatement("");
//result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTemplate.close(pstmt);
}
return result;
}
public List<Member> selectAll(Connection conn) {
// select -> ResultSet
List<Member> list = new ArrayList<>(); // 반환시킬 값들을 저장할 컬렉션
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectAll");
try {
pstmt = conn.prepareStatement(sql);
rset = pstmt.executeQuery();
while(rset.next()) {
Member m = new Member();
m.setMemberId(rset.getString("MEMBER_ID"));
m.setMemberName(rset.getString("MEMBER_NAME"));
m.setEnrollDate(rset.getDate("ENROLL_DATE"));
list.add(m);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTemplate.close(rset);
JDBCTemplate.close(pstmt);
}
return list;
}
public Member selectByUserId(Connection conn, String id) {
Member m = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectByUserId");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
rset = pstmt.executeQuery();
if(rset.next()) {
m = new Member();
m.setMemberName( rset.getString("MEMBER_NAME") );
m.setAge( rset.getInt("AGE") );
m.setPhone( rset.getString("PHONE") );
}
} catch (SQLException e) {
e.printStackTrace();
}
return m;
}
public List<Member> selectByUserName(Connection conn, String name) {
List<Member> list = new ArrayList();
Member m = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectByUserName");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
rset = pstmt.executeQuery();
while(rset.next()) {
m = new Member();
m.setMemberName( rset.getString("MEMBER_NAME") );
m.setAge( rset.getInt("AGE") );
m.setPhone( rset.getString("PHONE") );
list.add(m);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTemplate.close(rset);
JDBCTemplate.close(pstmt);
}
return list;
}
public int updateMember(Connection conn, Member m) {
int updateCount = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("updateMember");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, m.getMemberPwd());
pstmt.setString(2, m.getPhone());
pstmt.setString(3, m.getEmail());
pstmt.setString(4, m.getAddress());
pstmt.setString(5, m.getMemberId());
updateCount = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTemplate.close(pstmt);
}
return updateCount;
}
public int deleteMember(Connection conn, String id) {
int updateCount = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("deleteMember");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
updateCount = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTemplate.close(pstmt);
}
return updateCount;
}
//JDBC클래스 안의 *(모든) 스테틱
import static com.kh.chap03_MVC.model.template.JDBCTemplate.close;
import static com.kh.chap03_MVC.model.template.JDBCTemplate.commit;
import static com.kh.chap03_MVC.model.template.JDBCTemplate.getConnection;
import static com.kh.chap03_MVC.model.template.JDBCTemplate.rollback;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import com.kh.chap03_MVC.model.dao.MemberDao;
import com.kh.chap04_template.refactoring.Template;
import com.kh.jdbc.model.vo.Member;
*/
public class MemberService {
private MemberDao dao = new MemberDao();
public int insertMember(Member m) {
// 커넥션 객체 생성
Connection conn = getConnection();
// 회원가입 서비스
// Member테이블에 1행의 데이터(m) 추가
int rows = dao.insertMember(conn, m);
// 사용자의 회원가입 일자를 저장하는 테이블이 있다고 가정
// MemberEnroll 테이블에 1행의 데이터 추가
// rows = dao.insertMemberEnroll(conn, m);
// 사용자의 포인트 정보를 보관하는 테이블이 있다고 가정
// MemberPoint 테이블에 1행의 데이터 추가.
// rows = dao.insertMemberPoint(conn, m);
if(rows > 0) {
commit(conn);
}else {
rollback(conn);
}
// 커넥션 반환
close(conn);
return rows;
}
public List<Member> selectAll() {
Connection conn = null;
try {
conn = Template.getConnection();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
List<Member> list = dao.selectAll(conn);
return list;
}
public Member selectByUserId(String id) {
Connection conn = getConnection();
Member m = dao.selectByUserId(conn, id);
return m;
}
public List<Member> selectByUserName(String name) {
Connection conn = getConnection();
List<Member> list = dao.selectByUserName(conn, name);
close(conn);
return list;
}
public int updateMember(Member m) {
Connection conn = getConnection();
int result = dao.updateMember(conn, m);
if(result > 0) {
commit(conn);
}else {
rollback(conn);
}
return result;
}
public int deleteMember(String id) {
Connection conn = getConnection();
int updateCount = dao.deleteMember(conn , id);
if(updateCount == 1) {
commit(conn);
}else {
rollback(conn);
}
return updateCount;
}
}
/*
Refactoring
- 기존의 코드를 변경하여 코드를 더 이해하기 쉽고, 유지보수하기 쉽게 만드는 과정.
- 코드의 중복을 제거, 공용메서드나 클래스 추출, 변수의 이름을 직관성 있게 변경작업 등,
코드의 가독성과 유지보수성을 향상시키는 수행작업.
* */
/*
* Connection 객체 생성 방식
* 1) 자바 어플리케이션에서 DBMS의 드라이버를 통해 커넥션을 조회.
* 2) DBMS 드라이버는 DBMS와 TCP/IP 커넥션을 연결한다. (3 way handshake 발생)
* 1. 어플리케이션에서 DB에 접속요청
* 2. DB는 요청을 받고 요청 수락메시지를 보낸다.
* 3. 어플리케이션에서는 DB 서버에 다시 접속한다는 요청을 보낸다. 이떄 아이디,비밀번호를 전달한다
* 3) DBMS드라이버는 TCP/IP커넥션이 연결되면 아이디와,패스워드 기타 부가정보를 DBMS에 전달
* 4) 오라클서버는 아이디, 패스워드를 통해 인증작업을 거친 후 DBMS커넥션을 생성
* 5) 오라클서버는 커넥션 생성이 완료되었다는 응답 메시지를 보낸다
* 6) DBMS드라이버는 커넥션 객체를 생성해서 클라이언트에게 반환해준다.
*
* 미리 커넥션을 만들어두고(커넥션풀) 요청시마다 공급
*
* 커넥션은 객체를 생성 및 종료하는 작업은 시간이 많이 드는 작업이며, 커넥션 객체를 자주 생성하는
* 것을 효율적이지 못하다.
* 객체를 자주 생성하는 것이 문제, 객체를 사전에 미리 생성(준비)해두고 필요 할 떄 마다 사용하는 방식
* 커넥션풀(Connection Pool)
* */
/*
* 커넥션풀-Connection Pool
* - 커넥션 객체를 미리 생성하여 보관해두는 커넥션 저장소.
* - 커넥션객체를 생성하는 작업은 비용이 많이 드는 작업이므로, 사용자의 요청이 있을 때마다 생성하는것이
* 아니라 미리 커넥션풀에 생성해 두고, 사용자의 요청시마다 생성해둔 커넥션을 전달해주는 방식이다.
* - 대표 커넥션풀 구현해둔 대표 라이브러리 : DBCP, HikariCP, Tomcat DataSource
* - DBCP 라이브러리 다운로드 방법
* 1) 메이븐 레파지토리 이용
* 2) Apache 공식 사이트 다운로드
* */
public static Connection getConnection() throws SQLException, FileNotFoundException, IOException {
Connection conn = null;
/*
* BasicDataSource
* - DBMS 와의 연결 커넥션풀 관리, 트랙잭션 관리 등의 기능을 구현한 클래스
* - 데이터베이스에 연결 및 커넥션 풀 생성. 쿼넥션 생성 및 소멸 관리와 예외처리등을
* 효율적으로 수행하는 메서드를 제공한다
* */
BasicDataSource dataSource = new BasicDataSource();
Properties prop = new Properties();
prop.load(new FileInputStream("resource/driver.properties"));
//생성하고자 하는 커넥션에 대한 정보를 기술.
dataSource.setDriverClassName(prop.getProperty("driver"));
dataSource.setUrl(prop.getProperty("url"));
dataSource.setUsername(prop.getProperty("username"));
dataSource.setPassword(prop.getProperty("password"));
//커넥션풀 설정
dataSource.setInitialSize(10); //초기 커네션풀의 사이즈.
dataSource.setMaxTotal(50); // 커넥션풀의 최대 사이즈.
dataSource.setDefaultAutoCommit(false); // 커넥션들의 자동커밋 설정
dataSource.setMaxWaitMillis(10000); // 커넥션풀에서 커넥션이 없는경우 대기 최대시간 설정
dataSource.setRemoveAbandonedTimeout(300); //사용하고 있지 않은 커넥션
conn = dataSource.getConnection();
return conn;
/* --적절한 풀을 생성하는게 중요하다.
* 커넥션풀의 장접
* - 성능향상 : 커넥션풀은 데이터베이스와 미리 연결을 완료한 Connection객체를 생성해두고,
* 풀에 유지 시킴으로써 어플리케이션의 향상시킬 수 있다.
* - 메모리 누수 방지 : 사용하고 닫아두지 않은 커넥션들을 일정시간이 지났을때
* 자동으로 삭제시키는 기능이 추가되어 있다.
* - 확장성 : 커넥션풀 클래스에서 만들어 높은 다양한 기능들을 추가하여
* 원하는 형태의 커넥션들을 생성할수 있다
*
* 커넥션풀의 단점
* - 설정이 번거롭다.
* - 초기 로딩이 오래 걸린다
* - 메모리를 항상 점유하고 있으므로 낭비가 심하다.
* */
}
}