[TIL] 7주차 ⎮ BE - JPA
1. JPA (Java Persistence API)
JPA는 자바 객체와 관계형 데이터베이스 간의 매핑(ORM, Object-Relational Mapping)을 제공하는 표준 인터페이스이다. 데이터베이스와의 상호작용을 쉽게 관리하고 유지보수할 수 있게 한다.
- JDBC: SQL 쿼리가 자바 코드 내에 직접 포함되므로, SQL과 비즈니스 로직이 혼합되어 복잡
- MyBatis: SQL 쿼리를 XML 파일로 분리할 수 있지만 여전히 SQL을 직접 작성
- JPA: SQL을 직접 작성하지 않고, 객체지향적 엔터티 매핑과 JPQL(Java Persistence Query Language)을 사용해 데이터베이스 작업을 처리.
- SQL은 JPA가 내부적으로 자동 생성하여 실행
🌲 JPA는 뭘까 왜 쓰는가?
인터페이스 모임이다. 따라서 이 인터페이스를 구현한 라이브러리나 프레임워크가 필요한데 이러한 구현체 중 많이 사용되는 것이 Hibernate, EclipseLink, OpenJPA
등이다.
- JPA는 반복적인 CRUD SQL을 자동으로 생성해 처리해준다. 개발자는 SQL 쿼리를 작성할 필요 없음
- SQL이 아닌 객체 중심 개발이 가능해진다.
- JPA는 자바의 객체 지향적인 개념과 관계형 데이터베이스의 관계 모델 간의 불일치를 해결한다.
- 예를 들어, 자바에서는 상속을 통해 부모와 자식 클래스 간의 관계를 정의할 수 있지만, 기존 관계형 데이터베이스는 이러한 상속 관계를 지원하지 않는다. (상속 말고도 있음)
- JPA에서는 객체의 상속 관계를 데이터베이스에 매핑하는 방법을 제공한다.
단일 테이블 전략, 구현 테이블 전략, 매핑 테이블 전략
- JPA에서는 객체의 상속 관계를 데이터베이스에 매핑하는 방법을 제공한다.
- 예를 들어, 자바에서는 상속을 통해 부모와 자식 클래스 간의 관계를 정의할 수 있지만, 기존 관계형 데이터베이스는 이러한 상속 관계를 지원하지 않는다. (상속 말고도 있음)
JPA 저장 및 조회 구조
🌲 JPA는 어떻게 사용하는가?
1) 엔터티(Entity)
- 엔터티 클래스는 데이터베이스의 테이블과 매핑되는 자바 객체
- 클래스 위에
@Entity
어노테이션을 사용해 선언@Entity // 엔티티 클래스임을 선언. @Table(name = "users") // 해당 엔티티 클래스와 매핑될 데이터베이스 테이블 이름을 지정. public class User { @Id // 엔티티 클래스의 주요 식별자(primary key)임을 선언 @GeneratedValue(strategy = GenerationType.IDENTITY) // 엔티티의 식별자 값을 자동으로 생성 private Long id; // 해당 엔티티 클래스의 필드가 데이터베이스의 칼럼으로 매핑될 때, // 해당 칼럼의 제약 조건을 설정하는 어노테이션입니다. (널허용, 고유성, 길이 등) @Column(nullable = false, unique = true) private String username; @Column(nullable = false) private String password; // getters and setters }
2) 리포지토리(Repository)
- 리포지토리는 데이터베이스에 접근하는 인터페이스이다. 보통
JpaRepository
인터페이스를 상속받아 구현 - JpaRepository 인터페이스를 상속받기에 따로 메서드 구현, 구현체 작성을 하지 않아도 자동으로 생성된다.
// 해당 인터페이스가 스프링의 데이터 접근 계층(Data Access Layer)의 컴포넌트임을 선언
@Repository
// JpaRepository<User, Long> 인터페이스 : 스프링 데이터 JPA에서 제공하는 CRUD 메서드를 상속받아 사용할 수 있는 인터페이스입니다.
public interface UserRepository extends JpaRepository<User, Long> {
// username으로 User 엔티티를 찾는 메서드
Optional<User> findByUsername(String username);
}
3) 엔티티 매니저 팩토리 설정
- application.properties 또는 application.yml 파일을 이용하여 **JPA 설정 정보를 정의
- 스프링이 자동으로
EntityManagerFactory
를 설정
# JSP 뷰 설정
spring.mvc.view.prefix=/WEB-INF/views/ # JSP 파일 디렉토리
spring.mvc.view.suffix=.jsp # JSP 파일 확장자
# MySQL 데이터베이스 설정
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 데이터베이스 드라이버
spring.datasource.url=jdbc:mysql://localhost:3306/맞는걸로작성?useSSL=false&serverTimezone=UTC # DB 연결 URL
spring.datasource.username=root # DB 사용자 이름
spring.datasource.password=1234 # DB 비밀번호
# Hibernate 설정
spring.jpa.properties.hibernate.show_sql=true # SQL 쿼리 출력
spring.jpa.properties.hibernate.format_sql=true # SQL 쿼리 포맷
spring.jpa.show-sql=true # JPA SQL 쿼리 출력
# DDL 자동 생성 설정
spring.jpa.properties.hibernate.hbm2ddl.auto=update # DB 스키마 자동 업데이트
4) 서비스(Service) 클래스
- 비즈니스 로직을 처리. 여기서 데이터베이스에서 데이터를 조회하거나, 새로운 데이터를 삽입, 수정, 삭제(CRUD)할 수 있다.
@Service
public class UserService {
@Autowired // UserRepository 주입
private UserRepository userRepository;
// 모든 사용자 조회
public List<User> findAllUsers() {
return userRepository.findAll();
}
public User saveUser(User user) {
return userRepository.save(user);
}
}
4) 컨트롤러 클래스
- REST API를 제공하는 컨트롤러. 사용자를 조회하는 GET 요청과 사용자 생성을 위한 POST 요청을 처리.
@RestController // RESTful 웹 서비스의 컨트롤러임을 선언 @RequestMapping("/users") // 요청 URL의 기본 경로 설정 public class UserController { @Autowired // UserService 주입 private UserService userService; // 모든 사용자 정보를 반환하는 GET 요청 처리 @GetMapping // /users 경로로 GET 요청 시 public List<User> getAllUsers() { return userService.findAllUsers(); // 사용자 목록 반환 } // 새로운 사용자 정보를 저장하는 POST 요청 처리 @PostMapping // /users 경로로 POST 요청 시 public User createUser(@RequestBody User user) { return userService.saveUser(user); // 사용자 저장 후 반환 } }
🌲 JDBC, MyBatis, JPA 사용 비교
1) JDBC 사용
JDBC를 사용하여 SQL 쿼리를 직접 작성하고 데이터베이스와 상호작용해야 함
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class UserJdbcExample {
private static final String URL = "jdbc:mysql://localhost:3306/mydb";
private static final String USER = "username";
private static final String PASSWORD = "password";
// 사용자 저장 (INSERT)
public void saveUser(String name) {
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO users (name) VALUES (?)")) {
preparedStatement.setString(1, name);
preparedStatement.executeUpdate(); // SQL 쿼리 실행
} catch (Exception e) {
e.printStackTrace();
}
}
// 모든 사용자 조회 (SELECT)
public void findAllUsers() {
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM users");
ResultSet resultSet = preparedStatement.executeQuery()) {
while (resultSet.next()) {
System.out.println("User: " + resultSet.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2) MyBatis 사용
// MyBatis를 사용하여 XML 파일에 SQL을 분리하고, 매퍼 인터페이스를 통해 DB 접근
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
// SQL을 XML 파일이나 어노테이션으로 분리할 수 있음
@Insert("INSERT INTO users (name) VALUES (#{name})")
void saveUser(String name);
@Select("SELECT * FROM users")
List<String> findAllUsers();
}
// 서비스 클래스에서 MyBatis 매퍼 인터페이스 사용
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void saveUser(String name) {
userMapper.saveUser(name); // 사용자 저장 (MyBatis가 SQL을 실행)
}
public void findAllUsers() {
List<String> users = userMapper.findAllUsers(); // 사용자 목록 조회
users.forEach(user -> System.out.println("User: " + user));
}
}
3) JPA를 사용하는 경우
JPA를 사용하면 엔터티 클래스를 정의하고, 리포지토리 인터페이스를 통해 데이터베이스에 접근할 수 있음
import javax.persistence.*;
import java.util.List;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // ID 자동 생성
private Long id;
private String name;
// Getters and Setters
}
// 리포지토리---------------------------------------------------------
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
// 기본적인 CRUD 메서드는 자동 생성 (save, findAll 등)
}
// 서비스클래스-------------------------------------------------------
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void saveUser(String name) {
User user = new User();
user.setName(name);
userRepository.save(user); // JPA가 자동으로 SQL을 생성하고 실행
}
public void findAllUsers() {
List<User> users = userRepository.findAll();
for (User user : users) {
System.out.println("User: " + user.getName());
}
}
}
3. 비교하기
JDBC 사용
- 데이터베이스에 직접 SQL 쿼리를 작성해야 한다. 이는 코드가 복잡해질 수 있고, SQL 구문 오류가 발생할 수 있다.
- 데이터베이스 연결을 수동으로 관리해야 하며, 각 쿼리에 대해
PreparedStatement
와ResultSet
을 명시적으로 처리해야 한다. - 동일한 CRUD 작업을 여러 번 반복해야 하며, 코드가 중복될 수 있다.
MyBatis 사용
- SQL을 XML 파일 또는 어노테이션으로 분리해 자바 코드와 분리 가능
- SQL 쿼리 결과를 자바 객체로 자동으로 매핑 가능
- SQL 쿼리를 직접 작성해야 하지만, 자바 코드와 분리할 수 있다는 장점.
JPA 사용
- 데이터베이스의 테이블에 매핑된 자바 클래스를 정의하여 객체 지향적으로 데이터를 처리할 수 있음.
- 리포지토리 패턴: JPA의
JpaRepository
인터페이스를 통해 CRUD 작업을 간편하게 수행할 수 있으며, 추가적인 쿼리 메서드를 쉽게 정의할 수 있음. - 데이터베이스와의 상호작용이 단순화되어 코드가 훨씬 간결하고 읽기 쉬움 (유지보수 굿)
댓글남기기