이전 게시글 프로젝트 시작
mySQL을 처음 써봐서 그런지 ORACLE보단 설정이 복잡해 보였다.
하지만, 스프링부트 설정은 스프링 프레임워크와 다르게 간편했다.
mySQL 설치와 스프링부트 연동
https://gyuwon95.tistory.com/167
먼저 스프핑부트가 스프링과 다른 점은
- 간편한 설정
- 편리한 의존성 관리 & 자동 권장 버전 관리
- 내장 서버로 인한 간단한 배포 서버 구축
- 스프링 Security, Data JPA 등의 다른 스프링 프레임워크 요소를 쉽게 사용할 수 있다.
여기서 사용할 JPA란?
https://hoozy.tistory.com/entry/JAVA-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC
build.gradle 파일 추가
implementation 'mysql:mysql-connector-java:8.0.32' // 버전이 안들어갔을 때 에러가 뜰 경우 현재 mysql의 버전을 넣으면 된다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
MySQL application.yml 설정
server:
address: localhost
port: 8005
servlet:
encoding:
charset: UTF-8
enabled: true
force: true
// 주소와 포트 http 인코딩 설정.
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/{schema_name}?createDatabaseIfNotExist=true%useUnicode=true&serverTimezone=Asia/Seoul
username: {username}
password: {password}
// mySQL 기본 설정.
jpa:
database: mysql
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
show\_sql: true
format\_sql: true
- ddl-auto 란?
- create : 프로그램 시작할 때 스키마 drop 후 생성
- none : 아무것도 안함
- update : 변경된 스키마만 적용
- create-drop : 프로그램 시작할 때 스키마 drop 후 생성 후 종료할 때 drop
- validate : 엔티티와 테이블 정상 매핑 확인
JPA 사용법
JPA 객체 생성 방법
- 객체 생성 방법에는 생성자, 정적 팩토리 메소드, Builder 패턴 3가지가 있다.
- 엔티티 상황에 따라서 이 중 한가지를 선택하고, 파라미터에 객체 생성에 필요한 데이터를 다 넘기는 방법을 사용한다.
- 정적 팩토리 메소드와 Builder 패턴을 사용할 때는 셍상자를 private 처리를 하는데 JPA가 사용하려면 protected로 처리해야 한다.
- 객체 생성이 간단하면 단순 생성자를 이용하지만, 의미가 있거나 복잡하면 나머지 방법이 좋다.
- @Setter를 지양한다.
- 엔티티를 만들 때는 외부에서 값을 쉽게 변경할 수 없게 @Setter를 사용하지 않는다.
- Setter는 의도가 분명하지 않고, 변경하면 안되는 중요한 값임에도 변경가능한 값으로 착각할 수 있다.(안정성 보장X)
- 이때에는 Setter와 같은 기능을 하는 메소드를 만들면 된다.
public void setName(String name) {
this.name = name;
}
public void changeName(String name) {
this.name = name;
}
1. 생성자
public Member(Long id, String name) {
this.id = id;
this.name = name;
}
2. 정적 팩토리 메소드
// Setter 메소드를 고친 정적 팩토리 메소드
public static Member createMember(Long id, String name) {
Member member = new Member();
member.changeId(id);
member.changeName(name);
return member;
{
- 여기서 외부에서 생성할 수 없는 private 생성자를 생성하여 값을 변경하여 반환한다?
- 이런 상황에서는 사실상 객체를 생성할 때 Setter 메소드를 사용한거나 다름이 없다.
- Member를 생성할 때 id와 name 값을 파라미터로 넘겨줘서 3가지 생성 방법 중 하나로 생성하면 Setter 메소드를 제거할 수 있다.
- 즉, 생성 시점에서 전부 끝낼 수 있다.
// Setter를 제거한 정적 팩토리 메소드 1
public static Member createMember(Long id, String name) {
return new Member(id, name);
}
// Setter를 제거한 정적 팩토리 메소드 2
public static Member createMember(Long id, String name) {
return Member.builder()
.id(id)
.name(name)
.build();
}
@Builder
public Member(Long id, String name) {
this.id = id;
this.name = name;
}
3. Builder 패턴
// Builder 패턴
@Builder
public Member(Long id, String name) {
this.id = id;
this.name = name;
}
- 위에 패턴은 생성자와 비슷하지만, 매개변수가 많아질수록 힘들어지게 된다.
- 이럴 경우 점층적 생성자 패턴을 쓰게 된다.
- 예를 들면 필수 매개변수 id, name 과 선택 매개변수 city, street가 있다면,
public Member(Long id, String name) {
this.id = id;
this.name = name;
}
public Member(Long id, String name, String city) {
this.id = id;
this.name = name;
this.city = city;
}
public Member(Long id, String name, String city, String street) {
this.id = id;
this.name = name;
this.city = city;
this.street = street;
}
- 이렇게 선택 매개변수가 없는 생성자부터 선택 매개변수를 전부 받는 생성자까지 늘려나가는 방식을 점층적 생성자 패턴이라고 한다.
- 이 경우 street를 넣기 위해 city도 넣어줘야 하는 불편함이 생긴다. 즉, 원하지 않는 매개변수까지 값을 생성해야 한다.
- 이럴때 다른 디자인 패턴인 자바빈즈 패턴을 사용할 수 있다.
// 자바빈즈 패턴
Member member = new Member();
member.setId(id);
member.setName("이름");
member.setCity("시티");
member.setStreet("스트릿");
- 하지만 이 패턴은 함수 호출 한 번으로 객체 생성을 끝내지 못하고 여러번 메소드를 호출해야한다.
- 따라서 객체 일관성이 깨진다 -> 불변성 객체를 만들 수 없다는 단점이 생긴다.
위 두가지 패턴의 단점을 보완한 것이 빌더 패턴이다.
- 점층적 생성자 패턴의 안전성 과 자바빈즈 패턴의 가독성을 합친 빌더 패턴이다.
@Builder
public class Member {
private Long id; // 필수
private String name; // 필수
@Builder.Default
private String city = "city"; // 선택
@Builder.Default
private String street = "street"; // 선택
}
- @Builder 어노테이션은 class와 생성자 위치에 따라 차이점이 있다.
- 위 코드처럼 class 위치에 쓴다면 @AllArgsConstructor 를 붙인 것과 같은 효과이다.
- @AllArgsConstructor : 모든 필드에 대한 매개변수를 받는 기본 생성자를 만든다는 것이다.
- 즉, 위에선 id, name, city, street 4가지의 모든 매개변수로 한 생성자가 생성된다는 것이다.
- 만약 id가 자동으로 숫자가 매겨지는 필드(숫자형 PK)라면 좋지 않으므로 매개변수를 최소화 해주는 것이 좋다.
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
private Long id;
private String name;
private String city = "city";
private String street = "street";
@Builder(builderMethodName = "createMember")
public Member(String name, String city, String street) {
this.name = name;
this.city = city;
this.street = street;
}
}
- @NoArgsConstructor(access = AccessLevel.PROTECTED) 로 기본 생성자 접근 제한자를 protected로 바꿔준다.
- 외부에서 개발자는 직접 호출 못하지만 JPA가 접근 가능하게 만들어 주는 것이다.
- 최종적으로 위와 같이 기본 생성자를 제한해주고, Class위가 아닌 생성자에 @Builder 를 붙여주는 것이 좋다.
- @Builder 속성인 builderMethodName 을 지정하여 해당 Builder의 이름을 부여하여 쓸 수 있다.
// @Builder 패턴 - 생성자 호출
Member member = new createMember()
.name("이름")
.city("시티")
.street("스트릿")
.build();
빌더 패턴에서 제일 중요한 것은 생성자에 파라미터를 넘겨서 변경이 필요없는 객체를 생성하는 것이 핵심이다.
JPA 어노테이션 종류
객체와 테이블 매핑
- @Entity
- 데이터베이스의 테이블과 1:1로 매칭되는 객체 단위이며, Entity 객체의 인스턴스 하나가 테이블에서 하나의 레코드 값을 의미하며 테이블과 매핑할 클래스에 필수로 붙여줘야한다.
- 그래서 객체의 인스턴스를 구분하기 위한 유일한 키값을 가지는데 이것은 테이블 상의 Primary Key와 같은 의미를 가지며 @Id 어노테이션으로 표기된다.
- name 을 설정하지 않으면 클래스 이름을 그대로 사용한다. -> 테이블 명과 같으면 name 쓸 필요 없다.
@Entity
@Table(name = "tbl_member")
public class Member {
...
}
- 주의 사항
1. 기본 생성자는 필수다.
2. final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
3. 저장할 필드에 final을 사용하면 안된다.
- @Table
- name : 매핑할 테이블 이름
- catalog : catalog 기능이 있는 데이터베이스에서 catalog를 매핑한다.
- schema : schema 기능이 있는 데이터베이스에서 schema를 매핑한다.
- MySQL에서는 table 과 schema 와 같은 의미이지만, Oracle에서는 schema를 table의 집합, database를 schema의 집합인 의미이다.
- uniqueConstraints(DDL) : DDL 생성 시에 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다.
기본 키 매핑
- @Id
- Entity 클래스 상에 PK를 명시적으로 표시해야 하는데 그것을 이 이노테이션을 이용해 이것이 PK임을 지정한다.
- ddl-auto 속성이 create로 되어 있고, 아직 해당 테이블이 데이터베이스 상에 존재하지 않는다면 EntityManager가 DDL을 통해 테이블을 생성하면서 PK를 같이 생성해 준다.
- @GeneratedValue
- PK 컬럼의 데이터 형식은 정해져 있지는 않으나 구분이 가능한 유일한 값을 가지고 있어야하고, 데이터 경합으로 인해 발생되는 데드락 같은 현상을 방지하기 위해 대부분 BigInteger 즉 JAVA의 Long을 주로 사용한다.
- 데드락 : 동일한 시점에 요청이 유입되었을 때 데이터베이스는 테이블 혹은 레코드를 lock을 걸어 데이터가 변경되지 않도록 막아 놓고 다른 작업을 진행한다. 이 때 1번째 요청이 A 테이블의 값을 변경하고, lock을 걸어둔 상태에서 B 테이블을 사용하려고 하고, 2번째 요청이 B 테이블의 값을 변경하고 lock 을 걸어둔 상태에서 A 테이블을 사용하려고 할 때 데이터베이스는 우선순위를 판단 할 수 없어 교착상태에 빠진다. -> 이 때 어쩔 수 없이 강제로 시스템을 재시작하여 데이터베이스 커넥션을 초기화 해야한다.
- MySQL
- Long 타입의 키 값을 생성하는 방식이 auto increment 방식이다.
- auto increment : 숫자형의 PK 컬럼 속성을 auto increment로 지정하면 자동으로 새로운 레코드가 될 때마다 마지막 PK 값에서 자동으로 +1 해주는 방식이다. 이를 위해 @GenerateValue 어노테이션의 strategy 속성을 GenerationType.IDENTITY로 지정해 auto increment 컬럼인 것을 EntityManager에 알려준다.
- 이 때 자동으로 생성되는 값을 가지는 PK 컬럼의 이름은 명시적으로 id로 지정하는 것이 관례이다.
- PK 컬럼의 데이터 형식은 정해져 있지는 않으나 구분이 가능한 유일한 값을 가지고 있어야하고, 데이터 경합으로 인해 발생되는 데드락 같은 현상을 방지하기 위해 대부분 BigInteger 즉 JAVA의 Long을 주로 사용한다.
@id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
2. Oracle
- Long 타입의 키 값을 생성하는 방식이 sequence 방식이다.
- sequence : sequence Oracle 객체를 생성해두고 해당 sequence를 호출할 때마다 기존 값의 +1 이 된 값을 반환해 주는 방식이다. 이를 위해 @GeneratedValue 어노테이션의 strategy 속성을 GenerationType.SEQUENCE로 지정해 sequence를 사용해 PK 값을 사용하겠다고 지정한다.
@Id
@SequenceGenerator(name="seq", sequenceName="id_sequence")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
private Long id;
- @EmbeddedId
- 일반적인 경우에는 PK를 단일 @Id로 구성하지만 경우에 따라선 복합키로서 테이블의 PK를 정의하기도 한다.
- 복합키 : 두 개 이상의 @Id로 구성이 되는데 직접 Entity에 정의하는 것이 아니라 별도의 Value를 사용해 복합키를 정의한다.
- 먼저 Value를 생성한 다음 @Embeddable 어노테이션을 이용해 이 Value 가 Entity에 삽입이 가능함을 명시하고 Entity에서는 @EmbeddedId 어노테이션을 이용해 이 Entity에 해당 Value를 PK로 사용한다고 지정한다.
- 일반적인 경우에는 PK를 단일 @Id로 구성하지만 경우에 따라선 복합키로서 테이블의 PK를 정의하기도 한다.
@Embeddable
public class CompanyOrganizationKey implements Serializable {
@Column(name = "company_code")
private String companyCode;
@Column(name = "organization_code")
private String organizationCode;
}
@Entity(name = "company_organization")
public class CompanyOrganization {
@EmbeddedId
protected CompanyOrganizationKey companyOrganizationKey;
}
필드와 컬럼 매핑
- @Column
- 테이터베이스의 테이블에 있는 컬럼과 동일하게 1:1로 매핑되기 때문에 Entity 클래스 안에 내부변수로 정의 된다.
- 이 어노테이션은 별다른 옵션을 설정하지 않는다면 생략이 가능하다. -> Entity 클래스에 정의된 모든 내부변수는 기본적으로 이 어노테이션을 포함한다고 볼 수 있다.
- 스프링부트 application 설정에서 ddl-auto 설정이 create 일때는 최초에 한 번 컬럼이 생성이 되고, update 일때는 Entity 클래스에 있지만 해당 테이블에 존재하지 않는 컬럼을 추가로 생성해 준다.
- 하지만, 컬럼의 데이터 타입이 변경되었거나 길이가 변경되었을 때 자동으로 데이터베이스에 반영을 해주지는 않기 때문에 속성이 변경되면 기존 테이블을 drop 후 create 하던지 개별로 alter table을 통해 직접 DDL 구문을 적용하는 것이 좋다.
- create - drop 일때는 프로젝트가 시작될 때 자동으로 기존 테이블을 drop 한 후 create를 해준다. 하지만, 기존 스키마가 전부 삭제되기 때문에 시스템 설계와 개발 시점에만 사용해야하며 운영시점에 create, update, create-drop을 사용하지 않아야 한다.
- @Entity 와 동일하게 name 속성을 명시하지 않으면 Entity 클래스에 정의한 컬럼 변수의 이름으로 생성이 된다.
- 이 때문에 만약 컬럼 이름과 다르다면 name 속성을 넣어야 한다.
@Column
private String code;
@Column(length = 100)
private String name;
//@Column 은 생략이 가능하다.
private String desctiption;
@Column(precision = 11, scale = 2)
private BigDecimal amount;
@Column
private Integer size;
@Column(name = "register_date")
private LocalDateTime registerDate;
연관관계 매핑
- 실제로 서비스되는 웹 어플리케이션에서 하나의 엔티티 타입만을 이용하는 경우는 많이 안습니다.
- EX) Member 엔티티와 Team 엔티티가 있을 때, 하나의 Team은 여러 Member를 갖는 관계를 가지고 있다.
- 이처럼 엔티티들이 서로 어떤 연관관계를 갖는지 파악하는 것은 매우 중요하다.
- 연관관계 매핑 : 객체의 참조와 테이블의 외래 키를 매핑하는 것을 의미.
- JPA에서는 연관관계에 있는 상대 테이블의 PK를 멤버 변수로 갖지 않고, 엔티티 객체 자체를 통째로 참조합니다.
- 물론 단순히 참조하는 것만으로는 연관관계를 맺을 수 없습니다.
- 먼저, 연관관계 매핑을 이해하기 위한 3가지 키워드를 알아보겠습니다.
- 방향
- 단방향 관계 : 두 엔티티가 관계를 맺을 때, 한 쪽의 엔티티만 참조하고 있는 것
- 양방향 관계 : 두 엔티티가 관계를 맺을 때, 양 쪽이 서로 참조하고 있는 것
- 데이터 모델링에서는 관계를 맺어주기만 하면 자동으로 양방향 관계가 되어 서로 참조하지만, 객체지향 모델링에서는 구현하고자 하는 서비스에 따라 단방향 관계인지, 양방향 관계인지 적절한 선택을 해야한다.
- 다중성
- 관계에 있는 두 엔티티는 다음 중 하나의 관계를 갖습니다.
- ManyToOne : 다대일 -> N : 1
- OneToMany : 일대다 -> 1 : N
- OneToOne : 일대일 -> 1 : 1
- ManyToMany : 다대다 -> N : N
- EX) 하나의 Team은 여러 Member를 구성원으로 갖고 있으므로 Team 입장에서는 Member와 일대다 관계이며, Member의 입장에서는 하나의 Team에 속하므로 다대일 관계입니다.
- 연관관계의 주인 (Owner)
- 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야한다.
- 연관관계를 갖는 두 테이블에서 외래키를 갖게되는 테이블이 연관관계의 주인이다.
- 주인만이 외래 키를 관리(등록, 수정, 삭제) 할 수 있고, 주인이 아닌 엔티티는 읽기만 할 수 있다.
- @ManuToOne - 단방향
@Entity
@Getter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
@Entity
@Getter
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
}
- 단방향은 한 쪽의 엔티티가 상대 엔티티를 참조하고 있는 상태입니다. -> Member 엔티티에만 @ManyToOne 어노테이션이 있다.
- 연관관계를 매핑할 때 다중성을 나타내는 어노테이션을 필수로 사용해야 하며, 엔티티 자신을 기준으로 다중성을 생각해야 한다.
- JoinColum(name = "TEAM_ID")
- JoinColumn 어노테이션은 외래 키를 매핑할 때 사용합니다. name 속성에는 매핑할 외래 키 이름을 지정한다.
- Member 엔티티의 경우 Team 엔티티의 id 필드를 외래 키로 가지므로, TEAM_ID로 작성하였습니다.
- 데이터 모델링에서는 1:N 관계만 설정해주면 자동으로 양방향 관계가 되기 때문에 어느 테이블에서든 JOIN만 해주면 원하는 컬럼들을 가져올 수 있지만, JPA에서는 양방향 관계를 맺음으로서 해결할 수 있다.
- OneToMany 로 양방향 관계 맺기
@Entity
@Getter
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}
- Team은 Member를 List로 가지며, 연관관계의 주인을 정하기 위해 @OneToMany에 mappedBy 속성을 추가했습니다.
- 연관관계의 주인을 정하는 방법은 mappedBy 속성을 지정하는 것.
- 주인은 mappedBy 속성을 사용하지 않고, @JoinColumn을 사용
- 주인이 아닌 엔티티 클래스는 mappedBy 속성을 사용해 주인을 정할 수 있다.
- 주인은 mappedBy 속성을 사용할 수 없으므로 연관관계의 주인이 아닌 Team 엔티티에서 members 필드에 mappedBy 속성으로 Member 테이블의 Team 필드 이름을 명시해준다.
- mappedBy의 속성으로 Member 테이블의 Team 필드 이름을 명시해준다 (Team DB에 members FK 컬럼을 만들지 안헥 하기 위해)
- OneToOne
- 한 유저는 한 개의 블로그만 만들 수 있는 블로그 서비스가 있다고 가정하면, 유저와 블로그는 1:1 관계입니다.
- 유저 엔티티 입장에서 블로그의 @Id를 외래 키로 가져서 블로그를 참조할 수 있고, 그 반대 경우인 블로그 엔티티를 가져서 유저를 참조하는 것도 가능합니다. 여기서 외래 키를 갖고 있는 테이블을 주 테이블이라고 합니다.
- 여기서 만약 한 유저가 여러 개의 블로그를 가질 수 있도록 확장(OneToMany) 될 수 있음을 고려한다면, 블로그에서 유저의 외래 키를 갖는 것이 좋을 것입니다.
- 반대로 유저를 조회 했을 때 자동으로 블로그 엔티티도 조회되는 것이 좋겠다고 생각하면 유저에서 블로그의 외래 키를 갖는 것이 좋을 것입니다.
- ManyToMany (실무 사용 금지)
- 중간 테이블이 숨겨져 있기 때문에 자기도 모르는 복잡한 조인의 쿼리가 발생하는 경우가 생길 수 있기 때문에 실무에선 사용 금지입니다.
- 다대다로 자동 생성된 중간 테이블은 두 객체의 테이블의 외래 키만 저장되기 때문에 문제가 될 확률이 높습니다.
- 중간 테이블에 외래 키에 다른 정보가 들어가는 경우가 많기 때문에, 다대다를 일대다, 다대일로 풀어서 만드는 것(중간에 매핑 테이블을 만드는 것)이 추후 변경에도 유연하게 대처할 수 있습니다.
Repository 사용법
- Repository 생성
- 실제 DB에 접속하여 쿼리를 수행하는 등의 역할을 하는 인터페이스 입니다.
- @Repository 어노테이션을 추가하고, JpaRepository 를 상속합니다.
- 또한 정해진 규칙과 단어를 조합한 이름으로 메소드명을 작성하는 것만으로 다양한 쿼리를 생성할 수 있습니다.
- Repository 예제
// <엔티티 클래스 이름, PK 타입>
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 메소드명(findAllBy) 뒤에 필드명을 넣는다. List로 구현한다.
// @Query(value = "select * from table") -> 쿼리문을 직접 넣을 수 있다.
List<User> findAllById(Long id);
}
메소드
method | 기능 |
---|---|
save() | 레코드 저장(insert, update) |
findOneBy() | PK로 레코드 하나 찾기 |
findBy() | 뒤에 필드명 붙여 레코드 찾기 |
findAllBy() | 전체 레코드 불러오기. 정렬, 페이징 기능 |
countBy() | 레코드 개수 |
deleteBy() | 레코드 삭제 |
키워드 (쿼리 대신)
메소드 키워드 | 예제 | 설명 |
---|---|---|
And, Or | findByIdAnd(Or)Name(Long id, String name) | Id 가 id 인 값, Name이 name 인 값 찾음 |
Between | findByBetween(String fromDate, Date toDate) | from 과 toDate 사이 값 찾음 |
LessThan | findByAgeLessThan(int age) | Age 가 age 보다 작은 값 찾음 |
GreaterThanEqual | findByAgeGreaterThanEqual(int age) | Age가 age 보다 크거나 같은 값 찾음 |
Like, NotLike | findByIdLike(Long id) | Id가 id인 값 찾음 |
IsNull, IsNotNull | findByNameIsNull() | null 값 찾음 |
In | findByName(String a, String b, String c) | a, b, c 중 있는 것 찾음 |
OrderBy | findByNameOrderByAgeAsc(String name) | name을 찾아 오는데 age가 오름차순으로 찾음 |
StartingWith, EndingWith | findByNameStartingWith("A") | A로 시작하는 name 찾음 |
Before, After | findByDateBefore(new Date()) | Date가 Date타입인 경우 이전 것만 찾음 |
First, Last | findFirst2ByNameOrderByAgeAsc(String name) | 찾아온 값 중에 처음 2개를 찾음 |
- Service 예제
@Service
@RequiredArgsConstructor // final, 필드들을 매개변수로 하는 생성자를 자동으로 생성해주는 어노테이션
public class UserService {
private final UserRepository userRepository;
public List<Users> getUsersService(String name){
if(name.isBlank()) // name 파라미터가 Null이면 전체 user를 리턴
return userRepository.findAll();
else // name 이 존재를 하면, Like 쿼리로 2개만 리턴
return userRepository.findFirst2ByUsernameLikeOrderByIDDesc(name); // 유저이름이 들어간 유저 중 내림차순하고 그 중 상위 2개 추출
}
public String createUserService(Users user){
userRepository.save(user); // User Insert 쿼리 수행
return "등록 완료";
}
}
- Controller 예제
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping(value = "/users")
public List<Users> getUsers( @RequestParam(required = false, defaultValue = "") String name ){
return userService.getUsersService( name );
}
@PostMapping(value = "/user")
public String createUser(@RequestBody Users user){
return userService.createUserService(user);
}
}
- @RestController
- @Controller + @ResponseBody
- @ResponseBody : xml 이나 json 기반의 메시지를 사용하는 요청의 경우 유용 -> Ajax
연관관계에서는 ToString()을 주의
@ToString()
- 클래스의 필드를 생성하고 출력하면 문자열 형태로 나온다.
- EX) User(id = 아이디, name = 이름)
- 엔티티 간에 연관관계를 지정하는 경우엔 항상 @ToString() 어노테이션을 주의해야 합니다.
- @ToString()은 해당 클래스의 모든 멤버 변수를 출력하는데, 연관관계 매핑이 되어있을 경우 그 객체 역시 출력해야 하기 때문에 이때 데이터베이스 연결이 필요하게 됩니다.
- 이런 문제로 인해 연관관계가 있는 엔티티 클래스의 경우 @ToString() 할 때 습관적으로 exclude 속성을 사용하는 것이 좋습니다.
- exclude는 해당 속성값으로 지정된 필드는 @ToString()에서 제외해줍니다.
다음 게시글 프로젝트 1
https://hoozy.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%84%A4%EC%A0%95-1
참고 자료
https://blog.naver.com/PostView.nhn?blogId=ka28&logNo=222355010576
https://ssdragon.tistory.com/78
https://gwonbookcase.tistory.com/37
https://dev-coco.tistory.com/106
https://geonoo.tistory.com/149
https://kim-oriental.tistory.com/20
'Spring Boot (프로젝트)' 카테고리의 다른 글
프로젝트 1 설정 (Spring Boot, MySQL) (0) | 2023.03.08 |
---|---|
스프링 부트 로그 관리하기 (Slf4j) (0) | 2023.03.08 |
Thymeleaf 정리 (JSP와 차이) (0) | 2023.03.08 |
[MySQL] ERD를 활용한 테이블 생성 (0) | 2023.03.05 |
[스프링 부트] 프로젝트 시작 (0) | 2023.03.04 |
댓글