"자바 어노테이션으로 데이터 액세스 객체 만들기 1~5"를 통해 DAO(Data Access Object) 인터페이스(interface)와 구현체(implements)를 만들어 보았고 "자바 데이터베이스 매니저 시큐어 코딩"를 통해 "Statement"에서 "PreparedStatement"로 수정해보았습니다.
이번에는 "PreparedStatement"를 이용하여 CRUD(Create, Read, Update, Delete)에 대한 인터페이스(interface)와 구현체(implements)를 개발하겠습니다.
이후에는 쿼리문 파라미터 치환과 Resultset를 객체로 변환에 대해 알아보겠습니다.
"CRUD"는 데이터처리 방식인 Create(생성/등록/추가), Read(읽기/조회), Update(변경/수정), Delete(삭제/제거)의 첫 문자를 따서 만든 용어입니다. DAO에서는 데이터처리 방식으로 데이터베이스와 연동하여 쿼리문을 처리합니다.
데이터베이스 SQL문인 INSERT, SELECT, UPDATE, DELETE와 연관하여 생각하시면 됩니다.
Create(생성/등록/추가) -> INSERT
Read(읽기/조회) -> SELECT
Update(변경/수정) -> UPDATE
Delete(삭제/제거) -> DELETE
"CRUD"와 유사하게 "ABCD"라고도 합니다. "ABCD"는 Add(추가), Browse(보기), Change(변경), Delete(삭제)입니다.
DAO의 인터페이스에서 메소드 명을 만들때에 "CRUD"에 맞게 구분해주는 것이 좋습니다.
개인적으로 메소드 명에 접두사(Prefix)와 접미사(suffix)를 이용하여 다음과 같은 명명 규칙(NamingRule)으로 사용하고 메소드 명을 읽을 수 있게 길게 쓰는 것을 좋아합니다.
Create(생성/등록/추가) -> INSERT
-> add{메소드}Object (하나의 ROW를 등록함)
Read(읽기/조회) -> SELECT
-> get{메소드}Object (조건에 맞는 하나의 ROW를 가져옴)
-> get{메소드}Value (조건에 맞는 하나의 값을 가져옴)
-> get{메소드}ListCount (조건에 맞는 여러개의 ROW의 수를 가져옴)
-> get{메소드}List (조건에 맞는 여러개의 ROW를 가져옴)
Update(변경/수정, 제거/미사용) -> UPDATE
-> edit{메소드}Object (조건에 맞는 하나의 ROW를 변경함)
-> edit{메소드}List (조건에 맞는 여러개의 ROW를 변경함)
-> remove{메소드}Object (조건에 맞는 하나의 ROW에 필드를 변경함 - 검색 조건에서는 검색되지 않게하여 삭제된 것처럼 처리함 - 개념적인 삭제임)
-> remove{메소드}List (조건에 맞는 여러개의 ROW에 필드를 변경함 - 검색 조건에서는 검색되지 않게하여 삭제된 것처럼 처리함 - 개념적인 삭제임)
Delete(삭제) -> DELETE
-> delete{메소드}Object (조건에 맞는 하나의 ROW를 삭제함)
-> delete{메소드}List (조건에 맞는 여러개의 ROW를 삭제함)
이처럼 메소드 명에 접두사(Prefix)와 접미사(suffix)로 규칙있게 적용하면 어떤 쿼리인지 쉽게 알 수 있습니다. 여러명의 개발자들과 같이 대규모의 프로젝트를 진행한다면 이런 명명 규칙(NamingRule)이 있는 것이 좋습니다.
또는 SQL문인 insert, select, update, delete를 접두사(Prefix)로 사용하셔도 됩니다.
스프링 프레임워크를 사용하고 계신다면 트랜잭션(Transaction)를 설정할 때 위와 같은 명명 규칙(NamingRule)이 있다면 쉽게 적용할 수 있습니다.
그럼 이전에 생성한 DAO의 인터페이스(interface)와 구현체(implements)에 명명 규칙(NamingRule)을 적용하여 개발하겠습니다.
1. "test2" 프로젝트의 "Java Resources/src/main/java"에서 "com.home.project.test2.dao.ITestLoginDao.java"를 오픈합니다.
이전에는 이해를 돕기위해 명명 규칙을 사용하지 않고 직관적으로 알 수 있도록 SQL문을 이용하였습니다.
위의 명명 규칙을 이용하여 DAO의 인터페이스(interface)를 설정하겠습니다.
조건에 맞는 사용자를 조회하는 메소드를 추가합니다. 기존의 "selectMember()"메소드는 삭제합니다.
/**
* 사용자를 조회합니다.
* @param userVO 사용자 객체
* @return 검색된 사용자 객체
*/
public abstract UserVO getMemberObject(UserVO userVO);
조건에 맞는 사용자의 수를 가져오는 메소드를 추가합니다.
/**
* 사용자 리스트의 총 카운트를 조회한다.
* @return 검색된 사용자 객체 수
*/
public abstract int getMemberListCount();
조건에 맞는 사용자 리스트를 가져오는 메소드를 추가합니다.
/**
* 사용자 리스트를 조회합니다.
* @return 검색된 사용자 객체 리스트
*/
public abstract List<UserVO> getMemberList();
새로운 사용자를 추가합니다.
/**
* 사용자를 추가합니다.
* @param userVO 사용자 객체
* @return 사용자 추가 처리 수
*/
public abstract int addMemberObject(UserVO userVO);
조건에 맞는 사용자를 수정하는 메소드를 추가합니다.
/**
* 사용자를 수정합니다.
* @param userVO 사용자 객체
* @return 사용자 수정 처리 수
*/
public abstract int editMemberObject(UserVO userVO);
조건에 맞는 사용자를 삭제하는 메소드를 추가합니다.
/**
* 사용자를 삭제합니다.
* @param userVO 사용자 객체
* @return 사용자 삭제 처리 수
*/
public abstract int deleteMemberObject(UserVO userVO);
2. "test2" 프로젝트의 "Java Resources/src/main/java"에서 "com.home.project.test2.dao.TestLoginDaoImpl.java"를 오픈합니다.
DAO의 인터페이스(interface)가 변경되어 오류가 발생합니다.
클래스 명(TestLoginDaoImpl) 위로 마우스를 이동하면 컨텍스트가 나타납니다.
컨텍스트에서 "Add unimplemented methods"(구현되지 않은 메소드 추가)를 클릭하여 오버라이드(Override)합니다.
기존의 "selectMember()"메소드 명을 "getMemberObject()"메소드로 수정합니다.
아래에 추가된 "getMemberObject()"메소드는 삭제합니다.
로그인할 사용자의 아이디로 조회하는 쿼리문입니다. 이전에 설명한 부분을 참조하세요.
SELECT MBR_ID
, MBR_PWD
, MBR_PWD_SALT
, MBR_NM
FROM MBR_ACCOUNT_TB
WHERE MBR_ID='testid';
조건에 맞는 사용자의 수를 가져오는 쿼리문입니다. 여기서는 조건 없이 전체 사용자 수를 가져왔습니다.
SELECT COUNT(MBR_ID) AS CNT
FROM MBR_ACCOUNT_TB;
위의 쿼리문을 이용하여 "getMemberListCount()"메소드에 코딩을 추가합니다.
@Override
public int getMemberListCount() {
int result = 0;
String query = "SELECT COUNT(MBR_ID) AS CNT FROM MBR_ACCOUNT_TB";
Connection connection = testDatabaseManager.getConnection();
if (connection != null) {
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(query);
System.out.println("PreparedStatement를 생성함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 생성하지 못함");
e.printStackTrace();
}
if (preparedStatement != null) {
ResultSet resultSet = null;
try {
resultSet = preparedStatement.executeQuery();
System.out.println("Query[" + preparedStatement.toString() + "]를 실행함");
} catch (SQLException e) {
System.out.println("Query[" + preparedStatement.toString() + "]를 실행하지 못함");
e.printStackTrace();
}
try {
if (resultSet.next()) {
String count = resultSet.getString("CNT");
result = Integer.parseInt(count);
} else {
System.out.println("resultSet이 없음");
}
resultSet.close();
} catch (SQLException e) {
System.out.println("resultSet를 가져오지 못함");
e.printStackTrace();
}
try {
preparedStatement.close();
System.out.println("PreparedStatement를 종료함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 종료하지 못함");
e.printStackTrace();
}
}
testDatabaseManager.closeConnection(connection);
}
return result;
}
조건에 맞는 사용자 리스트를 가져오는 쿼리문입니다. 여기서는 조건 없이 상위 10개만 가져오게 하겠습니다. 향후에 검색 조건 객체와 페이징이 되게 추가하겠습니다.
SELECT MBR_ID
, MBR_NM
FROM MBR_ACCOUNT_TB
LIMIT 0, 10;
위의 쿼리문을 이용하여 "getMemberList()"메소드에 코딩을 추가합니다.
@Override
public List<UserVO> getMemberList() {
List<UserVO> list = new ArrayList<UserVO>();
String query = "SELECT MBR_ID, MBR_NM FROM MBR_ACCOUNT_TB LIMIT 0, 10";
Connection connection = testDatabaseManager.getConnection();
if (connection != null) {
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(query);
System.out.println("PreparedStatement를 생성함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 생성하지 못함");
e.printStackTrace();
}
if (preparedStatement != null) {
ResultSet resultSet = null;
try {
resultSet = preparedStatement.executeQuery();
System.out.println("Query[" + preparedStatement.toString() + "]를 실행함");
} catch (SQLException e) {
System.out.println("Query[" + preparedStatement.toString() + "]를 실행하지 못함");
e.printStackTrace();
}
try {
while (resultSet.next()) {
UserVO selectUserVO = new UserVO();
String memberID = resultSet.getString("MBR_ID");
String memberName = resultSet.getString("MBR_NM");
selectUserVO.setId(memberID);
selectUserVO.setName(memberName);
list.add(selectUserVO);
}
resultSet.close();
if (list.size() <= 0) {
System.out.println("resultSet이 없음");
}
} catch (SQLException e) {
System.out.println("resultSet를 가져오지 못함");
e.printStackTrace();
}
try {
preparedStatement.close();
System.out.println("PreparedStatement를 종료함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 종료하지 못함");
e.printStackTrace();
}
}
testDatabaseManager.closeConnection(connection);
}
return list;
}
새로운 사용자를 추가하는 쿼리문입니다. 입력된 데이터는 "{password}"와 "{password salt}"는 서비스에서 사용자 입력한 패스워드를 SHA-256과 SALT를 이용하여 생성되는 데이터입니다.
INSERT INTO MBR_ACCOUNT_TB
(
MBR_ID
, MBR_PWD
, MBR_PWD_SALT
, MBR_NM
, REG_ID
)
VALUES
(
'testid1'
, '{password}'
, '{password salt}'
, '홍길동'
, 'SYSTEM'
);
위의 쿼리문을 이용하여 "addMemberObject()"메소드에 코딩을 추가합니다.
@Override
public int addMemberObject(UserVO userVO) {
int result = 0;
if (userVO != null) {
String query = "INSERT INTO MBR_ACCOUNT_TB (MBR_ID, MBR_PWD, MBR_PWD_SALT, MBR_NM, REG_ID) VALUES (?, ?, ?, ?, 'SYSTEM')";
Connection connection = testDatabaseManager.getConnection();
if (connection != null) {
PreparedStatement preparedStatement = null;
try {
int index = 1;
preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(index++, userVO.getId());
preparedStatement.setString(index++, userVO.getPassword());
preparedStatement.setString(index++, userVO.getPasswordSalt());
preparedStatement.setString(index++, userVO.getName());
System.out.println("PreparedStatement를 생성함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 생성하지 못함");
e.printStackTrace();
}
if (preparedStatement != null) {
try {
result = preparedStatement.executeUpdate();
System.out.println("Query[" + preparedStatement.toString() + "]를 실행함");
} catch (SQLException e) {
System.out.println("Query[" + preparedStatement.toString() + "]를 실행하지 못함");
e.printStackTrace();
}
try {
preparedStatement.close();
System.out.println("PreparedStatement를 종료함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 종료하지 못함");
e.printStackTrace();
}
}
testDatabaseManager.closeConnection(connection);
}
}
return result;
}
여기서 쿼리문에 들어갈 파라메타를 설정하는 부분에 정수형 변수를 사용한 이유는 쿼리문에 들어갈 파라메타가 추가되거나 삭제되거나 순서가 이동하게되면 매번 파라메타 인덱스를 수정해야 하는 번거로움을 없애기 위해서 입니다. 개발하실때 파라메타 인덱스를 정수형 변수로 사용하기는 것이 좋습니다. 쿼리문에 들어갈 파라메타의 인덱스는 1부터 시작합니다.
int index = 1;
preparedStatement.setString(index++, userVO.getId());
만약, 인덱스를 0부터 하고 싶으시면 다음과 같이 수정하시면 됩니다.
int index = 0;
preparedStatement.setString(++index, userVO.getId());
조건에 맞는 사용자를 수정하는 쿼리문입니다.
UPDATE MBR_ACCOUNT_TB
SET MBR_NM='홍길동1'
WHERE MBR_ID='testid1';
위의 쿼리문을 이용하여 "editMemberObject()"메소드에 코딩을 추가합니다.
@Override
public int editMemberObject(UserVO userVO) {
int result = 0;
if (userVO != null) {
String query = "UPDATE MBR_ACCOUNT_TB SET MBR_NM=? WHERE MBR_ID=?";
Connection connection = testDatabaseManager.getConnection();
if (connection != null) {
PreparedStatement preparedStatement = null;
try {
int index = 1;
preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(index++, userVO.getName());
preparedStatement.setString(index++, userVO.getId());
System.out.println("PreparedStatement를 생성함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 생성하지 못함");
e.printStackTrace();
}
if (preparedStatement != null) {
try {
result = preparedStatement.executeUpdate();
System.out.println("Query[" + preparedStatement.toString() + "]를 실행함");
} catch (SQLException e) {
System.out.println("Query[" + preparedStatement.toString() + "]를 실행하지 못함");
e.printStackTrace();
}
try {
preparedStatement.close();
System.out.println("PreparedStatement를 종료함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 종료하지 못함");
e.printStackTrace();
}
}
testDatabaseManager.closeConnection(connection);
}
}
return result;
}
조건에 맞는 사용자를 삭제하는 쿼리문입니다.
DELETE FROM MBR_ACCOUNT_TB
WHERE MBR_ID='testid1';
위의 쿼리문을 이용하여 "deleteMemberObject()"메소드에 코딩을 추가합니다.
@Override
public int deleteMemberObject(UserVO userVO) {
int result = 0;
if (userVO != null) {
String query = "DELETE FROM MBR_ACCOUNT_TB WHERE MBR_ID=?";
Connection connection = testDatabaseManager.getConnection();
if (connection != null) {
PreparedStatement preparedStatement = null;
try {
int index = 1;
preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(index++, userVO.getId());
System.out.println("PreparedStatement를 생성함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 생성하지 못함");
e.printStackTrace();
}
if (preparedStatement != null) {
try {
result = preparedStatement.executeUpdate();
System.out.println("Query[" + preparedStatement.toString() + "]를 실행함");
} catch (SQLException e) {
System.out.println("Query[" + preparedStatement.toString() + "]를 실행하지 못함");
e.printStackTrace();
}
try {
preparedStatement.close();
System.out.println("PreparedStatement를 종료함");
} catch (SQLException e) {
System.out.println("PreparedStatement를 종료하지 못함");
e.printStackTrace();
}
}
testDatabaseManager.closeConnection(connection);
}
}
return result;
}