기록은 기억의 연장선

더 많은 것을 기억하기 위해 기록합니다


  • Home

  • Tags

  • Categories

  • Archives

  • Search

[java] java mysql 연동시 오류

Posted on 2018-12-13 | Edited on 2020-11-02 | In java | Comments:

The server time zone value ‘KST’ is unrecognized or represents more than one time zone

https://yenaworldblog.wordpress.com/2018/01/24/java-mysql-연동시-발생하는-에러-모음/

public key retrieval is not allowed

mysql 8.x 버전 이후로 발생
jdbc url에 allowPublicKeyRetrieval=true&useSSL=false 필요

1
jdbc:mysql://localhost:3306/dev-product?useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false
Read more »

[java] UTF-8, UTF-16과 java 문자형

Posted on 2018-12-08 | Edited on 2020-11-02 | In java | Comments:

UTF-8, UTF-16 이란?

UTF 뒤에 붙는 숫자의 의미는 유니코드 문자 하나를 표현할 때 사용하는 최소 bit를 의미한다
이게 무슨말이냐 하면은, UTF-8의 경우 최소 1byte로 유니코드 문자를 하나 담을 수 있고, UTF-16의 경우 최소 2byte로 유니코드 문자를 하나 담을 수 있다는 의미이다

이 두 인코딩 방식을 이용해 유니코드에서 기본 다국어 평면에 해당하는 BMP 영역의 문자를 담는다고 생각해보자

BMP의 경우 일반적인 다국어 문자 대부분을 포함하며, 범위는 0000 ~ FFFF 까지이다
https://namu.wiki/w/유니코드

UTF-16의 경우 자신이 가지고 있는 최소 공간에 BMP 영역의 모든 문자를 다 담을 수 있지만,
UTF-8의 경우 FF 의 범위가 넘어가는 문자의 경우 자신의 영역을 추가 확장한 뒤 해당 문자를 담아야 한다

이러한 특징으로 봤을때는 무조건 UTF-8이 좋아보인다
00 ~ FF 범위는 1byte를 사용해서 저장할것이고, 100 ~ FFFF 범위는 2byte를 사용해서 저장하면 되기 떄문이다
반면에 UTF-16의 경우 무조건 2byte니 낭비인 것 처럼 보인다

하지만 UTF-8 같은 가변 비트의 경우, 고정된 공간이 아니기 때문에 어디가 문자의 시작인지, 어디가 문자의 끝인지를 표시하는 영역이 추가로 필요하게 된다
이와 같은 이유로 UTF-8은 1byte의 경우 7bit, 2byte는 11bit, 3byte는 16bit 만을 문자저장에 사용할 수 있다
즉, 똑같은 BMP 문자를 저장하더라도 UTF-8은 최대 3byte가 필요하게 되는 것이다

그러므로 처음 인코딩 방식을 선택할때는 이러한 특징을 이용해 인코딩 방식을 선택해야 한다
예를 들어 영문자가 많이 사용되는 시스템에서는 UTF-8을 사용하는 것이 좋고, 다국어가 많이 사용되는 시스템에서는 UTF-16을 사용하는 것이 좋을 것이다
(확실하진 않지만 UTF-16의 경우 문자열 검색에서 더 빠르다고 한다)

웹의 경우 대부분 아스키코드를 사용하므로, UTF-8을 채택했다

그렇다면 java는?

java는 인코딩 방식으로 UTF-16을 채택했다
(이유는 모르겠지만 아마도 ascii 문자의 비율이 웹만큼 높지 않아서가 아닐런지… 그리고 문자열 처리 속도도 이유가 될 수 있을 것 같다)
그러므로 java에서 문자를 저장하기 위해 사용되는 char형의 경우, 2byte의 용량을 차지한다

BMP 영역을 넘어가는 문자(emoji 같은)는 char 형에 저장할 수 없다
String 에 char 배열로 저장하는 것 같다
이 과정에서 4byte 를 쓰게 될 것 같다(char 가 2byte 이므로)
이모지가 4 byte 가 필요하다는 것은 utf8 기준인 것 같다
어쩌피 utf16 은 최소용량이 2 byte 이므로 똑같이 4byte 가 쓰일 것 같긴 하지만…

뭐 이 이상 얘기할 것은 없는것 같고, char형과 관련된 몇가지 특징들은 아래와 같다

  • 문자의 경우 결국 다 유니코드 숫자값이기 때문에 숫자로 저장 가능하다

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    char a1 = 'a';
    char a2 = '97';
    char a3 = '\u0061';

    /**
    출력
    a
    a
    a
    **/
  • 문자와 숫자를 저장하면 숫자가 문자로 형 변환되어 연산된다

    7 + “7” == “77”

  • String의 경우 char의 배열이다

Read more »

[jpa] 다양한 연관관계 매핑

Posted on 2018-12-08 | Edited on 2020-11-02 | In jpa | Comments:

RDB에는 앞서 언급했던 것 보다 더 많은 관계가 존재한다.

  • 1:N(@OneToMany, @ManyToOne)
  • 1:1(@OneToOne)
  • N:M(@ManyToMany)

이를 다중성이라고 한다.
각각의 다중성에서 형성될 수 있는 연관관계들과 그 특징을 나열해보겠다.
모든 다중성은 왼쪽이 연관관계의 주인이라고 가정하겠다(다대일 -> 다가 연관관계의 주인)

다대일

N:1의 관계이고, N이 연관관계의 주인인(외래키를 관리하는) 형태이다.
RDB에서 외래키는 항상 N쪽에 존재한다는 특성에 가장 잘 들어맞는다.
그러므로 대부분 이 형태를 사용한다.

다대일 단방향(N:1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
class Member{
@Id
private String id;

private String username;

@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}

@Entity
class Team{
@Id
private String id;

private String name;
}

Member.team 필드로 TEAM 테이블의 TEAM_ID 외래키를 관리한다.
Member는 Team을 참조할 수 있지만 Team은 Member를 참조할 수 없다.
이 관계는 선택이 아닌 필수이다.

다대일 양방향(N:1, 1:N)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Entity
class Member{
@Id
private String id;

private String name;

@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;

// 연관관계 편의 메서드
}

@Entity
class Team{
@Id
private String id;

private String name;

@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();

// 연관관계 편의 메서드
}

참조가 양쪽 모두 있으므로 연관관계의 주인을 정해야 한다.
RDB에서 1:N, N:1 관계에서 외래키는 항상 N쪽에 있으므로 Member가 연관관계의 주인이 된다.
보다시피 mappedBy 속성으로 연관관계의 주인이 아님을 명시해주고 있다.
아주 일반적인 구조이다.

양방향 연관관계는 항상 서로 참조해야 하므로, 각각 연관관계 편의 메서드를 작성해주는 것이 좋다.
RDB에서는 외래키 하나만 넣어줘도 양방향 관계가 성립하지만, 객체에서는 그렇지 않기 때문이다.

이 관계는 편의를 위한 선택이다.

일대다

1:N의 관계이고, 1이 연관관계의 주인인 형태이다.
외래키는 당연히 N쪽 테이블에 있지만, 관리를 1쪽에서 하므로 관리에 불편함이 있다.
일반적으로 잘 쓰이지는 않는 방법이다.

일대다 단방향(1:N)

일대다 단방향 관계는 JPA 2.0부터 지원한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
class Team{
@Id
private String id;

private String name;

@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<>();
}

@Entity
class Member{
@Id
private String id;

private String username;
}

외래키는 N쪽 테이블에 있으나, N쪽 엔티티에 외래키를 매핑할 수 있는 참조 필드가 없고, 1쪽에만 참조필드가 있다.
단방향 관계에서는 참조를 가진쪽이 외래키를 관리한다.
즉, 이 상태에서는 1쪽에서 외래키를 관리하는, 조금 특이한(다소 불편한) 형태가 나오게 된다.

외래키를 관리해야하므로 보다시피 @JoinColumn을 꼭 명시해줘야 한다.
이를 명시해주지 않으면 JPA는 @JoinTable 전략을 기본으로 사용해버린다.

이렇게 하면 어떤점이 불편할까?

  • 기본적인 RDB의 1:N 구조에 역행하는 방식이므로 관리가 불편하다.

N 엔티티가 직접 외래키를 컨트롤 할 수 없으므로, 외래키를 변경하려면 무조건 Team을 통해야 한다.

  • 성능상 문제도 발생한다.

N 저장 시 연관관계를 설정하고 저장하는 것이 불가능하다.
insert 후 update로 연관관계를 설정해줘야 한다. 쿼리가 2번 필요하다.

아래는 1:N 단방향 형태에서 연관관계를 설정하는 예시이다.

1
2
3
4
5
6
7
8
9
10
public void save(){
Member member = new Member("member1");
// 외래키는 member가 가지지만 외래키를 설정할 수 없는 상황
em.persist(member);

Team team = new Team("team1");
team.getMembers().add(member); // 1이 연관관계의 주인이라 외래키 컨트롤 가능

em.persist(team);
}

실행되는 쿼리는 아래와 같다.

1
2
3
4
insert into Member (MEMBER_ID, username) values (null, ?)
insert into Team (TEAM_ID, name) values (null, ?)

update Member set TEAM_ID=? where MEMBER_ID=?

보다시피 불필요한 UPDATE 쿼리가 발생한다.
Member는 Team의 존재를 모르기 때문에 바로 외래키를 설정할 수 없기 때문이다.

보면 알겠지만 사용될 일이 많이 없는 형태이다.
항상 부모를 통해 접근하고, 자식이 직접 부모를 참조할 일이 없는 구조의 경우 가끔씩 사용하기도 한다.
(이럴 경우라도 위처럼 1쪽에서 의존관계를 관리할 경우는 거의 없다. 대부분 cascade 전략으로 처리한다)

하지만 위와 같지 않고 일반적인 상황이라면 이 구조보다는 다대일 양방향 매핑을 권장한다.

일대다 양방향

일대다 양방향 매핑은 존재하지 않는다.
양방향 연관관계를 형성하게 되면 N쪽 테이블에 @ManyToOne을 명시하게 되는데,
테이블 상에서 외래키를 가진 애가 객체상에서도 참조를 컨트롤 할수 있게 된 상황에서
굳이 1쪽에서 연관관계를 컨트롤 하도록 할 이유가 없다.
(기능이란건 결국 필요에 의해 만들어지는데, 이 기능은 굳이 지원할 이유가 전혀 없다)
그래서 @ManyToOne는 mappedBy 속성 자체가 없다.

근데 뭐… 완전히 불가능한 것은 아니고, 설정할 수는 있다.
기본적으로 일대다 단방향으로 설정하고, N쪽의 단방향 매핑을 읽기전용으로 설정하면 된다.

1
2
3
4
5
6
7
8
9
10
11
@Entity
public class Member {
@Id
private Long id;

private String name;

@ManyToOne
@JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
private Team team;
}

이렇게 까지 사용할 일이 있을라나 모르겠다.

일대일(1:1)

양쪽이 서로 하나의 관계만을 가지는 형태이다. 사람과 사물함의 관계와 같다고 보면 된다.
1:1 구조의 특징은 주 테이블, 대상 테이블 중 어느 곳이던 외래키를 가질 수 있다는 것이다.
즉 일대일 관계에서는 누가 외래키를 가질지 선택해야 한다.

주 테이블에 외래키

객체지향 개발자들이 선호하는 방법이다.
외래키를 객체 참조 비슷하게 사용 할 수 있고, 주 테이블만 확인해도 대상 테이블과의 연관관계를 확인 가능하다.

대상 테이블에 외래키

데이터베이스 개발자들이 선호하는 방법이다.
관계를 일대일에서 일대다로 변경할 떄 테이블 구조를 그대로 유지할 수 있는 장점이 있다.

이제 해당 방식에 대해 객체를 매핑할건데, 결론부터 얘기하자면 JPA는 대상 테이블에 외래키 방식을 지원하지 않는다.
그러므로 연관관계의 주인을 바꿔서 사용하는 방법밖에 없다.

왜 지원하지 않을까 생각해봤는데… 모호한 부분이 많은 듯 하다.
일단 문법적으로, 이런 모양으로 매핑할 수 있는 방법이 없다.
일대다 관계처럼 객체-컬렉션 형태로 구분지어지지도 않기 때문이다.

결국 문법적으로 구분할 수 있는 방법은 mappedBy로 명시해주는 방법밖에 없는데,
이럴려면 무조건 양방향 매핑을 사용해야 하고,
이렇게 해서 대상테이블에 외래키를 구현한다고 해도, 반대편 엔티티에서 보면 어쩌피 또 주 테이블에 외래키 전략이 된다.
나는 … 이러한 이유로 주 테이블에 외래키 전략을 더 선호한다.
너무 RDB의 형태에 갇혀있을 필요는 없는 것 같다.

일대일 단방향(주테이블에 외래키)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Entity
public class Member {
@Id
private Long id;

private String name;

@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
}

@Entity
public class Locker {
@Id
private Long id;
private String naame;
}

양방향(주테이블에 외래키)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Entity
public class Member {
@Id
private Long id;
private String username;

@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
}

@Entity
public class Locker {
@Id
private Long id;
private String naame;

@OneToOne(mappedBy = "locker")
private Member member;
}

나는 객체지향 관점에서 봤을떄, 주테이블에 외래키를 가지는것이 좀 더 객체지향스럽다고 생각하고, 이 방식을 좀 더 선호한다(!!)
사실상 대상 테이블에 외래키를 가지는 것은 RDB 스러운 방법이라고 생각한다.
주 테이블에 외래키를 가지는 식으로 설계하면 주 테이블에서 관계의 개수만큼 외래키를 관리해줘야 하기 때문이다.

근데… 어플리케이션을 만드는 개발자가, 특히 객체지향을 사용하는 개발자가 이런 관점에 굳이 얽매여있을 필요가 있을까?
객체지향의 장점을 끌어올리고자 ORM을 사용하는 입장에서, 그러한 설계에 얽매이고, 굳이 연관관계를 뒤집어 가며 개발해야 할 이유가 있나 생각이 든다…

게다가, 나중에 나오곘지만 ORM에서는 프록시의 한계 때문에 연관관계의 주인이 아닌쪽에서의 lazy 로딩을 허용하지 않는다.
이 말인 즉, 대상 테이블에 외래키를 사용하는 형태로 양방향 관계를 형성하면, 주 테이블 쪽에서는 조회될 때 마다 자신과 연관된 모든 관계를 다 가져와야 한다는 의미가 된다.
(물론 해결 방법은 있다. byte instrument…)

다대다(N:M)

RDB는 다대다 관계를 표현할 수 없다.
그러므로 연결 테이블이라는 것을 사용해야 한다.

회원과 상품은 바로 N:M 관계를 맺을 수 없으니 중간에 주문 같은 테이블을 넣어줘야 N:M 관계를 형성할 수 있다.

반면에 객체는 다대다 관계를 표현할 수 있다.
컬렉션을 사용해서 서로 참조하고 있기만 하면된다.

다대다 관계는 연결 테이블 여부라는 패러다임 차이가 있기 때문에, 이를 풀어줘야 한다.
그러기에 기존의 방식인 다중성 표현 + 외래키 지정으로는 위의 패러다임 차이를 풀 수 없다.
그래서 JPA는 @JoinTable이라는 전략을 사용해 이를 지원한다.

단방향

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 다대다 단방향 회원
@Entity
public class Member {
@Id
private String id;

private String username;

@ManyToMany
@JoinTable(
name = "MEMBER_PRODUCT",
joinColumns = @JoinColumn(name = "MEMBER_ID"),
inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
private List<Product> products = new ArrayList<>();
}

@Entity
public class Product {
@Id
private String id;

private String name;
}

다대다 관계이므로 @ManyToMany를 사용하였고, 위에서 언급한 @JoinTable이 등장하였다.
@ManyToMany는 별다른거 없고(진짜 그런건 아니지만), @JoinTable의 속성 대해 알아보자.

  • name : 연결 테이블을 지정한다
  • joinColumns : 현재 방향에서 매핑할 조인 컬럼 정보
  • inverseJoinColumns : 반대 방향에서 매핑할 조인 컬럼 정보

이 정보들을 기반으로 연결 테이블을 생성한다.
이로 인해 우리는 연결 테이블을 전혀 신경쓰지 않아도 된다!

아래는 저장하는 코드다.

1
2
3
4
5
6
7
8
9
10
11
12
13
public void save() {
Product product = new Product();
product.setId("productA");
product.setName("상품A");
em.persist(productA);

Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
member.getProducts().add(ProductA); // 연관관계 설정

em.persist(member1);
}
1
2
3
INSERT INTO PRODUCT ...
INSERT INTO MEMBER ...
INSERT INTO MEMBER_PRODUCT ...

연결 테이블에 데이터가 저장된다.

아래는 탐색하는 코드이다.

1
2
3
4
5
6
7
8
public void find() {
Member member = em.find(Member.class, "member1");
List<Product> products = member.getProducts(); // 객체 그래프 탐색

for(Product product : products) {
System.out.println("product.name = " + product.getName());
}
}
1
2
3
4
SELECT * 
FROM MEMBER_PRODUCT MP
INNER JOIN PRODUCT P ON MP.PRODUCT_ID=P.PRODUCT_ID
WHERE MP.MEMBER_ID=?

연결 테이블과 조인해서 데이터를 들고온다.

양방향

다대다의 반대 또한 다대다이므로, @ManyToMany로 연결해주면 된다.

1
2
3
4
5
6
7
8
@Entity
public class Product {
@Id
private String id;

@ManyToMany(mappedBy = "products")
private List<Member> members;
}

mappedBy로 연관관계의 주인만 지정해주면 된다.
사실상 연결 테이블로 관리되는 다대다 관계에서는 연관관계의 주인이 별로 의미가 없다…
물론 위의 상황에서는 Member만이 연관관계를 컨트롤할 수 있지만,
연관관계 편의 메서드만 추가해줘도 양쪽에서 컨트롤 할 수 있게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Entity
public class Member {
@Id
private String id;

private String username;

@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT"
,joinColumns = @JoinColumn(name = "MEMBER_ID")
,inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
private List<Product> products = new ArrayList<>();

// 연관관계 편의 메서드
public void addProduct(Product product){
if(!this.products.contains(product)){
this.products.add(product);
}

if(!product.getMembers().contains(this)){
product.getMembers().add(this);
}
}
}

@Entity
public class Product {
@Id
private String id;

private String name;

@ManyToMany(mappedBy = "products")
private List<Member> members;

// 연관관계 편의 메서드
public void addMember(Member member){
if(!this.members.contains(member)){
this.members.add(member);
}

if(!member.getProducts().contains(this)){
member.getProducts().add(this);
}
}
}

연관관계의 주인이 아닌쪽에서 편의메서드를 사용하면 결국 연관관계의 주인쪽에도 추가되므로, 연결 테이블이 영향을 받게 된다.
즉, 편의메서드를 통하면 양쪽에서 다 컨트롤 가능하다.

다대다에서는 연관관계 편의메서드를 작성하지 않는것이 좋아보인다. side effect가 많다.

다대다의 한계

@ManyToMany를 사용하면 연결 테이블을 알아서 관리해주므로 여러모로 편리하지만, 실제 실무에서는 이 정도로만 사용하기에는 한계가 있다.
MEMBER_ID와 PRODUCT_ID만 담지 않고, 추가적인 정보를 담는 경우가 많기 떄문이다.
(날짜, 수량등을 추가해서 ORDER 테이블로 사용한다거나…)

하지만 이렇게 컬럼을 추가하면 더이상 @ManyToMany를 사용할 수 없게된다.
추가 컬럼을 정의한 연결 테이블에 매핑되는 엔티티를 만들어야하고, 테이블간의 관계도 다대다에서 일대다, 다대일의 관계로 풀어야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Entity
public class Member {
@Id
private String id;

private String username;

@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts = new ArrayList<>();
}

@Entity
public class Product {
@Id
private String id;

private String name;

// 여기도 필요에 따라 추가할 수 있다
}

@Entity
@IdClass
public class MemberProduct{
@Id
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;

@Id
@ManyToOne
@JoinColumn(name = "PRODUCT_ID")
private Product product;

private Integer orderAmount;

@Temporal(TemporalType.TIMESTAMP)
private Date orderDate;
}

(현재 @IdClass라는 것을 사용해서 복합키를 매핑하였는데, 이는 뒷부분에서 다룬다.)
추가적인 컬럼을 가진 MemberProduct를 정의하였다.

  • 외래키를 직접 관리하므로 이 엔티티가 연관관계의 주인이 된다. @JoinColumn을 선언했음을 볼 수 있다.
  • Member 엔티티는 외래키를 관리하지 않으므로 mappedBy 속성을 줘서 연관관계의 주인이 아님을 명시했다.
  • Product 엔티티에서 직접 MemberProduct를 참조할 일이 없다고 판단해서 연관관계를 추가하지 않았다.

실무(아니 그냥 일반적으로)에서는 위와 같은 방식으로 더 많이 사용된다.
사용하는 방식은 일반적인 다대일, 일대다 관계와 같다.

Read more »

[jpa] 양방향 연관관계에서 서로간 컨트롤 할수있는 범위

Posted on 2018-12-06 | Edited on 2020-11-02 | In jpa | Comments:

전제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Entity
class Member{
@Id
@GeneratedValue
@Column("id")
private Integer id;

@Column("name")
private String name;

@Column("age")
private String age;

@ManyToOne(optional = false)
@JoinColumn(name = "team_id")
private Team team;
}

@Entity
class Team{
@Id
@GeneratedValue
@Column("id")
private Integer id;

@Column("name")
private String name;

@OneToMany(mappedBy = "team")
private List<Member> members;
}

기본기능

  1. Member
    외래키 컨트롤 가능
    Team 업데이트 가능

  2. Team
    외래키 컨트롤 불가능
    자신에게 속한 모든 Member들 update 가능

연관관계 편의메서드를 추가함으로써 Member에서도 외래키 컨트롤 가능

remove 연관관계 편의 메서드 + 삭제 기능 을 제공하는 orphanRemoval이 있음
일반적인 연관관계 편의메서드는 delete 까지 수행하진 않고, FK를 null 처리함

Read more »

[java] enum 정의 및 활용예제

Posted on 2018-12-05 | Edited on 2020-11-02 | In java | Comments:

enum 정의

http://www.nextree.co.kr/p11686/

상수 -> interface 내 상수 -> 클래스 -> enum

enum 활용사례

https://jojoldu.tistory.com/137

Read more »

[db] 파티셔닝, 샤딩

Posted on 2018-12-04 | Edited on 2020-11-02 | In db | Comments:

파티셔닝 : https://nesoy.github.io/articles/2018-02/Database-Partitioning
샤딩 : https://nesoy.github.io/articles/2018-05/Database-Shard

테이블을 나누는 과정 자체를 파티셔닝이라고 한다

  • Vertical Partitioning

    정규화와는 달리 이미 정규화된 테이블을 세로로 자른다

  • Horizontal Pratitioning

    이를 다른말로 샤딩이라고 한다

Read more »

process concept

Posted on 2018-12-02 | Edited on 2020-11-02 | In os | Comments:

복잡한 컴퓨터 시스템에서 가장 중요한것 == abstraction, decompositon
descomposition : 복잡한 문제를 단순한 여러개의 문제로 나누는 방법론

process == program in execution(수행중인 프로그램)

OS == 정부. 시민들을 관리한다(법을 만들어 시민들을 통제하는 등)
프로세스 == 시민, 수행의 주체, 자원할당의 주체

프로세스는 OS위에서 프로그램을 실행시키는 주체
OS의 입장에서 가장 중요한 단위

decomposition의 한 유닛이 프로세스이다.
각각의 쪼개진 조각들을 하나하나 실행할 수 있다면 편리하다.
decomposition 유닛들이 궁극적으로 수행의 단위까지 된 것 == 프로세스

Program과 Process의 차이점

  • Program
    스토리지만 점유한다.
    수동적인 존재이다.

  • Process
    CPU, memory, IO device 등등을 점유한다.
    능동적인 존재이다.

Process State

프로그램이 수행되는데 필요한 정보, 수행의 결과로 영향을 받을 수 있는 정보들

  1. Memory Context
  • code segment : 어셈블리어
  • data segment : 프로그램의 전역변수들
  • stack segment : 프로그램의 지역변수, 매개변수들
  • heap segment : 동적할당
  1. Hardware Context
  • cpu register
  • I/O register
  1. System Context
    OS가 여러개의 process들을 관리해야 하다보니 각 프로세스들의 정보들을 저장해둔다.
  • process table
  • open file table
  • page table

Executon Stream

프로세스가 수행한 모든 명령어들의 순서(sequence)

Multi programming, Multi Processing

  1. Multi Programming
    메모리의 입장이다.
    메인 메모리에 액티브한 프로세스가 여러개 올라와있는 상태를 말한다.

옛날에는 메모리의 용량이 작았기때문에 메인 메모리에 현재 실행되는 프로세스만 올라가고,
사용하지 않는 프로세스는 다른 저장 장치로 내보내는 식으로 멀티 프로그래밍을 구현하였다.
이를 swapping이라고 한다.
요즘은 메모리의 용량이 크므로 swapping 하지는 않는다.

  1. Multi Processing
    Multi Programming을 CPU의 관점에서 바라본 것이다.
    CPU는 하나의 명령만 수행가능하므로 계속 스위칭 하면서 여러 프로세스들을 실행한다.

Sw system 개발

  1. 설계

요구사항 명세서 -> 설계(decomposition) -> tasks

  1. 구현

tasks -> 구현 -> program

이 program이 OS에 의해 process가 된다.

Read more »

[jpa] 연관관계 매핑 기초

Posted on 2018-12-01 | Edited on 2020-11-02 | In jpa | Comments:

태아불(엔티티)이 서로 연관관계를 가질 때 드러나는 JPA와 SQL 패러다임 차이가 있다.
바로 조인과 참조이다.

SQL은 외래키라는 것을 통해 테이블끼리 관계를 가지고, 조인이라는 것을 통해 두 테이블의 모든 데이터에 접근 가능하다.
SQL의 경우 외래키만 있으면 어느쪽에서든 조회가 가능하다. 기본적으로 양방향이다.

하지만 객체에서는 이런 행위가 불가능하다.
엔티티간의 관계는 참조를 통해 형성된다.
클래스의 필드로 다른 클래스를 가지고 있어야하며, 한쪽으로만 접근, 즉 단방향 탐색만 가능하다.

객체 연관관계 vs 테이블 연관관계

  1. 객체
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Setter
@Getter
class Mamber{
private String id;
private String username;

private Team team;
}

@Setter
@Getter
class Team{
private String id;
private String name;
}

public void save(){
// 연관관계 세팅
Team team = new Team("team1", "어벤져스");
Member member1 = new Member("member1", "멤버1");
Member member2 = new Member("member2", "멤버2");

member1.setTeam(team);
member2.setTeam(team);

// 연관관계 탐색
Team foundTeam = member1.getTeam();
assertThat(foundTeam.getName(), is("어벤져스"));
}

위처럼 참조를 통해 연관관계를 탐색하는 것을 객체 그래프 탐색 이라고 한다.

  1. 테이블
1
2
3
4
SELECT T.*
FROM MEMBER M
INNER JOIN TEAM T ON M.TEAM_ID = T.ID
WHERE M.MEMBER_ID = 'member1'

위처럼 외래키를 통해 연관관계를 탐색하는 것을 조인 이라고 한다.

위와같은 패러다임을 풀기위해 나온것이 방향이라는 개념이고,
여기서 단방향, 양방향의 개념이 나온다.

단방향 연관관계

우리는 ORM을 사용중이다. 위의 객체연관 관계를 그대로 활용하되, JPA에게 알려주기만 하면 된다.

1
2
3
4
5
6
7
8
9
10
@Setter
@Entity
class Member{
private String id;
private String username;

@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}

단방향이므로 Team 쪽에 따로 해줄것은 없다.

  1. @ManyToOne
    N:1의 관계라는 것을 나타내주는 어노테이션이다.
    Teamp 하나에 Member 여러개가 소속될 수 있기 때문이다.
    사용할 수 있는 옵션은 아래와 같다.
속성 기능 기본값
optional FK nullable 한지의 여부이다 true
referencedColumnName 외래 키가 참조하는 대상 테이블의 컬럼명 참조하는 테이블의 기본키 컬럼명
fetch lazy로딩, eager 로딩을 설정할 수 있다. @ManyToOne = FetchType.EAGER, @OneToMany = FetchType.LAZY

optonal 속성에 따른 쿼리 방식
이 값이 true일 경우 JPA는 N쪽 테이블을 조회해온 후, fk 값에 따라 조회를 1쪽 테이블을 추가로 조회하거나(null일 경우 조회하지 않음) LEFT OUTER JOIN을 사용한다.
무작정 INNER JOIN을 하면 fk가 null일 경우 출력되지 않을 것이므로, 당연한 결과다.

반대로 false로 설정하면 바로 INNER JOIN으로 처리한다.
테이블 설계를 FK NOT NULL로 해도 이 속성값이 true일 경우 LEFT OUTER JOIN 등으로 처리하므로, FK NOT NULL일 경우에는 false로 주는 것이 좋다.

  1. @JoinColumn
    관계에 사용되는 외래키를 작성하는 부분이다.
    (이 외래키(TEAM_ID)에 해당하는 엔티티는 이것(Team)이다 라고 보면 편하다)
    결과적으로 객체를 RDB와 매핑할 것이기 때문에, 이렇게 하나라도 더 알려줘야 탐색의 시간을 줄일 수 있다.(리플렉션으로 객체의 모든 값을 탐색하며 관계를 알아내기에는 너무 낭비이기 떄문에)
    사용할 수 있는 옵션은 아래와 같다.
속성 기능 기본값
name 매핑할 외래키 이름 필드명 + _ + 참조하는 테이블의 기본 키 컬럼명
referencedColumnName 외래키가 참조하는 대상 테이블의 컬럼명 참조하는 테이블의 기본키 컬럼명
foreignKey(DDL) 외래키 제약조건 설정 가능

연관관계 사용

저장

1
2
3
4
5
6
7
8
9
10
11
12
13
public void save(){
Team team = new Team("team1", "어벤져스");
em.persist(team);

Member member1 = new Member("member1", "멤버1");
member1.setTeam(team);

Member member2 = new Member("member2", "멤버2");
member2.setTeam(team);

em.persist(member1);
em.persist(member2);
}

객체간에 관계를 맺고 persist를 땋! 때려주면

1
2
3
4
INSERT INTO TEAM VALUES("team1", "어벤져스");

INSERT INTO MEMBER VALUES("member1", "멤버1", "team1");
INSERT INTO MEMBER VALUES("member2", "멤버2", "team1");

처럼 team의 id값이 member의 외래키 값으로 세팅되어 저장된다.

엔티티 저장 시 연관된 모든 엔티티는 영속 상태여야 한다.
존재하는 엔티티라는 것이 보장되어야 하기 때문이다.

이러한 특징 때문에 비효율적이라고 생각할 수 있다.
외부에서 명확한 identity가 넘어왔음에도 불구하고, find로 조회해서 영속성 컨텍스트에 넣어줘야 하기 때문이다.
사실상 외부에서 명확한 identity가 넘어왔음에도 불구하고는 우리의 입장이지, framework는 그것을 모른다. 그러므로 고집을 부릴수는 없는 노릇…
조금 다른 방식으로 풀어볼 수는 있다(em.getReference)

조회

1
2
3
4
5
6
public void find(){
Member memver = em.find(Member.class, "member1");
Team team = member.getTeam();

assertThat(team.getName(), is("어벤져스");
}

객체 그래프 탐색으로 매우 간단하게 찾아갈 수 있다.
(또는 JPQL로도 조회 가능하다)

1
2
3
4
5
6
-- optional = false
SELECT M.*
FROM MEMBER M
INNER JOIN TEAM T ON M.TEAM_ID = T.ID
WHERE
M.ID = "member1";

수정

1
2
3
4
5
6
public void update(){
Team team = em.find(Team.class, "team2");

Member givenMember = em.find(Member.class, "member1");
givenMember.setTeam(team);
}

변경감지가 동일하게 동작하여 update문이 발생하게 된다.

1
2
3
4
5
UPDATE MEMBER
SET
TEAM_ID = 'team2', ...
WHERE
ID = 'member1'

연관관계 제거

1
2
3
4
public void remove(){
Member givenMember = em.find(Member.class, "member1");
givenMember.setTeam(null);
}

위처럼 null로 세팅해 연관관계를 제거해줄 수도 있다.
fk인 team_id가 null로 세팅된다.

1
2
3
4
5
UPDATE MEMBER
SET
TEAM_ID = null, ...
WHERE
ID = 'member1'

삭제

1
2
3
4
5
6
public void save(){
member1.setTeam(null);
member2.setTeam(null);

em.remove(team);
}

연관관계를 제거해주지 않고 삭제할 경우 외래키 제약조건에 걸리므로, 관계 제거를 선행해줘야 한다.

양방향 연관관계

현재는 Member -> Team의 관계만 형성되어있는데(객체지향 관점에서)
Team -> Member의 관계까지 추가하면 양방향 연관관계가 성립된다.

Member -> Team이 N:1 관계였으므로, Team -> Member는 1:N의 관계를 가진다.

관계는 반대편 관계에 달려있다. 반대편이 1:N 관계일 경우 N:1, 1:1일 경우 1:1 관계를 가진다.
자바에서 1:N의 관계를 표현하려면 배열을 사용해야하는데, JPA에서는 여기서 Collection을 사용한다.(List, Set, Map 등)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Setter
@Entity
class Team{
private String id;
private String name;

@OneToMany(mappedBy = "team")
private List<Member> memberList;
}

public void find(){
Team givenTeam = em.find(Team.class, "team");

List<Member> givenMembers = givenTeam.getMembers();

// do something...
}

mappedBy 속성은 양방향 매핑일 떄 사용하는데 반대쪽 매핑의 필드 이름을 값으로 주면 된다.

연관관계의 주인

SQL의 경우 기본적으로 양방향 연관관계를 가지지만,

위에서도 언급했지만, SQL의 경우 외래키 하나로 양방향 연관관계가 형성된다

객체지향의 경우 양방향 연관관계라는 것이 애초에 없다.
단방향 연관관계 2개를 로직으로 잘 묶어서 양방향 연관관계처럼 보이게 하는 것 일 뿐이다.

근데 이렇게 양방향으로 연관관계를 형성해주면 결과적으로 연관관계를 컨트롤 해줄 수 있는 곳이 2군데가 생기게 된다.
하지만 SQL의 경우 언급했다시피, 연관관계를 컨트롤 하는 곳은 단 한군데(외래키)이다.
즉 이 객체들이 SQL로 매핑되려면, 누가 연관관계를 컨트롤하는지 알려줘야 하고, 컨트롤하는 주체를 연관관계의 주인 이라고 하는 것이다.

말이 거창하지만, 그냥 외래키 관리자를 말하는 것이다.
일반적인 상황에서는 그냥 테이블상에서 외래키를 갖고있는 엔티티가 연관관계의 주인이 된다.

연관관계의 주인은 양방향 연관관계를 가졌을떄만 지정해주면 된다.
단방향으로 지정했을 경우에는 ORM 입장에서 혼동스러울 부분이 없기 때문이다.

아까 위에서 양방향 연관관계를 맺으면서 mappedBy 속성을 사용했는데, 이 속성이 곧 연관관계의 주인을 알려주는 속성이다.
mappedBy 속성 지정에는 아래와 같은 룰이 존재하는데, 이를 보면 용도를 알 수 있다.

  • 주인은 mappedBy 속성을 사용하지 않는다
  • 주인이 아니면 mappedBy 속성을 사용해서 연관관계의 주인을 지정해야 한다.

결국 위에서도 mappedBy 속성을 사용함으로써 내가 연관관계의 주인이 아니라고 알려주는 것이다.

ORM 입장에서는 @OneToMay에서 참조하는 클래스를 탐색한 뒤, mappedBy에 명시된 필드를 찾아가 외래키 정보를 얻을 것이다(아마도)

1:N 관계에서 외래키를 관리하는 쪽은 N 쪽이기 때문이다.
(그래서 @ManyToOne에 mappedBy 속성이 없다)

ORM 입장에서도 이 개념이 중요하게 작용하는게,
연관관계의 주인만이 연관관계와 매핑되는 외래키를 관리(등록, 수정, 삭제)할 수 있고,
주인이 아닌 쪽은 읽기만 가능하게 된다.

Team의 @OneToMany에 있는 members의 원소들을 백날 더하고 빼고 해봤자 아무일도 일어나지 않는다(ㅋㅋ)

1
2
3
4
5
6
7
8
9
10
11
public void owner(){
Team team = em.find(Team.class, "team1");

Member member1 = em.find(Member.class, "member1");
Member member2 = em.find(Member.class, "member1");

team.getMembers().add(member1); // 무시됨
team.getMembers().add(member2); // 무시됨

member.setTeam(team); // 설정됨
}

연관관계의 주인만이 연관관계(외래키)를 컨트롤 할 수 있다는걸 명심하자.

연관관계 편의 메서드

사실상 위의 행위는 객체지향 관점에서 보면 좀 문제가 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void save{
Team team = new Team("team1", "어벤져스");
em.persist(team);

Member member1 = new Member("member1", "멤버1");
Member member2 = new Member("member2", "멤버2");

member1.setTeam(team);
member2.setTeam(team);

em.persist(member1);
em.persist(member2);
}

public void find{
Team team = em.find(Team.class, "team1");

List<Member> members = team.getMembers();
assertThat(members.size(), is(2));
}

Team 내에 있는 List 에 아무도 값을 넣어준적이 없는데 find 메서드가 정상 동작한다.
이는 hibernate라는 애가 중간에 있기 때문인데, 객체지향을 중요시하는 ORM을 사용하면서 위처럼만 놔두게 되면 결국 RDB 와 다를게 없다고 생각된다.
(만약 ORM Framework가 중간에 없었다면 심각한 오류를 발생시켰을 것이다.)

그러므로 순수한 객체까지 고려하는, 연관관계 편의 메서드라는 것을 작성해줘야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Member{
public void setTeam(Team team){
this.team = team;
this.team.getMembers().add(this);
}
}

class Team{
public void addMembers(Member member){
this.members.add(member);
member.setTeam(this);
}
}

N 쪽에 set, 1 쪽에 add가 항상 묶여서 수행되도록 작성했다.

연관관계 편의 메서드 작성 시 주의사항

사실상 위의 연관관계 편의 메서드는 싱크를 완벽히 맞춰주는 메서드는 아니다.
위와 같은 편의메서드를 사용할 경우 아레와 같은 버그를 막을 수 없다.

1
2
3
4
member1.setTeam(team1);
member1.setTeam(team2); // team2로 변경

Member foundMember = team1.getMember(); // member1이 여전히 조회된다

별로 마주할 일 없는 시나리오라고 생각할 수 있으나, 그렇게 따지면 항상 hibernate를 거치면 되므로 연관관계 편의 메서드 자체도 필요가 없어지게 된다.
하지만… 위에서도 언급했지만 그건 정말 위험한 코드이다. 언제 어디서 예기치 못한 오류가 발생할지 모른다.

ORM으로 바라보기 보다 객체지향으로 먼저 바라봐야 한다고 (나는) 생각한다.(ㅋㅋ)
좀 더 완벽하게 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Member{
public void setTeam(Team team){
if(this.team != null){
this.team.getMembers().remove(this);
}

this.team = team;

if(team != null && !team.getMembers().contains(this)){
this.team.getMembers().add(this);
}
}
}

class Team{
public void addMember(Member member){
if(!this.members.contains(member)){
this.members.add(member);
}

member.setTeam(this);
}
}

무한루프에 빠질 수 있는 가성성과 위의 버그를 제거하였다.
양방향 연관관계를 형성할 때는 항상 위처럼 연관관계 편의 메서드를 만들어줘야 한다(필수!).

Read more »

DNS 기본 동작

Posted on 2018-11-25 | Edited on 2020-11-02 | In etc | Comments:

정의

https://netmanias.com/ko/?m=view&id=blog&no=5353

PC -> local DNS -> Root DNS -> Top level DNS -> Second level DNS… -> PC

Read more »

failover

Posted on 2018-11-25 | Edited on 2020-11-02 | In etc | Comments:

정의

http://www.terms.co.kr/failover.htm

1차 시스템에 장애가 발생했을 때 2차 시스템에서 이를 받아 수행하는, 무중단 서비스를 위한 방식.

Read more »
1…121314…19

JunYoung Park

182 posts
18 categories
344 tags
RSS
© 2020 JunYoung Park
Powered by Hexo v3.6.0
|
Theme – NexT.Muse v7.1.0