본문 바로가기
SpringBoot Server/API

SQL 대신 자바로 DB와 상호작용하기 JPA(Java Persistence API)

by ssury94 2025. 1. 5.

 

 
 
 

 

 

 

지금까지는 JDBC를 활용하여 DAO 클래스에서 SQL쿼리를 통해 DB 작업을 하였는데요.

//sql을 통한 회원가입 처리
@Repository
public class UserDAO {
    @Autowired
    JdbcTemplate jdbcTemplate;

    // todo 회원가입
    public int signUp(UserRequest userRequest) {
        String sql = "INSERT INTO `user` (email,password,nickname)\n" +
                "values (?,?,?);";
        System.out.println("UserDAO signUp");
        return jdbcTemplate.update(sql, userRequest.email, userRequest.password, userRequest.nickname);
    }
}

 

JPA을 이용하면 SQL 쿼리 대신, 자바 객체를 이용해 데이터 처리가 가능합니다.

 

 

JPA란?

Java Persistence API (자바 영속성 API)

 

Persistence - 영속성, 지속성

데이터를 생성한 프로그램이 종료되어도 사라지지 않는다는 뜻 = 메모리가 아닌, DB에 저장됨

 

자바를 통해 데이터를 관계형 데이터베이스에 저장, 검색, 업데이트 및 삭제 관리하는 API 입니다.

JPA 의 특징

  • 객체-관계 매핑 (ORM-Object Relational Mapping)
    JPA는 자바 객체와 데이터베이스 테이블 간의 매핑을 자동으로 처리합니다. 이를 통해 개발자는 복잡한 SQL 쿼리 대신 자바 객체를 통해 데이터를 조작할 수 있습니다.
  • 생산성 향상
    JPA를 사용하면 CRUD(Create, Read, Update, Delete) 작업을 간편하게 수행할 수 있습니다. 메서드 이름만으로 필요한 쿼리를 자동 생성하므로, 개발자는 SQL 작성에 시간을 들이지 않아도 됩니다.
  • 유지보수성 개선
    객체 지향적인 코드 작성이 가능해져 애플리케이션의 유지보수가 더욱 용이해집니다.
  • 데이터베이스 독립성
    특정 데이터베이스에 종속되지 않는 코드 작성이 가능합니다.
  • 객체 지향 프로그래밍 지원
    JPA 활용을 통해 다형성, 상속등의 OOP특성을 이용하여 유연하고 확장가능한 애플리케이션 개발이 가능합니다.

JPA의 한계

복잡한 쿼리 처리의 불편함
조인 연산이 많을 경우 성능 저하가 발생할 수 있습니다.

 

JPA 사용 예시

메모를 저장할 수 있는 간단한 JPA를 만들어보겠습니다.

 

application.yml 설정하기

spring:
  datasource:
    url: 
    username: 
    password: 
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        format_sql: true
        jdbc:
          time_zone: UTC

ddl-auto: update = DB에 데이터 추가

JPA 의존성 추가

 

Spring Data JDBC대신 Spring Data JPA를 추가합니다.

데이터 유효성을 검사하는 Validation 의존성도 추가하였습니다.

 

entity 패키지에 Memo 엔티티클래스 만들기

@Entity
@Table(name = "memo")
//DB에 저장될 테이블명을 memo 로 설정
public class Memo {
    @Id
    @GeneratedValue(generator = "increment")
    public Long id;
    // @Id 는 기본키를 나타내는 어노테이션 Pk 와 같음
    // @GeneratedValue 는 기본키의 값을 자동으로 생성하는 어노테이션 (generator = "increment")

    @Column(length = 200, nullable = false)
    public String content;
    // length 는 최대 길이를 나타냄 varchar(200) 과 같음

    @Column(nullable = false)
    public Instant createdAt;
    // Instant 는 날짜와 시간을 나타내는 클래스

    @PrePersist
    public void prePersist() {
        createdAt = Instant.now();
        // Instant.now() 는 현재 시간을 나타내는 객체를 반환
    }

}

 

entity 패키지의 클래스들은 DB의 테이블과 컬럼 역할을 수행합니다.

DB 컬럼에서 적용했었던 Primery key와 auto increment , not null, default값 now() 등 도 자바로 설정이 가능합니다.

@Column(
    name = "user_name",           // 컬럼 이름
    length = 100,                 // 문자 길이
    nullable = false,             // NULL 허용 여부
    unique = true,                // 유니크 제약조건
    updatable = false,            // 수정 가능 여부
    columnDefinition = "TEXT"     // 컬럼 정의
)
private String name;

 

@Entity 클래스는 기본 생성자가 필수 (JPA 규약) 니 잊지 맙시다.

 

repository 패키지에 MemoRepository클래스 만들기

JDBC에서는 DAO 클래스의 sql문을 통해 DB와 상호작용하였었죠.

JPA에서는 JpaRepository 인터페이스를 상속받는 Resporitory 클래스를 통해 DB와 상호작용 합니다.

상속받은 메서드를 통해 CRUD 작업이 가능합니다.

@Repository
public interface MemoRepository extends JpaRepository<Memo, Long> {
    // <테이블 클래스, Id 컬럼의 타입>
    // JpaRepository 를 상속받으면 CRUD 가 가능해짐

}

 

상속으로 사용가능한 JPA 제공 메서드들

Create 메서드 사용해보기

 

postman으로

{
"content" : "안녕하세요."
}
를 리퀘스트하면 컨텐트가 저장되도록 해보겠습니다.
 
 

리퀘스트를 담아올 DTO 클래스 준비

@Data
@NoArgsConstructor
@AllArgsConstructor

public class MemoRequest {

    public String content;

}

 

서비스클래스

@Service
public class MemoService {

    @Autowired
    MemoRepository memoRepository;
    public void createMemo(MemoRequest memoRequest){
        // DTO 를 Entity 로 변환
        Memo memo = new Memo();
        memo.content = memoRequest.content;
        // JDBC에서는 DAO를 통해 DB에 저장하는 SQL쿼리가 필요했지만,
        // JPA에서는 상속된 JpaRepository.save 메서드를 통해 객체를 데이터베이스에 저장할 수 있다.
        memoRepository.save(memo);
    }
}

 

MemoRepository클래스에 상속된 JpaRepository.save 메서드를 통해 메모 객체를 저장합니다.

 

컨트롤러 클래스

@RestController
public class MemoController {
@Autowired
MemoService memoService;


    @PostMapping("/memo")
    public ResponseEntity<Void>createMemo(@RequestBody MemoRequest memoRequest) {
        memoService.createMemo(memoRequest);
        return ResponseEntity.status(201).build();

    }

}

 

결과를 클라이언트에게 반환합니다. (저장 성공시 201)

 

 

 

JPA와 JDBC의 비교

JPA (Java Persistence API):

  • 상속 및 메서드 호출: JPA는 JpaRepository와 같은 인터페이스를 통해 데이터베이스와 상호작용합니다. 개발자는 SQL 쿼리를 직접 작성할 필요 없이 메서드 호출만으로 CRUD 작업을 수행할 수 있습니다.
  • ORM: JPA는 객체 관계 매핑(ORM) 기술로, 객체와 데이터베이스 간의 매핑을 자동으로 처리합니다. 이를 통해 객체 지향적인 접근 방식으로 데이터베이스 작업을 수행할 수 있습니다.
  • JDBC 사용: JPA는 내부적으로 JDBC를 사용하여 데이터베이스와 상호작용하며, SQL 쿼리를 생성하고 실행하는 과정을 자동으로 처리합니다.

JDBC (Java Database Connectivity):

  • SQL 직접 작성: JDBC는 개발자가 SQL 쿼리를 직접 작성해야 하며, 데이터베이스와의 연결 및 트랜잭션 관리를 수동으로 수행해야 합니다.
  • 저수준 API: JDBC는 저수준의 데이터베이스 접근을 제공하므로, SQL에 대한 이해가 필요하며 코드가 복잡해질 수 있습니다.

 

JpaRepository 기본 제공 메서드

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // 기본 제공되는 메서드들
    
    // 저장 & 수정
    User save(User user);                        // 엔티티 저장 또는 수정
    List<User> saveAll(List<User> users);        // 여러 엔티티 저장
    
    // 조회
    Optional<User> findById(Long id);            // ID로 조회
    List<User> findAll();                        // 전체 조회
    List<User> findAllById(List<Long> ids);      // 여러 ID로 조회
    
    // 삭제
    void delete(User user);                      // 엔티티 삭제
    void deleteById(Long id);                    // ID로 삭제
    void deleteAll();                            // 전체 삭제
    
    // 기타
    long count();                                // 전체 개수 조회
    boolean existsById(Long id);                 // ID 존재 여부 확인
}