728x90

어떤 관계들이 존재하는지에 대해서는 생략하고, 바로 OneToMany와 ManyToOne의 관계에 대해서 알아보자.

 

여기서는 사용자와 해당 사용자가 작성하는 보고서에 대한 관계로 설명을 해보려고 한다.

 

우선 OneToMany 관계이다.

한명의 사용자가 많은 양의 보고서를 작성할거기에 One이 사용자, Many가 보고서이다.

따라서 OneToMany는 User 쪽에 작성하는 데코레이션이다.

 

    @OneToMany(() => Report, (report) => report.users)
    reports: Report[];

 

@OneToMany로 2가지의 파라미터를 넘긴다.

우선 첫번째 파라미터는 Many 쪽의 타입이다.

Many로 가져올 타입을 명시하며, () => Report 처럼 함수형으로 반환하는 이유는 곧 보겠지만

Report 쪽에도 이렇게 Users 타입을 주게 되는데, 이 때 두 Entity 간에 순환 참조 에러가 발생하게 된다.

그렇기에 함수의 리턴타입으로 명시해주는 것이다.

 

그리고 2번째 파라미터는, Many 쪽에서 해당 Entity를 가지고 있는 property다.

지금은 하나밖에 없어서 그냥 users로 명시하고 있지만

 

만약 updateUser가 추가된다면

    @OneToMany(() => Report, (report) => report.updatedUser)
    updateReports: Report[];

이렇게 updateUser와 같은 속성을 명시해주는 것이다.

 

이런 이유로 OneToMany에도 2가지의 파라미터를 더 넘기게 된다.

 

이제 Many쪽으로 넘어가보자.

    @ManyToOne(() => Users, (Users) => Users.reports)
    users: Users

Many쪽도 다음과 같은 이유로 2개의 파라미터를 작성한다.

 

그리고 이렇게 작성을 하면 Many 쪽에는

다음과 같이 user쪽의 PK를 가지는 column이 추가된다.

 

근데 여기서 다음과 같은 문제가 발생한다.

우선 지금과 같이 작성해서는

    @Get("/check")
    @UseGuards(AuthGuard)
    async checkLogin(@Authentication() authentication: Users): Promise<Users>{
        console.log(authentication.reports)
        return authentication
    }

해당 코드에서 undefined가 출력되게 된다.

OneToMany 관계에서 Many를 불러오지 못하는 것이다.

 

불러오기 위해서는 다음과 같이 eager:true를 설정해줘야 한다.

@OneToMany(() => Report, (report) => report.users, {
        eager: true
    })
    reports: Report[];

이러면 출력은 된다.

 

하지만 또 문제가 발생한다.

이러면 로그인만 하더라도 모든 report까지 조회하게 된다.

로그인을 하면 user entity를 조회하게 될텐데, 그 때마다 report를 조회한다면 큰 오버헤드 일 것이다.

 

그렇기에 필요할 때만 가져오도록 만들어야 한다.

그러기 위해서는 해당 property를 다음과 같이 수정한다.

    @OneToMany(() => Report, (report) => report.users, {
        lazy: true
    })
    reports: Promise<Report[]>;

이렇게 타입을 Promise로 설정하고, lazy를 true로 하면 해당 property를 사용하려고 할 때 다시 쿼리를 날려서 report를 조회해온다.

 

테스트로 다음과 같이 코드를 작성하고 실행하면

    @Get("/check")
    @UseGuards(AuthGuard)
    async checkLogin(@Authentication() authentication: Users): Promise<Users>{
        console.log("before query")

        console.log(await authentication.reports)
        return authentication
    }

 

로그가 출력되고 그 다음에 다시 쿼리를 날리는 것을 볼 수 있다.

 

이 외에도 다양한 방법의 관계에서의 설정들이 있을텐데, 꾸준하게 nest를 공부하면서 알아보도록 하자.

728x90

우선 필요한 라이브러리들을 가져와준다.

npm install @nestjs/typeorm typeorm mysql2

 

그리고 AppModule에 우리가 사용할 데이터베이스의 연결정보를 추가해줘야 한다.

 

imports에 TypeOrmModule.forRoot()로 다음과 같이 추가해준다.

@Module({
  imports: [TypeOrmModule.forRoot({
    type: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'root',
    password: '1204',
    database: 'usedCars',
    entities: [__dirname + '/**/*.entity{.ts,.js}'],
    synchronize: true
  }), UsersModule, ReportsModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

mysql을 사용하기 때문에 type은 mysql이며, database로 스키마를 나타낸다.

synchronize를 사용하면 변경된 entity 정보가 데이터베이스의 스키마에도 반영된다.

개발 환경에서만 사용하고, 배포 환경에서는 사용하지 말도록 하자.

 

entities에 우리가 작성할 entity들을 넣어준다.

작성한 entity들을 하나하나 추가할 수 있지만, 많은 양의 entity를 작성하기 때문에 convention에 따라 .entity.ts로 끝나는 파일들을 모두 추가해주도록 path를 지정해주자.

 

이제 예시로 entity를 하나 만들어보자.

nest에서는 entity를 다음과 같은 과정으로 생성한다.

 

1번으로 UserEntity를 만들어보도록 하겠다.

@Entity('users')
@Unique(['email'])
export class Users{

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    email: string;

    @Column()
    password: string;
}

@Entity를 붙여 Entity임을 알려주고, 사용할 테이블의 이름을 적는다.

@Unique()에는 unique 속성을 가질 column 명을 적어준다.

@PrimaryGeneratedColumn을 사용하면 autoincrement의 int column이 생성된다.

 

그 다음에는 2번으로 UserModule에 UserEntity를 추가한다.

@Module({
  imports: [TypeOrmModule.forFeature([Users])],
  controllers: [UsersController],
  providers: [UsersService]
})
export class UsersModule {}

 

3번도 해야하지만, 해당 path의 모든 entity를 등록했기에 우리는 할 필요가 없다.

 

일단 이렇게 해서 서버를 실행해보면, synchronize가 true이기 때문에 데이터베이스에 다음과 같이 테이블이 생성된 것을 볼 수 있다.

 

UserEntity는 성공적으로 작성 한 것 같다.

 

728x90
  • 직접 할당

@Id 설정 대상에 직접 값 설정

저장하기 전에 생성자를 할당한다, 보통 생성 시점에 전달

@Entity
@Table(name = "user1")
public class User1{
    @Id
    private String name;
    private String email;
    
    protected User() {}
    
    public User (String name, String email){
    	this.name = name;
        this.email = email;
	}
}

이렇게 가장 단순한 방법으로 생성하는 방식이다.

  • 식별 칼럼 방식

칼럼 중에 auto_increment 같이 객체 생성시에 식별값을 설정하지 않는 경우가 있다.

그럴 때에는 설정을 추가하여 persist()를 실행할 때 객체에 식별자 값을 할당하게 할 수 있다.

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User2{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    
    protected User2(){}

    public User2(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

그 외에도 몇가지 방식이 더 있지만 많이 사용하지 않기 때문에 이렇게만 알아보도록 하겠다.

'Spring > JPA' 카테고리의 다른 글

JPA 7장 (Set collection mapping)  (0) 2023.03.18
JPA 6장 (@Embeddable)  (0) 2023.03.17
JPA 4장 (Entity에 대하여)  (0) 2023.03.16
JPA 3장 (간단한 CRUD 구현해보기)  (0) 2023.03.16
JPA 2장 (영속 컨텍스트)  (0) 2023.03.15
728x90
  • Entity 기본 annotation

@Entity: 엔티티 클래스에 설정, 필수!

@Table: mapping 테이블을 지정

@Id: 식별자 속성에 설정, 필수!

@Column: 매핑할 컬럼명 지정, 지정하지 않으면 필드명과 프로퍼티명을 사용

@Enumerated: enum 타입 매핑할 때 설정

 

  • Table annotation

annotation을 생략하면 클래스의 이름과 동일한 이름에 mapping

속성으로는

- name: 테이블 이름

- catalog: 카탈로그 이름

 

  • Enumerated

EnumType.STRING: enum 타입 값 이름을 저장

EnumType.ORDINAL: enum 타입의 값의 순서를 저장

 

 

Mapping이 된 설정의 예를 보도록 하자

//mapping을 합니다.
@Entity
//user 테이블에 mapping 합니다.
@Table(name = "user")
public class User {

	//식별자에 mapping 합니다.
    @Id
    //변수명에 mapping 합니다.
    private String email;
    private String name;
    
    //create_date에 mapping 합니다.
    @Column(name = "create_date")
    private LocalDateTime date;
	
    //열거타입 이름을 값으로 저장
    //grade에 mapping
    @Enumerated(EnumType.STRING)
    private Grade grade;

    protected User(){}
}

 

  • Entity 클래스의 제약 조건

1. 반드시 @Entity를 작성해야함

2. 반드시 @Id를 작성해야함

3. 인자 없는 기본 생성자 필요(해당 생성자는 public이나 protected로 작성이 되어야 함)

4. 최상위 클래스여야 함.

'Spring > JPA' 카테고리의 다른 글

JPA 6장 (@Embeddable)  (0) 2023.03.17
JPA 5장 (Entity 식별자 생성 방식)  (0) 2023.03.16
JPA 3장 (간단한 CRUD 구현해보기)  (0) 2023.03.16
JPA 2장 (영속 컨텍스트)  (0) 2023.03.15
JPA 1장 (우선 시작해보기)  (0) 2023.03.15

+ Recent posts