2021 09 25
2021-09-25¶
밸덩 GC¶
- 참고: https://www.baeldung.com/java-gc-cyclic-references
- 개요 - JVM 구현마다 GC 전략 다 달라. 만드는 놈 마음이야 - HotSpot JVM을 기준으로 살펴볼게요
- 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
trueif the first object that the query return is a ResultSet object - executeQuery: 하나의 ResultSet 객체 반환 - executeUpdate: returnsIntegerwhen 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) - 콜백 - 실행되는 특정 로직. 콜백을 매개변수로 주고받기 위해 "오브젝트"에 담아 전달 - 단일 메서드 인터페이스 활용