2026년 2월 3일 화요일 - 6주차
- 세션(Session): DB에 클라이언트가 연결되어있는 상태를 말하며, 클라이언트별로 세션이 생성된다.
- 트랜잭션(Transaction): 세션 안에서 작동하는 하나의 작업단위를 말하며, 작업별로 트랜잭션이 생성된다. 이 안에서 쿼리가 실행되는 구조이고, 쿼리를 통해 변경된 데이터가 트랜잭션 내부에 존재하게 된다.
- commit: 트랜잭션 안에 존재하는 변경사항을 확정지어서 하드디스크에 저장하고 트랜잭션을 종료하는 것이다. DBeaver는 기본적으로 Auto-Commit인데, 이러면 commit이 자동으로 이뤄져서 위험 할 수 있기 때문에 Manual Commit으로 바꿔주는게 좋다. 그러면 자동으로 commit되지 않고 트랜잭션 안에만 있다가 직접 commit을 실행시킬때 저장 된다. 참고로 Java 프로그램은 Auto-Commit이다.
- rollback: 트랜잭션 내부에서 변경된 사항을 하드디스크에 저장하지 않고 처음으로 되돌린 다음에 트랜잭션을 종료시키는 것이다.
- 데드락: 하나의 DB를 공유하는 두개의 트랜잭션을 동시에 실행시키면 교착상태가 발생하는데, 이게 데드락이다. 쓰레드처럼 공유된 자원을 동시에 접근하는게 문제다.. 한쪽에서 트랜잭션을 실행해서 수정작업을 진행중인데 다른쪽에서 트랜잭션을 실행해서 먼저 수정 작업을 끝내고 commit을 했을때 데드락에 걸리며, 계속해서 대기를 하게된다. 대기가 끝나기 위해서는 먼저 실행된 트랜잭션에서 commit이나 rollback을 해서 변경사항을 반영해야만 대기가 끝나고 commit이 된다. 하지만 먼저 commit된 내용을 반영하지 못하고 실행되기 때문에 터져버릴 수도 있다.
StudentManagerV3를 바탕으로 PL/SQL로 데이터베이스를 이용하여 StudentManegerV5 버전을 작성했다. 이제 이전 버전들과 달리 프로그램을 종료시키면 데이터가 사라지지 않고 DB에 저장된다.
[StudentManagerRepository]
package com.joongang.stm.repository;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.joongang.stm.dto.StudentDto;
public class StudentManagerRepository {
public void save(StudentDto studentDto) {
try {
Class.forName("org.mariadb.jdbc.Driver");
String url = "jdbc:mariadb://localhost:3306/madang";
String user = "madang";
String password = "1234";
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
// 여기까지는 DB를 연결시키는 과정.
// execute API로 sql문을 실행함.
// sql이 익숙하지 않다면 DBeaver에서 쳐보고 옮기는걸 추천.
String query = String.format("INSERT INTO stm_student(name, age, score) VALUES('%s', '%d', '%d')",
studentDto.getName(), studentDto.getAge(), studentDto.getScore());
statement.execute(query);
statement.close();
connection.close(); // connect를 close 하는 순간 네트워크가 끊김
} catch (Exception e) {
e.printStackTrace();
}
}
public List<StudentDto> findAll() {
List<StudentDto> result = new ArrayList<>();
try {
Class.forName("org.mariadb.jdbc.Driver");
String url = "jdbc:mariadb://localhost:3306/madang";
String user = "madang";
String password = "1234";
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
String query = "SELECT * FROM stm_student";
ResultSet resultSet = statement.executeQuery(query);
// INSERT, UPDATE, DELETE는 결과값이 없기때문에 리턴타입이 필요 없지만,
// SELECT는 결과를 반환하니까 리턴타입이 resultset인 executeQuery 사용.
while(resultSet.next()) {
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
int score = resultSet.getInt("score");
StudentDto studentDto = new StudentDto(name, age, score);
result.add(studentDto);
}
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public int count() {
try {
Class.forName("org.mariadb.jdbc.Driver");
String url = "jdbc:mariadb://localhost:3306/madang";
String user = "madang";
String password = "1234";
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
String query = "SELECT COUNT(*) AS cnt FROM stm_student";
ResultSet resultSet = statement.executeQuery(query);
int count = 0;
if (resultSet.next()) {
count = resultSet.getInt("cnt");
}
resultSet.close();
statement.close();
connection.close();
return count;
} catch (Exception e) {
e.printStackTrace();
}
return 0; // 예외 발생 시 기본값
}
// 원래는 모든것을 다 바꿀 수 있었지만, 이번에는 이름을 입력받아서
// 해당 학생의 점수만을 변경하는 것으로 한다.
public void update(String name, int score) {
try {
Class.forName("org.mariadb.jdbc.Driver");
String url = "jdbc:mariadb://localhost:3306/madang";
String user = "madang";
String password = "1234";
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
String query = String.format("UPDATE stm_student SET score = %d WHERE name = '%s'", score, name);
statement.execute(query);
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
[StudentManagerService]
package com.joongang.stm.service;
import java.util.List;
import com.joongang.stm.dto.StudentDto;
import com.joongang.stm.repository.StudentManagerRepository;
import com.joongang.stm.util.IoUtil;
public class StudentManagerService {
private StudentManagerRepository repository = new StudentManagerRepository();
public void register() {
IoUtil.print("======= 학생 정보 등록 =======");
String name = IoUtil.input("이름 > ");
int age = Integer.parseInt(IoUtil.input("나이 > "));
int score = Integer.parseInt(IoUtil.input("점수 > "));
StudentDto studentDto = new StudentDto(name, age, score);
repository.save(studentDto);
IoUtil.print("============================");
}
public void list() {
IoUtil.print("======= 학생 정보 목록 =======");
List<StudentDto> studentList = repository.findAll();
int count = repository.count();
for(StudentDto studentDto : studentList) {
String text = "";
text += "이름: " + studentDto.getName();
text += ", 나이: " + studentDto.getAge();
text += ", 점수: " + studentDto.getScore();
IoUtil.print(text);
}
IoUtil.print("총 " + count + "명이 존재합니다.");
IoUtil.print("============================");
}
public void modify() {
IoUtil.print("======= 학생 점수 수정 =======");
String name = IoUtil.input("수정할 학생의 이름 > ");
int score = Integer.parseInt(IoUtil.input("수정할 점수 > "));
repository.update(name, score);
IoUtil.print("점수가 수정되었습니다.");
IoUtil.print("============================");
}
}
이 이후에 Service에 기능 추가 하기만 하면 된다.