MyBatis
정의 | SQL Mapper의 한 종류로, JDBC 프로그래밍을 단순화하고 SQL과 코드를 분리. |
주요 목적 | - 반복되는 JDBC 코드 간소화- SQL과 애플리케이션 코드를 분리 관리. |
특징 | - SQL 쿼리 중심의 데이터 처리.- XML 파일과 어노테이션으로 SQL 관리. |
장점 | - SQL 작성에 유연성.- 빠른 개발 가능.- 코드와 SQL 분리로 유지보수 용이. |
한계점 | - SQL을 직접 작성해야 하므로 피로도 증가.- CRUD 코드 반복 발생.- DB 및 테이블에 종속적. |
적합한 경우 | - 데이터베이스 중심의 애플리케이션.- SQL에 세부적인 제어가 필요한 프로젝트. |
부적합한 경우 | - 객체 중심 설계(OOP)가 중요한 프로젝트.- 테이블 변경이 잦은 프로젝트. |
MyBatis 의 동작
(1) ~ (3)은 응용 프로그램 시작시 수행되는 프로세스입니다.
(1) 응용 프로그램이 SqlSessionFactoryBuilder를 위해 SqlSessionFactory를 빌드하도록 요청합니다.
(2) SqlSessionFactoryBuilder는 SqlSessionFactory를 생성하기 위한 Mybatis 구성 파일을 읽습니다.
(3) SqlSessionFactoryBuilder는 Mybatis 구성 파일의 정의에 따라 SqlSessionFactory를 생성합니다.
(4) ~ (10)은 클라이언트의 각 요청에 대해 수행되는 프로세스입니다.
(4) 클라이언트가 응용 프로그램에 대한 프로세스를 요청합니다.
(5) 응용 프로그램은 SqlSessionFactoryBuilder를 사용하여 빌드된 SqlSessionFactory에서 SqlSession을 가져옵니다.
(6) SqlSessionFactory는 SqlSession을 생성하고 이를 애플리케이션에 반환합니다.
(7) 응용 프로그램이 SqlSession에서 매퍼 인터페이스의 구현 개체를 가져옵니다.
(8) 응용 프로그램이 매퍼 인터페이스 메서드를 호출합니다.
(9) 매퍼 인터페이스의 구현 개체가 SqlSession 메서드를 호출하고 SQL 실행을 요청합니다.
(10) SqlSession은 매핑 파일에서 실행할 SQL을 가져와 SQL을 실행합니다.
- SqlSession Factory Builder (1), (2), (3)
- MyBatis 설정 파일을 읽어와서
- 설정정보 기반으로 SqlSession Factory 를 생성하는 빌더 객체
- MyBatis Config File (2)
- 매핑해줄 객체가 들어있는 패키지 경로와
- Mapping File 목록을 지정해주는 설정 파일
<!-- /resources/mybatis-config.xml -->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.thesun4sky.querymapper.domain"/>
</typeAliases>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
- SqlSession Factory (3), (5), (6)
- 설정에 맞게 SqlSession 을 생성하여 들고있는 객체
- SqlSession (6), (7), (9), (10)
- Mapping File 에서 쿼리를 조회해서 쿼리를 수행하고 응답을 받아올 수 있는 세션 객체
- Mapper Interface (8), (9)
- DB 에서 조회하는 객체와 Java 프로그램의 객체간에 인터페이스를 정의하는 객체
- 방법1. Dao 클래스 정의
- SqlSession 를 직접적으로 사용하는 방법
- SqlSession 멤버 변수로 사용하며 쿼리파일 수행 요청
// UserDao.java
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Component;
import com.thesun4sky.querymapper.domain.User;
@Component
public class UserDao {
// SqlSession 멤버 변수로 사용하며 쿼리파일 수행 요청
private final SqlSession sqlSession;
public UserDao(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public User selectUserById(long id) {
return this.sqlSession.selectOne("selectUserById", id);
}
}
- 장점
- 쿼리문 실행 전에 넣어줄 매개변수와 쿼리 결과값의 변형을 정의할 수 있다.
- Namespace를 내 마음대로 둘 수 있다.
- .xml 파일의 쿼리문 id와 mapper 메소드명을 일치시킬 필요가 없다.
- 단점
- Sqlsession 객체를 주입받아야 하며, 쿼리문 실행 시 항상 호출해야 한다.
- 쿼리문 호출 시 sqlsession에 .xml 파일의 namespce와 쿼리문 id를 매개변수로 넘겨야한다.
- 방법2. Mapper Interface 정의
- SqlSession 를 간접적으로 사용하는 방법
- ibatis 에서 구현해주는 org.apache.ibatis.annotations.Mapper 어노테이션을 사용하면 sqlSession 를 사용하여 자동으로 호출해줌
// UserMapper.java
@Mapper
public interface UserMapper {
User selectUserById(@Param("id") Long id);
}
- 장점
- 메소드의 내부 구현이 불필요하다.
- Sqlsession 객체 주입이 불펼요하다.
- .xml 파일의 쿼리문 id와 mapper 메소드 명이 일치한다.
- 단점
- .xml의 Namespace가 실제 Mapper.java 위치를 가르켜야 한다.
- 메소드 내부 정의가 불가능하다.
- Mapping File (10)
- SqlSession 가 실행하는 쿼리가 담긴 파일
- 정의된 인터페이스에 기반해서 수행할 쿼리를 담아두고
- 쿼리 수행결과를 어떤 인터페이스 매핑할지 정의해놓은 파일
<!-- UserMapper.xml -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.thesun4sky.querymapper.mapper.UserMapper">
<select id="selectUserById" resultType="User">
select id, name from users where id = #{id}
</select>
</mapper>
쿼리 코드 만들기 (JpaRepository)
QueryMapper 의 DB의존성 및 중복 쿼리 문제로 ORM 이 탄생했다.
- ORM 은 DB의 주도권을 뺏어왔다고 표현해도 과언이 아닙니다.
- ORM 은 DAO 또는 Mapper 를 통해서 조작하는것이 아니라 테이블을 아예 하나의 객체(Object)와 대응시켜 버립니다.
- 말이 쉽지…. 객체지향(Object) 을 관계형 데이터베이스(Relation) 에 매핑(Mapping) 한다는건 정말 많은 난관이 있습니다.
ORM 문제점
문제점 | 객체의 특징 | RDB의 특징 | 해결방법 |
상속의 문제 | 객체 간 상속관계 가능. | 테이블 간 상속관계 없음, 독립적으로 존재. | - 매핑 정보에 상속 정보 추가.예: @OneToMany, @ManyToOne |
관계 문제 | 객체는 참조를 통해 관계를 가짐.다대다 관계 가능. | 외래키(FK)로 관계 설정.다대다 관계는 매핑 테이블 필요. | - 매핑 정보에 방향 정보 추가.예: @JoinColumn, @MappedBy |
탐색 문제 | 참조로 객체 탐색 가능.콜렉션 순회 가능. | 참조 시 추가 쿼리 또는 Join 발생. | - 탐색 시점 관리.예: @FetchType, fetchJoin() |
밀도 문제 | 객체의 멤버 크기가 클 수 있음. | 기본 데이터 타입만 존재. | - 큰 객체는 테이블로 분리.예: @Embedded |
식별성 문제 | hashCode 또는 equals() 메서드로 식별. | PK로만 식별 가능. | - PK를 객체 ID로 설정.예: @Id, @GeneratedValue, EntityManager로 관리. |
ORM 해결책
- 영속성 컨텍스트와 쓰기 지연
영속성이란? | 데이터를 프로그램 종료 후에도 유지하기 위해 DB나 파일에 저장하여 영구적으로 보존하는 특성. |
영속성 상태 | 1. 비영속 (new/transient) : 객체가 영속성 컨텍스트에 포함되지 않은 상태. 2. 영속 (managed) : 객체가 영속성 컨텍스트에 저장되어 관리되는 상태. 3. 준영속 (detached) : 영속성 컨텍스트에서 분리된 상태. 4. 삭제 (removed) : 객체가 삭제되어 영속성 컨텍스트와 DB에서 제거된 상태. |
상태 전환 메서드 | new > (비영속상태) > persist(),merge() > (영속성 컨텍스트에 저장된 상태) > flush() > (DB에 쿼리가 전송된 상태) > commit() > (DB에 쿼리가 반영된 상태) |
쓰기 지연이란? | - flush() 호출 전까지 SQL 쿼리를 영속성 컨텍스트에 모아두었다가, 한 번에 DB로 전송하는 최적화 메커니즘. |
쓰기 지연 발생 시점 | - 트랜잭션 중 객체 생성, 수정, 삭제 시. - flush() 호출 전까지 쿼리를 최적화하여 보관. |
쓰기 지연 효과 | - 여러 동작을 모아 쿼리를 한번에 전송하여 최소화. - 생성/수정/삭제 작업의 중간 상태가 발생하더라도 실제 DB에는 최적화된 쿼리만 전송. - 불필요한 쿼리 전송 방지. |
주의점 | - GenerationType.IDENTITY 사용 시, 쓰기 지연이 적용되지 않음. - 이유: IDENTITY 전략은 키 생성 시점에 단일 쿼리가 필요하며, 외부 트랜잭션 간의 키 중복을 방지하기 위해 즉시 DB에 반영됨. |
예제 코드의 흐름
단계 | 동작 설명 |
1. 객체 생성 | 새로운 객체 생성 (new 상태). |
2. 엔티티 매니저 생성 | 영속성 컨텍스트를 관리할 엔티티 매니저 생성. |
3. 트랜잭션 시작 | 데이터의 무결성을 보장하기 위해 트랜잭션 시작. |
4. 객체 저장 | persist() 호출로 객체를 영속성 컨텍스트에 저장. |
5. flush() 호출 | 영속성 컨텍스트의 SQL 쿼리를 DB로 전송 (commit 시 자동 수행). |
6. commit() 호출 | DB에 쿼리를 최종 반영. |
7. 자원 해제 | 엔티티 매니저 및 팩토리 자원 반환 (close()). |
ORM 을 사용하는 가장 쉬운 방법 : JpaRepository
💁♂️ Repository vs JpaRepository
- 기존 Repository
- @Repository 을 클래스에 붙인다.
- @Component 어노테이션을 포함하고 있어서 앱 실행시 생성 후 Bean으로 등록된다.
- 앞서배운 Repository 기본 기능만 가진 구현체가 생성된다. (DB별 예외처리 등)
- 새로운 JpaRepository
- JpaRepository<Entity,ID> 인터페이스를 인터페이스에 extends 붙인다.
- @NoRepositoryBean 된 ****상위 인터페이스들의 기능을 포함한 구현체가 프로그래밍된다. (@NoRepositoryBean = 빈생성 막음 →상속받으면 생성돼서 사용가능)
- JpaRepository (마스터 셰프): 데이터 액세스를 위한 핵심 기능의 종합적인 요리책(기능) 을 제공합니다.
- @NoRepositoryBean 인터페이스 (셰프): 각 인터페이스는 특정 데이터 액세스 방법을 제공하는 전문적인 기술 또는 레시피를 나타냅니다.
- JpaRepository 상속: 마스터 셰프의 요리책과 셰프의 전문성을 얻습니다.
- SpringDataJpa 에 의해 엔티티의 CRUD, 페이징, 정렬 기능 메소드들을 가진 빈이 등록된다. (상위 인터페이스들의 기능)
- @NoRepositoryBean 된 ****상위 인터페이스들의 기능을 포함한 구현체가 프로그래밍된다. (@NoRepositoryBean = 빈생성 막음 →상속받으면 생성돼서 사용가능)
- JpaRepository<Entity,ID> 인터페이스를 인터페이스에 extends 붙인다.
- Repository 와 JpaRepository 를 통해 얼마나 간단하게 구현하게 될지 미리 확인해볼까요?
- Repository 샘플
- EntityManager 멤버변수를 직접적으로 사용
- Repository 샘플
// UserRepository.java
@Repository
public class UserRepository {
@PersistenceContext
EntityManager entityManager;
public User insertUser(User user) {
entityManager.persist(user);
return user;
}
public User selectUser(Long id) {
return entityManager.find(User.class, id);
}
}
- JpaRepository 샘플
- EntityManager 멤버변수를 간접적으로 사용
// UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
// 기본 메서드는 자동으로 만들어짐
}
테이블 객체 이해하기
도메인 모델과 테이블 설계
도메인 관계 요약 표
도메인 | 관계 설명 | 관계 유형 |
User | - 채널과 양방향 관계.- 다른 도메인과 단방향 관계. | - User ↔ Channel (양방향). |
Channel | - 유저와 다대다 관계.- 대화가 이루어지는 채널. | - Channel ↔ User (다대다). |
Thread | - 채널 내 대화 쓰레드.- 댓글(Comment), 이모지(Emotion), 멘션(Mention)과 관계. | - Thread ↔ Comment (일대다).- Thread ↔ Emotion (다대다).- Thread ↔ Mention (다대다). |
Comment | - 쓰레드 내 댓글.- 쓰레드와 다대일 관계.- 이모지(Emotion), 멘션(Mention)과 관계. | - Comment ↔ Thread (다대일).- Comment ↔ Emotion (다대다).- Comment ↔ Mention (다대다). |
Emotion | - 쓰레드, 댓글 내 이모지.- 쓰레드와 댓글 각각과 다대다 관계. | - Emotion ↔ Thread (다대다).- Emotion ↔ Comment (다대다). |
Mention | - 쓰레드, 댓글 내 멘션.- 쓰레드와 댓글 각각과 다대다 관계. | - Mention ↔ Thread (다대다).- Mention ↔ Comment (다대다). |
관계 예시
- User ↔ Channel:
- 한 명의 유저는 여러 채널에 참여할 수 있고, 한 채널은 여러 유저를 가질 수 있음 (다대다).
- Thread ↔ Comment:
- 한 쓰레드는 여러 댓글을 가질 수 있지만, 댓글은 하나의 쓰레드에만 속함 (다대일).
- Thread ↔ Emotion / Comment ↔ Emotion:
- 여러 이모지가 하나의 쓰레드 또는 댓글에 추가될 수 있고, 하나의 이모지는 여러 쓰레드나 댓글에 사용될 수 있음 (다대다).
- Thread ↔ Mention / Comment ↔ Mention:
- 여러 멘션이 하나의 쓰레드 또는 댓글에 포함될 수 있고, 멘션은 여러 쓰레드나 댓글에서 참조될 수 있음 (다대다).
이 구조는 소셜 미디어나 채팅 애플리케이션에서 자주 볼 수 있는 관계를 모델링합니다.
Raw JPA 테이블 타입 매핑 기능
애노테이션 | 설명 | 예시 코드 |
@Entity | - 객체 관점에서의 이름.- 기본값으로 클래스명을 사용.- JQL에서 사용되는 이름. | @Entity public class User { ... } |
@Table | - RDB 테이블의 이름을 지정.- @Entity의 이름과 다르게 설정 가능.- SQL에서 사용되는 이름. | @Table(name = "users") |
@Id | - 엔티티의 주 키(Primary Key) 매핑.- 기본 타입(primitive) 및 Date, BigDecimal, BigInteger 사용 가능. | @Id private Long id; |
@GeneratedValue | - 주 키 생성 전략을 지정.- 기본값: AUTO.- 선택 가능: TABLE, SEQUENCE, IDENTITY. | @GeneratedValue(strategy = GenerationType.IDENTITY) |
@Column | - 컬럼 속성 지정.- 주요 옵션: unique, nullable, length, columnDefinition. | @Column(name = "email", unique = true, length = 255) |
@Temporal | - 날짜/시간 타입 매핑.- Date, Calendar 타입만 지원. | @Temporal(TemporalType.DATE) |
@Transient | - 데이터베이스 컬럼으로 매핑하지 않을 필드에 사용. | @Transient private String tempData; |
JPA Anotation 특징 및 활용 요약
애노테이션 | 특징 | 활용 |
@Entity | - 클래스가 JPA에서 관리하는 엔티티임을 선언.- 데이터베이스 테이블과 매핑. | - JPA가 관리할 객체를 정의할 때 사용.- 예: @Entity public class User { ... } |
@Table | - 엔티티와 매핑되는 RDB 테이블 이름을 명시적으로 설정.- 생략 시 기본값은 클래스명. | - 엔티티 이름과 테이블 이름이 다를 때 사용.- 예: @Table(name = "users") |
@Id | - 엔티티의 **기본 키(Primary Key)**를 지정. | - 필수 설정.- 예: @Id private Long id; |
@GeneratedValue | - 기본 키의 생성 전략을 지정.- AUTO, IDENTITY, SEQUENCE, TABLE 지원. | - 데이터베이스에 적합한 키 생성 방식을 설정.- 예: @GeneratedValue(strategy = GenerationType.IDENTITY) |
@Column | - 컬럼 속성을 세부적으로 지정.- 주요 속성: unique, nullable, length, columnDefinition. | - 컬럼의 제약 조건 설정.- 예: @Column(name = "email", unique = true, nullable = false, length = 255) |
@Temporal | - 날짜/시간 데이터를 DB에 적합한 타입으로 매핑.- DATE, TIME, TIMESTAMP 지원. | - java.util.Date 또는 Calendar 사용 시.- 예: @Temporal(TemporalType.TIMESTAMP) |
@Transient | - 비영속 필드를 선언.- 해당 필드는 DB에 매핑되지 않음. | - DB에 저장되지 않고, 비즈니스 로직에서만 사용되는 데이터 정의.- 예: @Transient private String tempData; |
예제 코드
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "email", unique = true, nullable = false, length = 255)
private String email;
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
@Transient
private String tempData; // DB에 매핑되지 않음
}
Raw JPA 필드 타입 매핑 기능
구분 | 설명 | 관련 애노테이션 | 예시 코드 |
기본 타입 | - 문자열, 날짜, 불리언 등의 기본 데이터 타입.- 사이즈 제한, 필드명 지정 등 옵션 설정 가능. | - @Column- @Enumerated | java<br>@Column(name = "email", length = 255)<br>private String email;<br>@Enumerated(EnumType.STRING)<br>private UserType userType;<br> |
Composite Value 타입 | - 여러 필드를 하나의 값 객체로 묶어 매핑.- 복합 객체를 필드로 적용 가능. | - @Embeddable- @Embedded- @AttributeOverrides, @AttributeOverride | java<br>@Embeddable<br>public class Address {<br> private String city;<br> private String street;<br>}<br>@Embedded<br>@AttributeOverrides({...})<br> |
Collection Value 타입 | - 기본 타입 또는 Composite Value 타입의 컬렉션을 매핑.- 주로 @ElementCollection으로 구현. | - @ElementCollection | java<br>@ElementCollection<br>private List<String> tags = new ArrayList<>();<br> |
주요 활용 및 예제
1. 기본 타입 매핑
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
@Column(name = "email", length = 255, nullable = false)
private String email;
@Enumerated(EnumType.STRING) // Enum 값은 STRING으로 매핑
private UserType userType;
}
2. Composite Value 타입 매핑
@Embeddable
public class Address {
private String city;
private String street;
}
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "home_city")),
@AttributeOverride(name = "street", column = @Column(name = "home_street"))
})
private Address homeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "company_city")),
@AttributeOverride(name = "street", column = @Column(name = "company_street"))
})
private Address companyAddress;
}
생성되는 테이블:
CREATE TABLE MEMBER (
MEMBER_ID BIGINT NOT NULL AUTO_INCREMENT,
NAME VARCHAR(255) NOT NULL,
home_city VARCHAR(255) NOT NULL,
home_street VARCHAR(255) NOT NULL,
company_city VARCHAR(255) NOT NULL,
company_street VARCHAR(255) NOT NULL,
PRIMARY KEY (MEMBER_ID)
);
3. Collection Value 타입 매핑
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
@ElementCollection
@CollectionTable(name = "product_tags", joinColumns = @JoinColumn(name = "product_id"))
@Column(name = "tag")
private List<String> tags = new ArrayList<>();
}
생성되는 테이블:
CREATE TABLE product_tags (
product_id BIGINT NOT NULL,
tag VARCHAR(255),
PRIMARY KEY (product_id, tag)
);
특징 및 활용 요약
- 기본 타입: 일반적인 데이터 저장에 사용되며, 설정 옵션으로 제약조건 지정.
- Composite Value 타입: 코드의 응집도를 높이고 복합 데이터를 쉽게 관리.
- Collection Value 타입: 여러 값 관리에 유용하지만, 대규모 데이터에서는 다대일 연관관계를 선호.
테이블 객체 만들기
User Entity 만들어보기
- id, username, password 를 가지는 User Entity 를 만들어 봅니다.
// User.java
// lombok
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString
// jpa
@Entity
@Table(name = "users")
public class User {
/**
* 컬럼 - 연관관계 컬럼을 제외한 컬럼을 정의합니다.
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
private String username;
private String password;
/**
* 생성자 - 약속된 형태로만 생성가능하도록 합니다.
*/
@Builder
public User(String username, String password) {
this.username = username;
this.password = password;
}
/**
* 연관관계 - Foreign Key 값을 따로 컬럼으로 정의하지 않고 연관 관계로 정의합니다.
*/
@OneToMany
@Exclude
private Set<UserChannel> userChannel;
/**
* 연관관계 편의 메소드 - 반대쪽에는 연관관계 편의 메소드가 없도록 주의합니다.
*/
/**
* 서비스 메소드 - 외부에서 엔티티를 수정할 메소드를 정의합니다. (단일 책임을 가지도록 주의합니다.)
*/
public void updateUserName(String username) {
this.username = username;
}
public void updatePassword(String password) {
this.password = password;
}
}
기타 추천 플러그인
Key PromoterX
- 단축키 알림
Presentation Assistant
- 알림 이쁘게 보여주기
테이블 객체끼리 관계만들기
Raw JPA 연관관계 매핑 기능 요약 표
애노테이션 | 설명 | 주요 속성 | 예시 코드 |
@OneToOne | - 1:1 관계를 매핑. - 단방향 및 양방향 매핑 가능.- 테이블 분리 여부를 신중히 검토. |
- mappedBy: 연관관계 주인 지정.- cascade: 영속성 전이 설정.- fetch: 기본 EAGER. | java<br>@OneToOne @JoinColumn(name = "LOCKER_ID") private Locker locker;<br>@OneToOne(mappedBy = "locker") private Member member;<br> |
@OneToMany | - 1:N 관계를 매핑. - 단방향 사용 시 비효율적. - 양방향은 @ManyToOne과 함께 사용. |
- mappedBy: 연관관계 주인 필드 지정.- fetch: 기본 LAZY.- cascade: 영속성 전이. | java<br>@OneToMany(mappedBy = "parent") private List<Child> childList;<br>@ManyToOne @JoinColumn(name = "parent_id") private Parent parent;<br> |
@ManyToOne | - N:1 관계를 매핑. - 가장 많이 사용하는 연관관계. - JoinColumn과 함께 사용. |
- optional: 연관 객체 필수 여부.- fetch: 기본 EAGER, 실무에서는 LAZY 권장.- cascade: 영속성 전이. | java<br>@ManyToOne @JoinColumn(name = "parent_id") private Parent parent;<br> |
@JoinColumn | - 외래키 매핑 시 사용. - 주로 @ManyToOne과 함께 사용. |
- name: 외래키 이름.- referencedColumnName: 참조 대상 컬럼.- unique, nullable, columnDefinition. | java<br>@JoinColumn(name = "parent_id") private Parent parent;<br> |
@ManyToMany | - N:M 관계 매핑. - 중간 매핑 테이블은 자동 생성. - 실무에서는 사용을 지양. |
- mappedBy: 양방향 매핑 시 주인 지정.- joinTable: 중간 매핑 테이블 이름 지정. | java<br>@ManyToMany @JoinTable(name = "parent_child", joinColumns = @JoinColumn(name = "parent_id"), inverseJoinColumns = @JoinColumn(name = "child_id")) private List<Parent> parents;<br> |
주요 연관관계 예제
1. @OneToOne 단방향 매핑
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
@OneToOne
@JoinColumn(name = "locker_id")
private Locker locker;
}
@Entity
public class Locker {
@Id
@GeneratedValue
private Long id;
}
2. @OneToMany와 @ManyToOne 양방향 매핑
@Entity
public class Parent {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "parent")
private List<Child> childList;
}
@Entity
public class Child {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
}
3. @ManyToMany 매핑
@Entity
public class Parent {
@Id
@GeneratedValue
private Long id;
@ManyToMany(mappedBy = "parents")
private List<Child> childs;
}
@Entity
public class Child {
@Id
@GeneratedValue
private Long id;
@ManyToMany
@JoinTable(
name = "parent_child",
joinColumns = @JoinColumn(name = "parent_id"),
inverseJoinColumns = @JoinColumn(name = "child_id")
)
private List<Parent> parents;
}
실무 권장 사항
- @ManyToMany 지양: 중간 매핑 테이블을 직접 정의하여 관리.
- fetch 기본값 조정: 대부분의 관계를 LAZY로 설정.
- 단방향 vs 양방향: 필요에 따라 양방향 관계를 신중히 설정.
📌
📌
📌
📌
📌
📌
📌
📌
📌
📌
🐳
🐳
🐳
소제목
🧩 부모 타입 변수 = 자식 타입 객체; 는 자동으로 부모 타입으로 변환이 일어납니다.
소제목
🎵 클래스가 설계도라면 추상 클래스는 미완성된 설계도입니다.
'DB > JPA ( Java Persistence API )' 카테고리의 다른 글
[JPA] SpringData JPA 심화 (0) | 2025.01.28 |
---|---|
[JPA] 테이블 객체 (0) | 2025.01.27 |
[JPA] 데이터베이스 연결 (Driver) (1) | 2025.01.25 |
[JPA] JPA와 Transaction (0) | 2025.01.17 |
[JPA] 지연로딩, 즉시로딩 (0) | 2025.01.15 |