DBC 코드를 간단하게 까지 만들었다.
오늘은 Properties 의 특징을 이용하여 정보를 보호하고,
Exception처리를 하는 법을 배웠다.
Properties란
HashTable의 자식클래스
K,V가 String, String 형으로 고정되어 설정 정보를 관리하는데에 최적화 되어있다.
먼저 프로젝트 안에 resources 폴더를 만들고, 다음과 같은 파일 두개를 생성한다.
datasource.properties에서는 드라이버 클래스와 유저이름, 비밀번호, url등을 관리하고
member-sql.properties에서는 sql문에 대해 관리할 예정이다.
- 드라이버 클래스, 유저이름, 비밀번호 , url등 정보 관리
datasource.properties
#############################
# datasource.properties
#############################
driverClass = oracle.jdbc.OracleDriver
url = jdbc:oracle:thin:@localhost:1521:xe
user = student
password = student
다음과 같이 작성한다.
JDBCTemplate 윗부분을 다음과 같이 수정한다.
public class JdbcTemplate {
//final은 편집이 불가능하므로 사용 불가
static String driverClass;
static String url; // db접속프로토콜@ip:포트:db명(sid)
static String user;
static String password;
static {
//datasource.properties의 내용을 Properties객체로 불러오기
Properties prop=new Properties();
try {
prop.load(new FileReader("resources/datasource.properties"));
driverClass=prop.getProperty("driverClass");
url=prop.getProperty("url");
user=prop.getProperty("user");
password=prop.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
}
try {
//1. driver class 등록 - application실행시 최초 1회만!
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
이제 코드에서 url, user, password 등을 볼 수 없게 되었다.
(정보 보호의 목적)
2. sql문 관리
- MemberDao 객체 생성시 member-sql.properties의 내용을 불러와 Properties객체에 저장한다.
- dao의 각 메소드 호출시 Properties 객체에서 sql문을 가져와서 사용한다.
* properties 내부 sql문은 무조건 한줄로 작성한다.
MemberDao.java
public class MemberDao {
private Properties prop;
/**
* 1.MemberDao객체 생성시 member-sql.properties의 내용을 불러와 Properties객체에 저장
* 2.dao의 각메소드 호출시 Properties객체에서 sql문을 가져와서 사용
*
* properties에서는 반드시 한줄로 작성
*/
public MemberDao() {
prop=new Properties();
try {
prop.load(new FileReader("resources/member-sql.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}
MemberDao 윗부분에 다음과 같은 생성자와 내용을 추가한다.
MemberDao의 findAll 메소드에 대해 다음과 같은 코드로 수정한다.
public List<Member> findAll(Connection conn) {
PreparedStatement pstmt = null;
ResultSet rset = null;
List<Member> members = new ArrayList<>();
String sql = prop.getProperty("findAll");
System.out.println("sql@findAll = "+sql);
다음과 같이 수정하게되면, member-sql.properties에서 findAll 에 대한 sql문을 가져오게 된다.
마지막에 sql@findAll 을 통해 sql문이 제대로 들어갔는지 확인해본다.
member-sql.properties
#############################
# member-sql.properties
#############################
findAll= select * from member order by reg_date desc
updateMember = update member set # = ? where id = ?
실행해보면 제대로 나오는 것을 확인할 수 있다. (sql문도 올바르게 들어감)
예외처리
Exception중 런타임익셉션을 제외하고 나머지를 checked exception이라고 한다.
언체크드 익셉션은 예외처리 강제화가 없고, 확인이 불가능하지만
체크드 익셉션은 예외처리가 강제화 되어있어, 예외처리를 안하면 빨간줄이 생기며 컴파일 오류가 발생한다.
위에서 properties 로 중요한 정보와 sql문을 받아왔다.
sql문이 올바르지 않거나 주소나 값이 잘못 입력되어 오류가 발생한다면 이를 어떻게 해결해야할까?
예외발생시 후처리
1. 로깅(개발자용) -> 나중에는 예외 발생시 개발자 이메일 전송 등의 코드도 들어감
e.printStackTrace(); 사용
2. 사용자 알림 처리 -> 사용자 알려주는건 view에서 하기로 했음! (MVC패턴) / static 사용
view에 작성한 코드 불러오기!
Checked Exception 처리
MemberDao.java
findAll 의 메소드를 수정한다.
public List<Member> findAll(Connection conn) throw SQLException{//언체크드는 throwable 안써도됨
PreparedStatement pstmt = null;
ResultSet rset = null;
List<Member> members = new ArrayList<>();
String sql = prop.getProperty("findAlls");
System.out.println("sql@findAll = "+sql);
try {
// 1. pstmt객체 생성 & 미완성쿼리 값대입
pstmt = conn.prepareStatement(sql);
// 2. 실행 & rset처리
rset = pstmt.executeQuery();
while (rset.next()) {
Member member = handleMemberResultSet(rset);
members.add(member);
}
} catch (SQLException e) {
// e.printStackTrace();
throw e;
throw new MemberException("회원 전체조회 오류!",e); //message:String, err:Throwable
} finally {
// 3. 자원반납 rset, pstmt
close(rset);
close(pstmt);
}
return members;
}
throw e 를 적고 throw Exception을 메소드를 선언. service 와 controller에서도 코드를 수정한다.
MemberService.java
public List<Member> findAll() throws Exception{
Connection conn = getConnection();
List<Member> members = memberDao.findAll(conn);
close(conn);
return members;
}
MemberController.java
import static member.view.MemberMenu.*;
public List<Member> findAll() throws Exception{
List<Member> members=null;
try {
members = memberService.findAll();
}catch(Exception e) {
//예외발생시 후처리
//1. 로깅(개발자용) -> 나중에는 예외 발생시 개발자 이메일 전송 등의 코드도 들어감
// e.printStackTrace();
//2. 사용자 알림 처리 -> 사용자 알려주는건 view에서 하기로 했음! (MVC패턴) / static 사용
displayError(e.getMessage());
}
return members;
}
사용자 안내 오류(사용자용)가 제대로 나오는 것을 확인할 수 있다.
throw e 는 Checked Exception오류이므로 Unchecked Exception 오류로 만들어서 사용하는것이 더 좋다.
Unchecked Exception 처리
MemberDao.java에서 findAll 코드를 수정한다.
public List<Member> findAll(Connection conn) throws SQLException {//언체크드는 throwable 안써도됨
PreparedStatement pstmt = null;
ResultSet rset = null;
List<Member> members = new ArrayList<>();
String sql = prop.getProperty("findAlls");
System.out.println("sql@findAll = "+sql);
try {
// 1. pstmt객체 생성 & 미완성쿼리 값대입
pstmt = conn.prepareStatement(sql);
// 2. 실행 & rset처리
rset = pstmt.executeQuery();
while (rset.next()) {
Member member = handleMemberResultSet(rset);
members.add(member);
}
} catch (SQLException e) {
// e.printStackTrace();
throw new MemberException("회원 전체조회 오류!",e); //message:String, err:Throwable
} finally {
// 3. 자원반납 rset, pstmt
close(rset);
close(pstmt);
}
return members;
}
service와 controller에서 throws Exception선언한 것을 다 지워준다.
MemberMenu.java
public static void displayError(String msg) {
System.err.println("> 오류가 발생했습니다. 관리자에게 연락주세요 : "+msg);
}
view 에서 사용자에게 보이는 코드가 진행되어야 하므로 위 코드를 추가한다.
다음과 같이 MemberException을 만든다.
MemberException.java
package member.model.exception;
//런타임 예외 -> 언체크드익셉션
public class MemberException extends RuntimeException {
//alt+shift+s from superclass 클릭
public MemberException() {
super();
// TODO Auto-generated constructor stub
}
public MemberException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public MemberException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public MemberException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public MemberException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
Alt + shift + s 누르고 from superclass ... 를 클릭해서 다음과 같은 코드를 만든다.
로깅 오류(개발자용) 과 사용자 안내 오류(사용자용)가 제대로 나오는 것을 확인할 수 있다.
Exception 분기하기(나누기)
회원정보수정에서, 이미 존재하는 email으로 수정을 시도할 경우 오류가 발생한다.
이때는 오류 발생 원인이 정보 수정 자체의 오류라 관리자에게 연락이 필요하기 보다는,
Unique 제약조건을 위배한 것이므로 오류를 따로 작성해주는 것이 더 좋다.
MemberDao.java
public int updateMember(Connection conn, String id, String colName, Object newValue) {
PreparedStatement pstmt = null;
String sql = prop.getProperty("updateMember");
int result = 0;
try {
sql = sql.replace("#", colName);
pstmt = conn.prepareStatement(sql);
pstmt.setObject(1, newValue);
pstmt.setString(2, id);
result = pstmt.executeUpdate();
} catch (Exception e) {
// e.printStackTrace();
if(e.getMessage().contains("UQ_MEMBER_EMAIL"))
throw new MemberEmailNotUniqueException("회원 이메일 중복 오류!",e);
else
throw new MemberException("회원정보수정 오류!",e);
} finally {
close(pstmt);
}
return result;
}
if문으로 유니크 제약조건을 위배한다는 말이 포함되면, 회원 이메일 중복 오류를 프린트하도록 코드를 수정했다.
MemberEmailNotUniqueException도 MemberException처럼 작성해준다.
package member.model.exception;
public class MemberEmailNotUniqueException extends MemberException {
public MemberEmailNotUniqueException() {
super();
// TODO Auto-generated constructor stub
}
public MemberEmailNotUniqueException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public MemberEmailNotUniqueException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public MemberEmailNotUniqueException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public MemberEmailNotUniqueException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
MemberMenu.java
public static void displayClientError(String msg) {
System.err.println("> "+msg);
}
이메일 중복 오류는 관리자에게 연락 할 사항이 아니므로, 그냥 msg만 전달한다.
이메일 주소가 중복일 때 )
기타 오류일 때)