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 저장 및 조회 구조

image

image

🌲 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 구문 오류가 발생할 수 있다.
  • 데이터베이스 연결을 수동으로 관리해야 하며, 각 쿼리에 대해 PreparedStatementResultSet을 명시적으로 처리해야 한다.
  • 동일한 CRUD 작업을 여러 번 반복해야 하며, 코드가 중복될 수 있다.

MyBatis 사용

  • SQL을 XML 파일 또는 어노테이션으로 분리해 자바 코드와 분리 가능
  • SQL 쿼리 결과를 자바 객체로 자동으로 매핑 가능
  • SQL 쿼리를 직접 작성해야 하지만, 자바 코드와 분리할 수 있다는 장점.

JPA 사용

  • 데이터베이스의 테이블에 매핑된 자바 클래스를 정의하여 객체 지향적으로 데이터를 처리할 수 있음.
  • 리포지토리 패턴: JPA의 JpaRepository 인터페이스를 통해 CRUD 작업을 간편하게 수행할 수 있으며, 추가적인 쿼리 메서드를 쉽게 정의할 수 있음.
  • 데이터베이스와의 상호작용이 단순화되어 코드가 훨씬 간결하고 읽기 쉬움 (유지보수 굿)

댓글남기기