이전 글까지 배운 내용이 이거였다면, 이번에는
이렇게 코드를 짜보려고 한다.
맨처음처럼 짤 경우에는 dao에 너무 많은 기능들이 들어가 코드가 길어지고 기능 분할이 되지 않아 가독성도 떨어진다.
그래서 service를 추가해서 코드를 분할해보려고 한다.
Service
업무로직 business logic 수행.
트랜잭션처리나 업무수행은 Service 에서 하고 Dao는 진짜 DB로 내용 전달만 한다.
|
Dao 만 존재할 때
|
Dao 와 Service 분리
|
Service
|
X
|
1. jdbc driver class 등록
2. Connection 객체 생성 & set AutoCommit (false) |
Dao
(DQL, DML, 공통) |
1. jdbc driver class 등록
2. Connection 객체 생성 & set AutoCommit (false) 3. PreparedStatement 객체 생성 & 미완성 쿼리 값대입 4. (executeQuery or executeUpdate) 실행 & (Result or int) 결과값 리턴받기 5. ResultSet 처리 or 트랜잭션 하기 6. 자원반납 (pstmt, rset, conn) |
3. PreparedStatement 객체 생성
& 미완성 쿼리 값대입 4. (executeQuery or executeUpdate) 실행 & (Result or int) 결과값 리턴받기 5. 자원반납 (pstmt, rset) |
Service
|
X
|
6. ResultSet 처리 or 트랜잭션 하기
7. 자원반납 (conn) |
다음과 같이 분리한다.
Dao 에서는 실제로 객체를 생성하고, 값대입 , 쿼리 업데이트 등만 진행하고
나머지 기능은 Service가 실행하는 것이다.
이에 따라 코드들을 변환하면,
view -> Controller -> Dao 였던 코드들이,
view -> Controller -> Service -> Dao 로 한단계를 더 거치게 된다.
회원 조회를 Service와 Dao가 같이 있는 코드로 바꾸어보자.
View : MemberMenu 는 그대로 둔다.
Controller : MemberController
package member.controller;
import java.util.List;
import member.model.service.MemberService;
import member.model.vo.Member;
public class MemberController {
private MemberService memberService = new MemberService();
public int insertMember(Member member) {
int result = memberService.insertMember(member);
return result;
}
public int updateMember(Member member) {
return memberService.updateMember(member);
}
public int deleteMember(String id) {
return memberService.deleteMember(id);
}
public List<Member> findAll() {
List<Member> members = memberService.findAll();
return members;
}
public Member findById(String id) {
Member member = memberService.findById(id);
return member;
}
public List<Member> findByName(String name) {
List<Member> members = memberService.findByName(name);
return members;
}
}
Service : MemberService
package member.model.service;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import member.model.dao.MemberDao;
import member.model.vo.Member;
public class MemberService {
final String driverClass = "oracle.jdbc.OracleDriver";
final String url = "jdbc:oracle:thin:@localhost:1521:xe"; // db접속프로토콜@ip:포트:db명(sid)
final String user = "student";
final String password = "student";
private MemberDao memberDao = new MemberDao();
public int insertMember(Member member) {
Connection conn=null;
int result=0;
try {
//1. driver class 등록
Class.forName(driverClass);
//2. connection 객체 생성
conn=DriverManager.getConnection(url,user,password);
conn.setAutoCommit(false);
//3. dao 요청
result = memberDao.insertMember(conn,member);
//4. 트랜잭션 처리 (DML만 처리)
conn.commit();
} catch (ClassNotFoundException | SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return result;
}
public int updateMember(Member member) {
// TODO Auto-generated method stub
return 0;
}
public int deleteMember(String id) {
// TODO Auto-generated method stub
return 0;
}
public List<Member> findAll() {
// TODO Auto-generated method stub
return null;
}
public Member findById(String id) {
// TODO Auto-generated method stub
return null;
}
public List<Member> findByName(String name) {
// TODO Auto-generated method stub
return null;
}
}
Dao : MemberDao
package member.model.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import member.model.vo.Member;
public class MemberDao {
final String driverClass = "oracle.jdbc.OracleDriver";
final String url = "jdbc:oracle:thin:@localhost:1521:xe"; // db접속프로토콜@ip:포트:db명(sid)
final String user = "student";
final String password = "student";
public int insertMember(Connection conn, Member member) {
PreparedStatement pstmt=null;
int result=0;
String sql="insert into member values(?,?,?,?,?,default,default)";
try {
//1. PreparedStatement 객체 생성 & 미완성 쿼리 값대입
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, member.getId());
pstmt.setString(2, member.getName());
pstmt.setString(3, member.getGender());
pstmt.setDate(4, member.getBirthday());
pstmt.setString(5, member.getEmail());
//2. 실행 & 결과값처리
result=pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//3. 자원반납 pstmt -- 여기서 conn 닫으면안됨!!
try {
pstmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return result;
}
}
그리고 실행하면, 다음과 같이 잘 나오게 된다.
JDBC Template
보다시피, MemberService나 MemberDao나, DML / DQL 레시피가 비슷하고 반복되는 코드들이 많다.
그래서 코드가 너무 길어지게 되는데 이를 줄이기 위해 JDBCTemplate를 이용한다.
JDBC Template 이란?
jdbc api를 사용하면서 중복된 코드를 static메소드로 작성한 코드를 의미.
package member.common;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* jdbc api를 사용하면서 중복된 코드를 static메소드로 작성
*
*/
public class JdbcTemplate {
static final String driverClass = "oracle.jdbc.OracleDriver";
static final String url = "jdbc:oracle:thin:@localhost:1521:xe"; // db접속프로토콜@ip:포트:db명(sid)
static final String user = "student";
static final String password = "student";
static {
try {
//1. driver class 등록 - application실행시 최초 1회만!
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* Connection 객체 생성
* setAutoCommit(false) - 트랜잭션을 직접 관리
* @return
*/
public static Connection getConnection() {
Connection conn=null;
try {
conn=DriverManager.getConnection(url,user,password);
conn.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public static void close(Connection conn) {
try {
if(conn!=null && conn.isClosed())
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(PreparedStatement pstmt) {
try {
if(pstmt!=null && pstmt.isClosed())
pstmt.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();
}
}
public static void commit(Connection conn) {
try {
if(conn!=null && !conn.isClosed())
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void rollback(Connection conn) {
try {
if(conn!=null && !conn.isClosed())
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
그리고 이 코드들을 이용하기 위해 static method의 특성을 활용한다.
MemberService와 MemberDao에 package 선언 아래에 import static member.common.JdbcTemplate.*; 를 적어준다.
Service : MemberService
package member.model.service;
import static member.common.JdbcTemplate.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import member.common.JdbcTemplate;
import member.model.dao.MemberDao;
import member.model.vo.Member;
public class MemberService {
final String driverClass = "oracle.jdbc.OracleDriver";
final String url = "jdbc:oracle:thin:@localhost:1521:xe"; // db접속프로토콜@ip:포트:db명(sid)
final String user = "student";
final String password = "student";
private MemberDao memberDao = new MemberDao();
public int inSertMember(Member member) {
Connection conn=null;
int result=0;
try {//commit, rollback을 위한 예외처리
conn=getConnection();
result=memberDao.insertMember(conn, member);
commit(conn);
}catch(Exception e) {
rollback(conn);
}finally {
JdbcTemplate.close(conn); //static으로 선언했으므로 JdbcTemplate빼고 쓸 수 있음.
}
return result;
}
public List<Member> findAll() {
Connection conn = getConnection();
List<Member> members=memberDao.findAll(conn);
return members;
}
Dao : MemberDao
package member.model.dao;
import static member.common.JdbcTemplate.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import member.model.vo.Member;
public class MemberDao {
public int insertMember(Connection conn, Member member) {
PreparedStatement pstmt=null;
int result=0;
String sql="insert into member values(?,?,?,?,?,default,default)";
try {
//1. PreparedStatement 객체 생성 & 미완성 쿼리 값대입
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, member.getId());
pstmt.setString(2, member.getName());
pstmt.setString(3, member.getGender());
pstmt.setDate(4, member.getBirthday());
pstmt.setString(5, member.getEmail());
//2. 실행 & 결과값처리
result=pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//3. 자원반납 pstmt -- 여기서 conn 닫으면안됨!!
close(pstmt);
}
return result;
}
public List<Member> findAll(Connection conn) {
PreparedStatement pstmt = null;
ResultSet rset=null;
List<Member>members = new ArrayList<>();
String sql="select * from member order by reg_date desc";
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();
}finally {
//3. 자원반납 rset, pstmt
close(rset);
close(pstmt);
}
return members;
}
코드가 훨씬 짧고 간결해졌다.