챕터 14: 데이터베이스 연동
데이터베이스는 대부분의 애플리케이션에서 핵심적인 역할을 합니다. 이 장에서는 JDBC를 통한 데이터베이스 연동의 기초, Spring을 활용한 데이터 접근, 그리고 JPA와 Hibernate를 이용한 ORM(Object-Relational Mapping)에 대해 다룹니다.
14.1 JDBC 기초
배경과 역사
JDBC(Java Database Connectivity)는 Java 언어로 데이터베이스에 접근하기 위한 표준 API입니다. JDBC는 Sun Microsystems(현재 Oracle)이 1997년에 발표하였으며, 다양한 관계형 데이터베이스와 상호 작용할 수 있는 방법을 제공합니다. JDBC는 데이터베이스 독립성을 유지하면서도 SQL 쿼리를 실행하고 결과를 처리할 수 있게 합니다.
14.1.1 JDBC API 개요
JDBC API는 다음과 같은 주요 인터페이스와 클래스를 포함합니다:
- DriverManager: 데이터베이스 드라이버를 관리하고 데이터베이스 연결을 획득하는 데 사용됩니다.
- Connection: 데이터베이스와의 연결을 나타냅니다.
- Statement: SQL 쿼리를 실행하는 데 사용됩니다.
- PreparedStatement: SQL 쿼리를 미리 컴파일하여 실행하는 데 사용됩니다.
- ResultSet: SQL 쿼리의 결과를 저장하고 처리하는 데 사용됩니다.
14.1.2 데이터베이스 연결과 기본 CRUD 작업
JDBC를 사용하여 데이터베이스에 연결하고 기본적인 CRUD(Create, Read, Update, Delete) 작업을 수행하는 예제를 살펴보겠습니다.
예제: 데이터베이스 연결과 CRUD 작업
- 데이터베이스 연결
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JDBCExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";
try (Connection connection = DriverManager.getConnection(url, user, password)) {
System.out.println("Database connected!");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
설명:
- DriverManager.getConnection: 데이터베이스에 연결을 설정하는 메서드입니다. URL, 사용자 이름, 비밀번호를 인수로 받습니다.
- Connection: 데이터베이스와의 연결을 나타냅니다. try-with-resources 구문을 사용하여 자동으로 연결을 닫습니다.
- url: 데이터베이스에 연결하기 위한 JDBC URL입니다.
- user: 데이터베이스 사용자 이름입니다.
- password: 데이터베이스 비밀번호입니다.
- Create 작업
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class CreateExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";
String query = "INSERT INTO users (name, email) VALUES (?, ?)";
try (Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement preparedStatement = connection.prepareStatement(query)) {
preparedStatement.setString(1, "John Doe");
preparedStatement.setString(2, "john.doe@example.com");
int rowsAffected = preparedStatement.executeUpdate();
System.out.println(rowsAffected + " row(s) inserted.");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
설명:
- PreparedStatement: 미리 컴파일된 SQL 쿼리를 실행하기 위해 사용됩니다.
- setString: SQL 쿼리의 매개변수를 설정하는 메서드입니다. 첫 번째 매개변수는 매개변수의 인덱스를, 두 번째 매개변수는 설정할 값을 나타냅니다.
- executeUpdate: 데이터베이스에 대한 변경 작업을 실행하고 영향을 받은 행 수를 반환합니다.
- query: SQL 쿼리 문자열로, 데이터베이스에 삽입할 데이터를 지정합니다.
- rowsAffected: 쿼리 실행으로 영향을 받은 행 수를 저장합니다.
- Read 작업
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class ReadExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";
String query = "SELECT * FROM users";
try (Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement preparedStatement = connection.prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery()) {
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String email = resultSet.getString("email");
System.out.println("ID: " + id + ", Name: " + name + ", Email: " + email);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
설명:
- executeQuery: SQL 쿼리를 실행하고 결과 집합을 반환합니다.
- ResultSet: SQL 쿼리 결과를 저장하고 처리하는 데 사용됩니다.
- next: 결과 집합의 다음 행으로 이동합니다. 더 이상 행이 없으면 false를 반환합니다.
- getInt, getString: 결과 집합의 열 값을 가져오는 메서드입니다. 열 이름이나 인덱스를 인수로 받습니다.
- query: SQL 쿼리 문자열로, 데이터베이스에서 데이터를 조회합니다.
- Update 작업
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class UpdateExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";
String query = "UPDATE users SET email = ? WHERE name = ?";
try (Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement preparedStatement = connection.prepareStatement(query)) {
preparedStatement.setString(1, "new.email@example.com");
preparedStatement.setString(2, "John Doe");
int rowsAffected = preparedStatement.executeUpdate();
System.out.println(rowsAffected + " row(s) updated.");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
설명:
- executeUpdate: 데이터베이스에 대한 변경 작업을 실행하고 영향을 받은 행 수를 반환합니다.
- setString: SQL 쿼리의 매개변수를 설정하는 메서드입니다. 첫 번째 매개변수는 매개변수의 인덱스를, 두 번째 매개변수는 설정할 값을 나타냅니다.
- query: SQL 쿼리 문자열로, 데이터베이스에서 데이터를 업데이트합니다.
- rowsAffected: 쿼리 실행으로 영향을 받은 행 수를 저장합니다.
- Delete 작업
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DeleteExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";
String query = "DELETE FROM users WHERE name = ?";
try (Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement preparedStatement = connection.prepareStatement(query)) {
preparedStatement.setString(1, "John Doe");
int rowsAffected = preparedStatement.executeUpdate();
System.out.println(rowsAffected + " row(s) deleted.");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
설명:
- executeUpdate: 데이터베이스에 대한 변경 작업을 실행하고 영향을 받은 행 수를 반환합니다.
- setString: SQL 쿼리의 매개변수를 설정하는 메서드입니다. 첫 번째 매개변수는 매개변수의 인덱스를, 두 번째 매개변수는 설정할 값을 나타냅니다.
- query: SQL 쿼리 문자열로, 데이터베이스에서 데이터를 삭제합니다.
- rowsAffected: 쿼리 실행으로 영향을 받은 행 수를 저장합니다.
14.2 Spring 데이터 접근
Spring 프레임워크는 데이터 접근을 단순화하고 다양한 데이터베이스와의 통합을 용이하게 합니다. Spring JDBC와 JdbcTemplate은 JDBC 작업을 단순화하고 보일러플레이트 코드를 줄이는 데 도움을 줍니다.
배경과 역사
Spring 프레임워크는 2003년 로드 존슨(Rod Johnson)에 의해 처음 발표되었습니다. 당시 엔터프라이즈 Java 애플리케이션 개발은 복잡한 설정과 무거운 프레임워크로 인해 어려움이 많았습니다. Spring은 이러한 문제를 해결하고자 경량화된 프레임워크로 개발되었습니다. Spring JDBC는 데이터베이스와의 상호작용을 단순화하고 보일러플레이트 코드를 줄이기 위해 설계되었습니다.
14.2.1 Spring JDBC와 JdbcTemplate
JdbcTemplate은 Spring에서 제공하는 유틸리티 클래스로, JDBC 작업을 단순화하고 예외 처리를 일관되게 관리할 수 있게 합니다.
예제: Spring JDBC와 JdbcTemplate
- Spring 설정
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydatabase
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
- JdbcTemplate 사용
User.java
public class User {
private int id;
private String name;
private String email;
// getters and setters
}
UserRepository.java
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
public UserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 모든 사용자 조회
public List<User> findAll() {
String sql = "SELECT * FROM users";
return jdbcTemplate.query(sql, rowMapper());
}
// 사용자 저장
public void save(User user) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
jdbcTemplate.update(sql, user.getName(), user.getEmail());
}
// 사용자 업데이트
public void update(User user) {
String sql = "UPDATE users SET email = ? WHERE name = ?";
jdbcTemplate.update(sql, user.getEmail(), user.getName());
}
// 사용자 삭제
public void delete(String name) {
String sql = "DELETE FROM users WHERE name = ?";
jdbcTemplate.update(sql, name);
}
// ResultSet을 User 객체로 매핑
private RowMapper<User> rowMapper() {
return (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
return user;
};
}
}
UserService.java
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 모든 사용자 조회
public List<User> getAllUsers() {
return userRepository.findAll();
}
// 사용자 생성
public void createUser(User user) {
userRepository.save(user);
}
// 사용자 업데이트
public void updateUser(User user) {
userRepository.save(user);
}
// 사용자 삭제
public void deleteUser(int id) {
userRepository.deleteById(id);
}
}
설명:
- JdbcTemplate: JDBC 작업을 단순화하고 예외 처리를 일관되게 관리하는 유틸리티 클래스입니다.
- query: SQL 쿼리를 실행하고 결과 집합을 매핑하는 메서드입니다.
- update: 데이터베이스에 대한 변경 작업을 실행하는 메서드입니다.
- RowMapper: ResultSet의 행을 객체로 매핑하는 함수형 인터페이스입니다.
14.2.2 트랜잭션 관리
Spring은 선언적 트랜잭션 관리 기능을 제공하여 코드의 분리를 유지하면서 트랜잭션을 쉽게 관리할 수 있게 합니다.
예제: 트랜잭션 관리
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydatabase
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
UserService.java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 사용자 생성 트랜잭션
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
// 사용자 업데이트 트랜잭션
@Transactional
public void updateUser(User user) {
userRepository.update(user);
}
// 사용자 삭제 트랜잭션
@Transactional
public void deleteUser(String name) {
userRepository.delete(name);
}
}
설명:
- @Transactional: 메서드나 클래스에 적용하여 트랜잭션 범위를 지정합니다. 트랜잭션 관리가 필요한 비즈니스 로직에 사용됩니다.
14.3 JPA와 Hibernate
JPA(Java Persistence API)는 자바 애플리케이션에서 관계형 데이터베이스를 사용하기 위한 표준 명세입니다. Hibernate는 JPA를 구현한 가장 널리 사용되는 ORM 프레임워크 중 하나입니다.
배경과 역사
JPA는 2006년 Java EE 5의 일부로 도입되었습니다. JPA는 객체와 관계형 데이터베이스 간의 매핑을 제공하여, 데이터베이스 작업을 쉽게 하고, 객체 지향 프로그래밍과 관계형 데이터베이스 간의 불일치를 해소하는 데 도움을 줍니다.
Hibernate는 2001년에 처음 발표되었으며, JPA의 초기 구현체 중 하나입니다. Hibernate는 다양한 데이터베이스를 지원하고, 강력한 ORM 기능을 제공하여 개발자들 사이에서 널리 사용됩니다.
14.3.1 ORM(Object-Relational Mapping) 개념
ORM은 객체 지향 프로그래밍과 관계형 데이터베이스 간의 불일치를 해소하기 위한 기술입니다. ORM은 객체를 데이터베이스 테이블에 매핑하고, SQL 쿼리를 자동으로 생성하여 데이터베이스 작업을 수행합니다. 이를 통해 데이터베이스 작업을 쉽게 하고, 객체 지향 프로그래밍의 장점을 유지할 수 있습니다.
14.3.2 JPA와 Hibernate 설정 및 사용법
Spring Boot와 JPA를 사용하여 데이터베이스 작업을 수행하는 예제를 살펴보겠습니다.
예제: JPA와 Hibernate 설정 및 사용
1. Spring 설정
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydatabase
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
2. 엔티티 클래스
User.java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String email;
// getters and setters
}
설명:
- @Entity: 이 클래스가 JPA 엔티티임을 나타냅니다. 데이터베이스 테이블에 매핑됩니다.
- @Id: 엔티티의 기본 키를 나타냅니다.
- @GeneratedValue: 기본 키의 값을 자동으로 생성하는 전략을 지정합니다.
3. 리포지토리 인터페이스
UserRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
}
설명:
- JpaRepository: JPA 엔티티에 대한 CRUD 작업을 제공하는 인터페이스입니다. 기본적인 데이터베이스 작업을 자동으로 처리합니다.
4. 서비스 클래스
UserService.java
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 모든 사용자 조회
public List<User> getAllUsers() {
return userRepository.findAll();
}
// 사용자 생성
public void createUser(User user) {
userRepository.save(user);
}
// 사용자 업데이트
public void updateUser(User user) {
userRepository.save(user);
}
// 사용자 삭제
public void deleteUser(int id) {
userRepository.deleteById(id);
}
}
설명:
- findAll: 모든 엔티티를 조회하는 메서드입니다.
- save: 엔티티를 저장하거나 업데이트하는 메서드입니다.
- deleteById: 기본 키를 사용하여 엔티티를 삭제하는 메서드입니다.
5. 컨트롤러 클래스
UserController.java
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// 모든 사용자 조회
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
// 사용자 생성
@PostMapping
public void createUser(@RequestBody User user) {
userService.createUser(user);
}
// 사용자 업데이트
@PutMapping
public void updateUser(@RequestBody User user) {
userService.updateUser(user);
}
// 사용자 삭제
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable int id) {
userService.deleteUser(id);
}
}
설명:
- @RestController: RESTful 웹 서비스를 처리하는 컨트롤러 클래스에 사용됩니다.
- @RequestMapping: 요청 URL을 매핑합니다.
- @GetMapping, @PostMapping, @PutMapping, @DeleteMapping: HTTP 메서드와 매핑되는 요청을 처리합니다.
- @RequestBody: HTTP 요청 본문을 매핑하여 파라미터로 사용합니다.
- @PathVariable: URL 경로 변수를 매핑하여 파라미터로 사용합니다.
추가 예제: 페이징 및 정렬
Spring Data JPA는 페이징 및 정렬 기능도 제공합니다.
1. 리포지토리 인터페이스 수정
UserRepository.java
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
Page<User> findAll(Pageable pageable);
}
설명:
- Pageable: 페이징 및 정렬 정보를 캡슐화하는 인터페이스입니다.
- Page: 페이징된 결과를 나타내는 인터페이스입니다.
2. 서비스 클래스 수정
UserService.java
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 모든 사용자 조회 (페이징)
public Page<User> getAllUsers(Pageable pageable) {
return userRepository.findAll(pageable);
}
// 사용자 생성
public void createUser(User user) {
userRepository.save(user);
}
// 사용자 업데이트
public void updateUser(User user) {
userRepository.save(user);
}
// 사용자 삭제
public void deleteUser(int id) {
userRepository.deleteById(id);
}
}
UserController.java
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// 모든 사용자 조회 (페이징)
@GetMapping
public Page<User> getAllUsers(Pageable pageable) {
return userService.getAllUsers(pageable);
}
// 사용자 생성
@PostMapping
public void createUser(@RequestBody User user) {
userService.createUser(user);
}
// 사용자 업데이트
@PutMapping
public void updateUser(@RequestBody User user) {
userService.updateUser(user);
}
// 사용자 삭제
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable int id) {
userService.deleteUser(id);
}
}
설명:
- Pageable: 페이징 및 정렬 정보를 캡슐화하는 인터페이스입니다.
- Page: 페이징된 결과를 나타내는 인터페이스입니다.
- 페이징 및 정렬 기능을 사용하여 클라이언트 요청에 맞게 데이터를 효율적으로 제공할 수 있습니다.
이로써 JDBC를 통한 데이터베이스 연동의 기초, Spring을 활용한 데이터 접근, 그리고 JPA와 Hibernate를 이용한 ORM에 대해 자세히 설명했습니다. 이를 통해 데이터베이스와의 상호 작용을 쉽게 하고, 객체 지향 프로그래밍과 관계형 데이터베이스 간의 불일치를 해소할 수 있게 됩니다. 다음 챕터에서는 테스팅과 유지보수에 대해 다루겠습니다.
'IT 강좌(IT Lectures) > Java' 카테고리의 다른 글
16강. 실전 프로젝트 (0) | 2024.07.03 |
---|---|
15강. 테스팅과 유지보수 (0) | 2024.07.02 |
13강. Spring 프레임워크 (0) | 2024.06.30 |
12강. 보안과 암호화 (0) | 2024.06.29 |
11강. Java 성능 최적화 (0) | 2024.06.28 |