반응형

우선, JPA의 관계 매핑을 알아보기 전에 객체의 연관 관계와 테이블의 연관 관계의 차이를 알아봅시다.

 

테이블 : 외래키로 조인하여 연관된 테이블을 찾습니다.

객체 : 객체를 참조하여 연관된 객체를 찾습니다.

 

테이블은 조인을 통해 양방향 연관 관계 설정이 가능하지만 객체에는 양방향 연관 관계 설정이 없으므로 다른 두 객체에 각각 단방향 연관 관계를 설정해야 합니다.

 

게시판 예제를 통해 확인해봅시다.

 

Article

@Entity
public class Article {

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

    ...
}

Member

@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String memberId;

    private String memberPass;
    
    ...
}

 

게시판 객체와 회원 객체가 있을 때 게시판 객체에서는 회원 정보를 얻고 반대로 회원 객체에서는 회원이 작성한 모든 글을 가지고 오고 싶습니다.

 

@ManyToOne과 OneToMany

두 객체를 연결하기 위해 Article 객체 필드에 Member 객체를 만들었고, Member 객체 필드에는 List 타입의 Articles라는 필드를 만든 후 각각 @ManyToOne과 @OneToMany를 붙여줬습니다.

 

@ManyToOne : 다대일, 한 명의 회원이 여러 게시글을 작성할 수 있으므로 게시글(Article) 기준으로 @ManyToOne을 선언합니다.

@OneToMany : 일대다, 회원 한 명이 게시글을 여러 개 작성할 수 있으므로 회원(Member) 기준으로 @OneToMany를 선언합니다.

 

@Entity
public class Article {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;
    
    private String content;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member member;

    ...
}


@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String memberId;

    private String memberPass;
    
    @OneToMany(mappedby = "member")
    private List<Article> articles = new ArrayList<Article>();
    
    ...
}

 

@ManyToOne(fetch = FetchType.LAZY) : fetch로 로딩 전략을 설정합니다. FetchType.LAZY는 지연 로딩을 의미합니다.

@OneToMany(mappedby = "member") : 일대다 관계를 연결할 상대 객체의 @JoinColumn이 선언된 필드값을 적습니다.

 

mappedby란?

JPA로 연관 관계를 설정할 때 가장 혼동하기 쉬운 부분인데 mappedby는 연관 관계 객체의 반대 객체에 선언합니다.

 

연관 관계 주인 : 외래키가 있는 객체

* 예시에서는 외래키가 Article에 있으므로 Article이 연관 관계의 주인

 

즉, 위 예시에서는 Article과 Member 중에서 Article이 외래키 member_id 라는 컬럼에 해당하는 member를 갖고 있으므로 Article이 연관 관계의 주인입니다.

 

따라서 연관 관계 주인 Article과 연관 관계로 설정된 Member 객체의 articles 필드 위 @OneToMany 안에 (mappedby를 넣고 연관 관계 주인 객체의 연결 필드명 "member"를 넣어주시면 됩니다.

 

처음 해보시면 되게 혼란스러우실 수 있는데 우리나라 JPA 최고 권위자이신 김영한 선생님의 강의 영상 첨부하니 확인해보세요!

 

반응형

'Java Web > JPA' 카테고리의 다른 글

[JPA] persistence.xml 작성 예시  (0) 2022.03.02
[JPA] 연관 관계 편의 메소드  (0) 2022.02.28
[JPA] Auditing 정리  (0) 2022.02.14
[JPA] findAll 정렬  (0) 2021.07.23
[JPA] JPA란?  (0) 2021.05.13
반응형

JPA Auditing

Java 표준 ORM 기술 JPA를 사용할 때 데이터 생성일자, 수정일자 등 어떤 테이블이든 공통으로 갖고 있는 컬럼을 관리하기 위해 제공하는 기능입니다. 이 기능을 사용하면 테이블과 매핑하여 데이터 입력 시 자동으로 시간을 관리할 수 있습니다.

 

BaseTimeEntity 생성

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

}

 

@MappedSuperClass : JPA에서 관리하는 Entity 클래스가 해당 추상 클래스를 상속하면 createdDate와 updateDate를 컬럼으로 인식하게 해 줍니다.

 

@EntityListeners(AuditingEntityListener.class) : 해당 클래스가 Auditing 기능을 포함하게 됩니다.

 

@CreatedDate : 데이터가 입력될 때 자동으로 시간을 저장해줍니다.

 

@LastModifiedDate : 데이터의 값이 변경될 때 자동으로 시간을 저장해줍니다.

 

Article

@Getter
@NoArgsConstructor
@Entity
public class Member extends BaseTimeEntity {

    ... 내용 생략

}

 

위처럼 @Enitity로 지정된 클래스가 BaseTimeEntity를 상속받으면 사용 가능합니다. 

 

BoardApplication

@EnableJpaAudithing
@SpringBootApplication
public class BoardApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
}

 

마지막으로 해당 프로젝트의 Application 클래스에 @EnableJpaAuditing 어노테이션을 추가함으로써 어플리케이션 내에서 Auditing 기능을 활성화할 수 있습니다.

 

UserRepositoryTest

@SpringBootTest
public class MemberRepositoryTest {

    @Autowired
    private UserRepository UserRepository;

    @Test
    public void insert() {
        String userId = "test01";

        // 실행 시간보다 전으로 임의 시간 지정
        LocalDateTime currentTime = LocalDateTime.of(2022, 2, 14, 0, 0, 0);

        User user = User.builder()
                .userId(userId)
                .userPass("test01")
                .userName("test01")
                .userEmail("test01@gmail.com")
                .build();

        userRepository.save(user);

        // 11행에서 선언한 currentTime보다 테이블에서 새로 입력된 값의 시간이 뒤인지 확인
        assertThat(userRepository.findAll().get(0).getCreatedDate()).isAfter(currentTime);

    }
}

 

Test 클래스를 하나 만들어서 board 테이블에 실행한 시간을 잘 저장했는지 테스트해봅니다.

 

MySQL Workbench에서 확인한 결과 작성일자와 수정일자이 현재 시간으로 저장된 것을 볼 수 있습니다.

반응형

'Java Web > JPA' 카테고리의 다른 글

[JPA] persistence.xml 작성 예시  (0) 2022.03.02
[JPA] 연관 관계 편의 메소드  (0) 2022.02.28
[JPA] 양방향 관계 매핑 @OneToMany, @ManyToOne 그리고 mappedby  (1) 2022.02.16
[JPA] findAll 정렬  (0) 2021.07.23
[JPA] JPA란?  (0) 2021.05.13
반응형

findAll의 매개변수로 Sort.by(Sort.Direction.DESC, "id")

 

이런 식으로 넘겨주면 id 컬럼을 기준으로 내림차순 정렬해줍니다. 

public List<Article> articleList() {
        List<Article> list = articleRepository.findAll(Sort.by(Sort.Direction.DESC, "id"));
        return list;
}
반응형

'Java Web > JPA' 카테고리의 다른 글

[JPA] persistence.xml 작성 예시  (0) 2022.03.02
[JPA] 연관 관계 편의 메소드  (0) 2022.02.28
[JPA] 양방향 관계 매핑 @OneToMany, @ManyToOne 그리고 mappedby  (1) 2022.02.16
[JPA] Auditing 정리  (0) 2022.02.14
[JPA] JPA란?  (0) 2021.05.13
반응형

JPA

JPA는 Java Persistence API의 약자로 자바 영속성 API라는 의미를 가지고 있다.

JPA 특징

1) 데이터를 객체 지향적으로 관리할 수 있어 개발자가 비즈니스 로직에 집중할 수 있다.

2) 자바 객체와 DB 테이블 사이의 매핑 설정을 통해 SQL을 생성한다.

3) 객체를 통해 쿼리를 작성할 수 있는 JPQL(Java Persistence Query Language)를 지원한다.

4) JPA는 성능 향상을 위해 지연 로딩이나 즉시 로딩과 같은 몇 가지 기법을 제공하는데 이것을 잘 활용할 경우 SQL을 직접 사용하는 것과 유사한 성능을 얻을 수 있다.

엔티티(Entity)

JPA 사용을 위한 Entity 객체

 

JPA가 자동으로 생성한 user 테이블

JPA에서 Entity는 데이터베이스와 통신하기 위한 객체로 필요에 따라서는 JPA 스스로 Entity에 맞는 테이블을 생성할 수도 있다.

레포지토리(Repository)

JPA의 강점은 myBatis처럼 일일이 Mapper와 Mapper 내의 SQL문을 정의하지 않고도 데이터베이스에 접근 가능하도록 메소드를 제공한다는 것이다. JpaRepository를 상속받은 인터페이스는 데이터베이스에 접근하여 기본적이 CRUD 기능을 수행할 수 있다. 다음은 JpaRepository에서 제공하는 몇 가지 메소드와 그에 따른 기능이다.

 method  기능
 save()  레코드 저장 (insert, update)
 findOne()  primary key로 레코드 한건 찾기
 findAll()  전체 레코드 불러오기. 정렬(sort), 페이징(pageable) 가능
 count()  레코드 갯수
 delete()  레코드 삭제

Query Method를 통해 필요로 하는 조회 및 삭제, 수정 등이 가능하다.

JPA에서 기본적으로 CRUD 기능을 제공하고 있지만 필요에 따라서 쿼리 메소드를 생성하여 필요에 맞게 데이터를 조회하거나 삭제, 수정을 할 수 있다는 강점이 있다. 물론 myBatis와 다르게 일일이 SQL을 정의하지 않아도 JPA 규칙에 따라 메소드 생성을 하면 메소드 이름에 맞게 쿼리문을 수행한다.

JPA VS myBatis

대한민국을 기준으로 한 JPA와 myBatis 검색량 비교

구글 트렌드에서 검색한 myBatis와 JPA의 조회량을 비교하면 국내에서는 지난 10년간 myBatis가 압도적이었으나 최근에는 비슷한 양상을 보이고 있다.

전세계를 기준으로 한 JPA와 myBatis 비교

하지만 전세계적으로 봤을 때 myBatis가 JPA를 앞선 적은 한 번도 없다. 한국에서는 긴 시간 myBatis를 사용하였기 때문에 유지 보수를 위해서라도 myBatis에 대한 학습이 어느 정도 필요할 지도 모르지만 앞으로는 개발자가 SQL보다 비즈니스 로직에 더 집중할 수 있는 환경을 제공하는 JPA가 국내에서도 myBatis를 앞지를 것으로 예상된다.

반응형

+ Recent posts