post_template

[SpringBoot] 엔티티 내부 객체를 JPA 레포지토리에서 사용

개요

현재 단방향 관계로 사용중인 엔티티가 있는데,

이 엔티티에서 다른 테이블을 가르키는 객체를 이용해서 정렬을 하는 방법을 정리한다

Entity 관계

현재 엔티티 관계는 다음과 같다

Board(게시글)와 Comment(댓글)에서 좋아요나 싫어요를 할 수 있는데 이를 각각 엔티티에서 담지 않고 Likes라는 테이블에서 관리를 하고자 하였다.

그렇게 할려고 각 Board와 Comment에서 Likes를 단방향관계로 묶어주었다

Entity 코드

코드는 다음과 같으며 주석으로 **표시한 부분을 주로 보면 될 것 같다

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

@Getter
@Setter
@NoArgsConstructor
@Entity
@DynamicInsert
@Table(name = "BOARD")
public class Board extends BaseTimeEntity

{

//gallery 이름, user 이름으로 foregin key 설정할거임 이거 칼럼에서 추가하는거 있잖아 그거 하자
@Column(nullable = false, length = 45)
private String gallery;

@Column(nullable = false)
private String user;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "content_idx")
private Long contentIdx;

@Column(nullable = false, length = 45)
private String title;

@Column(columnDefinition = "text", nullable = false)
private String content;

//***********************************************
@OneToOne(cascade = CascadeType.ALL)
private Likes likes;
//***********************************************
@Column(length = 80, nullable = false)
private String password;

@Column(columnDefinition = "tinyint(1) default 0", name = "is_logged_user",nullable = false)
private Long isLoggedUser;
//이하생략
}
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
@Getter
@Entity
@EntityListeners(AuditingEntityListener.class)
@DynamicInsert
@Table(name = "COMMENT")
@NoArgsConstructor
public class Comment extends BaseTimeEntity
{
@Id
@Column(name = "comment_idx")
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long contentIdx;

@Column(name = "board_idx",nullable = false)
Long boardIdx;

@Column(nullable = false)
String gallery;

@Column(nullable = false)
String user;

@Column(nullable = false)
String content;

@Column(name = "comment_password",nullable = false,length = 80)
String commentPassword;

@Column(columnDefinition = "tinyint(1) default 0",nullable = false,name = "is_logged_user")
Long isLogged;

//***********************************************
@OneToOne(cascade = CascadeType.ALL)
private Likes likes;
//***********************************************

//이하생략
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Entity
@Getter
@NoArgsConstructor
@DynamicInsert
@Table(name= "likes")
@IdClass(LikesID.class)
public class Likes implements Serializable
{
@Column(name = "is_comment")
@Id
private boolean isComment;

//board,comment 둘의 개추들 모두 담음
@Id
@Column(name = "content_idx",nullable = false)
private Long contentIdx;

@Column(columnDefinition = "bigint default 0")
private Long like;

@Column(columnDefinition = "bigint default 0")
private Long dislike;

Repository에서 사용

1
2
3
4
5
public interface BoardRepository extends JpaRepository<Board, Long>
{
List<Board> findTop10ByOrderByLikesLikeDesc();
}

findTop10 : 정렬 기준이 가장 높은 10가지를 가져온다

OrderByLikesLike : Board 엔티티의 Likes 객체의 Like를 기준으로 내림차순 정렬을 한다

마무리

레포지토리에서 객체안의 값을 기준으로 정렬할려면 어떻게 해야하지? 고민했는데

LikesLike처럼 이어서 사용하면 되는 것이었다

참고

https://stackoverflow.com/questions/55579240/spring-data-find-by-property-of-a-nested-object/55579268

스프링부트 커뮤니티 사이트 개발환경,reload 설정 (mindaces 02)

개요

개발하기에 앞서서 설치 및 환경설정을 해볼려고 한다.

IDE는 인텔리제이 유료버전을 사용하고 있는데 인텔리제이에서도 몇가지 설정을 해야한다.

이번 포스팅에서는 위 두가지를 해볼려고 한다.

이번 포스팅에서 할 것

  • 스프링부트 설치
  • IDE 설정 및 스프링부트 설정

SpringBootInitalize

먼저 다음의 링크에 접속한다.

https://start.spring.io/

접속 후 다음 이미지와 같이 설정한다.

spring_init.PNG

이후 GENERATE를 눌러 나오는 압축파일을 푼다.

build.gradle는 다음과 같이 생성된다.

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
plugins {
id 'org.springframework.boot' version '2.5.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}

group = 'com.mindaces'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '16'

configurations {
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
useJUnitPlatform()
}

MYSQL 설정

아직은 바로 실행하면 데이터베이스가 연결되지 않았다는 오류 메시지가 뜰 것이다.

해결방법은 시키는대로 데이터베이스를 연결하면 된다.

우선 MYSQL 실행해서 다음의 코드로 spring-demo-mysql 데이터베이스를 생성한다.

1
CREATE DATABASE spring-demo-mysql default CHARACTER SET UTF8;

이후 아까 압축해제한 루트 폴더에서부터 src/main/ resources/application.properties 파일을 연다

파일을 열면 아직은 아무것도 없는 상황인데 여기에 다음을 입력해 데이터베이스에 관한 설정을 해주어야 한다.

1
2
3
4
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/spring-demo-mysql?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
spring.datasource.username=유저네임
spring.datasource.password=비밀번호

이 설정을 한 뒤에는 실행이 제대로 될 것이다.

LOMBOK

인텔리제이 IDE를 사용한다면 LOMBOK을 스프링부트 디펜던시에 추가한 뒤에

플러그인도 설치해주어야 한다.

인텔리제이에서 파일 -> 설정 -> 플러그인에 LOMBOK을 검색하여 설치한다.

reload 설정

앞으로 개발을 하면서 몇가지를 수정하게 될탠데

Springboot는 자체적으로 빌드도 느린데다가 일일이 재시작하기도 귀찮다

그래서 앞서 디펜던시에 추가한 SpringBoot devtools의 재시작기능을 사용할 것이다.

앞서 디펜던시에 추가된 상태여서 추가적인 것은 할 것이 없고 대신 인텔리제이에서 몇가지 설정을 해주어야 한다.

프로젝트 자동 빌드

설정 -> 빌드, 실행, 배포 -> 컴파일러에 들어가서 프로젝트 자동 빌드를 체크해준다.

프로젝트 자동 빌드

업데이트 설정

오른쪽 상단에서 MindacesApplication을 클릭해 구성편집을 눌러 다음 이미지에서 처럼 설정해준다.

업데이트 설정

레지스트리 설정

Ctrl + Shift + A를 눌러 검색창에 레지스트리를 검색해서 다음과 같이 설정한다(영어 버전이면 Registry 검색)

레지스트리 설정

여기까지 한 뒤 앞으로 프로젝트를 디버깅 모드로 실행하면 코드 수정된 것이 알아서 반영된다.

선택 사항

재시작이 자동으로 된 것은 좋은데 아무래도 수정하다가 중간에 재시작돼서 에러를 뿜어버리면 곤란하다.

그렇다고 프로젝트를 다시 키면 너무 오래걸리고 수동으로 갱신하게 하고 싶을 때가 있다.

src/main/ resources/application.properties에 다음 한줄을 추가하면 된다.

1
spring.devtools.restart.enabled=false

이후에는 프로젝트 빌드 Ctrl + F9를 누르면 변경사항이 갱신될 것이다.

여기까지가 reload 기능을 사용하는 방법에 대해서 알아봤다.

빌드를 꽤 빨리할 수 있다는 점에서 개발을 좀 더 원활하게 할 수 있게 해주는 설정인 것 같다!

스프링부트 커뮤니티 사이트 설계 (mindaces 01)

개요

spring_project_main_page_01.PNG

스프링부트 공부 겸 펫 프로젝트를 시작해볼려고 한다.

만들려는 사이트는 커뮤니티 사이트로

우선 사이트에 접속하면 위와 같은 메인페이지가 나오도록 만들어 보려고 하는 것이 1차 목표이다.

아예 노베이스기 때문에 설명하기에 부족함이 많겠지만

설명할 때 애매한 부분같은 것을 알게될때까지 제대로 공부하여 제대로된 포스팅을 하는 것이 목표이다.

개발 환경

스프링부트에는 여러가지 디펜던시라던가 결합할 수 있는 환경들이 무지막지하게 많은데

우선은 초기설정한 디펜던시는 다음과 같다.
본인은 gradle이 최근에 개발된 프레임워크인 것 같아 gradle을 선택했다.

spring_init.PNG

현재 목표

  • 메인 화면 만들기

프로그래머스 2레벨 77486 다단계 칫솔 판매 JAVA

문제

링크 : https://programmers.co.kr/learn/courses/30/lessons/77486

민호는 다단계 조직을 이용하여 칫솔을 판매하고 있습니다. 판매원이 칫솔을 판매하면 그 이익이 피라미드 조직을 타고 조금씩 분배되는 형태의 판매망입니다. 어느정도 판매가 이루어진 후, 조직을 운영하던 민호는 조직 내 누가 얼마만큼의 이득을 가져갔는지가 궁금해졌습니다. 예를 들어, 민호가 운영하고 있는 다단계 칫솔 판매 조직이 아래 그림과 같다고 합시다.

그림1.png

민호는 center이며, 파란색 네모는 여덟 명의 판매원을 표시한 것입니다. 각각은 자신을 조직에 참여시킨 추천인에 연결되어 피라미드 식의 구조를 이루고 있습니다. 조직의 이익 분배 규칙은 간단합니다. 모든 판매원은 칫솔의 판매에 의하여 발생하는 이익에서 10% 를 계산하여 자신을 조직에 참여시킨 추천인에게 배분하고 나머지는 자신이 가집니다. 모든 판매원은 자신이 칫솔 판매에서 발생한 이익 뿐만 아니라, 자신이 조직에 추천하여 가입시킨 판매원에게서 발생하는 이익의 10% 까지 자신에 이익이 됩니다. 자신에게 발생하는 이익 또한 마찬가지의 규칙으로 자신의 추천인에게 분배됩니다. 단, 10% 를 계산할 때에는 원 단위에서 절사하며, 10%를 계산한 금액이 1 원 미만인 경우에는 이득을 분배하지 않고 자신이 모두 가집니다.

예를 들어, 아래와 같은 판매 기록이 있다고 가정하겠습니다. 칫솔의 판매에서 발생하는 이익은 개당 100 원으로 정해져 있습니다.

판매원 판매 수량 이익금
young 12 1,200 원
john 4 400 원
tod 2 200 원
emily 5 500 원
mary 10 1,000 원

판매원 young 에 의하여 1,200 원의 이익이 발생했습니다. young 은 이 중 10% 에 해당하는 120 원을, 자신을 조직에 참여시킨 추천인인 edward 에게 배분하고 자신은 나머지인 1,080 원을 가집니다. edward 는 young 에게서 받은 120 원 중 10% 인 12 원을 mary 에게 배분하고 자신은 나머지인 108 원을 가집니다. 12 원을 edward 로부터 받은 mary 는 10% 인 1 원을 센터에 (즉, 민호에게) 배분하고 자신은 나머지인 11 원을 가집니다. 이 상태를 그림으로 나타내면 아래와 같습니다.

그림2.png

그 후, 판매원 john 에 의하여 400 원의 이익이 발생합니다. john 은 10% 인 40 원을 센터에 배분하고 자신이 나머지인 360 원을 가집니다. 이 상태를 그림으로 나타내면 아래와 같습니다.

그림3.png

또 그 후에는 판매원 tod 에 의하여 200 원 이익이 발생하는데, tod 자신이 180 원을, 추천인인 jaimie 가 그 중 10% 인 20 원을 받아서 18 원을 가지고, jaimie 의 추천인인 mary 는 2 원을 받지만 이것의 10% 는 원 단위에서 절사하면 배분할 금액이 없기 때문에 mary 는 2 원을 모두 가집니다. 이 상태를 그림으로 나타내면 아래와 같습니다.

그림4.png

그 다음으로 emily 가 칫솔 판매를 통하여 얻은 이익 500 원은 마찬가지의 규칙에 따라 emily 에게 450 원, mary 에게 45 원, 그리고 센터에 5 원으로 분배됩니다. 이 상태를 그림으로 나타내면 아래와 같습니다.

그림5.png

마지막으로, 판매원 mary 는 1,000 원의 이익을 달성하고, 이 중 10% 인 100 원을 센터에 배분한 후 그 나머지인 900 원을 자신이 가집니다. 이 상태를 그림으로 나타내면 아래와 같습니다.

그림6.png

위와 같이 하여 모든 조직 구성원들의 이익 달성 현황 집계가 끝났습니다. 지금까지 얻은 이익을 모두 합한 결과를 그림으로 나타내면 아래와 같습니다.

그림7.png

이 결과가 민호가 파악하고자 하는 이익 배분 현황입니다.

각 판매원의 이름을 담은 배열 enroll, 각 판매원을 다단계 조직에 참여시킨 다른 판매원의 이름을 담은 배열 referral, 판매량 집계 데이터의 판매원 이름을 나열한 배열 seller, 판매량 집계 데이터의 판매 수량을 나열한 배열 amount가 매개변수로 주어질 때, 각 판매원이 득한 이익금을 나열한 배열을 return 하도록 solution 함수를 완성해주세요. 판매원에게 배분된 이익금의 총합을 계산하여(정수형으로), 입력으로 주어진 enroll에 이름이 포함된 순서에 따라 나열하면 됩니다.

풀이

약간 구현력문제라는 생각을 한 문제

각 seller에 대해서 문제에서 주어진 것처럼 수익을 배분하는데 이것은 재귀로 구현하였다.

각 seller를 추천한 추천인에 대해서는 enrollIdxMapMap이 누가 누구를 추천했는지 담도록 하였다.

수익을 분배하는 조건 그리고 얼마나 분배할지가 관건인 문제

그리고 seller와 enroll을 어떻게 묶을지를 조금 생각해주어야 하는데

이러한 기반을 쌓고 나면 풀 수 있는 문제🙃

코드

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.util.HashMap;
import java.util.Set;

class Solution{

static int ans[];
static HashMap<String,Integer> enrollIdxMap = new HashMap<String,Integer>();

static boolean isDivideAble(int money)
{
if(money / 10 >= 1)
{
return true;
}
return false;
}

static void divideToRefer(String curSeller, int curMoney,String[] enroll,String[] referral)
{

//System.out.println(curSeller + ", " + curMoney + " ing");
int enrollIdx = getIdxFromEnroll(curSeller);
if(enrollIdx == -1)
{
//System.out.println(curSeller);
return;
}
if(curMoney / 10 < 1)
{
//System.out.println(curMoney + " add");
ans[enrollIdx] += curMoney;
}
else
{
ans[enrollIdx] += curMoney - curMoney / 10;
//System.out.println((curMoney - curMoney / 10) + " add");
divideToRefer(referral[enrollIdx],curMoney / 10,enroll,referral);
}
}


static int getIdxFromEnroll(String curSellerString)
{
return enrollIdxMap.get(curSellerString);
}

static void setEnrollIdxMap(String[] enroll)
{
for(int i=0;i<enroll.length;i++)
{
enrollIdxMap.put(enroll[i],i);
}
enrollIdxMap.put("-",-1);
}

public int[] solution(String[] enroll, String[] referral, String[] seller, int[] amount) {
//각 enroll이 받은 수익금
int[] answer = new int[enroll.length];
ans = new int[enroll.length];
setEnrollIdxMap(enroll);
for(int i=0;i<seller.length;i++)
{
int curMoney = amount[i] * 100;
divideToRefer(seller[i], curMoney, enroll, referral);
}

answer = ans;
return answer;
}
}

메모

스스로 풀었는가 : ✅