inblog logo
|
LifeLog, DevLog
    JPA

    알고 있어야할 어노테이션들 - JPA

    JPA
    KYJTHEYJ's avatar
    KYJTHEYJ
    Dec 25, 2025
    알고 있어야할 어노테이션들 - JPA
    Contents
    알고 있어야할 어노테이션들 - JPAJPA 관련

    알고 있어야할 어노테이션들 - JPA

    JPA 관련

    @Entity

    클래스가 DB의 테이블과 매핑되도록

    @Entity
    @Table(name = "users")
    public class User {
        @Id
        @GeneratedValue
        private Long id;
    
        private String name;
    }
    
    // @Id -> 기본키 선언
    
    // @GeneratedValue -> DB가 자동 생성
    // MySQL: IDENTITY -> MySQL은 시퀸스가 없음
    //@GeneratedValue(strategy = GenerationType.IDENTITY)
    
    // Oracle: SEQUENCE
    //@GeneratedValue(strategy = GenerationType.SEQUENCE)

    @Column

    필드의 컬럼 속성을 지정

    @Entity
    public class User {
    
        @Column(nullable = false, length = 50, unique = true)
        private String email;
    }
    
    // nullable == NOT NULL
    
    // 컬럼 속성 지정
    //    @Column(
    //        name = "user_name",        // 컬럼 이름
    //        nullable = false,          // NOT NULL
    //        unique = true,             // UNIQUE
    //        length = 50,               // VARCHAR(50)
    //        columnDefinition = "TEXT"  // 컬럼 타입 직접 지정
    //    )
    
    // 다만 BLOB이나 TEXT 타입 지정시는 따로 @Lob 라는 어노테이션 명시 필요
    
    // 조건 위반시 DataIntegrityViolationException 발생

    @Transient

    JPA 매핑 제외하기

    @Entity
    public class User {
        
        @Id @GeneratedValue
        private Long id;
        
        private String name;
        
        // DB에 저장 안 함 (계산 필드)
        @Transient
        private int age;
        
        @Transient
        private String tempData;
    }

    @Enumerated(EnumType.STRING)

    Enum 사용시 매핑

    public enum UserRole {
        ADMIN, USER, GUEST
    }
    
    @Entity
    public class User {
        
        @Id @GeneratedValue
        private Long id;
        
        // STRING 필수! (ORDINAL 금지) 
    	//-> ORDINAL은 선언된 순서를 Integer로 변환하여 저장함
        @Enumerated(EnumType.STRING)
        @Column(length = 20)
        private UserRole role;
    }
    
    // DB 저장: "ADMIN", "USER", "GUEST"

    @OneToMany, @ManyToOne, @OneToOne ***

    연관 관계의 표시, 좀 더 정리했다

    @OneToMany 는 주인이 아닌 쪽에 사용

    @ManyToOne 이 제일 중요하고 모든 것을 사실 ManyToOne으로 정의 가능하다
    DB 상에서 조인 관계를 생각해보면 대부분이 N : 1이기 때문

    // User (1) : Order (N) -> 양방향
    @Entity
    public class User {
        @Id
        private Long id;
    
    	// 추가로 (cascade = CascadeType.XXX, orphanRemoval = true)
    	// 선언시 Order 도 Delete cascade로 전부 삭제
    	@OneToMany(mappedBy = "user") // 이 user는 주인이 아님을 명시 ***
        private List<Order> orders = new ArrayList<>();
    }
    
    @Entity
    public class Order {
        @Id
        private Long id;
    
    	// 이 user 가 주인임을 명시, 즉 FK가 있다면 관리
    	// Optional -> 연관관계에 따라 left join, inner join 선택됨
    
    	// true는 기본값, 객체의 null을 허용하므로 left join 조회 방식 쿼리 발생
    	// false 시 객체가 null이 아님을 명시하는 것이므로 안되므로 
    	// inner join 활용됨
    
    	// Optional 은 JPA 가 쿼리를 생성할 때 영향을 끼침
    	// nullable 과 같은 옵션으로 사용하는게 좋음
    	@ManyToOne(fetch = FetchType.LAZY, optional = false)
        @JoinColumn(name = "user_id", nullable = false) // 주인 명시 ***
        private User user;
    }
    
    // --------
    
    // Member (1) : Locker (1) -> 1대1 관계
    @Entity
    public class Member {
        
        @Id @GeneratedValue
        private Long id;
        
        private String name;
        
        // 1:1 관계
        @OneToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "locker_id", unique = true) // -> 주인
        private Locker locker;
    }
    
    // ========== 주인 아닌 쪽 ==========
    @Entity
    public class Locker {
        
        @Id @GeneratedValue
        private Long id;
        
        private String number;
        
        // 1:1 관계 (주인 아님)
        @OneToOne(mappedBy = "locker") // -> mappedBy? 주인 아님
        private Member member;
    }
    
    // ----------
    
    // Member(N) : Team(1) -> 단방향 관계 *************
    // 다수에서 단일을 참조하는 관계
    @Entity
    public class Member {
    	@Id @GeneratedValue
    	private Long id;
    	private String name;
    	
    	@ManyToOne(fetch = FetchType.LAZY)
    	@JoinColumn(name = "team_id")
    	private Team team;
    }
    
    @Entity
    public class Team {
    	@Id @GeneratedValue
    	private Long id;
    	private String name;
    }

    • FetchType.LAZY와 EAGER 의 전략 선택이 있는데
      FetchType.LAZY 만 우선 사용해야 한단 것을 알아두자

    • Proxy의 개념을 알고 있어야 둘의 차이에 대해 기술할텐데..
      정말 간단히 적어보면 원래 객체는 연관된 객체 간의 탐색이 자유롭지만
      데이터베이스에 매핑된 엔티티 객체는 데이터베이스 처럼 조인을 해야
      연관된 엔티티 객체를 탐색할 수 있다


      이러면 연관 관계가 있다고 해도,
      연관 관계 테이블을 사용하지 않을 경우에도 굳이 탐색을 위해서
      모든 걸 조인 조회를 해야할까? 에서 시작된 실제 사용하는 시점에
      데이터베이스에서 조회할 수 있도록 해주는 것이 Proxy 다
      → 실제 엔티티 객체 대신 데이터베이스 조회를 지연(LAZY)할 수 있는 가짜 객체

      비유적으로 풀어보자면,
      책의 목차만 가져와서 이 책엔 이러한 내용이 있습니다 라고 말해주는 객체

    • EAGER는 왜 사용하지 않을까?

      • 대표적인 케이스만 설명하자면…

        @Entity
        public class Post {
            @Id @GeneratedValue
            private Long id;
        
            private String title;
        
            @ManyToOne(fetch = FetchType.EAGER)
            @JoinColumn(name = "author_id")
            private Member author;
        
            @OneToMany(mappedBy = "post", fetch = FetchType.EAGER)
            private List<Comment> comments = new ArrayList<>();
        }
        
        // --------------------
        // postRepository.findAll(); 실행
        // -> select * from posts;
        
        // EAGER 이므로 member의 author_id 들의 연관 관계를 즉시 표현해야하므로
        // -> select author_id from members where id = :id; 
        // 즉, 게시글의 작성자를 게시글 수 만큼 조회
        
        // EAGER 이므로 post의 comment 들의 연관 관계도 즉시 표현해야하므로
        // -> select * from comment where post_id = :post_id; 
        // 즉, 게시글의 아이디로 게시글의 모든 댓글 정보를 조회
        • EAGER는 위에서 언급한 연관관계이지만 연관관계가 맺어진 테이블을
          사용하지 않아도 그 테이블의 모든 정보를 일일히 조회하여 갖고 있어야 한다
          → 단순하게 select 하나 했는데 N번의 조회가 더 이뤄질 수 있다는 말

        • EAGER는 2001년에 개발되었는데 그 때는 데이터의 규모가
          이렇게 까지 크지 않고 인터넷 속도도 느려 어느 정도 조절하며
          사용이 가능했지만 현대에선 이런 연속된 조회로 DB 전체가 조회 될 수
          있으니 소규모의 운영이 아닌이상 사장되어 있음

    @JoinColumn

    외래키 설정에 사용 (주인 쪽에 사용)

    @Entity
    public class Order {
        
        @Id @GeneratedValue
        private Long id;
        
        @ManyToOne
        @JoinColumn(
            name = "member_id",                  // 외래키 컬럼명
            nullable = false,                    // NOT NULL
            unique = false,                      // UNIQUE 여부
            insertable = true,                   // INSERT 가능
            updatable = true,                    // UPDATE 가능
            columnDefinition = "BIGINT",         // 컬럼 타입
            foreignKey = @ForeignKey(
                name = "fk_order_member",        // FK 제약조건 이름
                value = ConstraintMode.CONSTRAINT // FK 제약조건 생성
            )
        )
        private Member member;
    }

    @ForeignKey

    외래키의 제약조건 설정

    @ForeignKey ( 
    	name = "fk name" // fk 이름 설정
    	, value = ConstraintMode.CONSTRAINT // 물리적 FK
    )
    
    // ConstraintMode.CONSTRAINT -> 물리적 FK (FK가 실제 DB 생김)
    // ConstraintMode.NO_CONSTRAINT -> 논리적 FK

    @EmbeddedId

    복합 키 일 때 사용, 따로 클래스를 만들어줘야함

    // ========== 복합 키 클래스 ==========
    @Embeddable // 같이 사용해야함
    @Getter @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    @EqualsAndHashCode // 같이 사용해야함
    // 복합키 일때는 Serializable 도 사용해야함
    public class OrderProductId implements Serializable {
        
        private Long orderId;
        private Long productId;
    }
    
    // ========== Entity ==========
    @Entity
    public class OrderProduct {
        
        @EmbeddedId
        private OrderProductId id;
        
    	// @MapsId 복합키의 일부를 외래키로 매핑해주는 어노테이션
        @MapsId("orderId") // id.orderId와 매핑
        @ManyToOne
        @JoinColumn(name = "order_id")
        private Order order;
        
        @MapsId("productId") // id.productId와 매핑
        @ManyToOne
        @JoinColumn(name = "product_id")
        private Product product;
        
        private Integer quantity;
    }
    Share article
    Contents
    알고 있어야할 어노테이션들 - JPAJPA 관련

    LifeLog, DevLog - https://github.com/KYJTHEYJ

    RSS·Powered by Inblog