콘텐츠로 이동

2021 09 25

2021-09-25

밸덩 GC

  • Reference Counting - 스위프트가 이 방식 씀 - JVM에서는 이런 GC 알고리즘 안써 안써! - Also, there is no GC algorithm based on reference counting in the JVM. - JavaScript에서도 Mark-And-Sweep 방식 쓴다하네 - 이건 너무 나태한 구현이야 - 순환참조도 해결하지 못하는걸? - 더블링크드리스트도 가비지로 처리하지 못하는걸?
  • Trading GC - GC root로 부터 reachability 판단 - 직/간접적으로 루트와 연결되있다면 살아있는 것으로 판단

JDBC API

  • Java DataBase Connectivity - 자바 프로그램 내에서 DB 종류에 상관없이 DB 관련작업 처리할 수 있도록 도와주는 API
  • JDBC로 SQL 처리 - 참고: https://docs.oracle.com/javase/tutorial/jdbc/basics/processingsqlstatements.html 1. 연결 설정 - Datasource와의 연결 설정 - Connection 객체로 표시 2. Statement 만들기 - Statement == SQL을 나타내는 인터페이스 - ResultSet을 통해 DB 결과 집합을 나타내는 객체 생성 - stmt = con.createStatement(); - Statement: 매개 변수 없는 단순 SQL문 - PreparedStatement: 입력 매개변수 포함할 수 있는 SQL문 - CallableStatement: 입력 및 출력 매개변수 모두를 저장하는 SQL 문 3. 쿼리문 실행하기 - execute: returns true if the first object that the query return is a ResultSet object - executeQuery: 하나의 ResultSet 객체 반환 - executeUpdate: returns Integer when INSERT, UPDATE, DELETE 4. ResultSet 처리하기
    ResultSet rs = stmt.executeQuery(query);
    while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        int supplierID = rs.getInt("SUP_ID");
        float price = rs.getFloat("PRICE");
        int sales = rs.getInt("SALES");
        int total = rs.getInt("TOTAL");
        System.out.println(coffeeName + ", " + supplierID + ", " + price +
                           ", " + sales + ", " + total);
    }
    

    5. 연결 종료 - Connection, Statement, ResultSet 다 쓰고나면 close 메서드 호출해주자 - try-with-resources를 사용하면 자동으로 close 가능

  • JDBC 코드 보기 - 일반적인 코드 구성
    Connection conn = null;
    try {
        String jdbcDriver = "jdbc:mysql://localhost:3306/test";
        String dbUser = "joel";
        String dbPass = "joel";
        conn = DriverManager.getConnection(jdbcDriver, dbUser, dbPass);
        ...
    } catch (SQLException ex) {
        // 에러 발생의 경우
    } finally {
        if (conn != null) { try { conn.close(); } catch (SQLException ex) {} }
    }
    

    - Statement를 이용한 쿼리 실행

    Statement stmt = null;
    ResultSet rs = null;
    try {
        stmt = conn.createStatement();
        int insertedCount = stmt.executeUpdate("insert ...");
        rs = stmt.executeQuery("select * from ...");
        ...
    } catch (SQLException ex) {
        ...
    } finally {
        if (rs != null) try { rs.close(); } catch (SQLException ex) {}
        if (stmt != null) try { stmt.close(); } catch (SQLException ex) {}
    }
    

      - Connection.createStatement()를 통해 Statement 생성!
      - Statement가 제공하는 메서드로 쿼리 실행!
          - SELECT: ResultSEt executeQuery(String query)
          - INSERT, UPDATE, DELETE: int executeUpdate(String query)
    

JdbcTemplate 기억나니?

@Repository
public class StationDao {

    private final JdbcTemplate jdbcTemplate;

    private final RowMapper<Station> stationRowMapper = (resultSet, rowNum) -> new Station(
            resultSet.getLong("id"),
            resultSet.getString("name")
    );

    public StationDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public Optional<Station> findByName(String stationName) {
        String sql = "SELECT * FROM STATION WHERE name = ?";
        final List<Station> result = jdbcTemplate.query(sql, stationRowMapper, stationName);
        return result.stream().findAny();
    }

    public Station save(String stationName) {
        String sql = "INSERT INTO STATION (name) values (?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();

        jdbcTemplate.update(con -> {
            PreparedStatement ps = con.prepareStatement(sql, new String[]{"id"});
            ps.setString(1, stationName);
            return ps;
        }, keyHolder);

        final long stationId = Objects.requireNonNull(keyHolder.getKey()).longValue();
        return new Station(stationId, stationName);
    }

    public List<Station> findAll() {
        String sql = "SELECT * FROM STATION";
        return jdbcTemplate.query(sql, stationRowMapper);
    }

    public Optional<Station> findById(long stationId) {
        String sql = "SELECT * FROM STATION WHERE id = ?";
        final List<Station> result = jdbcTemplate.query(sql, stationRowMapper, stationId);
        return result.stream().findAny();
    }

    public void delete(long stationId) {
        String sql = "DELETE FROM STATION WHERE id=?";
        jdbcTemplate.update(sql, stationId);
    }
}

토비의 스프링 3장. 템플릿

  • JDBC try/catch/finally의 문제점 - 복잡한 try/catch/finally가 중첩으로 매 메서드 마다 반복됨 - 제대로 자원 해제 못할 수도 있어
  • 전략 패턴으로의 분리 - DAO 메서드 마다 새로운 StatementStrategy 구현 클래스 만들기 - DAO 메서드에서 StatementStrategy에 전달할 User와 같은 부가정보 있는 경우, 생성자 주입 필요
  • JDBC 작업 흐름으로의 분리
    public class JdbcContext {
        private DataSource dataSource;
    
        public void setDataSource(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        public void workWithStatementStrategy(StatementStrategy stmt) throws SQLException {
            Connection c = null;
            PreparedStatement ps = null;
    
            try {
                c = this.dataSource.getConnection();
                ps = stmt.makePreparedStatement(c);
                ps.executeUpdate();
            } catch (SQLException e) {
                throw e;
            } finally {
                if (ps != null) try { ps.close(); } catch (SQLException ex) {}
                if (c != null) try { c.close(); } catch (SQLException ex) {}
            }
        }
    }
    
    public class UserDao {
    
        private JdbcContext jdbcContext;
    
        public void setJdbcContext(JdbcContext jdbcContext) {
            this.jdbcContext = jdbcContext;
        }
    
        public void add(final User user) throws SQLException {
            this.jdbcContext.workWithStatementStrategy(
                new StatementStrategy() {...}
            );
        }
    
        public void deleteAll() throws SQLException {
            this.jdbcContext.workWithStatementStrategy(
                new StatementStrategy() {...}
            );  
        }
    }
    
  • 템플릿과 콜백 - 템플릿 - 전략 패턴의 컨텍스트 - 콜백 - 익명 내부 클래스 - 하나의 메서드를 가진 인터페이스를 구현 - 작업 흐름 - 클라이언트가 실행 로직을 담은 콜백 오브젝트 만듦 - 콜백이 참조할 정보를 제공 - 템플릿 정해진 흐름에 따라 작업 진행 - 내부 생성한 참조 정보 가지고 콜백 메서드 호출 - 템플릿은 콜백이 돌려준 정보 활용해 작업 마주 수행 - 아래와 같이 쿼리문을 호출하는 로직을 분리할 수 있다!
    public void deleteAll() throws SQLException {
        executeSql("delete from users");
    }
    
    private void executeSql(final String query) throws SQLException {
        this.jdbcContext.workWithStrategy(
            new StatementStrategy() {
                public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
                    return c.prepareStatement(query);
                }   
            }   
        );
    }
    
      - 해당 코드는 이제 JdbcContext 안으로 옮길 수 있음
      ``` java
      public classs JdbcContext {
          ...
          public void executeSql(final String query) throws SQLException {
              workWithStatementStrategy (
                  new StatementStrategy() {
                      public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
                          return c.prepareStatement(query);
                      }   
                  }   
              );
          }
      }
      ```
    

    - 성격은 다르지만, 하나의 목적으로 위해서로 긴밀하게 연관되어 동작하는 "응집력"이 강한 코드 -> 한 곳에 있는게 유리!

템플릿 콜백 패턴

  • 참고 1: https://limkydev.tistory.com/85
  • 참고 2: https://western-sky.tistory.com/41
  • 전략 패턴의 변형으로, 전략 패턴에 익명 내부 클래스를 가미해서 사용하는 방법
    public interface Strategy {
        void runStrategy();
    }
    
    public class Soldier {
        // 전략을 사용할 컨텍스트
        public void runContext(String sound) {
            System.out.println("시작!");
            execute(sound).runStrategy();
            System.out.println("끝!");
        }
    
        // 이 안으로 이전된 전략
        private Strategy execute(final String sound) {
            return new Strategy() {
                @Override 
                public void runStrategy() {
                    System.out.println(sound);    
                }
            }
        }
    }
    
    public class Client {
        // 클라이언트는 넘겨줄 "sound"에만 집중!
        public static void main(String[] args){
            Soldier rambo = new Soldier();
            rambo.runContext("탕탕탕!");
            System.out.println();
            rambo.runContext("통통통!");
        }
    }
    
    결과값
    ---
    시작
    탕탕탕!
    
    시작
    통통통!
    
    
  • 템플릿 콜백 패턴 - 전략 패턴의 기본 구조에 인터페이스를 상속하는 클래스를 만들지 않고, 익명 내부 클래스를 활용하는 방식 - 템플릿 - 정해져 있는 "틀", 전략 사용자 (Context) - 콜백 - 실행되는 특정 로직. 콜백을 매개변수로 주고받기 위해 "오브젝트"에 담아 전달 - 단일 메서드 인터페이스 활용