Hello

JPA란? (feat. ORM)

by 볼빵빵오춘기

JPA (Java Persistence API)

JPA는 자바 애플리케이션에서 데이터베이스와 상호작용하는 방식을 표준화한 것 이다.

이를 통해 개발자는 데이터베이스 테이블을 자바 객체에 매핑하고, 객체지향적으로 데이터베이스를 다룰 수 있다.

JPA는 인터페이스의 모음으로, JPA를 구현한 ORM 프레임워크 (예: Hibernate)를 사용하여 실제 동작을 수행한다.

 

ORM (Object-Relational Mapping)

ORM은 객체 지향 프로그래밍 언어의 객체를 관계형 데이터베이스의 테이블에 매핑하는 기술이다.

이를 통해 객체 지향 프로그래밍 패러다임을 유지하면서 데이터베이스 작업을 할 수 있다.

ORM은 데이터베이스의 구조와 객체 지향 언어의 구조 간의 불일치를 해결해주어, 개발자가 객체 모델을 통해 데이터베이스를 직관적으로 다룰 수 있게 한다.

 

ORM의 장점

객체 지향 프로그래밍

객체 모델을 사용하여 데이터베이스와 상호작용하므로, 객체 지향 프로그래밍 패러다임을 유지할 수 있다.

객체와 데이터베이스 간의 불일치를 해결하여, 개발자는 더 직관적이고 유지 보수가 쉬운 코드를 작성할 수 있다.

 

자동화된 매핑

  • 엔티티 클래스를 데이터베이스 테이블에 자동으로 매핑하여, 수작업으로 SQL을 작성하는 번거로움을 줄여준다.
  • 매핑 설정을 통해 객체와 데이터베이스 필드 간의 변환을 자동으로 처리한다.

 

데이터베이스 독립성

JPA를 사용하면 특정 데이터베이스에 종속되지 않고, 다른 데이터베이스로 쉽게 변경할 수 있다.
⇒ 애플리케이션 이식성이 높아진다.

 

생산성 향상

  • CRUD(Create, Read, Update, Delete) 작업을 간편하게 처리할 수 있는 API를 제공하여, 개발 생산성을 크게 향상시킨다.
  • 복잡한 쿼리도 메서드 이름만으로 생성할 수 있으며, 필요 시 JPQL(Java Persistence Query Language)을 사용하여 직접 작성할 수 있다.

 

트랜잭션 관리

JPA는 트랜잭션 관리를 자동으로 처리해주어, 일관성과 무결성을 유지하면서 데이터베이스 작업을 수행할 수 있다.
⇒ 개발자는 비즈니스 로직에 집중할 수 있다.

 

 

캐시 및 성능 최적화

  • JPA는 1차 캐시(엔티티 매니저 레벨)와 2차 캐시(애플리케이션 레벨)를 제공하여 데이터베이스 액세스를 최소화하고 성능을 최적화한다.
  • 지연 로딩(Lazy Loading)과 즉시 로딩(Eager Loading)을 통해 필요할 때만 데이터를 가져올 수 있다.

 

예시코드

User 엔티티를 정의하고, JPA를 통해 데이터베이스 작업을 수행한다.

JPA를 사용하기위해서는 JPA 의존성 추가를 해야하며, 데이터베이스 연결 설정을 해주어야 한다.

 

JPA 의존성 추가

  • build.gradle (Gradle을 사용하는 경우)
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'com.h2database:h2' // H2 데이터베이스를 사용할 경우, 실제 사용하는 데이터베이스 드라이버로 변경
}

 

  • pom.xml (Maven을 사용하는 경우)
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

 

엔티티 클래스

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    // Getters and Setters
}

 

리포지토리 인터페이스

리포지토리를 만들 때 JpaRepository를 상속 받아야한다.

타입을 적어주는 부분(<>)에 첫 번째 타입은 entity를 통해 만들어서 DB와 매핑되는 그 entity 클래스를 써주고, 두번째 타입에는 그 entity에 pk값을 넣어주어야한다. 

public interface UserRepository extends JpaRepository<User, Long> {
}

 

서비스 클래스

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    public User createUser(User user) {
        return userRepository.save(user);
    }

    public User updateUser(Long id, User userDetails) {
        User user = userRepository.findById(id).orElse(null);
        if (user != null) {
            user.setName(userDetails.getName());
            user.setEmail(userDetails.getEmail());
            return userRepository.save(user);
        }
        return null;
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

 

컨트롤러 클래스

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        if (user != null) {
            return ResponseEntity.ok(user);
        }
        return ResponseEntity.notFound().build();
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
        User updatedUser = userService.updateUser(id, userDetails);
        if (updatedUser != null) {
            return ResponseEntity.ok(updatedUser);
        }
        return ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return ResponseEntity.noContent().build();
    }
}

 

정리

위의 예시코드를 보면 JPA를 통해 데이터베이스 작업을 추상화하고, ORM의 장점을 살려 객체 지향적으로 데이터를 처리한다.

⇒ 개발자는 더 직관적이고 유지 보수하기 쉬운 코드를 작성할 수 있으며, 데이터베이스와의 상호작용을 보다 효율적으로 관리할 수 있다.

 


참고 링크

https://ccomccomhan.tistory.com/131

https://dbjh.tistory.com/77

https://f-lab.kr/insight/understanding-jpa-and-spring-data-jpa-20240525?gad_source=1&gclid=CjwKCAjw7s20BhBFEiwABVIMrVHzDAIp-K_u0G04i-g4PBy2ylCZFokkSv6lRQgsm1ix1UwD8ELXIxoC2sYQAvD_BwE

 

 

 

블로그의 정보

Hello 춘기's world

볼빵빵오춘기

활동하기