이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
기초개념 › Spring
자바 코드는 어떻게 DB에 SQL을 보내는가
DB는 자바 프로그램과 따로 일하는 프로그램입니다. (MySQL 같은 DB는 아예 다른 프로그램이고, 이 실습의 H2는 자바 프로그램 안에서 돌지만 코드가 하는 일은 같습니다.) 자바 코드가 DB에 일을 시키려면 JDBC라는 약속대로 세 가지를 합니다. 먼저 DB와 연결(Connection)을 맺습니다. 그 연결로 SQL을 글자로 보냅니다. 돌아온 결과(ResultSet)를 한 행씩 읽습니다. 연결이 없으면 SQL을 보낼 수 없고, SQL 글자가 틀리면 DB가 오류로 답해서 자바에서는 SQLException(오류가 났다는 신호인 예외)이 됩니다. 이 실습은 member 테이블에서 id가 1인 회원의 name을 읽는 코드에 conn.close() 줄을 넣거나 SQL 글자를 바꿔 가며, DB가 받은 SQL과 자바가 출력한 값과 예외를 봅니다. (화면의 코드는 main 메서드 안에 있다고 보고 import 줄과 main 선언은 줄였습니다. 이 반복되는 코드를 대신 써 주는 JdbcTemplate과 JPA는 세부 동작 실습에서 다룹니다.)
실행으로 대조 · JDK 21과 H2 2.4.240에서 여섯 가지 조합을 컴파일해 새 자바 프로세스로 실행해 대조, 규칙은 Java SE 21 API 문서와 대조
// member 테이블에는 (id 1, name kim) 한 행이 들어 있다
// 연결: 주소가 가리키는 DB와 연결을 맺는다
Connection conn = DriverManager.getConnection("jdbc:h2:mem:shop");
// 도구: 이 연결로 SQL을 보내는 객체
Statement st = conn.createStatement();
// 보내기: SQL 글자를 DB에 보내고, 결과를 받는다
ResultSet rs = st.executeQuery("SELECT name FROM member WHERE id = 1");
// 읽기: 다음 행으로 옮긴다. 행이 있으면 true
boolean found = rs.next();
System.out.println(found);
System.out.println(rs.getString("name"));
JDK 21.0.12.1과 H2 2.4.240(메모리 DB)에서 실행해 대조했고, MySQL에서의 실행 대조는 하지 않았습니다. 여섯 가지 조합을 모두, 화면의 코드를 main 메서드가 든 Main.java에 넣어(import java.sql.*와 main 선언만 더했습니다) javax.tools로 컴파일하고, 조합마다 새 자바 프로세스에서 실행했습니다. 실행 전에 같은 프로세스에서 member 테이블(id INT 기본 키, name VARCHAR(20))과 (1, kim) 한 행을 만들어 두었는데, 화면에서는 이 준비 코드를 줄였습니다. DB는 자바 프로세스 안에서 도는 H2라서 실제로는 같은 프로세스에 있고, MySQL처럼 따로 떨어진 프로그램이 아닙니다. 연결과 SQL 전송과 결과 읽기의 차례는 같지만, 그 사이를 오가는 것이 네트워크 통신이라는 점은 이 실습에서 확인하지 않았습니다. 대조한 것은 결과 표의 세 항목, 곧 DB가 받은 SQL, 출력된 값, 예외입니다. DB가 받은 SQL은 H2의 INFORMATION_SCHEMA.QUERY_STATISTICS(실행 기록)에 그 SELECT가 남았거나, 예외가 DB 엔진의 파서(org.h2.command.Parser)에서 났고 오류 메시지가 그 SQL을 인용한 경우로 판정했습니다. 닫힌 연결에서는 두 가지 모두 없었고, 예외가 JdbcConnection.checkClosed에서 났습니다. 예외의 종류는 H2의 SQLState와 오류 코드(42102 테이블을 찾을 수 없음, 2000 읽을 데이터가 없음, 90007 이미 닫힌 객체)를 화면의 한국어로 옮긴 것이고, 이 코드는 H2의 것이어서 MySQL에서는 다를 수 있습니다. 세 예외는 모두 SQLException의 하위 클래스입니다. 단계 문장이 기대는 사실로 연결을 맺으면 DB 쪽 세션이 하나 늘고 닫으면 줄어드는 것, createStatement만으로는 DB의 실행 기록에 SELECT가 남지 않고 executeQuery 뒤에는 남는 것, next() 메서드를 부르기 전에 getString을 하면 SQLException이 나는 것, next() 메서드를 한 번 더 부르면 false인 것도 실행으로 확인했습니다. 세션 수에는 실행 전에 준비 코드가 연 연결 하나가 들어 있습니다. 실행 기록(QUERY_STATISTICS)은 한 번 읽은 뒤에는 새 기록이 보이지 않아서(원인은 확인하지 않았습니다) 시점마다 DB를 따로 만들어 한 번씩만 읽었습니다. 화면의 코드는 짧게 하려고 연결과 ResultSet을 닫는 줄과 예외를 처리하는 코드를 적지 않았습니다. 예외를 처리하지 않으므로 예외가 난 줄에서 프로그램이 멈추고 아래 줄이 실행되지 않는 것은 출력에 뒤 줄의 값이 없는 것으로 확인했습니다. 문서는 Java SE 21 API 문서를 직접 열어 확인했습니다. java.sql 패키지 설명에서 DriverManager로 연결을 맺고 Statement로 SQL을 보내고 ResultSet으로 결과를 받는다는 것, Connection의 설명에서 SQL이 연결 안에서 실행되고 결과가 돌아온다는 것과 createStatement가 닫힌 연결에서 SQLException을 던진다는 것, Statement.executeQuery가 ResultSet을 돌려주고 닫힌 Statement에서 SQLException을 던진다는 것, ResultSet의 설명에서 커서가 처음에 첫 행의 앞에 있고 next가 한 칸 옮기며 행이 없으면 false 값을 돌려준다는 것과 현재 행이 필요한 메서드가 그때 SQLException을 던진다는 것, SQLException의 설명에서 SQLState와 오류 코드를 담는 예외라는 것, DriverManager.getConnection의 URL이 jdbc:부프로토콜:이름 꼴이라는 것을 따랐습니다. H2 문서(features 페이지)에서는 jdbc:h2:mem:이름이 같은 이름으로 같은 JVM 안에서 하나의 메모리 DB를 가리킨다는 것과 마지막 연결이 닫히면 메모리 DB가 사라진다는 것을 확인했습니다. 닫은 연결로 만든 Statement를 쓰면 H2가 오류를 내는 것은 문서가 정한 것이 아니라 이 H2에서 관찰한 동작입니다(Statement 문서는 닫힌 Statement에서 예외를 던진다고만 적고 있습니다). 이 밖에 PreparedStatement와 SQL에 값을 끼워 넣는 방법, 데이터를 바꾸는 SQL(executeUpdate), 트랜잭션, 연결을 닫는 올바른 방법(try-with-resources)은 다루지 않았고, 반복되는 코드를 줄여 주는 JdbcTemplate은 "세부 동작 실습: JdbcTemplate과 예외 변환", 연결을 매번 새로 맺지 않고 빌려 쓰는 방식은 "커넥션 풀과 OSIV", 객체와 테이블을 이어 주는 JPA는 "JPA가 푸는 문제"에서 다룹니다.
앞 실습: 설정 파일 · 다음 실습: @Transactional
줄을 그은 곳은 처음 코드에서 지운 것이고, 색을 칠한 곳은 새로 쓴 것입니다.