- 예제 프로젝트 작성
1) application.properties 설계
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost/jpa_ex?characterEncoding=utf8&serverTimezone=Asia/Seoul
spring.datasource.username = root
spring.datasource.password = root
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=false
spring.jpa.show-sql=true
spring.jpa.database=mysql
logging.level.org.hibernate=info
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
2) 각각의 엔티티 클래스 설계
* 연관관계를 맺을 엔티티 클래스가 존재해야 하므로 데이터베이스를 고려해서 생성
- org.zerock.domain.Member.java
package org.zerock.domain;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@Entity
@Table(name="tbl_members")
@EqualsAndHashCode(of="uid")
public class Member {
@Id
private long uid;
private String upw;
private String uname;
}
- org.zerock.domain.Profile.java
package org.zerock.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(exclude = "member") //Member와 관련된 toString()을 호출하지 않게 한 부분
@Entity
@Table(name="tbl_profile")
@EqualsAndHashCode(of="fname")
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
//데이터베이스에 맞게 개인정보를 식별키로 처리
private Long fno;
private String fname;
private boolean current;
@ManyToOne
//연관 관계는 다대일
//Member와 Profile 일대다
//Profile과 Member는 다대일
private Member member;
}
3) Repository 작성
* Member와 Profile은 Member를 처리하는 Repository를 생성해서 회원 데이터를 처리
* 반면 Profile을 저장할 때는 Member 객체를 통해 Profile을 처리할 수 없어서 Profile을 처리하는 Repository를 설계해야함
- org.zerock.persistence.MemberRepository.java
package org.zerock.persistence;
import org.springframework.data.jpa.repository.JpaRepository;
import org.zerock.domain.Member;
public interface MemberRepository extends JpaRepository<Member, String> {
}
- org.zerock.persistence.ProfileRepository.java
package org.zerock.persistence;
import org.springframework.data.jpa.repository.JpaRepository;
import org.zerock.domain.Profile;
public interface ProfileRepository extends JpaRepository<Profile, Long> {
}
3) pom.xml에 query, maven 코드 삽입
https://github.com/zerockcode/boot06
GitHub - zerockcode/boot06: 스타트 스프링 부트 6장
스타트 스프링 부트 6장. Contribute to zerockcode/boot06 development by creating an account on GitHub.
github.com
위의 깃허브에 들어가셔서 파일들을 다운로드 받으셔야 합니다. 그 다음 pom.xml 파일을 엽니다.
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
- query 관련 구문
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
- maven 관련 구문
위에 있는 두 구문을 각각 프로젝트에 있는 pom.xml에 <dependencies></dependencies>와 <plugins></plugins>사이에 추가한다.
4) 테스트 검증
- org.zerock.controller.BoardController.java
package org.zerock.controller;
import java.util.stream.IntStream;
import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import org.zerock.domain.Member;
import org.zerock.domain.Profile;
import org.zerock.persistence.MemberRepository;
import org.zerock.persistence.ProfileRepository;
@RestController
public class BoardController {
@Autowired
MemberRepository memberRepo;
@Autowired
ProfileRepository profileRepo;
@Transactional
@PostConstruct
public void init() {
IntStream.range(1, 101).forEach(i-> {
//100명의 회원 데이터 생성
Member member = new Member();
member.setUid(Long.valueOf(i));
member.setUpw("pw"+i);
member.setUname("사용자"+i);
memberRepo.save(member);
});
//////////////////////////////////////////////////////////////////////
Member member = new Member();
member.setUid(Long.valueOf(1L));
for (int i = 1; i < 5; i++) {
//4개의 데이터가 생성
Profile profile1 = new Profile();
profile1.setFname("face"+i+".jpg");
if (i==1) {
profile1.setCurrent(true);
}
profile1.setMember(member);
profileRepo.save(profile1);
}
}
}
5) 실행후 결과
1) tbl_members
2) tbl_profile
- 예제 프로젝트 작성(자료실과 첨부 파일의 관계)
* PDSBoard: 자료를 의미
* PDSFile: 각 자료에 첨부되는 파일을 의미
1) 엔티티 클래스 작성
- org.zerock.domain.PDSBoard.java
package org.zerock.domain;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(exclude = "files")
@Entity
@Table(name="tbl_pds")
@EqualsAndHashCode(of="pid")
public class PDSBoard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long pid;
private String pname;
private String pwriter;
@OneToMany(cascade = CascadeType.ALL)
//cascade가 없으면 JPA에서 한 번에 여러 엔티티 객체들의 상태를 변경해야 함
//즉, 한번에 PDSBoard 객체로 보관해야 하고, PDSFile의 상태도 보관해야 함.
//cascade(영속성 전이)를 입력하고 ALL을 붙여서 모든 변경에 대해 전이가 가능하게 해야함
@JoinColumn(name="pdsno")
private List<PDSFile> files;
}
- org.zerock.domain.PDSFile.java
package org.zerock.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@Entity
@Table(name="tbl_pdsfiles")
@EqualsAndHashCode(of="fno")
public class PDSFile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long fno;
private String pdsfile;
}
2) 연관관계에 따른 Repository 설계
- org.zerock.persistence.PDSBoardRepository.java
<Update구문>
package org.zerock.persistence;
import javax.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.zerock.domain.PDSBoard;
public interface PDSBoardRepository extends JpaRepository<PDSBoard, Long>{
@Modifying //이 어노테이션으로 DML(insert, update, delete) 작업을 처리할 수 있음
@Transactional
//스프링에서 트랜잭션 처리를 지원하는 어노테이션(delete, update를 사용할 때 선언)
@Query("UPDATE FROM PDSFile f set f.pdsfile = ?2 WHERE f.fno = ?1 ")
public int updatePDSFile(Long fno, String newFileName);
}
<Delete 구문>
package org.zerock.persistence;
import javax.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.zerock.domain.PDSBoard;
public interface PDSBoardRepository extends JpaRepository<PDSBoard, Long>{
@Modifying
@Transactional
@Query("DELETE FROM PDSFile f WHERE f.fno = ?1")
public int deletePDSFile(Long fno);
}
3) 테스트 검증
package org.zerock.controller;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import org.zerock.domain.PDSBoard;
import org.zerock.domain.PDSFile;
import org.zerock.persistence.PDSBoardRepository;
@RestController
public class BoardController {
@Autowired
PDSBoardRepository pdsBoardRepo;
@Transactional
@PostConstruct
public void init() {
PDSBoard pdsBoard = new PDSBoard();
pdsBoard.setPname("Document");
PDSFile file1 = new PDSFile();
file1.setPdsfile("file1.doc");
PDSFile file2 = new PDSFile();
file2.setPdsfile("file2.doc");
pdsBoard.setFiles(Arrays.asList(file1,file2));
pdsBoardRepo.save(pdsBoard);
//////////////////////////////////////////////////////////////////////
List<PDSBoard> list = new ArrayList<>();
//특정 자료의 번호와 자료의 제목, 첨부 파일의 수를 같이 보여주어야 하는 상황
//이럴 때 @Query를 이용해 조인을 처리해 해결
IntStream.range(1, 100).forEach(i-> {
/*
100개의 200개의 파일 데이터 추가
각각의 데이터를 save 하지 않고 List<PDSBoard>에 데이터들을 보관했다가,
한 번에 저장
*/
PDSBoard pds = new PDSBoard();
pds.setPname("자료 "+i);
PDSFile file_1 = new PDSFile();
file_1.setPdsfile("file_1.doc");
PDSFile file_2 = new PDSFile();
file_2.setPdsfile("file_2.doc");
pds.setFiles(Arrays.asList(file_1, file_2));
list.add(pds);
});
pdsBoardRepo.saveAll(list);
}
}
4) 결과
- tbl_pds
- tbl_pdsfiles
'Spring_boot > Project' 카테고리의 다른 글
[Spring] Thymeleaf 동작 확인 (0) | 2021.08.08 |
---|---|
[Spring] Thymeleaf 사용하기 (0) | 2021.08.07 |
[Spring] Spring JPA 이용한 단순 게시물 처리 (0) | 2021.07.31 |
[Spring] 엔티티 클래스 설계하기(feat. SQL) (0) | 2021.07.29 |
[Spring] Spring Tool Suite 4와 MySQL Workbench 8.0 연결방법(재업) (0) | 2021.07.27 |