DAO
- Data Access Object는 DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트
Incorrect UserDao
public class UserDao {
public void add(user user) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection c = DriverManager.getConnection("jdbc:mysql://localhost/usersDB","ID","PASSWORD");
PreparedStatement ps = c.prepareStatement("insert into users(id,name,password)values(?,?,?)");
ps.setString(1,user.getId());
ps.setString(2,user.getName());
ps.setString(3,user.getPassword());
ps.executeUpdate();
ps.close();
c.close();
}
public User get(String id) throws ClassNotFoundException,SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection c = DriverManager.getConnection("jdbc:mysql://localhost/usersDB","ID","PASSWORD");
PreparedStatement ps = c.prepareStatment("select * from users where id = ?");
ps.setString(1,id);
ResultSet rs = ps.executeQuery();
rs.next();
User user = new User();
user.setId(rs.getString("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
rs.close();
ps.close();
c.close();
return user;
}
}
CODE 분석
- DriverManager
- JDBC Driver 세트를 관리하기 위한 기본 서비스
- getConnection(String url) Method --> 주어진 데이터베이스 URL에 대한 연결 설정을 시도
- 공식 문서 DOC
- PreparedStatement interface
- Statement class 확장
- 사전 컴파일된 SQL문을 나타내는 객체, 해당 객체는 여러번 반복하여 사용이 가능함
- setString(int parameterIndex, String x) Method --> 지정된 매개 변수를 주어진 string 값으로 설정
- 공식 문서 DOC
- Connection interface
- 특정 데이터베이스와의 연결, SQL문이 실행되고 연결 내용 내에서 결과 반환
- Connection.prepareStatement(SQL String)
- 매개변수가 있는 SQL문을 데이터베이스로 보내기 위한 개체를 만든다.
- 매개변수 --> "?"이 포함 할 수 있는 SQL String
- Return --> PreparedStatement 객체로 반환
- 공식 문서 DOC
- ResultSet class
- 일반적으로 데이터베이스 쿼리문 실행하여 생성되는 결과 집합을 나타내는 데이터 테이블
- 공식 문서 DOC
- Class.forName(String className)
- Class로더를 사용하여 주어진 문자열 이름을 가진 클래스 또는 인터페이스와 관련된 객체를 반환한다.
문제점
커넥션 만들기의 추출
- 중복 코드 메소드 추출
public class UserDao {
public void add(user user) throws ClassNotFoundException, SQLException {
Connection c = getConnection();
PreparedStatement ps = c.prepareStatement("insert into users(id,name,password)values(?,?,?)");
ps.setString(1,user.getId());
ps.setString(2,user.getName());
ps.setString(3,user.getPassword());
ps.executeUpdate();
ps.close();
c.close();
}
public User get(String id) throws ClassNotFoundException,SQLException {
Connection c = getConnection();
PreparedStatement ps = c.prepareStatment("select * from users where id = ?");
ps.setString(1,id);
ResultSet rs = ps.executeQuery();
rs.next();
User user = new User();
user.setId(rs.getString("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
rs.close();
ps.close();
c.close();
return user;
}
private Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection c = DriverManager.getConnection("jdbc:mysql://localhost/usersDB","ID","PASSWORD");
return c;
}
}
- 중복 코드 분리, 해당 내용이 독립적이므로 수정도 간단해졌다.
- 하지만 해당 DAO가 각기 다른 드라이버를 사용해야하는 경우 소스코드 내부를 수정해야하는 문제점 발생
상속을 통한 확장
- DB 커넥션은 완성했지만, 여러 종류의 DB 커넥션을 연결하기에는 일일이 수정해줘야 한다.
- 코드를 한 단계 더 분리하여 완성한다.
- 추상 메소드로 만들어서 사용
public abstract class UserDao {
public void add(user user) throws ClassNotFoundException, SQLException {
Connection c = getConnection();
PreparedStatement ps = c.prepareStatement("insert into users(id,name,password)values(?,?,?)");
ps.setString(1,user.getId());
ps.setString(2,user.getName());
ps.setString(3,user.getPassword());
ps.executeUpdate();
ps.close();
c.close();
}
public User get(String id) throws ClassNotFoundException,SQLException {
Connection c = getConnection();
PreparedStatement ps = c.prepareStatment("select * from users where id = ?");
ps.setString(1,id);
ResultSet rs = ps.executeQuery();
rs.next();
User user = new User();
user.setId(rs.getString("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
rs.close();
ps.close();
c.close();
return user;
}
public abstract Connection getConnection() throws ClassNotFoundException, SQLException;
}
public class MysqlDao extends UserDao {
public Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection c = DriverManager.getConnection("jdbc:mysql://localhost/usersDB","ID","PASSWORD");
return c;
}
}
public class MariadbDao extends UserDao {
public Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("org.mariadb.jdbc.Driver");
Connection c = DriverManager.getConnection("jdbc:mariadb://localhost/userDB","ID","PASSWORD");
return c;
}
}
- 이렇게 수정함으로써 UserDao의 클래스는 수정할 필요 없이 다른 데이터베이스 드라이버를 추가할 수 있게 되었다.
- 이렇게 슈퍼클래스에 기본적인 로직의 흐름을 만들고 그 기능의 일부를 추상 메소드나 오버라이딩이 가능하게 한 후 서브클래스에서 구현하는 방법을 템플릿 메소드 패턴이라고 한다.
- 상속을 많이 사용한다는 단점이 있다.
- 만약 UserDao가 다른 목적을 위해 상속을 사용하고 있다면? --> Java는 다중 상속을 허용하지 않는다.
- 상속의 긴밀한 결합을 허용하기 때문에 슈퍼클래스의 메소드를 사용할 수 있다.
- 다른 DAO 적용 불가