Install Oracle with Docker

Mac 환경에서 Oracle 사용하기 위한 방법

1. Docker에 Oracle image 설치

  • 도커 이미지 찾아보기
    • oracle-xe로 검색하여 oracle-xe-11g을 설치
docker search oracle-xe

  • 도커 이미지 pull
docker pull jaspeen/oracle-xe-11g

2. Oracle Image 실행

docker run --name oracle11g -d -p 8080:8080 -p 1521:1521 jaspeen/oracle-xe-11g

3. Docker의 Oracle 실행 및 접근

docker exec -it oracle11g sqlplus

Enter user-name: system
Enter password: {oracle}

4. SqlDeveloper 툴 및 그 외 접근을 위한 정보

사용자이름: system
비밀번호 : oracle
호스트 이름: localhost
포트 : 1521
SID : xe

5. SqlDeveloper에서 접근 시 오류 해결

Local not recognize 에러로 인해 접속 불가

  • SqlDeveloper.app의 패키지 내용 보기

  • sqldeveloper.conf 파일 확인

  • Language, Country 설정 정보 입력

6. Oracle Docker에 설치 및 실습 준비 완료

'Server > docker' 카테고리의 다른 글

[Docker] Docker로 DB 사용하기  (0) 2020.06.06

어노테이션 설명

  • 매핑 정보가 상속되는 엔티티에 적용되는 클래스를 지정
  • 매핑된 상위클래스는 별도의 테이블이 없다.
  • @MappedSuperclass가 지정된 클래스는 Entity와 동일한 방법으로 매핑될 수 있다.
  • 매핑은 테이블이 없으므로 하위 클래스에만 적용된다.
  • 슈퍼클래스에 적용되었을 떄, 상속받은 클래스에 상위클래스 테이블의 context를 적용할 수 있다.
  • AttributeOverrideAssociationOverride 어노테이션 또는 해당 XML elements를 사용하여 이러한 하위 클래스에 하여 재정의 할 수 있다.

어노테이션 용도

  • Entity별 공통 요소를 상속 필요성
  • @MappedSuperclass를 이용하여 공통요소를 Super Class에 정의
  • 간소화된 Entity
  • 공통 컬럼명을 override가 필요한 경우 @AttributeOverride로 재정의

예시 코드

@MappedSuperclass
public class Employee {
    @Id
    protected Integer empId;
    @Version
    protected Integer version;
    @ManyToOne
    @JoinColumn(name = "ADDR")
    protected Address address;

    public Integer getEmpId() { ... }

    public void setEmpId(Integer id) { ... }

    public Address getAddress() { ... }

    public void setAddress(Address addr) { ... }
}

 

// Default table is FTEMPLOYEE table
@Entity
public class FTEmployee extends Employee {

    // Inherited empId field mapped to FTEMPLOYEE.EMPID
    // Inherited version field mapped to FTEMPLOYEE.VERSION
    // Inherited address field mapped to FTEMPLOYEE.ADDR fk

    // Defaults to FTEMPLOYEE.SALARY
    protected Integer salary;

    public FTEmployee() {
    }

    public Integer getSalary() { ... }

    public void setSalary(Integer salary) { ... }
}

- @AssociationOverride 어노테이션을 통해 address 속성을 ADDR_ID를 조인컬럼으로 사용

@Entity
@Table(name = "PT_EMP")
@AssociationOverride(name = "address", joincolumns = @JoinColumn(name = "ADDR_ID"))
public class PartTimeEmployee extends Employee {

    // Inherited empId field mapped to PT_EMP.EMPID
    // Inherited version field mapped to PT_EMP.VERSION
    // address field mapping overridden to PT_EMP.ADDR_ID fk
    @Column(name = "WAGE")
    protected Float hourlyWage;

    public PartTimeEmployee() {
    }

    public Float getHourlyWage() { ... }

    public void setHourlyWage(Float wage) { ... }
}

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

[JPA] CRUD 전략  (0) 2020.07.14
[JPA] JPA 연관  (0) 2020.06.16
[JPA] 살펴보기  (0) 2020.06.16

1. 엔티티 연관

  • 시스템 개발에서 사용되는 구성요소들은 서로 연관을 가진다.

연관관계 매핑 키워드

  • 방향
    • 단방향, 양방향이 있다.
    • 사원 -> 회사 또는 회사 -> 사원과 같이 한 쪽만 참조하는 것을 단방향 관계라고 한다.
    • 사원 -> 회사, 회사 -> 사원과 같이 서로 참조하는 것을 양방향 관계라고 한다.
    • 방향은 객체 관계에서만 존재하고 테이블 관계는 항상 양방향이다.
  • 다중성(Multiplicity)
    • 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)가 있다.
    • 여러 사원은 하나의 회사에 속하므로 사원과 회사는 다대일 관계이다.
    • 하나의 회사는 여러 사원이 소속될 수 있으므로 회사와 사원은 일대다 관계이다.
    • 하나의 회원은 하나의 회원상세 정보만을 가질 수 있으므로 회원과 회원상세는 일대일 관계이다.
    • 하나의 고객은 여러 상품을 구매할 수 있고, 하나의 상품은 여러 고객에게 판매될 수 있으므로 다대다 관계이다.
  • 연관관계의 주인(Owner)
    • 엔티티를 양방향 연관관계로 설정하면 객체 관계의 참조는 둘인데 테이블 외래 키는 하나이다.
    • 양방향 연관관계를 매핑할 때 두 연관관계 중 하나를 연관관계의 주인으로 정해야 한다.
    • 연관관계의 주인은 외래 키를 관리(등록, 수정, 삭제) 할 수 있고 주인이 아닌 쪽은 읽기만 할 수 있다.
    • 연관관계의 주인을 정할 때 mappedBy 속성을 사용하는데 mappedBy 속성을 사용하지 않은 쪽이 주인이 된다.

연관관계 어노테이션

  • @JoinColumn
    • @JoinColumn은 외래키를 매핑할 때 사용, 생략 가능
속성 기능 기본값
name 매핑할 외래 키 이름을 지정한다. 필드명, 참조하는 테이블의 기본키 컬럼명
referencedColumnName 외래 키가 참조하는 대상 테이블의 컬럼명을 지정한다. 참조하는 테이블의 기본키 컬럼명
  • @ManyToOne
    • @ManyToOne은 다대일 관계에서 사용한다. 다중성을 나타내는 어노테이션은 필수
속성 기능 기본값
fetch 글로벌 Fetch 전략을 설정 N:1, 1:1은 FetchType.EAGER이고 1:N은 FetchType.LAZY로 설정
cascade 영속성 전이 기능을 사용한다.
  • @OneToMany
    • @OneToMany은 일대다 관계에서 사용한다.
속성 기능 기본값
mappedBy 연관관계의 주인을 지정
fetch 글로벌 Fetch 전략을 설정 N:1, 1:1은 FetchType.EAGER이고 1:N은 FetchType.LAZY로 설정
casecade 영속성 전이 기능을 사용
  • @ManyToMany
    • @ManyToMany 다대다 관계에서 사용한다.
속성 기능 기본값
mappedBy 연관관계의 주인을 지정
fetch 글로벌 Fetch 전략을 설정 N:1, 1:1은 FetchType.EAGER이고 1:N은 FetchType.LAZY로 설정
casecade 영속성 전이 기능을 사용
  • @JoinTable
    • @JoinTable은 다대다 관계를 일대다, 다대일 관계로 풀어내기 위해 필요한 연결 테이블을 매핑할 때 사용한다.
속성 기능 기본값
name 연결 테이블을 지정한다.
joinColumns 순 방향 엔티티와 매핑할 조인 컬럼 정보를 지정한다.
inverseJoinColumns 역 방향 엔티티와 매핑할 조인 컬럼 정보를 지정한다.

2. 일대다 단방향

  • @OneToMany 어노테이션을 사용하여 일대다 단방향 연관을 설정할 수 있다.

연관관계

  • 관계 정의

    • 상위 클래스는 여러 하위 클래스를 가질 수 있다.
    • 상위 클래스와 하위 클래스는 일대다 관계이다.
  • 객체 연관관계

    • 상위 객체는 List<하위객체> 컬렉션 필드로 하위객체와 연관관계를 맺는다.
    • 상위 객체와 하위 객체는 단방향 관계이다.
    • 상위 객체는 하위 객체에 접근할 수 있는 필드를 갖고 있지만 반대로 하위객체는 상위 객체에 접근할 수 없다.
  • 테이블 연관관계

    • 하위객체의 테이블은 상위객체에 접근할 수 있는 필드 값으로 상위객체 테이블과 연관관계를 맺는다.
    • 하위객체 테이블에서 상위 객체에 접근할 수 있는 필드 값을 통해 하위 객체와 상위 객체를 조인할 수 있고 반대로 상위 객체와 하위객체를 조인할 수있다.
  • 연관관계 매핑

    • @OneToMany: 상위객체와 하위 객체는 일대다 관계이다.
    • @JoinColumn(name = "외래키"): name 속성에는 매핑할 외래 키 이름을 지정한다.
  • 연관관계 구현

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "상위객체의 pk 필드")
private List<하위객체> 하위객체List;

3. 다대일 단방향

  • @ManyToOne 어노테이션을 사용하여 다대일 단방향 연관을 설정할 수 있다.

연관관계

  • 관계정의

    • 여러 하위객체는 하나의 상위객체에 속할 수 있다.
    • 하위객체와 상위객체는 다대일 관계이다.
  • 객체 연관관계

    • 하위객체는 상위객체에 접근할 수 있는 참조 키 필드로 상위객체와 연관관계를 맺는다.
    • 하위객체와 상위객체는 단방향 관계이다.
    • 하위객체는 참조키 필드를 통해 상위객체를 알 수 있지만 반대로 상위 객체는 하위객체를 알 수 없다.
  • 테이블 연관관계

    • 하위객체 테이블은 외래키로 상위객체 테이블과 연관관계를 맺는다.
    • 하위객체 테이블은 외래키를 통해 상위객체와 하위객체를 조인할 수 있고, 반대로 상위객체와 하위객체를 조인할 수 있다.
  • 연관관계 매핑

    • @ManyToOne: 코드상세와 코드그룹은 다대일 관계이다.
    • @JoinColumn(name = "외래키"): name 속성에는 매핑할 외래 키 이름을 지정한다.
  • 연관관계 구현

    • 하위객체 -> 상위객체 다대일 연관
@ManyToOne
@JoinColumn(name = "외래키")
private 상위객체 상위객체;

4. 다대일 양방향

  • @OneToMany와 @ManyToOne 어노테이션을 사용하여 다대일 양방향 연관을 설정할 수 있다.

연관관계

  • 관계정의

    • 상위객체는 여러 코드 상세를 가질 수 있다.
    • 여러 하위객체는 하나의 상위객체에 속할 수 있다.
    • 상위객체와 하위객체는 일대다 관계이다.
    • 하위객체와 상위객체는 다대일 관계이다.
  • 객체 연관관계

    • 하위객체는 외래키 필드로 상위객체와 연관관계를 맺는다.
    • 상위객체는 List<하위객체> 컬렉션 필드로 상위객체와 연관관계를 맺는다.
    • 하위객체와 상위객체는 양방향 관계이다.
    • 하위객체는 외래키필드를 통해서 상위객체에 접근할 수 있고 반대로 상위객체는 하위객체 필드를 통해서 하위객체에 접근할 수 있다.
  • 테이블 연관관계

    • 하위객체 테이블은 외래키로 상위객체 테이블과 연관관계를 맺는다.
    • 하위객체 테이블은 외래키를 통해서 하위객체와 상위객체를 조인할 수 있고, 반대로 상위객체와 하위객체를 조인할 수 있다.
  • 연관관계 매핑

    • @ManyToOne: 코드상세와 코드그룹은 다대일 관계이다.
    • @OneToMany(mappedBy = "상위객체 PK 필드") 상위객체와 하위객체는 일대다 관계이다.
      • mappedBy 속성은 양방향 매핑일 때 사용하는데 반대쪽 매핑의 필드 이름을 값으로 주면 된다.
      • 하위객체 테이블이 외래 키를 가지고 있으므로 SubClass.SuperClass가 주인이 된다.
      • 주인이 아닌 SuperClass.subClass에는 mappedBy = "superClass" 속성을 사용해서 주인이 아님을 설정한다.
    • @JoinColumn(name = "외래키"): name 속성에는 매핑할 외래 키 이름을 지정한다.
  • 연관관계 구현

  1. 하위객체 -> 상위객체 다대일 연관
@ManyToOne
@JoinColumn(name = "외래키")
private 상위객체 상위객체;
  1. 상위객체 -> 하위객체 일대다 연관
@OneToMany(mappedBy = "상위객체의 PK필드", casecade = CasecadeType.ALL, fetch = FetchType.LAZY)
private List<하위객체> 하위객체List;
  1. 연관관계 편의 메서드
public class SuperClass {
    public void addSubClass(SubClass subClass) {
        subClass.setSuperClass(this);
        this.subClass.add(subClass);
    }
}
  • 주종관계란?

5. 일대일 단방향

  • 하나의 회원은 하나의 회원상세를 갖는다.
  • 회원상세 클래스에서 @OneToOne 어노테이션과 @JoinColumn 어노테이션을 사용하여 일대일 단방향 연관을 설정할 수 있다.

연관관계

  • 관계 정의
    • 하나의 회원상세는 하나의 회원에 속할 수 있다.
    • 회원상세와 회원은 일대일 관계이다.
  • 객체 연관관계
    • 회원상세 객체는 member 필드로 회원 객체와 연관관계를 맺는다.
    • 회원상세 객체와 회원 객체는 단방향 관계이다.
    • 회원상세는 member 필드를 통해서 회원을 알 수 있지만 반대로 회원은 회원상세를 알 수 없다.
  • 테이블 연관관계
    • 회원상세 테이블은 user_no 외래 키로 회원 테이블과 연관관계를 맺는다.
    • 회원상세 테이블의 user_no 외래 키를 통해서 회원상세와 회원을 조인할 수 있고, 반대로 회원과 회원상세를 조인할 수 있다.
  • 연관관계 매핑
    • @OneToOne: 회원상세와 회원은 일대일 관계이다.
    • @JoinColumn(name = "user_no"): name 속성에는 매핑할 외래 키 이름을 지정한다.
  • 연관관계 구현
@OneToOne
@JoinColumn(name = "user_no")
private Member member;

6. 일대일 단방향

  • 하나의 회원은 하나의 회원상세를 갖는다.
  • 회원 클래스에서 @OneToOne 어노테이션과 @JoinColumn 어노테이션을 사용하여 일대일 단방향 연관을 설정할 수 있다.

연관관계

  • 관계 정의
    • 하나의 회원은 하나의 회원상세를 가질 수 있다.
    • 회원과 회원상세는 일대일 관계이다.
  • 객체 연관관계
    • 회원 객체는 memberDetail 필드로 회원상세 객체와 연관관계를 맺는다.
    • 회원 객체와 회원상세 객체는 단방향 관계이다.
    • 회원은 memberDetail 필드를 통해서 회원상세에 접근할 수 있지만 반대로 회원상세는 회원에 접근할수 없다.
  • 테이블 연관관계
    • 회원상세 테이블은 user_no 외래 키로 회원 테이블과 연관관계를 맺는다.
    • 회원상세 테이블의 user_no 외래 키를 통해서 회원상세와 회원을 조인할 수 있고, 반대로 회원과 회원상세를 조인할 수 있다.
  • 연관관계 매핑
    • @OneToOne: 회원관 회원상세는 일대일 관계이다.
    • @JoinColumn(name = "user_no"): name 속성에는 매핑할 외래 키 이름을 지정한다.
  • 연관관계 구현
@OneToOne(casecade = CasecadeType.ALL)
@JoinColumn(name = "user_no")
private MemberDetail memberDetail;

7. 일대일 양방향

  • 하나의 회원은 하나의 회원상세를 갖는다.
  • 회원 클래스와 회원상세 클래스에서 @OneToOne 어노테이션을 사용하여 일대일 양방향 연관을 설정할 수 있다.

연관관계

  • 관계정의
    • 회원은 여러 회원상세를 가질 수 있다.
    • 여러 회원상세는 하나의 회원에 속할 수 있다.
    • 회원과 회원상세는 일대일 관계이다.
    • 회원상세와 회원은 일대일 관계이다.
  • 객체 연관관계
    • 회원상세 객체는 member 필드로 회원 객체와 연관관계를 맺는다.
    • 회원 객체는 memberDetail 필드로 회원상세 객체와 연관관계를 맺는다.
    • 회원상세 객체와 회원 객체는 양방향 관계이다.
    • 회원상세는 member 필드를 통해서 회원에 접근할 수 있고, 반대로 회원은 memberDetail 필드를 통해서 회원상세에 접근할 수 있다.
  • 테이블 연관관계
    • 회원상세 테이블은 user_no 외래 키로 회원 테이블과 연관관계를 맺는다.
    • 회원상세 테이블의 user_no 외래 키를 통해서 회원상세와 회원을 조인할 수 있고, 반대로 회원과 회원상세를 조인할 수 있다.
  • 연관관계 매핑
    • @OneToOne: 회원상세와 회원은 일대일 관계이다.
    • @OneToOne:(mappedBy = "member") 회원과 회원상세는 일대일 관계이다.
      • mappedBy 속성은 양방향 매핑일 때 사용하는 데 반대쪽 매핑의 필드 이름을 값으로 주면된다.
      • 회원상세 테이블이 외래 키를 가지고 있으므로 MemberDetail.member가 주인이 된다.
      • 주인이 아닌 Member.memberDetail에는 meppedBy="member" 속성을 사용해서 주인이 아님을 설정한다.
      • @JoinColumn(name = "user_no"): name 속성에는 매핑할 외래 키 이름을 지정한다.
  • 연관관계 구현
  1. MemberDetail -> Member 일대일 연관
@OneToOne
@JoinColumn(name = "user_no")
private Member member;
  1. Member -> MemberDetail 일대일 연관
@OneToOne(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private MemberDetail memberDetail;

8. 다대다 단방향

  • 한 명의 회원은 여러 상품을 구입할 수 있고 하나의 상품은 여러 명의 회원에 팔릴 수 있다.
  • 회원 클래스에서 @ManyToMany 어노테이션을 사용하여 다대다 단방향 연관을 설정할 수 있다.

연관관계

  • 관계 정의

    • 하나의 회원은 여러 상품을 구매할 수 있다.
    • 하나의 상품은 여러 명의 회원에 팔릴 수 있다.
    • 회원과 상품은 다대다 관계이다.
  • 객체 연관관계

    • 회원 객체는 List itemList 컬렉션 필드로 상품 객체와 연관관계를 맺는다.
      • 회원 객체와 상품 객체는 단방향 관계이다.
    • 회원은 items 필드를 통해서 상품에 접근할 수 있지만 반대로 상품은 회원에 접근할 수 없다.
  • 테이블 연관관계

    • 회원 테이블과 상품 테이블과 외래키로 연관관계를 가지는 회원상품 테이블이 생성된다.
    • 회원 테이블은 user_no 외래 키로 회원상품 테이블과 연관관계를 맺는다.
    • 상품 테이블은 item_no 외래 키로 회원상품 테이블과 연관관계를 맺는다.
      • 회원 테이블은 user_no 외래 키를 통해서 회원과 회원상품을 조인할 수 있다.
    • 상품 테이블은 item_no 외래 키를 통해서 상품과 회원상품을 조인할 수 있다.
  • 연관관계 매핑

    • @ManyToMany: 회원과 상품은 다대다 관계이다.
      • @JoinTable.name: 연결 테이블을 지정
      • @JoinTable.joinColumns: 순 방향 엔티티와 매핑할 조인 컬럼 정보를 지정한다.
    • @JoinTable.inverseJoinColumns: 역 방향 엔티티와 매핑할 조인 컬럼 정보를 지정한다.
  • 연관관계 구현

@ManyToMany
@JoinTable(
    name = "user_item",
    joinColumns = @JoinColumn(name = "user_no"),
    inverseJoinColumns = @JoinColumn(name = "item_no"))
private List<Item> items = new ArrayList<>();

9. 다대다 양방향

  • 한 명의 회원은 여러 개의 상품을 구입할 수 있고, 하나의 상품은 여러 명의 회원에 팔릴 수 있다.
  • 회원 클래스와 상품 클래스에서 @ManyToMany 어노테이션을 사용하여 다대다 양방향 연관을 설정할 수 있다.

연관관계

  • 관계 정의

    • 회원은 여러 상품을 구매할 수 있다.
    • 여러 상품은 하나의 회원에 판매될 수 있다.
    • 회원과 상품은 다대다 관계이다.
  • 객체 연관관계

    • 상품 객체는 List members 필드로 회원 객체와 연관관계를 맺는다.
    • 회원 객체는 List items 필드로 상품 객체와 연관관계를 맺는다.
    • 상품 객체와 회원 객체는 양방향 관계이다.
    • 상품은 members 필드를 통해서 회원에 접근할 수 있고, 반대로 회원은 items 필드를 통해서 상품에 접근할 수 있다.
  • 테이블 연관관계

    • 회원 테이블과 상품 테이블과 외래키로 연관관계를 가지는 회원상품 테이블이 생성된다.
    • 회원 테이블은 user_no 외래 키로 회원상품 테이블과 연관관계를 맺는다.
    • 상품 테이블은 item_no 외래 키로 회원상품 테이블과 연관관계를 맺는다.
    • 회원 테이블은 user_no 외래 키를 통해서 회원과 회원상품을 조인할 수 있다.
    • 상품 테이블의 item_no 외래 키를 통해서 상품과 회원상품을 조인할 수 있다.
  • 연관관계 매핑

    • @ManyToMany: 회원과 상품은 다대다 관계이다.
    • @JoinTable.name: 연결 테이블을 지정한다.
    • @JoinTable.joinColumns: 순 방향 엔티티와 매핑할 조인 컬럼 정보를 지정한다.
    • @JoinTable.inverseJoinColumns: 역 방향 엔티티와 매핑할 조인 컬럼 정보를 지정한다.
    • @ManyToMany(mappedBy = "items"): 주인이 아닌 item.members 에는 mappedBy = "items" 속성을 사용해서 주인이 아님을 설정한다.
  • 연관관계 구현

  1. Member -> Item 다대다 연관
@ManyToMany
@JoinTable(name = "user_item",
    joinColumns = @JoinColumn(name = "user_no"),
    inverseJoinColumns = @JoinColumn(name = "item_no"))
private List<Item> items = new ArrayList<>();
  1. Item -> Member 다대다 연관
@ManyToMany(mappedBy = "items")
private List<Member> members = new ArrayList<>();

10. 다대다 연결 엔티티

  • 한 명의 회원은 여러 개의 상품을 구입할 수 있고, 하나의 상품은 여러 명의 회원에 팔릴 수 있다.
  • 회원 상품 연결 엔티티 클래스를 추가하여 회원 클래스와 상품 클래스의 다대다 연관을 해소할 수 있다.

연관관계

  • 관계 정의

    • 회원은 여러 회원상품을 가질 수 있다.
    • 여러 회원상품은 하나의 회원에 속할 수 있다.
    • 회원과 회원상품은 일대다 관계이다.
    • 회원상품과 회원은 다대일 관계이다.
    • 상품은 여러 회원상품에 포함될 수 있다.
    • 여러 회원상품은 하나의 상품에 속할 수 있다.
    • 상품과 회원상품은 일대다 관계이다.
    • 회원상품과 상품은 다대일 관계이다.
  • 객체 연관관계

    • 회원상품 객체는 member 필드로 회원 객체와 연관관계를 맺는다.
    • 회원 객체는 List userItems 컬렉션 필드로 회원상품 객체와 연관관계를 맺는다.
      • 회원상품 객체와 회원 객체는 양방향 관계이다.
      • 회원상품은 member 필드를 통해서 회원에 접근할 수 있고, 반대로 회원은 userItems 필드를 통해서 회원상품에 접근할 수 있다.
      • 회원상품 객체는 Item 필드로 상품 객체와 연관관계를 맺는다.
      • 회원상품은 item 필드를 통해서 상품에 접근할 수 있고, 반대로 상품은 회원상품에 접근할 수 있다.
  • 테이블 연관관계

    • 회원상품 테이블은 user_no 외래 키로 회원 테이블과 연관관계를 맺는다.
    • 회원상품 테이블은 item_no 외래 키로 상품 테이블과 연관관계를 맺는다.
    • 회원상품 테이블의 user_no 외래 키를 통해서 회원상품과 회원을 조인할 수 있고, 반대로 회원과 회원상품을 조인할 수 있다.
    • 회원상품 테이블의 item_no 외래 키를 통해서 회원상품과 상품을 조인할 수 있고, 반대로 상품과 회원상품을 조인할 수 있다.
  • 연관관계 매핑

- @ManyToOne: 회원상품과 회원은 다대일 관계이다.
- @JoinColumn(name = "user_no"): name 속성에는 매핑할 외래 키를 지정한다.
- @OneToMany(mappedBy = "member"): 회원과 회원상품은 일대다 관계이다.
    - mappedBy 속성은 양방향 매핑일 때 사용하는데 반대쪽 매핑의 필드 이름을 값으로 주면된다.
    - 회원상품 테이블이 외래 키를 가지고 있으므로 UserItem.member가 주인이 된다.
    - 주인이 아닌 Member.userItems에는 mappedBy = "member" 속성을 사용해서 주인이 아님을 설정한다.
-   @ManyToOne: 회원상품과 상품은 다대일 관계이다.
    -   @JoinColumn(name = "item\_no"): name 속성에는 매핑할 외래 키 이름을 지정한다.
  • 연관관계 구현
  1. UserItem -> Member 다대일 연관
@ManyToOne
@JoinColumn(name = "user_no")
private Member member;
  1. Member -> UserItem 다대일 연관
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<UserItem> userItems = new ArrayList<>();
  1. UserItem -> Item 다대일 연관
@ManyToOne
@JoinColumn(name = "item_no")
private Item item;

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

[JPA] CRUD 전략  (0) 2020.07.14
[Annotation] @MappedSuperclass  (0) 2020.06.22
[JPA] 살펴보기  (0) 2020.06.16

JPA 요소

  • 엔티티(Entity)
    • 데이터베이스에서 지속적으로 저장된 데이터를 자바 객체에 매핑한 것
    • 메모리 상에 자바 객체의 인스턴스 형태로 존재하며 EntityManager에 의해 데이터베이스의 데이터와 동기화된다.
  • 엔티티 매니저(Entity Manager)
    • 필요에 따라 Entity와 데이터베이스의 데이터를 동기화한다.
    • EntityManager에서 제공하는 Entity 조작 API를 이용해 Entity에 대해 CRUD 작업을 할 수 있다.
  • 영속성 컨텍스트(Persistence context)
    • 영속성 영구적으로 저장하는 환경이다.
    • 엔티티를 저장하거나 검색할 때 엔티티 관리자는 영속성 컨텍스트에서 엔티티를 저장하고 관리한다.
    • 영속성 컨텍스트에 접근하거나 관리를 하려면 엔티티 매니저를 통해야 한다.

엔티티 상태

상태 설명
비영속(new/transient) 영속성 컨텍스트와 관련이 없는 상태
영속(managed) 영속성 컨텍스트에 저장된 상태
준영속(detached) 영속성 컨텍스트에 저장되었다가 분리된 상태
삭제(removed) 영속성 컨텍스트에서 삭제된 상태

영속성 전이(persistence cascade)

  • 엔티티의 영속성 상태 변화를 연관된 엔티티에도 함께 적용하는 것
  • 엔티티를 저장할 때 연관된 엔티티도 함께 저장하고 엔티티를 삭제할 때 연관된 엔티티도 함께 삭제하는 것

JPA 메서드

기능 설명 Method
목록조회 findAll()
상세조회 findById()
수정 findById() -> optional.isPresent() -> optional.get() -> repository.save()
삭제 deleteById() or findById() -> delete()
전체삭제 deleteAll()
  • deleteById() vs delete()

JPA Annotation

어노테이션 설명
@Entity JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야 한다.
@Entity가 붙은 클래스는 JPA가 관리하는 것으로 엔티티라 부른다.  
@Table @Table은 엔티티와 매핑할 테이블을 지정한다.
생략하면 매핑할 엔티티 이름을 테이블 이름으로 사용한다.  
name 속성을 이용해서 테이블 이름을 지정할 수 있다.  
@Id @Id는 기본 키(Primary Key)를 매핑한다.
@GeneratedValue DB의 식별 컬럼을 사용해서 식별자를 생성하기 위한 어노테이션
@Column @Column은 객체 필드를 테이블 컬럼에 매핑한다.
@Enumerated 열거 타입에 대한 매핑을 설정할 수 있다.
@Temporal java.util.Date 타입을 매핑하는 경우 사용
@CreationTimeStamp 엔티티 생성 시 시점의 날짜 데이터를 기록
@UpdateTimeStamp 엔티티가 업데이트 되는 시점의 날짜 데이터를 기록

Spring Data JPA

  • CrudRepository 인터페이스
Interface CrudRepository<T,ID> extends Repository<T,ID>
메서드 설명
long count() 사용 가능한 엔티티 수를 반환한다.
void delete(T entity) 주어진 엔티티를 삭제한다.
void deleteAll() 저장소에서 관리하는 모든 엔티티를 삭제한다.
void deleteAll(Iterable<? extends T> entities) 주어진 엔티티를 삭제한다.
void deleteById(ID id) 주어진 ID를 가진 엔티티를 삭제한다.
boolean existsById(ID id) 주어진 ID를 가진 엔티티가 존재하는 지 여부를 반환한다.
Iterable findAll() T 타입의 모든 인스턴스를 반환한다.
Iterable findAllById(Iterable ids) 주어진 ID를 가진 T 타입의 모든 인스턴스를 반환한다.
Optional findById(ID id) ID로 엔티티를 검색한다.
S save(S entity) 주어진 엔티티를 저장한다.
Iterable saveAll(Iterable entities) 주어진 엔티티를 모두 저장한다.

참조 사이트 - docs.spring.io

Query Method

  • Spring Data JPA에서 메서드 형식으로 제공하는 쿼리 호출
  • 쿼리 메서드 지원 키워드(Supported Keywords inside method names)
Keyword sample query
And findByLastnameAndFirstname where lastname = ? and firstname = ?
Or findByLastnameOrFirstname where lastname or firstname = ?
Is, Equals findByFirstname, findByFirstnameIs, findByFirstnameEquals where firstname = ?
Between findByStartDateBetween where startDate between ? and ?
LessThan findByAgeLessThan where age < ?
LessThanEqual findByAgeLessThanEqual where age <= ?
GreaterThan findByAgeGreaterThan where age > ?
GreaterThanEqual findByAgeGreaterThanEaual where age >= ?
After findByStartDateAfter where startDate > ?
Before findByStartDateBefore where startDate < ?
IsNull, Null findByAge(is)Null where age is null
IsNotNull, NotNull findByAge(is)NotNull where age not null
Like findByFirstnameLike where firstname like ?
NotLike findByFirstnameNotLike where firstname not like ?
StartingWith findByFirstnameStartingWith where firstname like ?
(parameter bound with appended %)    
EndingWith findByFirstnameEndingWith where firstname like ?
(parameter bound prepended in %)    
Containing findByFirstnameContaing where firstname like ?
(parameter bound wrapped in %)    
OrderBy findByAgeOrderByLastnameDesc where age = ? order by lastname desc
Not findByLastnameNot where lastname <> ?
In findByAgeIn(Collection ages) where age in ?
NotIn findByAgeNotIn(Collection ages) where age not in ?
True findByActiveTrue() where active = true
False findByActiveFalse() where active = false
IgnoreCase findByFirstnameIgnoreCase where UPPER(firstname) = UPPER(?)

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

[JPA] CRUD 전략  (0) 2020.07.14
[Annotation] @MappedSuperclass  (0) 2020.06.22
[JPA] JPA 연관  (0) 2020.06.16

IntelliJ Auto Import

  • Ctrl + Alt + s로 settings 띄우기
    • Add unambiguous imports on the fly
    • Optimize imports on the fly (for current project)
      • 코드가 변경됨에 따라 자동으로 패키지 import문을 삽입/삭제해주는 옵션
    • Insert imports on paste
      • All (모두 자동)
      • Ask (확인 후 선택)
      • None (자동 import 하지 않기)

'tools > IntelliJ' 카테고리의 다른 글

[IntelliJ] 단축키(Short Cut)  (0) 2020.06.01
[IntelliJ] Build Automatically  (0) 2020.05.31
  • 노트북에 DB Server 안깔고 도커로 MySQL 이미지 받아서 Container 사용하기

Docker

  • docker

    • engine (server)
      • 시스템 상의 서비스
    • client (docker command)
      • 내부 또는 외부의 docker engine으로 명령을 전달
  • docker hub

    • github와 같은 사이트
  • docker image 명령어

# 현재 docker에 있는 image 확인하기
$ docker images

# docker hub에 공유되어 있는 이미지 가져오기
$ docker pull {image_name or container_id}
  • docker container
# 컨테이너 진입하여 쉘 띄우기
$ docker exec -it {image_name} bash

# 컨테이너 실행
$ docker start {image_name or container_id}

# 컨테이너 실행 중지
$ docker stop {image_name or container_id}

# 컨테이너 전체 정지
$ docker stop $(docker ps -a -q)

# 컨테이너 삭제
$ docker rm {image_name or container_id}

Docker 설치

  • 도커 설치 후 PowerShell 실행
$ docker images

MySQL 이미지 생성 및 컨테이너 실행

  • docker-compose.yml을 이용한 실행
# file version
version: "3"

# container service

services:
  # service name
  db: 
    # mysql:version
    image: mysql:8.0.17

    container_name: {컨테이너명}

    # container access port (external:internal)
    ports:
      - "3306:3306"

    # -e option
    environment:

      # mysql password
      MYSQL_ROOT_PASSWORD: {password}

    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci

    # -v option (dir mount settings)
    volumes:
      - {local}:/var/lib/mysql
  • MySQL Container 접근
$ docker exec -it health_eat_db-mysql bash 
  • MySQL 로그인
root@~:/# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.17 MySQL Community Server - GPL

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>                                                       

PC IP 정보로 접근하기

'Server > docker' 카테고리의 다른 글

[Docker] Docker로 Oracle 사용하기  (0) 2020.06.30

DTO

  • Value Object라고도 불리는 Transfer Object는 데이터를 전송하기 위한 객체에 대한 패턴이다.
  • 웹 개발 시 DTO, VO, Entity를 구분하는 것을 목표

DTO (Data Transfer Object)

  • DTO의 속성 및 역할

    1. 하나의 객체에 여러 타입의 값을 전달하는 일을 수행

      • 데이터 전송 객체라는 의미
    2. Transfer Object를 사용할 때 필드를 private으로 지정하여 getter() 메서드와 setter() 메서드를 만들어야 할 지, 아니면 public으로 지정하여 메서드들을 만들지 않을 지에 대한 정답은 없다.

    3. 성능상으로 따져 볼 때 getter()와 setter() 메서드를 만들지 않는 것이 더 빠르다.

      • 하지만, 정보를 은닉하고, 모든 필드의 값들을 아무나 수정할 수 없게 하려면 getter, setter 메서드를 작성하는 것이 일반적이다.
    4. getEmpName() 메서드처럼empName이 null 값이라도 null을 리턴하지 않고, 길이가 0인 String을 리턴한다.

      • 즉, TransferObject에서 잘 처리하여 각 소스에서 일일이 null 체크를 할 필요가 없기 때문에 개발에 편해진다.
    5. toString() 메서드를 해당 객체에서 구현하지 않고 호출하는 경우 알 수 없는 값을 리턴하게된다.

      • toString() 메서드를 구현하는 경우, 객체에 대한 값 비교를 명확하게 할 수 있다.
    6. Serializable의 구현 이유

      • 이 인터페이스를 구현했다고 해서 추가적으로 구현해야 하는 메서드가 있는 것은 아니다.
      • 그러나 인터페이스를 구현하는 경우 객체를 직렬화할 수가 있다.
      • 다시말해 서버 사이의 데이터 전송이 가능해진다.
      • 원격지 서버에 데이터를 전송하거나, 파일로 객체를 저장할 경우에 인터페이스를 구현해야 한다.
    7. 주로 비동기 처리를 하는 경우 사용

      • SpringBoot에서 Jackson 라이브러리의 ObjectMapper를 사용하여 Json 타입으로 변환
public class EmployeeTO implements Serializable {

    private long empId;
    private String empName;
    private String empPhone;

    public EmployeeTO() {
        super();
    }

    public EmployeeTO(long empId, String empName, String empPhone) {
        super();
        this.empId = empId;
        this.empName = empName;
        this.empPhone = empPhone;
    }

    public long getEmpId() {
        return empId;
    }

    public void setEmpId(long empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        if(empName == null) return "";
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpPhone() {
        return empPhone;
    }

    public void setEmpPhone(String empPhone) {
        this.empPhone = empPhone;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("empId=").append(empId);
        sb.append(" empName=").append(empName);
        sb.append(" empPhone=").append(empPhone);
        return sb.toString();
    }
}

VO (Value Object)

  • VO의 속성 및 역할
    1. 값, 객체를 의미
    2. 핵심 역할인 equals, hashCode를 오버라이딩하여 구현
      • VO 내부에 선언된 속성의 모든 값들이 VO 객체마다 값이 같아야 똑같은 객체로 판별
    3. Getter, Setter를 가질 수 있다.
      • 테이블 내에 있는 속성 외에 추가적인 속성을 가질 수 있다.
      • 여러 테이블에 대한 공통 속성을 모아 만든 BaseVO 클래스를 상속받아 사용할 수 있다.
public class EmployeeVO {

    private long empId;
    private String empName;
    private String empPhone;

    public long getEmpId() {
        return empId;
    }

    public void setEmpId(long empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpPhone() {
        return empPhone;
    }

    public void setEmpPhone(String empPhone) {
        this.empPhone = empPhone;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        EmployeeVO empVo = (EmployeeVO) obj;

        return Objects.equals(empId, empVo.empId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(empId);
    }
}

Entity

  • Entity
    1. DB 테이블 내에 존재하는 컬럼만을 속성 값으로 갖는 클래스
    2. Entity 클래스는 상속을 받거나 구현체여서는 안된다.
    3. 테이블 내에 존재하는 컬럼을 가져서도 안된다.
@Entity
public class EmployeeEntity {

    private long empId;
    private String empName;
    private String empPhone;

}

'Basic > DesignPattern' 카테고리의 다른 글

[Design Pattern] Builder  (0) 2020.07.05

Spring Security 기반의 테스트 코드 작성하기

  • 목표

    • HelloControllerTest (페이지 접근)
    • PostsAPIControllerTest (API 호출 기능)
    • Spring Security로 인한 접근 권한에 연관된 테스트는 Security 옵션이 필요
    • 따라서 작성한 전체 테스트가 정상적으로 수행

Tasks -> verification -> test (전체 테스트)
전체 테스트 확인

  • 기존에 SpringSecurity 설정이 되어 있지 않은 상태에서 작성된 테스트 코드는 Security 적용 시 오류가 발생할 수 있다.

API 호출 및 페이지 접근 Controller 테스트 오류 해결하기

이슈 1. CustomOAuth2UserService를 찾을 수 없습니다.

  • CustomOAuth2UserService
    • 소셜로그인 관련 설정값에 따른 실행
      • 기본적으로 application.properties 파일을 src/main, src/test 모두 읽어서 사용한다.
      • src/test/resources에 application.properties가 없을 경우 src/main/resources에 있는 application.properties를 자동으로 읽는다.
      • src/main/resources/application-oauth.properties는 읽지 못한다.
      • 즉, application-oauth.properties가 test쪽에 파일이 없기때문에 발생하는 오류이다.
      • 해결책은 application.properties에 오류만 안나도록 설정 값을 임의로 넣는다.

test용 설정 파일

  • 설정 파일은 application.yml로 작성하였다.
# server setting
server:
  port: 8085

# jpa setting
spring:
  profiles:
    include: oauth # oauth 관련 properties 추가
  session:
    store-type: jdbc
  h2:
    console:
      enabled: true
  jpa:
    show_sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
  # Test 용 security 설정
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: test
            client-secret: test
            scope:
              - profile
              - email

이슈 2. 302 Status Code

  • PostsAPIController 테스트

    • Spring Security 설정으로 인한 인증되지 않은 사용자의 요청으로 인해 리다이렉션 응답(302 status code)을 주게 된다.
    • 이를 해결하기 위해서는 테스트 메서드에 임의로 인증된 사용자를 추가하여 API만 테스트 할 수 있도록 수정해야 한다.
  • SpringSecurity Test를 위한 spring-security-test 의존성 추가

dependencies {
    // ...
    testCompile('org.springframework.security:spring-security-test')
}
  • PostsAPIController의 테스트 메서드에 임의 사용자 인증 설정 추가
    1. @WithMockUser(roles="USER")
      • 인증된 임의 사용자를 만들어 사용
      • roles 설정으로 권한 추가가 가능
      • 해당 어노테이션으로 인하여 ROLE_USER 권한을 가진 사용자가 API를 요청하는 것과 동일한 효과를 갖는다.
      • @WithMockUser가 MockMvc에만 작동하기 때문에 MockMvc에서 테스트 할 수 있도록 @Before, mvc.perform 기능을 추가
    2. @Before
      • 매번 테스트가 시작되기 전에 MockMvc 인스턴스를 생성
    3. mvc.perform
      • 생성된 MockMvc를 통해 API를 테스트한다.
      • 본문(Body) 영역은 문자열로 표현하기 위해서 ObjectMapper를 통해 문자열 JSON으로 변환
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsAPIControllerTest {
    // ...

    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders
                .webAppContextSetup(webApplicationContext)
                .apply(springSecurity())
                .build();
    }

    @Test
    @WithMockUser(roles="USER")
    public void testSave() throws Exception {
        // ...

        // when
        mockMvc.perform(post(url)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content(new ObjectMapper().writeValueAsString(requestDto)))
                .andDo(print())
                .andExpect(status().isOk());

        // ...
    }

    @Test
    @WithMockUser(roles="USER")
    public void testUpdate() throws Exception {
        // ...

        mockMvc.perform(put(url)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content(new ObjectMapper().writeValueAsString(requestDto)))
                .andDo(print())
                .andExpect(status().isOk());

        // ...
    }
}

이슈 3. @WebMvcTest에서 CustomOAuth2UserService을 찾을 수 없는 문제

  • HelloControllerTest의 경우 위에서 처리한 방법과는 다르다.
    • 위 이슈를 함으로써 SpringSecurity 설정은 작동했지만 @SpringBootTest와 다르게 @MockMvcTest는 CustomOAuth2UserService를 스캔하지 않는다.
    • @WebMvcTest는 WebSecurityConfigurerAdapter, WebMvcConfigurer를 비롯한 @ControllerAdvice, @Controller를 읽는다.
    • 즉, @Repository, @Service, @Component는 스캔 대상이 아니다.
    • SecurityConfig는 읽었지만 SecurityConfig를 생성하기 위해 필요한 CustomOAuth2UserService는 읽을 수가 없어 오류가 발생했던 것이다.
    • 위 문제를 해결하기 위해서는 스캔 대상에서 SecurityConfig를 제거해야 한다.
@RunWith(SpringRunner.class)
@WebMvcTest(
        controllers = HelloController.class,
        excludeFilters = {
                @ComponentScan.Filter(
                        type = FilterType.ASSIGNABLE_TYPE,
                        classes = SecurityConfig.class
                )
        }
)
public class HelloControllerTest {

    @Test
    @WithMockUser(roles="USER")
    public void return_hello() throws Exception {

    }
    @Test
    @WithMockUser(roles="USER")
    public void return_helloDto() throws Exception {

    }
}
  • 원인 분석
    • @EnableJpaAuditing을 사용하기 위해서는 최소 하나의 @Entity 클래스가 필요하다.
    • @WebMvcTest의 경우 @Entity 클래스가 없다.
    • @EnableJpaAuditing와 @SpringBootApplication 설정을 함께 사용하였기 때문에 @WebMvcTest에서 둘다 읽게 된다.
    • 이 문제를 해결하기 위해서는 @EnableJpaAuditing을 사용하기 위한 JpaConfig 설정 클래스를 새로 작성한다.
// @EnableJpaAuditing 제거
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • @EnableJpaAuditing 설정을 사용하는 JpaConfig 클래스
    • JPA Auditing 활성화
@Configuration
@EnableJpaAuditing
public class JpaConfig {}
  • 작성한 패키지 구조 및 파일 확인

  • 테스트 코드 패키지 및 파일 확인

정리하기

  • 스프링 부트 통합 개발환경 설정
  • 테스트, JPA 데이터 처리
  • Mustache를 활용한 화면 구성
  • Security와 OAuth2로 인증, 권한을 적용하여 게시판 작성

+ Recent posts