오늘은 DAO와 DTO에 대해 정리해보고, DB에 접속해 CRUD(Create Read Update Delete) 작업을 진행하는 예시를 만들어보며 이해를 해보도록 하겠습니다. 추가적으로 VO에 대한 기본적인 개념과 간단한 예시를 들어보겠습니다.
▪️ 데이터베이스와의 상호 작용을 관리하고 데이터베이스 접근 로직을 캡슐화 하는 역할을 합니다.
▪️ 데이터베이스와 직접 상호작용하며, SQL 쿼리 실행을 통해 데이터 검색, 삽입, 수정, 삭제 등의 작업을 수행합니다.
▪️ 데이터의 전송을 위한 구조화된 객체를 뜻합니다.
▪️ 서로 다른 계층 간 데이터 전달을 용이하게 하고 데이터를 표현하는데 사용됩니다.
▪️ 데이터 전송 과정에서 객체 형태로 데이터를 유지합니다.
▪️ 주로 필드와 해당 필드에 접근하는 메서드인 getter/setter 로 구성됩니다.
▪️ VO (Value Object)는 비즈니스 도메인의 특정 값을 나타내기 위한 객체로, 데이터의 불변성과 동등성을 유지하는 것이 중요합니다.
▪️ 불변성 : 한 번 생성된 후에는 그 값을 변경할 수 없습니다.
▪️ 동등성 : VO는 내부 데이터 값에 기반하여 두 객체가 동등하다고 판단될 때 같은 것으로 간주됩니다. 즉, 값 자체가 같으면 VO가 같다고 간주됩니다.
VO 예시코드를 보면 다음과 같습니다.
public class Coordinate {
private final double latitude;
private final double longitude;
public Coordinate(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
// 다른 메서드, 유효성 검사 로직 등을 추가할 수 있음
}
다음과 같이 car_no(int AI PK), number (char(20)), model (varchar(20)), price (int), brand (varchar(20)) 필드의 DB가 있을 때, 여기에 CRUD를 진행하는 DAO 코드를 작성해보면 다음과 같습니다.

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `car`
--
DROP TABLE IF EXISTS `car`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `car` (
`car_no` int NOT NULL AUTO_INCREMENT,
`number` char(20) DEFAULT NULL,
`model` varchar(20) NOT NULL,
`price` int DEFAULT NULL,
`brand` varchar(20) DEFAULT NULL,
PRIMARY KEY (`car_no`),
UNIQUE KEY `number` (`number`)
) ENGINE=InnoDB AUTO_INCREMENT=12412415 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `car`
--
LOCK TABLES `car` WRITE;
/*!40000 ALTER TABLE `car` DISABLE KEYS */;
INSERT INTO `car` VALUES (1,'12345678','아반떼',2500,'현대'),(2,'55552233','그랜저',4000,'현대'),(3,'77771123','벤츠 C-Class',12000,'메르세데스벤츠');
/*!40000 ALTER TABLE `car` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
우선 데이터를 등록하는 C(Create) 메서드를 작성하면 이와 같습니다.
@Override
public int insertCar(CarDto carDto) throws SQLException {
// 1. sql 작성
String sql = "insert into car\n" +
"(car_no,number,model,price,brand)\n" +
"values(?, ?, ?, ?, ?);";
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 2. DB 연결
conn = dbUtil.getConnection();
// 3. sql 실행
// 3-1. sql 실행 준비시키기
pstmt = conn.prepareStatement(sql);
// 3-2. sql 실행시키기 - 삽입 완료 했다면 1, 아니면 0 반환
int cnt = pstmt.executeUpdate();
return cnt;
} finally {
// 4. 자원 반납
// 4-1. dbUtil의 close 메서드를 통해 자원 반납
dbUtil.close(conn,pstmt);
}
}
다음으로 데이터를 전체 조회 혹은 상세 조회하는 R(Read) 메서드를 작성하면 이와 같습니다.
@Override
public List<CarDto> sellectAllCars() throws SQLException {
System.out.println("DAO sellectAll 실행");
// 1. sql 작성
String sql = "SELECT * FROM car;";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 2. DB와 연결
// 2-1. dbUtil getConnection 메서드를 통해 db 연결 시키기
conn = dbUtil.getConnection();
// 3. sql 실행
// 3-1. sql 실행 준비시키기
pstmt = conn.prepareStatement(sql);
// 3-2. sql 실행시켜서 결괏값 가져오기
rs = pstmt.executeQuery();
// 4. 조회 데이터 파싱
// 4-1. carDto 담을 list 만들기
List<CarDto> list = new ArrayList<>();
while(rs.next()) {
// 4-2. 가져온 결괏값에서 데이터 파싱해오기
int carNo = rs.getInt("car_no");
String number = rs.getString("number");
String model = rs.getString("model");
int price = rs.getInt("price");
String brand = rs.getString("brand");
// 4-3. CarDto 객체 만들고 리스트에 넣어주기
CarDto car = new CarDto(carNo,number,model,price,brand);
list.add(car);
}
// 4-4. 완성된 list 반환
return list;
}finally {
// 5. 자원 반납
// 5-1. dbUtil의 close 메서드를 통해 자원 반납
dbUtil.close(conn,pstmt,rs);
}
}
@Override
public CarDto SearchCar(int carNo) throws SQLException {
// 1. sql 작성
String sql = "select *\n" +
"from car\n" +
"where car_no=?;";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 2. DB와 연결
// 2-1. dbUtil getConnection 메서드를 통해 db 연결 시키기
conn = dbUtil.getConnection();
// 3. sql 실행
// 3-1. sql 실행 준비시키기
pstmt = conn.prepareStatement(sql);
// 3-2. ?에 값 setting 해주기
pstmt.setInt(1, carNo);
// 3-3. sql 실행시켜서 결괏값 가져오기
rs = pstmt.executeQuery();
// 4. 조회 데이터 파싱
if(rs.next()) {
// 4-2. 가져온 결괏값에서 데이터 파싱해오기
String number = rs.getString("number");
String model = rs.getString("model");
int price = rs.getInt("price");
String brand = rs.getString("brand");
// 4-3. CarDto 객체 만들고 리스트에 넣어주기
CarDto car = new CarDto(carNo,number,model,price,brand);
return car;
}
return null;
}finally {
// 4. 자원 반납
// 4-1. 자원 반납
dbUtil.close(conn,pstmt,rs);
}
}
다음으로 데이터를 수정하는 U(Update) 메서드를 작성하면 이와 같습니다.
@Override
public int updateCar(CarDto car) throws SQLException {
//1. sql 작성
String sql = "update country\r\n" +
"set number=?, model=?,price=?,brand=?\r\n" +
"where car_no =?;";
Connection conn = null;
PreparedStatement pstmt = null;
try {
//2. DB 연결
conn = dbUtil.getConnection();
//3. 쿼리 실행
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, car.getNumber());
pstmt.setString(2, car.getModel());
pstmt.setDouble(3, car.getPrice());
pstmt.setString(4, car.getBrand());
pstmt.setInt(5, car.getCarNo());
int cnt = pstmt.executeUpdate();
return cnt;
} finally {
dbUtil.close(pstmt, conn);
}
}
마지막으로 D(Delete)를 구현하면 다음과 같습니다.
@Override
public int deleteCar(int carNo) throws SQLException {
// 1. sql 작성
String sql = "delete from car\n" +
"where car_no=?;";
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 2. DB 연결
conn = dbUtil.getConnection();
// 3. sql 실행
// 3-1. sql 실행 준비시키기
pstmt = conn.prepareStatement(sql);
// 3-2. ?에 값 setting 해주기
pstmt.setInt(1, carNo);
// 3-3. sql 실행시키기 - 삽입 완료 했다면 1, 아니면 0 반환
int cnt = pstmt.executeUpdate();
return cnt;
} finally {
// 4. 자원 반납
// 4-1. dbUtil의 close 메서드를 통해 자원 반납
dbUtil.close(conn,pstmt);
}
}