본문 바로가기
JAVA

[JPA] JPA의 정의와 Spring Data JPA와의 차이점

by 무사뎀벨레 2024. 3. 22.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

JPA (Java Persistence API)


JPA는 Java에서 제공하는 API, 관계형 데이터베이스 모델과 객체 모델 간의 패러다임 불일치를 해결해 주는 ORM 기술에 대한 표준 명세입니다.

 

Java에서 JPA에게 명령하면 JPA가 JDBC API를 사용하여 SQL을 만들어서 DB로 보내주는 기능을 합니다.

이 기능은 스프링에서 제공하는 것이 아닌 JAVA에서 제공하는 기능이며, 라이브러리가 아닌 인터페이스입니다.

 

ORM이란?
Object-relational mapping의 약자이며, 객체와 관계형 모델사이의 불일치가 존재할 수 있는데 이 부분을 ORM이 자동으로 매핑해 줍니다.

 

 

 

 

 

 

 

 

JPA의 장점


 

1. 객체 지향적인 코드를 구성하여 직관적이며, 비즈니스 로직에 더 집중

SQL Query를 구성하지 않고, 메서드로 데이터를 조작할 수 있기 때문에 객체 모델로 프로그래밍하는데 집중할 수 있습니다. 이런 이유로, SQL의 절차적인 방식이 아닌 객체 지향적인 접근을 하여 생산성이 증가합니다. 또한, 각종 객체에 대한 코드를 별도로 작성하여 코드의 가독성을 올려줄 수 있습니다.

 

2. 재사용 및 유지보수 간편

특정 컬럼의 변경이 생겼을 때, SQL은 모든 쿼리문을 수정해야 하지만, ORM은 특정 객체만 수정합니다. 맵핑 정보가 명확하여, ERD에 대한 의존도를 낮출 수 있습니다.

 

3. DBMS에 대한 종속성 축소

객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여, RDBMS의 데이터 구조와 Java의 객체지향 모델 사이의 간격을 좁힐 수 있습니다.

 

4. 도메인 주도 개발 가능

애플리케이션의 코드가 SQL 데이터베이스 관련 코드에 잠식당하는 것을 방지하고, 도메인 기반의 프로그래밍으로 비즈니스 로직을 구현하는데 집중할 수 있습니다.

 

5. SQL의 DDL작업을 JPA의 Entity 설정을 통해서 진행

아래 코드는 JPA를 통해 Entity설정을 하고 있습니다. @Id, @GeneratedValue를 통해 IDENTITY를 설정하고 @Column을 통해 title, content라는 컬럼을 생성할 수 있게 설정하였습니다.

@Entity
@Getter
Public class JPA_TEST1 {

	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column
    private String title;
    
    @Column
    private String content;

}

 

위와 같이 설정된 JPA의 코드를 다음과 같이 SQL Query를 따로 작성하지 않아도 SQL로 실행시켜 객체 지향적인 코드를 구성할 수 있다는 장점이 있습니다.

2024-03-21 10:46:51.115 DEBURG 65940 --- [            main] org.hibernate.SQL

		create table JAP_TEST1 {
                    id int8 generated by default as identity,
                     content varchar(225),
                     title varchar(225),
                     primary key (id)
                }
2024-03-21 10:46:51.119 DEBURG 65940 --- [            main] org.hibernate.SQL

 

ex) SQL의 DML작업을 JPA를 통해서 진행

@GetMapping("/member-list")
publick String memeberListView(Model model, Account account){

    SessionUser user = (Session)httpSession.getAttribute("user");
    account.setId(user.getId());
    model.addAttribute("memberList", memberService.getMemberList(account));
    
    return "view/memberList";

}

 

public List<Member> getMemberList(Member member){
	List<Member> memberList = memberRepository.findAllByAccount_idOrderByIdAsc(account.getId());    
        for(int i=0; i<memberList.getSize(); i++){
            if(memberList.get(i).getDelYn().equals("Y")){
                memberList.remove(i);
            }
        }
        return memberList;
}

위 코드는 JPA을 통해 DML작업을 수행하는 코드입니다. 회원 목록을 가져오는 페이지에서 회원 목록을 가져오기 위해 memberService라는 클래스에서 getMemberList라는 메서드를 실행합니다.

getMemberList라는 메서드에서는 memberRepository라는 클래스에서 findAllByAccount_idOrderByIdAsc라는 메서드를 실행합니다.

 

메서드가 길게 느껴질 수 있지만, 쿼리로 작성했을 때 굉장히 길게 나올 SQL에 비하면 비교적 짧다고 볼 수 있습니다.

위 메서드는 아래와 같이 간다히 설명할 수 있습니다.

findAll : 모든 컬럼

Account_id : Account_id라는 컬럼의 값으로 찾음

OrderByIdAsc : Id로 오름차순

 

위 findAllByAccount_idOrderByIdAsc메서드를 실행하게 되면, 아래와 같이 SQL문을 실행합니다.

JPA를 사용하지 않았다면, 다음과 같은 긴 SQL을 직접 작성해야 했을 겁니다.

2024-03-21 10:46:51.278 DEBURG 65940 --- [nio-8080-exec-9] org.hibernate.SQL

    select
        memberList0_.account_id as account_id1_3_,
        memberList0_.mem_name as mem2_3_,
        memberList0_.regDt as regDt3_3_
    from
    	memberList memberList0_
    left outer join
    	account account1_
    		on memberList0_.account_id=account1_.id
    where
    	account1_.id-=?
    order by
    	memberList0_.id asc
2024-03-21 10:46:51.288 TRACE 65940 --- [nio-8080-exec-9] o.h.type.descriptor.sql.BasicBinder
2024-03-21 10:46:51.296 DEBURG 65940 --- [nio-8080-exec-9] org.hibernate.SQL

where절의 account1_.id=? 에서의 ?는 파라미터로 받아온 값을 의미합니다. 위 예제를 기준으로 보면 findAllByAccount_idOrderByIdAsc메서드에 매개변수로 넣었던 account.getId()의 리턴값입니다.

 

이처럼 JPA를 사용하여 객체지향적인 코드를 작성할 수 있고 SQL은 작성하지 않기 때문에 로직에 더욱 집중할 수 있고, 짧은 코드를 통해 가독성을 향상할 수 있다는 장점이 있습니다.

 

 

 

 

 

 

 

 

 

 

JPA의 단점


ORM으로만 서비스를 구현하기 어렵습니다.

사용하기 편하지만, 설계는 어렵고 신중해야 하며, 잘못 구현하게 될 경우 속도 저하 및 일관성이 무너지는 문제점이 생길 수 있습니다. JPA, ORM의 지식이 풍부하다면 충분히 적용해 볼 수 있지만 그렇지 않다면 오히려 역효과가 나타날 수 있습니다.

 

 

 

 

 

 

 

 

 

 

주요 기능 및 사용 예제


 

JpaRepository Interface

JPA에 특화된 기능을 가진 인터페이스로서 Entity의 데이터를 조회하거나 저장, 변경, 삭제등의 기능이 있습니다.

 

Create

JpaRepository를 이용하여 테이블의 생성을 할 수 있습니다.

 

Select

JpaRepository를 이용하여 데이터를 조회할 수 있습니다.

 

insert

JpaRepository를 이용하여 데이터를 삽입할 수 있습니다.

 

update

JpaRepository를 이용하여 데이터를 수정할 수 있습니다.

 

delete

JpaRepository를 이용하여 데이터를 삭제합니다.

 

 

 

 

 

 

 

 

 

 

 

JPA와 Spring Data JPA의 차이점


Spring Data JPA는 Spring에서 제공하는 모듈 중 하나로, JPA 위에 추가적인 기능을 제공하여 JPA 기반 애플리케이션 개발을 보다 간편하게 만드는 라이브러리/프레임워크입니다.

 

이는 Repository라는 인터페이스를 제공함으로써 이루어집니다. 사용자가 Repository 인터페이스에 정해진 규칙대로 메서드를 입력하면, Spring이 알아서 해당 메서드 이름에 적합한 쿼리를 날리는 구현체를 만들어서 Bean으로 등록해 줍니다.

 

CRUD 연산, 페이징, 정렬과 같은 JPA 리포지토리를 구현하는 데 필요한 반복적인 코드 양을 줄이는 인터페이스와 클래스를 제공합니다. Spring Data JPA는 또한 쿼리 메서드와 사용자 정의 쿼리를 지원하며, Spring 표현 언어(SpEL)와 유사한 구문을 사용하여 작성할 수 있습니다.

 

 

 

 

 

 

 

 

 

 

Spring Data JPA 예


findAll()

모든 엔티티 조회

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

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

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

 

 

findById(Long id)

특정 id를 가진 엔티티 조회

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

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public Optional<User> findById(Long id) {
        return userRepository.findById(id);
    }
}

 

 

findByName(String name)

특정 이름으로 엔티티 조회

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

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public List<User> findByName(String name) {
        return userRepository.findByName(name);
    }
}

 

 

save()

엔티티 저장

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

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

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

 

 

update()

엔티티 업데이트

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

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public User update(User user) {
        return userRepository.update(user);
    }
}

 

 

delete()

엔티티 삭제

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

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void delete(User user) {
        userRepository.delete(user);
    }
}

 

 

페이지네이션 기능을 이용해 엔티티 조회

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

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public Page<User> findAll(Pageable pageable) {
        return userRepository.findAll(pageable);
    }
}

 

 

정렬 기능을 이용하여 엔티티를 조회하는 방법

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

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

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

 

 

Native Queries 네이티브 쿼리

public interface LikesRepository extends JpaRepository<Likes, Long> {
    @Modifying
    @Query(value = "INSERT INTO likes(image_id, user_id, created_date) VALUES(:imageId, :sessionId, now())", nativeQuery = true)
    int cLikes(@Param(value = "imageId") Long imageId, @Param(value = "sessionId") Long sessionId);
}

 

 

Named Queries 네임드 쿼리

@Entity
@NamedQuery(name = "Employee.findByLastName", query = "SELECT e FROM Employee e WHERE e.lastName = ?1")
public class Employee {
    // ...
}

 

 

Auditing 감사

엔티티를 생성하거나 업데이트할 때 자동으로 생성일과 수정일을 감사하는 방법을 보여줍니다.

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {
    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;
}
반응형

댓글