티스토리 뷰

DAO Pattern Refactoring with Generic

  • Generic을 사용하여 DAO 인터페이스와 DaoImpl 클래스를 구현 클래스와 분리한다.
  • DAO 인터페이스와 DaoImpl 클래스를 일반화하여 사용할 수 있다. 따라서 다른 클래스에서도 상속하여 원하는 대로 함수를 오버라이드하여 사용할 수 있다.
  • DAO 인터페이스 <- DaoImpl 클래스 <- CustomClassUseDAO 클래스로 상속관계가 이뤄진다.
  • DAO에 사용될 Data와 Key의 타입을 CustomClassUseDao에서 정의한다.
    • 즉 자신에게 맞게 저장한 객체의 클래스와 Key 자료형을 선언해주면 된다.
    • 여기서는 CustomClassUseDao가 IdPwInfoDaoImpl 클래스이다.

다이어그램

WebSitedIdManage 클래스 코드

class WebSitedIdManage {
    public static void main(String[] args) {
        // 클래스에 맞게 구현한 구체 클래스를 구현체로 사용
        DAO<SiteIdPwInfo, String> userSiteIdPwInfoDAO = new IdPwInfoDaoImpl("idPasswordTable");

        // insert
        userSiteIdPwInfoDAO.insert(
                new SiteIdPwInfo("https://www.smu.ac.kr", "smu", "abcde")
        );
        
        userSiteIdPwInfoDAO.insert(
                new SiteIdPwInfo("https://www.smu2.ac.kr", "smu2", "abcdefg")
        );

        // findByKey
        System.out.println(userSiteIdPwInfoDAO.findByKey("https://www.smu2.ac.kr"));
        
		// findAll()
        for (SiteIdPwInfo siteIdPwInfo : userSiteIdPwInfoDAO.findAll()) {
            System.out.println("reading > " + siteIdPwInfo);
        }
        
        // 그외 생략..
    }
}

DAO Interface 코드

import java.util.List;

public interface DAO<D, K> {
    // crud
    void insert(D data);

    D findByKey(K key);

    List<D> findAll();

    void update(D data);

    void delete(D data);

    void deleteByKey(K key);
}

DaoImpl _ 코드

  • 일반화가 가능하도록 구현 코드를 작성한다.
  • 일반화가 불가능한 부분은 하위 클래스에게 구현을 맡긴다. -> abstract 메소드를 사용한다.
  • 하위 클래스와 철저하게 분리하여 작성해야한다.
public abstract class DaoImpl<D extends DbData<K>, K> implements DAO<D, K> {

    String dbTableName;
    
    // 추상 메소드
    public abstract Statement getStatement();
    public abstract String getInstanceValueQuery(D data);
    public abstract String getUpdateInstanceValueQuery(D data);
    public abstract D getInstance(ResultSet resultSet);
    public abstract String getKeyColumnName();

    public DaoImpl(String dbTableName) {
        this.dbTableName = dbTableName;
    }

    @Override
    public void insert(D data) {
        try {
            String format = "INSERT INTO %s VALUES(%s)";
            String query = String.format(
                    format, dbTableName, getInstanceValueQuery(data)
            );
            getStatement().executeUpdate(query);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public D findByKey(K key) {
        try {
            ResultSet rs;
            String format = "SELECT * from %s where %s = '%s'";
            String query = String.format(format, dbTableName, getKeyColumnName() , key.toString());
            rs = getStatement().executeQuery(query);
            if (rs.next()) {
                return getInstance(rs);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<D> findAll() {
        try {
            List<D> dataList = new ArrayList<>();
            ResultSet rs;

            String format = "SELECT * from %s";
            String query = String.format(format, dbTableName);
            rs = getStatement().executeQuery(query);

            while (rs.next()) {
                dataList.add(getInstance(rs));
            }
            return dataList;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void update(D data) {
        if (findByKey(data.getKey()) != null) {
            try {
                String format = "update %s SET %s where '%s' = '%s'";
                String query = String.format(
                        format, dbTableName, getUpdateInstanceValueQuery(data),
                        getKeyColumnName(), data.getKey().toString()
                );
                getStatement().executeUpdate(query);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void delete(D data) {
        deleteByKey(data.getKey());
    }

    @Override
    public void deleteByKey(K key) {
        try {
            String format = "delete from %s where %s = '%s'";
            String query = String.format(
                    format, dbTableName, getKeyColumnName(), key
            );
            getStatement().executeUpdate(query);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

IdPwInfoDaoImpl 코드

  • DAO를 사용할 Object 클래스에 맞게 abstract class를 오버라이딩 하는 클래스이다.
  • 클래스 명은 사용자가 원하는대로 수정하게 된다.
  • DaoImpl에서 구체화하지 못한 메소드를 implement 한다.
public class IdPwInfoDaoImpl extends DaoImpl<SiteIdPwInfo, String> {
    final static String DB_FILE_NAME = "IdPassword.db";

    Connection connection = null;
    Statement statement = null;

    public IdPwInfoDaoImpl(String dbTableName) {
        super(dbTableName);

        try {
            connection = DriverManager.getConnection("jdbc:sqlite:" + DB_FILE_NAME);
            
            statement = connection.createStatement();
            statement.setQueryTimeout(30);
            
            final String table = "(url text PRIMARY KEY, id text, password text)";
            statement.executeUpdate("DROP TABLE IF EXISTS " + dbTableName);
            statement.executeUpdate("CREATE TABLE " + dbTableName + table);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Statement getStatement() {
        return statement;
    }

    @Override
    public String getInstanceValueQuery(SiteIdPwInfo data) {
        String format = "'%s', '%s', '%s'";

        return String.format(
                format,
                data.getKey(),
                data.getUserId(),
                data.getUserPw()
        );
    }

    @Override
    public String getUpdateInstanceValueQuery(SiteIdPwInfo data) {
        String format = "url='%s', id='%s', password='%s'";
        return String.format(
                format,
                data.getKey(),
                data.getUserId(),
                data.getUserPw()
        );
    }

    @Override
    public SiteIdPwInfo getInstance(ResultSet resultSet) {
        try {
            String url = resultSet.getString("url");
            String uid = resultSet.getString("id");
            String upw = resultSet.getString("password");
            return new SiteIdPwInfo(url, uid, upw);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String getKeyColumnName() {
        return "URL";
    }
}

DbData 인터페이스

  • 실제 CRUD에 사용되는 Object 클래스가 implement하는 인터페이스이다.
  • Object 클래스의 key 타입을 통일하기 위해 사용한다.
  • DaoImpl의 Data가 이 인터페이스를 상속하고 있으므로 Object 클래스도 똑같이 상속하여 Data로서 사용할 수 있게 한다.
public interface DbData<K> {
    K getKey();
}

SiteIdPwInfo 클래스 코드

  • CRUD의 대상이 되는 Objcet 클래스이다.
  • DbData를 implements 하여 DAO의 Data로 사용할 수 있게한다.
  • 필요한 정보 및 Getter/Setter를 갖고 있다.
public class SiteIdPwInfo implements DbData<String>{
    private String url;
    private String userId;
    private String userPw;

    public SiteIdPwInfo(String url, String userId, String userPw) {
        this.url = url;
        this.userId = userId;
        this.userPw = userPw;
    }

    @Override
    public String getKey() {
        return this.url;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserPw() {
        return userPw;
    }

    public void setUserPw(String userPw) {
        this.userPw = userPw;
    }

    @Override
    public String toString() {
        return "UserSiteIdPwInfo{" +
                "url='" + url + '\'' +
                ", userId='" + userId + '\'' +
                ", userPw='" + userPw + '\'' +
                '}';
    }
}

설명

위와 같이 DAO <- DaoImpl <- IdPwInfoDaoImpl 관계를 둠으로서 DAO가 필요한 객체마다 동일한 인터페이스를 상속 받아서 필요한 부분만 Override하여 사용할 수 있다.

결과

  • 한 층 더 추상화가 되었다.
  • Dao, DaoImpl이 일반화 되어 더욱 효과적으로 코드의 중복을 제거해 사용할 수 있다. 또한 DAO를 필요로 하는 다른 Object에도 재사용이 용이하다.
반응형
Comments
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday