DB 저장 방식
Server Mode
- 애플리케이션 외부에서 디비 엔진이 실행되기 때문에 애플리케이션을 종료해도 데이터가 사라지지 않음
In-memory Mode
- 애플리케이션 내부에서 디비 엔진이 실행(실행 주체가 스프링)되기 때문에 애플리케이션을 종료하면 디비 엔진도 함께 종료
- 애플리케이션의 메모리에 데이터가 저장됨
- mem을 기재하여 애플리케이션실행 메모리(스프링 실행 메모리) 자체에서 디비를 사용하겠다는 것을 선언
- 설정 코드 : spring.datasource.url=jdbc:h2:mem:{DB 이름}
Embedded Mode
- 애플리케이션 내부에서 디비 엔진이 실행(실행 주체가 스프링)
- 애플리케이션 외부에 데이터가 저장되므로 애플리케이션을 종료해도 데이터는 사라지지 않음
- 설정 코드 : spring.datasource.url=jdbc:h2:{DB가 저장될 경로}
JDBC
Java 앱과 DB 를 연결시켜주기 위해 만들어진 기술 -> JPA도 이 기술을 사용하여 구현
JDBC Driver
DB와 애플리케이션(스프링)간의 통신을 중개하는 역할
JDBC Driver 동작 방식
- 연결 초기화
- 애플리케이션이 드라이버에 연결 요청
- 드라이버는 디비 서버에 로그인하고 연결 완료
- sql 전송 및 실행
- 애플리케이션에서 받은 명령을 디비가 이해할 수 있는 형태로 변환
- 변환된 명령을 디비 서버로 전송해서 실행
- 결과 처리
- 디비에서 작업의 결과를 드라이버로 보내면 이 결과를 애플리케이션에서 이해할 수 있는 형태로 변환
- 해당 결과를 드라이버는 애플리캐이션으로 전송
- 연결 종료
- 작업이 완료되면 드라이버는 디비서버와의 연결을 종료
JDBC Driver Manager
1. Connection(연결) 을 생성하여 쿼리를 요청할 수 있는 상태를 만듦
2. Statement(상태) 를 생성하여 쿼리를 요청하게 함
3. ResultSet(결과셋) 을 생성해 쿼리 결과를 받아올 수 있게 해줌
Statement
동작방식 및 실행 방법
- executeQuery() 나 executeUpdate() 를 실행하는 시점에 파라미터로 SQL문을 전달
- SQL문을 수행하는 과정에서 구문 분석을 수행하기 때문에 효율성이 떨어짐
PreparedStatement
- Statement를 상속하고 있는 Interface
- 내부적으로 Statement의 4단계(구문분석, 치환, 실행, 인출) 과정 중 첫 번째 parse 과정의 결과를 캐싱하고,
- 나머지 3가지 단계만 거쳐서 SQL문이 실행
- 구문 분석(parse)의 결과를 캐싱해서 과정을 생략할 수 있으므로 Statement보다 성능이 향상
- SQL Injection 도 방어
순수 JDBC
- try가로 안에 자원을 넣으면 해당 try문이 끝나면 close 메서드를 호출해서 자원을 반환
- 직접 Connection, Statement, ResultSet을 만들어 줘야 한다
public class JdbcApplication {
public static void main(String[] args) throws SQLException {
// 어플리케이션 실행 컨텍스트 생성
SpringApplication.run(JdbcApplication.class, args);
// 데이터베이스 연결정보
String url = "jdbc:h2:mem:test"; // spring.datasource.url
String username = "sa"; // spring.datasource.username
try (Connection connection = DriverManager.getConnection(url, username, null)) {
// 테이블 생성
String createSql = "CREATE TABLE USERS (id SERIAL, username varchar(255))";
try (PreparedStatement statement = connection.prepareStatement(createSql)) {
statement.execute();
}
// 데이터 추가
String insertSql = "INSERT INTO USERS (username) VALUES ('teasun kim')";
try (PreparedStatement statement = connection.prepareStatement(insertSql)) {
statement.execute();
}
// 데이터 조회
String selectSql = "SELECT * FROM USERS";
try (PreparedStatement statement = connection.prepareStatement(selectSql);
ResultSet rs = statement.executeQuery()) {
while (rs.next()) {
System.out.printf("%d, %s%n", rs.getInt("id"), rs.getString("username"));
}
}
} catch (SQLException e) {
if (e.getMessage().equals("ERROR: relation \"account\" already exists")) {
System.out.println("USERS 테이블이 이미 존재합니다.");
} else {
throw new RuntimeException(e);
}
}
}
}
JDBC Template (QueryMapper)
- 직접 SQL을 실행하여 매핑
- SQL 쿼리 요청시 중복 코드 발생 및 Connection, Statement 등.. 자원 관리를 따로 해줘야하고 예외가 모두 Checked Exception (SQL Exception) 처리되는 문제 발생
public List<User> getUsers() {
String sql = "SELECT id, name, age FROM users";
return jdbcTemplate.query(sql, (rs, rowNum) ->
new User(rs.getLong("id"), rs.getString("name"), rs.getInt("age"))
);
}
QueryMapper의 문제를 해결하기 위한 Persistence Framework 등장
SQL Mapper : JDBC Template, MyBatis
ORM : JPA, Hibernate
JDBC Template (RowMapper)
- SQL Mapper 첫번째 주자로 JDBCTemplate 에 RowMapper 탄생
- 쿼리 수행 결과와 객채 필드 매핑
- RowMapper 로 응답필드 매핑코드 재사용
- Connection, Statement, ResultSet 반복적 처리 대신 해줌
- 결과값을 객체 인스턴스에 매핑하는데 여전히 많은 코드가 필요함
public class UserRowMapper implements RowMapper<User> {
// JDBCTemplate 에서 row 응답을 mapRow() 메서드에 rs 파라미터로 넘겨주어 객체에 매핑하기 쉽도록 도와준다.
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
var user = new User();
user.setId(rs.getInt("ID"));
user.setName(rs.getString("NAME"));
return user;
}
}
@Repository
public class DataRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
// 테이블 생성
public void createTable() {
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS users (id SERIAL, name VARCHAR(255))");
}
// 사용자 추가 (Create)
public void insertUser(String name) {
jdbcTemplate.update("INSERT INTO users (name) VALUES (?)", name);
}
// 사용자 ID로 User 조회 (Read)
public User findUserById(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new UserRowMapper(), // 이자리에 매퍼를 생성자로 넣어주면 됨
id
);
}
// 사용자 이름 변경 (Update)
public void updateUser(Long id, String newName) {
jdbcTemplate.update("UPDATE users SET name = ? WHERE id = ?", newName, id);
}
// 사용자 삭제 (Delete)
public void deleteUser(Long id) {
jdbcTemplate.update("DELETE FROM users WHERE id = ?", id);
}
}
'Spring' 카테고리의 다른 글
영속성(persist,merge..)과 쓰기지연 (0) | 2025.03.11 |
---|---|
장바구니 및 주문 기능 구현하기 (0) | 2025.03.06 |
배달앱 ERD 설계하기 (feat : 장바구니 기능) (0) | 2025.03.03 |
N + 1 문제와 해결 방법 (0) | 2025.02.27 |
AOP를 이용하여 API 로깅하기 (RequestBody, ResponseBody) (0) | 2025.02.27 |