본문 바로가기
Java/ORM

JAVA ORM - JPA(기본 세팅, 간단한 JPA예제)

by 티코딩 2024. 7. 30.

본격적으로 QueryDsl을 써보기 전에 Java 표준 ORM인 JPA부터 자세히 파보고 해봐야겠다 생각이 들어 JPA강의를 샀다.

먼저 ORM이란? 객체 관계 매핑(Object-Relational Mapping, ORM)은 객체 지향 프로그래밍 언어를 사용하여 데이터베이스를 조작할 수 있게 해주는 기술이다. 데이터베이스 테이블을 객체로 매핑해서 쿼리를 작성하지 않고도 데이터를 다룰 수 있다는 것이다.

 

내가 여태껏 사용했던 Spring Data JPA와 그냥 JPA는 뭐가 다른걸까?

ㅇ JPA (Java Persistence API)

JPA는 Entity, Entity Manager, JPQL(Java Persistence Query Language) 등을 제공해 데이터베이스의 데이터를 객체로 매핑하고, 이를 통해 CRUD 작업을 수행할 수 있게 한다. JPA를 사용하려면 엔터티 클래스를 작성하고, 이를 매핑하기 위한 어노테이션을 추가해야 한다. 그리고 EntityManagerFactory와 EntityManager를 설정하여 데이터베이스 연결 및 트랜잭션 관리를 해야한다.

 

ㅇ Spring Data JPA

Spring Data JPA는 리포지토리 패턴을 도입해서 데이터 접근 계층을 추상화하고 인터페이스를 통해 데이터베이스 작업을 수행할 수 있다. 기본 CRUD 메소드와 커스텀 쿼리를 쉽게 정의할 수 있다는 장점이 있다. 리포지토리 인터페이스를 정의하면 Spring Data JPA가 런타임에 해당 인터페이스의 구현체를 자동으로 생성해 준다. Spring Boot와 함께 사용하면 데이터베이스 설정 및 JPA 설정이 자동으로 구성되서 편하다.

 

나는 여태껏 Spring Data Jpa만 사용해왔다. 

예를들어

 public interface UserRepository extends JpaRepository<User, Long> {
	List<User> findByUsername(String Username);
}

 

 

이런식으로 정의만 해두면 기본 JpaRepository에서 제공하는 기능들을 자동으로 사용가능한데, 

데이터를 저장할때,

//데이터 저장
User savedUser = userRepository.save(user);
//복수 데이터 저장
List<User> users = Arrays.asList(user1, user2, user3);
userRepository.saveAll(users);
//데이터 조회
Optional<User> user = userRepository.findById(1L);
//모든 엔티티 조회
List<User> users = userRepository.findAll();
//데이터 삭제
userRepository.deleteById(1L);
//존재여부
boolean exists = userRepository.existsById(1L);
//엔터티 개수
long count = userRepository.count();

이런식의 편리한 기능을 제공해준다.

 

 

ㅇ ORM을 사용하지않으면?

JDBC나 마이바티스 템플릿을 이용해서 sql을 한땀한땀 작성해야함.

(매우 번거로움)

 

실무에서는 객체와 테이블이 매우 복잡하므로 객체와 테이블을 잘 설계하고 매핑하는것이 중요하다. 

그리고 JPA의 내부동작 방식을 알아야 제대로 쓸 수 있다.

 

출처:https://dev-aiden.com/jpa/what-is-jpa/

결국 자바에서 DB와 통신하기위해선 JDBC를 사용해야하는데, JDBC API를 JPA가 대신 써준다는거다.

 

ㅇ JPA를 사용하는 이유

생산성증가 : JPA를 사용하면 자바 컬렉션에 저장하듯이 저장하게 되어 객체 지향적 설계를 그대로 유지하면서 데이터베이스 작업을 수행할 수 있게 해준다. 그래서 생산성이 증가하고 유지보수가 편해진다.

패러다임 불일치 : 그리고 패러다임 불일치 문제를 해결해주는데, 패러다임 불일치 문제는 객체 지향 프로그래밍과 관계형 데이터베이스 사이에는 몇 가지 근본적인 차이점이 존재하는데, 객체 지향 프로그래밍에서는 클래스와 객체를 통해 데이터를 모델링하고, 상속, 다형성 등의 개념을 사용하는데 반면 관계형 데이터베이스에서는 테이블, 행, 열을 통해 데이터를 저장하고, SQL을 사용해 데이터를 조작한다. 이러한 차이로 인해 발생하는 불일치다.

 

  • 객체-관계 매핑 문제: 객체의 속성은 데이터베이스의 컬럼과 매핑되지만, 객체 간의 관계는 데이터베이스의 외래 키와 매핑된다. 이는 객체 지향적 접근 방식과 관계형 데이터베이스 접근 방식 간의 불일치를 초래한다.
  • 상속 문제: 객체 지향 프로그래밍에서는 클래스 상속을 사용하지만, 관계형 데이터베이스는 이를 직접적으로 지원하지 않는다.
  • 동일성 문제: 객체 지향 프로그래밍에서는 동일한 객체를 참조하지만, 관계형 데이터베이스에서는 동일한 데이터를 여러 번 조회할 수 있다.

트랜잭션 관리 : JPA는 트랜잭션 관리를 쉽게 해줍니다. EntityManager를 사용하여 트랜잭션을 시작하고 커밋하거나 롤백할 수 있으며, Spring과 함께 사용할 때는 트랜잭션 관리가 더욱 간단해진다.

지연로딩 & 즉시로딩 : JPA는 지연 로딩(Lazy Loading)과 즉시 로딩(Eager Loading)을 지원하여 필요한 시점에 데이터를 로드할 수 있다. 이를 통해 애플리케이션의 성능을 최적화할 수 있다.

 

작동원리

Persistence 클래스가 우리가설정한 persistence.xml 설정파일을 읽고 EntityManagerFactory라는 클래스를 만들고 필요할때마다 EntityManager라는걸 만들어서 돌린다.

간단한 JPA 예제

public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction et = em.getTransaction();

        et.begin();
        try{
            Member member = new Member();
            member.setId(1L);
            member.setName("name");
            em.persist(member);
//            Member findMember = em.find(Member.class, 1L);
//            findMember.setName("name2");
//            em.remove(findMember);
            et.commit();
        } catch (Exception e){
            et.rollback();
        }finally {
            em.close();
        }
        emf.close();

    }
}

 

엔티티 매니저 팩토리, 엔티티 매니저,  엔티티 트랜잭션 객체하나씩 만들어주고, 트랜잭션을 시작해준다.

멤버를 하나 만들어주고 아이디와 이름을 설정해주고, 엔티티매니저객체로 persist로 저장해준다. 그리고 커밋해주고 엔티티매니저를 닫고 엔티티매니저 팩토리를 닫아주면, 데이터가 잘 저장된다.

이렇게 쿼리를 따로 작성하지 않아도 JPA가 쿼리를 촥촥 자동으로 작성해준다.

DB를 확인해보자.

잘 반영된게 보인다.

 

참고로 Member 클래스를 보면

@Entity
public class Member {
    @Id
    private Long id;
    private String name;

    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

 

@Entity, @Id 애너테이션을 붙혀줘야 JPA가 매핑해줄 수 있다.

 

JPQL을 사용하면?

List<Member> result = em.createQuery("Select m from Member as m", Member.class)
                    .getResultList();
            for(Member member : result ) {
                System.out.println("member = " + member.getName());
            }
            et.commit();

try문에 위에 코드를 넣는다. JPQL을 사용하려면 저렇게 쿼리를 직접 작성해야한다. 

JPQL은 엔티티 객체를 대상으로한 쿼리고, SQL은 데이터베이스 테이블을 대상으로 한 쿼리라는 다른점이 있다.

SQL을 추상화해 특정 데이터베이스 SQL에 의존하지않는다.

'Java > ORM' 카테고리의 다른 글

JAVA ORM - JPA(상속을 매핑?)  (0) 2024.08.05
JAVA ORM - JPA(연관관계 매핑2)  (0) 2024.08.03
JAVA ORM - JPA(연관관계 매핑)  (0) 2024.08.01
JAVA ORM - JPA(엔티티 매핑)  (0) 2024.07.31
JAVA ORM - JPA(영속성 컨텍스트)  (0) 2024.07.31