객체 지향 프로그래밍에서의 객체와 관계형 데이터베이스의 테이블간의 차이를 해결하기 위한 연관관계 매핑은 JPA에서 정말 중요한 부분이다.

연관관계 매핑에 관한 용어들 먼저 정리하면 다음과 같다.

  • 방향(Direction): 단방향, 양방향
  • 다중성(Multiplicity): 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:N)
  • 연관관계의 주인(Owner): 객체 양방향 연관관계에서의 관리 주인

여기서는 방향(단방향, 양방향)에 따른 연관관계 매핑 방법을 설명하려 한다.

 

회원과 팀이 있고 회원은 하나의 팀에만 소속될 수 있다고 하자. 즉, 회원과 팀은 다대일 관계이다.

먼저 객체를 테이블에 맞춰 모델링 하면 다음과 같다.

위 그림에서 MEMBER테이블에는 TEAM_ID라는 외래 키가 있고, Member객체에는 teamId라는 필드가 있다. 이를 코드로 나타내면 다음과 같다.

@Entity
@Getter @Setter
public class Member { 
   @Id @GeneratedValue
   private Long id;
   
   @Column(name = "USERNAME")
   private String name;
   
   @Column(name = "TEAM_ID")
   private Long teamId; 
 } 
 
 @Entity
 @Getter @Setter
 public class Team {
   @Id @GeneratedValue
   private Long id;
   
   private String name; 
 }

이러한 엔티티를 저장하고 조회하는 방법은 다음과 같다.

//팀 저장
 Team team = new Team();
 team.setName("TeamA");
 em.persist(team);
 
 //회원 저장
 Member member = new Member();
 member.setName("member1");
 member.setTeamId(team.getId());
 em.persist(member);
 
//Member 조회
 Member findMember = em.find(Member.class, member.getId()); 
 
 //연관관계가 없음
 Team findTeam = em.find(Team.class, team.getId());

테이블은 외래 키를 통해 조인연산을 하여 연관된 테이블을 찾는 반면, 객체는 참조를 사용해서 연관된 객체를 찾는다.

이처럼 관계형 데이터베이스의 테이블과 객체 사이에는 큰 간격이 있다.

 

 

우리는 연관관계 매핑을 통해 테이블과 객체 사이의 간격을 매울수 있다.

이제 연관관계를 사용한 객체 지향 모델링을 살펴보자.

위 그림처럼 테이블 연관관계에서는 MEMBER테이블에 TEAM_ID라는 외래키가 있고,

객체 연관관계에서는 Member객체에 Team객체를 참조하는 team필드가 있음을 알 수 있다.

이를 코드로 표현하면 다음과 같다.

@Entity
@Getter @Setter
public class Member { 

  @Id @GeneratedValue
  private Long id;
  
  @Column(name = "USERNAME")
  private String name;
  private int age;
  
  @ManyToOne
  @JoinColumn(name = "TEAM_ID")
  private Team team;
 }
 
 @Entity
 @Getter @Setter
 public class Team {
 
   @Id @GeneratedValue
   private Long id;
   
   private String name; 
 }

여기서 @ManyToOne 어노테이션은 다대일(Member객체 입장에서 Team과의 관계) 매핑정보임을 나타낸다.

@JoinColumn은 외래키를 매핑 할 때 사용되는데, name은 매핑할 외래키의 이름을 지정한다.

 

이런식으로 엔티티를 설계한 경우 저장, 조회, 수정하는 코드는 다음과 같다.

// 팀 저장
 Team team = new Team();
 team.setName("TeamA");
 em.persist(team);
 
 //회원 저장
 Member member = new Member();
 member.setName("member1");
 member.setTeam(team); //단방향 연관관계 설정, 참조 저장
 em.persist(member);
 
 //조회
 Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
 Team findTeam = findMember.getTeam();
 
 // 새로운 팀B
 Team teamB = new Team();
 teamB.setName("TeamB");
 em.persist(teamB);
 // 회원1에 새로운 팀B 설정
 member.setTeam(teamB);

 

이 시점에서 우리는 방향(단방향, 양방향)에 대해 얘기할 수 있다.

관계형 데이터베이스의 경우 외래키만으로 두 테이블의 관계를 나타낼 수 있다. 하지만 우리는 앞서 말했듯이 연관관계를 이용한 객체 지향 모델링을 하고자 한다.

이 경우 두 엔티티 사이의 관계를 외래키(관계된 상대 테이블의 id값)가 아닌 상대 객체 자체를 참조하여 관계를 나타낸다.

이때 위에 엔티티를 정의한 코드에서처럼 Member 엔티티에만 Team객체를 참조하게 되면 단방향 연관관계라 하고

Team 엔티티에서는 자체적으로 자신에게 속한 Member들이 어떤것들이 있는지 알 수 없게 된다.

즉, 단방향 연관관계만으로는 team.getMembers()같은 코드를 구현할 수 없게 된다.

이를 해결하기 위해 등장한게 양방향 연관관계이다. 양방향 연관관계를 표현한 객체와 테이블 사이의 관계는 다음과 같다.

 @Entity
 public class Member {
 	@Id @GeneratedValue
 	private Long id;
    
 	@Column(name = "USERNAME")
 	private String name;
 	private int age;
    
 	@ManyToOne
 	@JoinColumn(name = "TEAM_ID")
    	private Team team;
   	...
 }
 
 @Entity
 public class Team {
 	@Id @GeneratedValue
 	private Long id;
 	private String name;
    
 	@OneToMany(mappedBy = "team")
 	List<Member> members = new ArrayList<Member>();
 	...
 }

단방향 연관관계와는 다르게 Team 엔티티에 List형태의 members필드가 추가된것을 확인할 수 있다.

이 필드를 통해 이제 Team에서도 어떤 Member들이 속해 있는지를 알 수 있게 된다.

하지만 양방향 연관관계에서는 주의해야할 점이 있다. 바로 연관관계의 주인을 정해야 하는것이다.

만약 Member 객체의 team필드를 수정하였지만 해당관계를 동일하게 나타내는 Team 객체의 members필드는 수정하지 않았다고 가정해보자.

이때 jpa는 수정을 반영해야할지 말아야할지 결정을 내려야만 한다. 때문에 연관관계의 주인이라는 개념이 필요하고

jpa는 연관관계의 주인인 쪽의 값을 반영하여 테이블을 관리하게 된다.

다르게 말하면 주인이 아닌쪽은 수정이 받아들여지지 않고 오직 읽기만 가능하다.

예를들어 Member가 Member와 Team사이 관계의 연관관계 주인이라면 team.getMembers().add(member)같은 코드는 데이터베이스에 반영되지 않는다.

 

연관관계의 주인을 정하는 방법은 간단하다. 외래 키가 있는 곳을 주인으로 정하면 된다.

위를 예시로 들면, Member와 Team사이의 관계에서 Member(Member.team)를 연관관계의 주인으로 하면 된다.

연관관계의 주인을 정했으면, 이제 연관관계의 주인만이 외래 키를 관리(등록, 수정)해야 한다.

주인이 아닌쪽은 읽기만 가능하며, mappedBy 속성으로 자신의 주인을 지정해 주면 된다.

 

양방향 연관관계에서 주의할 점은 연관관계를 나타내는 참조 값(Member의 team필드 또는 Team의 members필드)을 수정할때 양쪽 모두 수정해야 한다는 것이다.

그렇지 않으면 데이터베이스에서 받아오는 정보와 1차캐시에 있는 정보의 불일치가 일어나는등의 문제가 생길 수 있다.

 

참조: 참고: 자바 ORM 표준 JPA 프로그래밍 (https://www.inflearn.com/course/ORM-JPA-Basic#)

알고스팟 문제 링크: https://algospot.com/judge/problem/read/SOLONG

 

algospot.com :: SOLONG

안녕히, 그리고 물고기는 고마웠어요! 문제 정보 문제 문자 입력이 불편한 핸드폰이나 타블렛 같은 기계에서는 빠른 타이핑을 위해 강력한 자동 완성 기능을 제공합니다. 시리우스 사이버네틱

algospot.com

사전에 들어있는 단어들이 빈도수와 함께 주어진다. 특정한 문장을 완성시키려할때 탭키를 사용하여 사전을 참조한 자동완성 기능을 사용할 수 있다.

타이핑한 알파벳을 접두사로 갖는 단어들의 빈도수를 우선순위로 하고 빈도수가 같은  단어가 두개 이상있으면 사전순을 우선순위로 한다.

이때 특정한 문장을 완성시키기 위한 총 타이핑 횟수를 구하는 문제이다.

 

일단 사전에 들어갈 문자들을 트라이 형태로 삽입한다. 이때 삽입하는 단어들을 빈도수와 사전순을 고려하여 정렬한 후 차례로 삽입한다.

이런식으로 삽입할 경우 자동완성을 위한 first를 한번만 갱신하면 되므로 자동완성 단어를 계산하기 위한 메모리와 시간이 최소화 된다.

 

코드 원본: https://github.com/sbl133/JongmanBook/blob/main/26.%20Trie/solong.cpp

 

GitHub - sbl133/JongmanBook

Contribute to sbl133/JongmanBook development by creating an account on GitHub.

github.com

댓글을 통한 코드리뷰, 질문, 지적 언제든 환영입니다!

reference: 프로그래밍 대회에서 배우는 알고리즘 문제해결전략2

BOJ 문제 링크: https://www.acmicpc.net/problem/1520

 

1520번: 내리막 길

첫째 줄에는 지도의 세로의 크기 M과 가로의 크기 N이 빈칸을 사이에 두고 주어진다. 이어 다음 M개 줄에 걸쳐 한 줄에 N개씩 위에서부터 차례로 각 지점의 높이가 빈 칸을 사이에 두고 주어진다.

www.acmicpc.net

각 칸마다 높이가 적혀있는 지도가 입력으로 주어진다. 현재지점보다 낮은 지점으로 상하좌우 이동이 가능하다.

좌측 상단에서 시작해서 우측 하단으로 도착하는 경우의 수를 구하는 문제이다.

 

현재 위치 (curX, curY) 에서 도착지점 (n-1, m-1) 까지 갈수있는 경로의 수를 cache[curX, curY]에 저장하는 다이나믹 프로그래밍을 이용하면 문제를 풀 수 있다.

 

댓글을 통한 코드리뷰, 질문, 지적 언제든 환영입니다!

'Algorithm > BOJ' 카테고리의 다른 글

[BOJ] 백준 1759번 암호 만들기 c++  (0) 2022.02.15
[BOJ] 백준 5430번 AC c++  (0) 2022.02.11
[BOJ] 백준 14499 주사위 굴리기 C++  (0) 2021.11.11
[BOJ] 백준 17829 222-풀링 c++  (0) 2021.10.25
[BOJ] 백준 14502 연구소 c++  (0) 2021.10.24

BOJ 문제 링크: https://www.acmicpc.net/problem/14499

 

14499번: 주사위 굴리기

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x, y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다. 둘째 줄부터 N개의 줄에 지

www.acmicpc.net

각 칸마다 숫자가 적혀있는 지도안에서 주사위를 굴려가며 주사위가 위치한 칸에 적혀있는 숫자에 따라 다음과 같이 행동한다.

0이 아니면 주사위 밑면에 지도에 적혀있는 숫자를 복사하고 해당칸은 0이된다.

0이면 주사위 밑면에 쓰여있는 수가 칸에 복사된다.

 

주사위의 6면을 각각 위, 밑, 동, 서, 남, 북을 바라보는 면으로 나눠서 각 면에 적혀있는 숫자를 배열에 저장한다.

주사위를 굴릴때마다 위에서 정의한 배열을 갱신한다.

예를 들어 주사위를 동쪽으로 굴린경우 서쪽을 바라보는 면은 위를 바라보는 면으로 바뀌므로 dice[0]에 적혀 있는 숫자가 dice[3]으로 옮겨져야 된다.

 

댓글을 통한 코드리뷰, 질문, 지적 언제든 환영입니다!

'Algorithm > BOJ' 카테고리의 다른 글

[BOJ] 백준 5430번 AC c++  (0) 2022.02.11
[BOJ] 백준 1520번 내리막길 c++  (0) 2021.11.22
[BOJ] 백준 17829 222-풀링 c++  (0) 2021.10.25
[BOJ] 백준 14502 연구소 c++  (0) 2021.10.24
[BOJ] 백준 17830 이진수씨의 하루 일과 c++  (0) 2021.10.19

알고스팟 문제 링크: https://algospot.com/judge/problem/read/ROUTING

 

algospot.com :: ROUTING

신호 라우팅 문제 정보 문제 위 그림은 여러 개의 컴퓨터들과 각 컴퓨터들을 잇는 회선을 나타냅니다. 각 회선들은 각각 품질이 다르며, 각 회선을 지날 때마다 신호에 있던 노이즈가 증폭될 수

algospot.com

정점에서 다른정점으로 가기 위한 회선을 지날 때 노이즈가 회선에 부여된 수 만큼 곱으로 증폭 된다.

0번째 정점에서 n-1번째 정점으로 가는 최소 증폭량을 구하는 문제이다.

 

다익스트라 알고리즘을 이용하면 쉽게 문제를 해결할 수 있다.

주의할 점은 곱연산으로 dist를 계산하기 때문에 초기 증폭값을 0이 아닌 1.0으로 두어야 한다.

 

코드 원본: https://github.com/sbl133/JongmanBook/blob/main/30.%20ShortestPath/ROUTING.cpp

 

GitHub - sbl133/JongmanBook

Contribute to sbl133/JongmanBook development by creating an account on GitHub.

github.com

댓글을 통한 코드리뷰, 질문, 지적 언제든 환영입니다!

reference: 프로그래밍 대회에서 배우는 알고리즘 문제해결전략2

알고스팟 문제 링크: https://algospot.com/judge/problem/read/HANOI4

 

algospot.com :: HANOI4

하노이의 네 탑 문제 정보 문제 하노이의 탑은 세 개의 기둥에 꽂혀 있는 N개의 원반을 가지고 하는 게임입니다. N개의 원반은 크기가 모두 다르며, 게임의 시작 때는 그림과 같이 맨 왼쪽의 기둥

algospot.com

기둥이 4개인 하노이의 탑 문제이다. 단 초기 상태는 입력으로 주어진다.

 

양방향 BFS를 이용하여 문제를 풀 수 있다. 이때 각 디스크가 위치할 수 있는 기둥은 총 4개이므로 두개의 비트로 각 디시크의 위치를 표현가능하다.

따라서 총 12*2개의 비트로 디스크의 전체 위치를 표현 가능하므로 비트마스크 기법을 이용하여 int로 디스크의 전체 위치를 표현할 수 있다.

 

코드 원본: https://github.com/sbl133/JongmanBook/blob/main/29.%20BFS/HANOI4B.cpp

 

GitHub - sbl133/JongmanBook

Contribute to sbl133/JongmanBook development by creating an account on GitHub.

github.com

댓글을 통한 코드리뷰, 질문, 지적 언제든 환영입니다!

reference: 프로그래밍 대회에서 배우는 알고리즘 문제해결전략2

알고스팟 문제 링크: https://algospot.com/judge/problem/read/CHILDRENDAY

 

algospot.com :: CHILDRENDAY

어린이날 문제 정보 문제 어린이집을 경영하는 원석이는 어린이날을 맞아 어린이집에 다니는 N 명의 아이들에게 장난감을 나눠 주기로 마음먹었습니다. 모든 아이들에게 같은 수의 장난감을 주

algospot.com

문자열로 주어진 특정 십진수(0~9)로만 장난감 갯수를 확보할 수 있다.

장난감을 n명의 아이들에게 나눠주는데 그 중 m명은 같은 개수의 장난감을 받으면 삐지므로 한 개 더 주려 한다.

위 조건을 만족하면서 가능한 적은 수의 장난감의 갯수 c를 계산하는 문제이다.

 

문제에 주어진 c에 대한 조건을 정리하면 다음과 같다.

1. n+m 이상이어야 한다.

2. n으로 나눈 나머지가 m이어야 한다.

3. d에 포함된 숫자들로만 구성되어 있어야 한다.

먼저 3번 조건을 만족하는 숫자들 중에서 2번을 만족하는 경우를 계산하는 방법을 생각해보면,

c의 뒤에 숫자 x를 붙여나가는 방식으로 c를 확장한다면 ((c mod n)*10+x) mod n = (c*10+x) mod n 이므로 c를 n으로 나눈 나머지가 m인 최소 숫자를 찾을 수 있다.

이때 주의할 점은 n+m이상인 조건도 만족시켜야 하므로 n+m미만인 나머지값 정점이랑 n+m이상인 나머지값 정점을 구분해야 한다.

 

코드 원본: https://github.com/sbl133/JongmanBook/blob/main/29.%20BFS/CHILDRENDAY.cpp

 

GitHub - sbl133/JongmanBook

Contribute to sbl133/JongmanBook development by creating an account on GitHub.

github.com

댓글을 통한 코드리뷰, 질문, 지적 언제든 환영입니다!

reference: 프로그래밍 대회에서 배우는 알고리즘 문제해결전략2

알고스팟 문제 링크: https://algospot.com/judge/problem/read/SORTGAME

 

algospot.com :: SORTGAME

Sorting Game 문제 정보 문제 중복이 없는 정수 수열이 주어진다. 이 때, 우리는 이 수열의 임의의 구간을 선택해서 해당 구간을 뒤집을 수 있다. 이 뒤집기 연산을 통해 전체 수열을 정렬하고 싶다.

algospot.com

중복이 없는 정수 수열에 임의의 구간을 선택해서 뒤집는 작업을 반복하여 수열을 정렬시킨다 했을때 필요한 최소 뒤집기 횟수를 구하는 문제이다.

 

상대적 크기가 같은 배열의 최소 뒤집기 횟수는 같다.

예를들어 {1, 3, 2, 4}와 {10, 30, 20, 40}의 최소 뒤집기 횟수는 같다. 따라서 크기가 n인 수열은 원소가 1~n인 경우만 고려하면 된다.

수열의 배치가 다른 각각의 경우의 수는 n!이므로 n!개의 정점과 현재 상태에서 뒤집기 한번 했을때의 수열이 서로 연결된 형태의 그래프를 이용하여 문제를 풀 수 있다.

이때 테스트케이스의 최대가 1000이므로 크기가 1~n인 경우들의 최소 뒤집기 횟수들을 미리 구해놓는다.

 

코드 원본: https://github.com/sbl133/JongmanBook/blob/main/29.%20BFS/SORTGAME.cpp

 

GitHub - sbl133/JongmanBook

Contribute to sbl133/JongmanBook development by creating an account on GitHub.

github.com

댓글을 통한 코드리뷰, 질문, 지적 언제든 환영입니다!

reference: 프로그래밍 대회에서 배우는 알고리즘 문제해결전략2

+ Recent posts