독서/📚

[Next Step] 7장 DB를 활용해 데이터를 영구적으로 저장하기

leejinwoo1126 2023. 11. 17. 11:24
반응형

 


 

실습 프로젝트 저장소

실습의 경우 처음에 fork 받았는데, 깃 허브 잔디가 심어지지 않아 기술 블로그 참고(링크)하여 저장소 설정을 변경하도록 함

 

jwp-basic 

https://github.com/slipp/jwp-basic/tree/step2-user-with-mvc-framework

 

GitHub - slipp/jwp-basic: 자바 웹 프로그래밍 기본 실습

자바 웹 프로그래밍 기본 실습. Contribute to slipp/jwp-basic development by creating an account on GitHub.

github.com

 


 

자바 진영은 데이터베이스에 대한 접근 로직 처리를 담당하는 객체를 별도로 분리해 구현하는 것을 추천한다. 이 객체를 DAO(Data Access Object)라고 부른다. (p228)

UserDao 클래스 생성

public class UserDao {
    public void insert(User user) throws SQLException {
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = ConnectionManager.getConnection();
            String sql = "INSERT INTO USERS VALUES(?, ?, ?, ?)";
            pstmt = con.prepareStatement(sql);
            pstmt.setString(1, user.getUserId());
            pstmt.setString(2, user.getPassword());
            pstmt.setString(3, user.getName());
            pstmt.setString(4, user.getEmail());

            pstmt.executeUpdate();

        } finally {
            if(pstmt != null) pstmt.close();
            if(con != null) con.close();
        }
    }

    public void update(User user) throws SQLException {
        Connection con = null;
        PreparedStatement pstmt = null;

        try {
            con = ConnectionManager.getConnection();
            pstmt = con.prepareStatement("UPDATE USERS SET password = ?, name = ?, email = ? WHERE userId = ?");
            pstmt.setString(1, user.getPassword());
            pstmt.setString(2, user.getName());
            pstmt.setString(3, user.getEmail());
            pstmt.setString(4, user.getUserId());

            pstmt.executeUpdate();
        } finally {
            if(pstmt != null) pstmt.close();
            if(con != null) con.close();
        }
    }

    public List<User> findAll() throws SQLException {
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            con = ConnectionManager.getConnection();
            pstmt = con.prepareStatement("SELECT * FROM USERS");
            rs = pstmt.executeQuery();

            List<User> users = new ArrayList<>();
            while(rs.next()) {
                User user = new User(rs.getString("userId"),
                        rs.getString("password"),
                        rs.getString("name"),
                        rs.getString("email"));
                users.add(user);
            }

            return users;
        } finally {
            if(rs != null) rs.close();
            if(pstmt != null) pstmt.close();
            if(con != null) con.close();
        }
    }

    public User findByUserId(String userId) throws SQLException {
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            con = ConnectionManager.getConnection();
            pstmt = con.prepareStatement("SELECT * FROM USERS WHERE userId = ?");
            pstmt.setString(1, userId);

            rs = pstmt.executeQuery();
            User user = null;
            while(rs.next()) {
                user = new User(rs.getString("userId"), rs.getString("password"), 
                rs.getString("name"), rs.getString("email"));
            }

            return user;
        } finally {
            if(rs != null) rs.close();
            if(pstmt != null) pstmt.close();
            if(con != null) con.close();
        }
    }
}

 

 

p234

중복 코드를 리팩토링하려면 먼저 변화가 발생하는 부분(개발자가 구현할 수 밖에 없는 부분)과 변화가 없는 부분(공통 라이브러리로 분리할 부분)을 분리해야 한다. UserDao를 통해 분리해보면 다음과 같다

 

개발자가 구현할 부분 3가지를 제외한 나머지는 공통 라이브러리에 책임을 위임할 수 있다.

 

 

p235

UserDao를 리팩토링할 때 해결해야 할 또 다른 한가지는 SQLException에 대한 처리이다. (..)

“expert one-on-one J2EE 설계와 개발” 책을 보면 컴파일 타입 Exception과 런타임 Exception을 사용해야 되는 가이드 라인을 다음과 같이 제시하고 있다.  


① API를 사용하는 모든 곳에서 이 예외를 처리해야 하는가? 예외가 반드시 메소드에 대한 반환 값이 되어야 하는가?
 이 질문에 대한 답이 “예”일 경우 컴파일 타임 Exception을 사용해 컴파일러의 도움을 받는다

② API를 사용하는 소수 중 이 예외를 처리해야 하는가?
 이 질문에 대한 답이 “예”일 경우 런타임 Exception으로 구현한다. API를 사용하는 모든 코드가 Exception을 catch하도록 강제하지 않는 것이 좋다.

③ 무엇인가 큰 문제가 발생했는가? 이 문제를 복구할 방법이 없는가?
 이 질문에 대한 답이 "예"라면 런타임 Exception으로 구현한다. API를 사용하는 코드에서 Exception을 catch하더라도 에러에 대한 정보를 통보 받는 것 외에 아무것도 할 수 있는 것이 없다.

④ 아직도 불명확한가? 그렇다면 런타임 Exception으로 구현하라. Exception에 대해 문서화하고 API를 사용하는 곳에서 Exception에 대한 처리를 결정하도록 하라

 


UserDao 리팩토링

*강의 영상 링크(p244)

DAO 리팩토링 1 insert, update, delete 중복 제거
DAO 리팩토링 2 select 문 중복 제거
DAO 리팩토링 3 JdbcTemplate과 SelectJdbcTemplate 통합
DAO 리팩토링 4 라이브러리 리팩토링 및 목록 기능 추가
DAO 리팩토링 5 SQLException을 DataAccessException으로 래핑
DAO 리팩토링 6 람다 표현식을 사용하도록 리팩토링

 

 

1. 메소드 추출(Extract Method)

-메소드가 한 가지 작업만 처리하도록 작은 단위로 분리하다보면 중복 코드가 명확하게 드러나는 경우를 종종 경험한다

-이번 리팩토링의 경우 개발자가 DB 접근 로직 구현할 때 매번 구현해야 하는 부분과 그렇지 않을 부분을 기준으로 분리하면 더 명확한 기준을 가지고 분리할 수 있다

(예로 query 생성, PrepareStatement 파라미터 설정, ResultSet의 Row 데이터 추출 부분을 개발자가 직접 구현해야 한다)

 

public class UserDao {
    public void insert(User user) throws SQLException {
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = ConnectionManager.getConnection();
            pstmt = con.prepareStatement(createQueryForInsert());

            setValuesForInsert(user, pstmt);

            pstmt.executeUpdate();
        } finally {
            if(pstmt != null) pstmt.close();
            if(con != null) con.close();
        }
    }


    private void setValuesForInsert(User user, PreparedStatement pstmt) throws SQLException {
        pstmt.setString(1, user.getUserId());
        pstmt.setString(2, user.getPassword());
        pstmt.setString(3, user.getName());
        pstmt.setString(4, user.getEmail());
    }

    private String createQueryForInsert() {
        return "INSERT INTO USERS VALUES(?, ?, ?, ?)";
    }

    public void update(User user) throws SQLException {
        Connection con = null;
        PreparedStatement pstmt = null;

        try {
            con = ConnectionManager.getConnection();
            pstmt = con.prepareStatement(createQueryForUpdate());

            setValuesForUpdate(user, pstmt);

            pstmt.executeUpdate();
        } finally {
            if(pstmt != null) pstmt.close();
            if(con != null) con.close();
        }
    }

    private void setValuesForUpdate(User user, PreparedStatement pstmt) throws SQLException {
        pstmt.setString(1, user.getPassword());
        pstmt.setString(2, user.getName());
        pstmt.setString(3, user.getEmail());
        pstmt.setString(4, user.getUserId());
    }

    private String createQueryForUpdate() {
        return "UPDATE USERS SET password = ?, name = ?, email = ? WHERE userId = ?";
    }

    public List<User> findAll() throws SQLException {
       [..]
    }

    public User findByUserId(String userId) throws SQLException {
       [..]
    }
}

 

2. InsertJdbcTemplate과 UpdateJdbcTemplate 생성

InsertJdbcTemplate 추상 클래스

public abstract class InsertJdbcTemplate {
    public void insert(User user) throws SQLException {
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = ConnectionManager.getConnection();
            pstmt = con.prepareStatement(createQueryForInsert());

            setValuesForInsert(user, pstmt);

            pstmt.executeUpdate();
        } finally {
            if(pstmt != null) pstmt.close();
            if(con != null) con.close();
        }
    }

    abstract void setValuesForInsert(User user, PreparedStatement pstmt) throws SQLException;
    abstract String createQueryForInsert();
}

 

UpdateJdbcTemplate 추상 클래스

public abstract class UpdateJdbcTemplate {
    public void update(User user) throws SQLException {
        Connection con = null;
        PreparedStatement pstmt = null;

        try {
            con = ConnectionManager.getConnection();
            pstmt = con.prepareStatement(createQueryForUpdate());

            setValuesForUpdate(user, pstmt);

            pstmt.executeUpdate();
        } finally {
            if(pstmt != null) pstmt.close();
            if(con != null) con.close();
        }
    }

    abstract void setValuesForUpdate(User user, PreparedStatement pstmt) throws SQLException;
    abstract String createQueryForUpdate();
}

 

UserDao 리팩토링 

- InsertJdbcTemplate 메서드에서 UserDao 인자 제거

- 개발자가 구현해야 할 추상 메서드 선언

- 추상 클래스로 변경

- UserDao에서 익명 클래스로 추상 메서드 구현 담당

public class UserDao {
    public void insert(User user) throws SQLException {
        InsertJdbcTemplate insertJdbcTemplate = new InsertJdbcTemplate() {
            @Override
            void setValuesForInsert(User user, PreparedStatement pstmt) throws SQLException {
                pstmt.setString(1, user.getUserId());
                pstmt.setString(2, user.getPassword());
                pstmt.setString(3, user.getName());
                pstmt.setString(4, user.getEmail());
            }

            @Override
            String createQueryForInsert() {
                return "INSERT INTO USERS VALUES(?, ?, ?, ?)";
            }
        };
        insertJdbcTemplate.insert(user);
    }


    public void update(User user) throws SQLException {
        UpdateJdbcTemplate updateJdbcTemplate = new UpdateJdbcTemplate() {
            @Override
            void setValuesForUpdate(User user, PreparedStatement pstmt) throws SQLException {
                pstmt.setString(1, user.getPassword());
                pstmt.setString(2, user.getName());
                pstmt.setString(3, user.getEmail());
                pstmt.setString(4, user.getUserId());
            }

            @Override
            String createQueryForUpdate() {
                return "UPDATE USERS SET password = ?, name = ?, email = ? WHERE userId = ?";
            }
        };

        updateJdbcTemplate.update(user);
    }
    
    public List<User> findAll() throws SQLException {
    	[..]
    }
    
    public User findByUserId(String userId) throws SQLException {
    	[..]
    }   
}

 

 

3. SelectJdbcTemplate 클래스 생성

추상 메서드로 PrepareStatement 파라미터 설정, ResultSet의 Row 데이터 추출용 선언

 

MySelectJdbcTemplate<T> 추상 클래스 

public abstract class MySelectJdbcTemplate<T> {
    private static final Logger log = LoggerFactory.getLogger(MySelectJdbcTemplate.class);
    public List<T> query(String query) throws SQLException {
        List<T> result = new ArrayList<>();

        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = ConnectionManager.getConnection();
            ps = con.prepareStatement(query);

            rs = ps.executeQuery();

            while(rs.next()) {
                result.add(mapRow(rs));
            }

            return result;
        } finally {
            if(rs != null) rs.close();
            if(ps != null) ps.close();
            if(con != null) con.close();
        }
    }

    public T queryForObject(String query) throws SQLException {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = ConnectionManager.getConnection();
            ps = con.prepareStatement(query);

            setValues(ps);

            rs = ps.executeQuery();

            T result = null;
            while(rs.next()) {
                result = mapRow(rs);
            }

            return result;
        } finally {
            if(rs != null) rs.close();
            if(ps != null) ps.close();
            if(con != null) con.close();
        }
    }

    abstract protected void setValues(PreparedStatement ps) throws SQLException;

    abstract protected T mapRow(ResultSet rs) throws SQLException;
}

 

 

UserDao 클래스 리팩토링

public class UserDao {
    public void insert(User user) throws SQLException {
      [..]
    }


    public void update(User user) throws SQLException {
      [..]
    }


    public List<User> findAll() throws SQLException {
        MySelectJdbcTemplate<User> jdbcTemplate = new MySelectJdbcTemplate() {
            @Override
            protected void setValues(PreparedStatement ps) throws SQLException {
            }

            @Override
            protected User mapRow(ResultSet rs) throws SQLException {
                return new User(rs.getString("userId"),
                        rs.getString("password"),
                        rs.getString("name"),
                        rs.getString("email"));
            }
        };
        return jdbcTemplate.query("SELECT * FROM USERS");
    }

    public User findByUserId(String userId) throws SQLException {
        MySelectJdbcTemplate<User> jdbcTemplate = new MySelectJdbcTemplate() {
            @Override
            protected void setValues(PreparedStatement ps) throws SQLException {
                ps.setString(1, userId);
            }

            @Override
            protected User mapRow(ResultSet rs) throws SQLException {
                return new User(rs.getString("userId"), rs.getString("password"), rs.getString("name"), rs.getString("email"));
            }
        };

        return jdbcTemplate.queryForObject("SELECT * FROM USERS WHERE userId = ?");
    }
}

 

 

4. JdbcTemplate 통합

InsertJdbcTemplate과 UpdateJdbcTemplate의 구현 부분이 동일하다. 둘 중 하나를 사용하도록 리팩토링한다.

- *JdbcTemplate 함수 호출시 인자로 String query 전달

- SQL 쿼리와 같이 변경되는 부분을 추상 메소드가 아닌 메소드의 인자로 전달한다

이때 UserDao 에서 추상메서드 구현시 콜백 방식으로 직접 User, String query 전달하여 쿼리 생성 메소드 삭제 가능
→ 코딩량도 줄고 가독성도 향상됨
→ 이를 template method 디자인 패턴이라 한다 (values, rowMapper 추상 메서드)

 

추가로 MySelectJdbcTemplate도 추가하도록 한다

 

MyJdbcTemplate<T> 추상 클래스 생성 

public abstract class MyJdbcTemplate<T> {
    private static final Logger log = LoggerFactory.getLogger(MyJdbcTemplate.class);
    
    public void update(String query){
        try (
            Connection con = ConnectionManager.getConnection();
            PreparedStatement ps = con.prepareStatement(query);
        ){
            setValues(ps);
            ps.executeUpdate();
        } catch (SQLException e) {
            log.error(e.getMessage());
        }
    }

    public List<T> query(String query) throws SQLException {
        List<T> result = new ArrayList<>();

        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = ConnectionManager.getConnection();
            ps = con.prepareStatement(query);

            rs = ps.executeQuery();

            while(rs.next()) {
                result.add(mapRow(rs));
            }

            return result;
        } finally {
            if(rs != null) rs.close();
            if(ps != null) ps.close();
            if(con != null) con.close();
        }
    }

    public T queryForObject(String query) throws SQLException {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = ConnectionManager.getConnection();
            ps = con.prepareStatement(query);

            setValues(ps);

            rs = ps.executeQuery();

            T result = null;
            while(rs.next()) {
                result = mapRow(rs);
            }

            return result;
        } finally {
            if(rs != null) rs.close();
            if(ps != null) ps.close();
            if(con != null) con.close();
        }
    }

    abstract protected void setValues(PreparedStatement ps) throws SQLException;

    abstract protected T mapRow(ResultSet rs) throws SQLException;
}

 

UserDao 리팩토링

public class UserDao {
    public void insert(User user) throws SQLException {
        MyJdbcTemplate jdbcTemplate = new MyJdbcTemplate() {
            @Override
            protected void setValues(PreparedStatement ps) throws SQLException {
                ps.setString(1, user.getUserId());
                ps.setString(2, user.getPassword());
                ps.setString(3, user.getName());
                ps.setString(4, user.getEmail());
            }

            @Override
            protected Object mapRow(ResultSet rs) throws SQLException {
                return null;
            }
        };
        jdbcTemplate.update("INSERT INTO USERS VALUES(?, ?, ?, ?)");
    }


    public void update(User user) throws SQLException {
        MyJdbcTemplate jdbcTemplate = new MyJdbcTemplate() {
            @Override
            protected void setValues(PreparedStatement pstmt) throws SQLException {
                pstmt.setString(1, user.getPassword());
                pstmt.setString(2, user.getName());
                pstmt.setString(3, user.getEmail());
                pstmt.setString(4, user.getUserId());
            }

            @Override
            protected Object mapRow(ResultSet rs) throws SQLException {
                return null;
            }
        };

        jdbcTemplate.update("UPDATE USERS SET password = ?, name = ?, email = ? WHERE userId = ?");
    }


    public List<User> findAll() throws SQLException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate() {
            @Override
            protected void setValues(PreparedStatement ps) throws SQLException {
            }

            @Override
            protected User mapRow(ResultSet rs) throws SQLException {
                return new User(rs.getString("userId"),
                        rs.getString("password"),
                        rs.getString("name"),
                        rs.getString("email"));
            }
        };
        return jdbcTemplate.query("SELECT * FROM USERS");
    }

    public User findByUserId(String userId) throws SQLException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate() {
            @Override
            protected void setValues(PreparedStatement ps) throws SQLException {
                ps.setString(1, userId);
            }

            @Override
            protected User mapRow(ResultSet rs) throws SQLException {
                return new User(rs.getString("userId"), rs.getString("password"), rs.getString("name"), rs.getString("email"));
            }
        };

        return jdbcTemplate.queryForObject("SELECT * FROM USERS WHERE userId = ?");
    }
}

 

이때 추상클래스 MyJdbcTemplate에 추상 메서드 RowMapper가 추가됨으로써 Insert/Update 할 때 사용하지 않는 데도 구현을 해줘야 한다(Templte Method 디자인 패턴의 단점)

추상 메서드를 인터페이스로 분리하도록 한다

 

5. PrepareStatementSetter, RowMapper 인터페이스 선언 및 사용

PreparedStatementSetter 인터페이스

@FunctionalInterface
public interface PreparedStatementSetter {
    void values(PreparedStatement ps) throws SQLException;
}

 

RowMapper 인터페이스

@FunctionalInterface
public interface RowMapper<T> {
    T mapRow(ResultSet rs) throws SQLException;
}

 

MyJdbcTemplte<T> 클래스 변경

-try-with-resources 구문을 적용

-자바 7버전부터 해당 문법을 적용해 자원을 반납하는 것이 가능하다. 해당 문법을 통해 finally 절의 복잡도를 낮추도록 리팩토링하였다

-추가로 UserDao의 문제점이었던 SQLException(컴파일 예외) 처리하는 부분을 DataAccessException(런타임 예외)로 변경하여 문제를 해결한다

public class MyJdbcTemplate<T> {
    private static final Logger log = LoggerFactory.getLogger(MyJdbcTemplate.class);
   
   public void update(String query, PreparedStatementSetter preparedStatementSetter) throws DataAccessException {
        try (
            Connection con = ConnectionManager.getConnection();
            PreparedStatement ps = con.prepareStatement(query);
        ){
            preparedStatementSetter.values(ps);
            ps.executeUpdate();
        } catch (SQLException e) {
            log.error(e.getMessage());
        }
    }

    public List<T> query(String query, PreparedStatementSetter preparedStatementSetter, RowMapper<T> rowMapper) throws DataAccessException {
        List<T> result = new ArrayList<>();
        ResultSet rs = null;

        try (
             Connection con = ConnectionManager.getConnection();
             PreparedStatement ps = con.prepareStatement(query);
        ) {
            preparedStatementSetter.values(ps);
            rs = ps.executeQuery();

            while(rs.next()) {
                result.add(rowMapper.mapRow(rs));
            }

            return result;
        } catch (SQLException e) {
            throw new DataAccessException(e.getMessage());
        } finally {
            if(rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }

        }
    }

    public T queryForObject(String query, PreparedStatementSetter preparedStatementSetter, RowMapper<T> rowMapper) throws DataAccessException {
        ResultSet rs = null;

        try (
             Connection con = ConnectionManager.getConnection();
             PreparedStatement ps = con.prepareStatement(query);
        ){
           preparedStatementSetter.values(ps);
           rs = ps.executeQuery();

            T result = null;
            while(rs.next()) {
                result = rowMapper.mapRow(rs);
            }

            return result;
        } catch (SQLException e) {
            throw new DataAccessException(e.getMessage());
        } finally {
            if(rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }

        }
    }
}

 

 

DataAccessException

public class DataAccessException extends RuntimeException {
    public DataAccessException() {
        super();
    }

    public DataAccessException(String message) {
        super(message);
    }
	
    [..]
}

 

 

 

UserDao 클래스 리팩토링

-인터페이스의 메서드가 하나이기 때문에 람다식으로 표현가능하다

-앞서 불필요하게 구현해야 했던 부분이 제거되었다

public class UserDao {
    public void insert(User user) throws DataAccessException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate();
        jdbcTemplate.update("INSERT INTO USERS VALUES(?, ?, ?, ?)", (ps) -> {
            ps.setString(1, user.getUserId());
            ps.setString(2, user.getPassword());
            ps.setString(3, user.getName());
            ps.setString(4, user.getEmail());
        });
    }


    public void update(User user) throws DataAccessException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate();
        jdbcTemplate.update("UPDATE USERS SET password = ?, name = ?, email = ? WHERE userId = ?", ps -> {
            ps.setString(1, user.getPassword());
            ps.setString(2, user.getName());
            ps.setString(3, user.getEmail());
            ps.setString(4, user.getUserId());
        });
    }


    public List<User> findAll() throws DataAccessException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate();
        return jdbcTemplate.query("SELECT * FROM USERS",
                (ps) -> {},
                (rs) -> new User(rs.getString("userId"),
                        rs.getString("password"),
                        rs.getString("name"),
                        rs.getString("email")));
    }

    public User findByUserId(String userId) throws DataAccessException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate();
        return jdbcTemplate.queryForObject("SELECT * FROM USERS WHERE userId = ?",
                (ps) -> {ps.setString(1, userId);},
                (rs) -> new User(rs.getString("userId"), rs.getString("password"), rs.getString("name"), rs.getString("email"))
        );

    }
}

 

 

6. 가변 인자 메서드 추가 및 사용 

- 가변인자는 인자로 전달할 값의 갯수가 고정되지 않고 동적으로 변경되는 경우 유용하게 사용할 수 있다

- SQL문에 따라 전달할 값의 갯수가 달라지기 때문에 가변인자를 활용할 수 있는 적절한 부분이다

- 인터페이스의 추상메서드가 하나 뿐이므로 람다식을 사용하여 좀 더 깔끔하게 구현할 수 있다

 

MyJdbcTemplate<T> 클래스

public class MyJdbcTemplate<T> {
    [..]

    private PreparedStatementSetter createPreparedStatementSetter(Object... parameters) {
        return (ps) -> {
            for(int i = 1; i <= parameters.length; i++) {
                ps.setString(i, String.valueOf(parameters[i - 1]));
            }
        };
    }
       
    public void update(String query, Object... parameters) {
        update(query, createPreparedStatementSetter(parameters));
    }
    
    public T queryForObject(String query, RowMapper<T> rowMapper, Object... parameters) throws DataAccessException {
        return queryForObject(query, rowMapper, createPreparedStatementSetter(parameters));
    }
    
    public List<T> query(String query, RowMapper<T> rowMapper, Object... parameters) throws DataAccessException {
        return query(query, rowMapper, createPreparedStatementSetter(parameters));
    }
}

 

UserDao 리팩토링 (최종)

public class UserDao {
    public void insert(User user) throws DataAccessException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate();
        String sql = "INSERT INTO USERS VALUES(?, ?, ?, ?)";
        jdbcTemplate.update(sql, user.getUserId(), user.getPassword(), user.getName(), user.getEmail());
    }


    public void update(User user) throws DataAccessException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate();
        String sql = "UPDATE USERS SET password = ?, name = ?, email = ? WHERE userId = ?";
        jdbcTemplate.update(sql, user.getPassword(), user.getName(), user.getEmail(), user.getUserId());
    }


    public List<User> findAll() throws DataAccessException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate();
        return jdbcTemplate.query("SELECT * FROM USERS",
                (rs) -> new User(rs.getString("userId"), rs.getString("password"), rs.getString("name"), rs.getString("email")));
    };

    public User findByUserId(String userId) throws DataAccessException {
        MyJdbcTemplate<User> jdbcTemplate = new MyJdbcTemplate();
        return jdbcTemplate.queryForObject("SELECT * FROM USERS WHERE userId = ?",
                (rs) -> new User(rs.getString("userId"), rs.getString("password"), rs.getString("name"), rs.getString("email")),
                userId
        );
    }
}
반응형