월 16,940원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
경로 표현식을 사용하면 묵시적 조인에 해당하나요?
경로 표현식을 사용한다는 것은 묵시적 조인을 사용하는 것과 같은 의미인가요?
- 해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
em.find() 및 쓰기 지연
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]//given Member member = new Member(); member.setUsername("영한님 최고"); em.persist(member); em.flush(); em.clear(); //when & then System.out.println("================="); Member findMember = em.find(Member.class , member.getId()); System.out.println("================="); tx.commit();em.persist()로 저장 이후 em.flush & em.clear()로 영속성 컨텍스트의 1차 캐시를 깔끔하게 비우고 em.find()를 실행해보았습니다. 위와 같이 코드가 되어있을 때 ========== 사이에서 select 문이 db로 나감을 알 수 있었습니다.즉 이에 따라 em.find() 시점에 flush가 되는 것을 알 수 있었습니다. 해당 부분에서 궁금한 점이 두 가지 생겼습니다. [핵심 질문]flush 타이밍은 em.flush() , 트랜잭션 커밋 , JPQL 실행 일 경우에만 flush가 일어나서 실제 DB에 쿼리가 나간다고 알고 있습니다. 즉 이에 따라 em.find()는 내부적으로 JPQL을 통해서 조회를 한다고 이해하면 될까요? 사실 맨 처음 기대했던 것은 코드 맨 마지막 트랜잭션 커밋 시점(tx.commit())에 flush가 되며 저장 쿼리 및 조회 쿼리가 순차적으로 나가는 것을 기대하였습니다. 하지만 em.persist()로 저장하는 경우에는 트랜잭션 커밋 시점에 flush가 일어나고 em.find()로 조회하는 경우에는 그 즉시 flush가 되며 조회 쿼리가 나가는 것을 알 수 있었습니다 따라서 영속성 컨텍스트의 "쓰기 지연" 기능은 em.persist()를 통한 "엔티티 저장" 시에만 반영되는 특성인가요? 물론 조회 후 수정을 할 경우엔 트랜잭션 커밋 시점에 더티 체킹을 통해 update 쿼리를 날리는 것은 인지하고 있습니다 !
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
자식 생명 주기 관리, Cascade.PERSIST + orphanRemovel=true
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]CascadeType.ALL + orphanRemoval = true 를 사용하여 부모 엔티티를 통해 자식 엔티티의 생명주기를 관리할 수 있다는 것을 이해했습니다. orphanRemoval = true 가 CascadeType.REMOVE 의 기능도 같이 한다고 말씀해주셨는데, 제 생각에는 그렇다면 CascadeType.PERSIST + orphanRemoval = true 로도 부모 엔티티가 자식 엔티티의 생명주기를 관리할 수 있는 것 같은데, 맞는지 궁금합니다. ALL 의 경우는 아무래도 기능이 정확하게 명시되어 있는게 아니다 보니 가능하면 이렇게 사용하고 싶어서 질문드립니다. 코드로는 1,2번에 대해 동일한 결과를 출력하는 것을 확인했습니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
이번 강의 내용과 pdf 자료 관련
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.[질문 내용]이번 강의는 Member 자체를 지연 로딩하는것을 설명하고 있는데, pdf 자료에선 "Member 를 조회할 때 Team 도 함께 조회해야 할까?" 라는 내용이 초반부터 등장해서요.. 특정 엔티티 조회 시, 관련된 엔티티에 대한 지연로딩은 나중 강의에서 언급되는 걸까요?
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
프로젝션 'new 명령어로 조회' 질문 입니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.안녕하세요? 프로젝션 쿼리에 new 명령어 관련 헷갈리는 부분이 있어서 질문 드립니다!try{ Member member = new Member(); member.setUsername("member1"); member.setAge(10); em.persist(member); em.flush(); em.clear(); List<MemberDTO> result = em.createQuery("SELECT new jpql.MemberDTO(m.username, m.age) FROM Member m", MemberDTO.class) .getResultList(); MemberDTO memberDTO = result.get(0); System.out.println("memberDTO = " + memberDTO.getAge()); System.out.println("memberDTO = " + memberDTO.getUsername()); tx.commit(); }위와 같은 쿼리를 실행하게 되면 상위에서 생성한 member 객체에 있는 값을 result 로 복사하고 result 값을 memberDTO List의 인덱스 0으로 들어 간 값을 조회하라는 뜻이 맞는건가요..?수업듣다가 제가 이해한게 맞는지 헷갈려서 질문 드립니다..답변 부탁드립니다!!감사합니다.
- 해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
영속성 컨텍스트 초기화 후의 변수값
안녕하세요 강의 잘 듣고 있습니다. 항상 감사드립니다.먼저 질문이 두가지가 있는데요.1) Member member1 = em.find(1L, Member.class);을 실행하면 엔티티의 참조값이 member1에 대입되나요?2) 1번이 맞다면 이후 em.clear()로 영속성 컨텍스트를 초기화하면, member1에 참조값이 대입된 엔티티가 준영속 상태가 되잖아요. 이때 member1.getName() 와 같은 조회 메소드를 호출하면, JPA가 다시 DB에서 member1의 pk값을 통해 조회해오나요, 아니면 그냥 DB와 상관없이 메모리에 남아있는 준영속 객체를 읽는건가요?
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
@SessionAttribute로 가져온 데이터가 LazyInitializationException을 발생시킬 수 있나요.
배운것을 가지고 개인적으로 게시판을 만들어보고있습니다. 게시물 추가(POST)를 하는 과정에서 세션에서 회원 정보를 가져오기 위해 컨트롤러의 매개변수로 @SessionAttribute를 사용했습니다. 컨트롤러 코드는 아래와 같습니다. 그런데 이 부분에서부터 LazyInitializationException이 발생하게 됩니다.org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.myproject.jpaboard.domain.Member.posts: could not initialize proxy - no Session /** * 게시물 추가 */ @PostMapping("/new") public String addPost(@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, @Validated PostForm postForm, BindingResult bindingResult) { log.info("postForm={}", postForm); if (bindingResult.hasErrors()) { log.info("errors={}", bindingResult); return "newPost"; } postService.addPost(postForm, loginMember); return "redirect:/board/list"; }이렇게 해도, 저렇게 해도 해결이 되지 않다가 Member 엔티티의 post를 EAGER로 세팅해주는 방식으로 하니 정상적으로 작동했습니다. 하지만 영한님이 강의에서 알려주셨듯, 이는 좋지 않은 방법입니다. 나름 배운대로 다 적용했다고 생각했는데 어디서 문제가 생긴건지 궁금합니다. 아래에 엔티티와 서비스 코드를 추가합니다. @Entity @Getter @Setter public class Post { @Id @GeneratedValue @Column(name = "post_id") private Long id; private String title; private String writer; @Lob @Column(columnDefinition="LONGTEXT") private String content; @Column(updatable = false) private LocalDateTime createdTime; private LocalDateTime lastModifiedTime; private Long viewCount; @ManyToOne(fetch = FetchType.LAZY) // 다쪽이 연관관계의 주인 @JoinColumn(name = "member_id") private Member member; @Enumerated(EnumType.STRING) private CategoryType category; @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) private List<Comment> comments = new ArrayList<>(); // 연관관계 편의 메서드 public void setMember(Member member) { this.member = member; member.getPosts().add(this); } @Override public String toString() { return "Post{" + "id=" + id + ", title='" + title + '\'' + ", writer='" + writer + '\'' + ", content='" + content + '\'' + ", createdTime=" + createdTime + ", lastModifiedTime=" + lastModifiedTime + ", viewCount=" + viewCount + ", category=" + category + '}'; } }@Entity @Getter @Setter @NoArgsConstructor(access = AccessLevel.PUBLIC) public class Member { @Id @GeneratedValue @Column(name = "member_id") private Long id; private String email; private String password; private String name; @Embedded private Address address; @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List<Post> posts = new ArrayList<>(); @Override public String toString() { return "Member{" + "address=" + address + ", name='" + name + '\'' + ", password='" + password + '\'' + ", email='" + email + '\'' + ", id=" + id + '}'; } } @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class PostService { private final BoardRepository boardRepository; private final PostRepository postRepository; /** * 게시물 추가 */ @Transactional public Post addPost(PostForm postForm, Member loginMember) { Post post = new Post(); postForm.setCreatedTime(LocalDateTime.now()); // setter로 세팅 post.setTitle(postForm.getTitle()); post.setWriter(postForm.getWriter()); post.setContent(postForm.getContent()); post.setCreatedTime(postForm.getCreatedTime()); post.setCategory(postForm.getCategory()); post.setViewCount(0L); post.setWriter(loginMember.getName()); post.setMember(loginMember); postRepository.save(post); return post; }
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
페이징 조회 시 쿼리가 달라요.
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]2분 58초에 페이징 조회 시 쿼리 로그가 저는 아래와 같이 나옵니다.Hibernate: /* select m from Member m order by m.age desc */ select m1_0.id, m1_0.age, m1_0.TEAM_ID, m1_0.username from Member m1_0 order by m1_0.age desc offset ? rows fetch first ? rows onlydirect가 달라서 그런것일까요? 저는 h2다이렉트를 사용하고 있습니다.<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
jpa문법 강의 질문있습니다.
.
- 해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
엔티티 영속 상태일 때 값 할당
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]영속성 컨텍스트의 1차 캐시 메모리 공간에 엔티티 객체가 할당되면 동시에 애플리케이션에 실제 객체에도 값이 할당되는 것인가요?그래서 @GeneratedValue의 IDENTITY 전략일 경우 persist() 즉시 INSERT 쿼리를 DB에 날려서 영속성 컨텍스트에 반환된 id 및 엔티티 객체를 그대로 애플리케이션에서도 쓸 수 있는 것인가요?핵심은 영속성 컨텍스트에 값 할당 = 애플리케이션 엔티티에도 값 할당이 동시에 되는 것인지 궁금합니다 !
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
즉시 로딩과 지연 로딩 23분 그림에서 질문있습니다..
즉시 로딩과 지연 로딩 23분 그림에서 질문있습니다..동그라미 점선 표시는 프록시로 보이는데 이거는 왜 lazy가 아닌가요?
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
프록시와 연관관계 관리-프록시 질문있습니다.
refMember.getUsername()을 하면 프록시가 초기화 된다고 하셨는데요. em.detach나 em.clear가 영속상태에서 준영속상태로 바껴야지 초기화인데 단순히 get만 호출한건데 준영속으로 바뀔수가 있나요? 즉시 로딩과 지연 로딩3분 16초 m.getTeam().getName()이부분이 프록시라고 하셨는데 이부분만 보고 프록시인지 아닌지 어떻게 알아요?
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
JPA repository질문입니다.
JPA repository를 public interface MemberRepository extends JpaRepository<Member, Long> { } 이런식으로 사용하면 기본적인 crud가 된다고 구글링하다가 알게됐습니다.아직 선생님 수업을 다 듣지 못했지만 이런식으로 사용하지않는걸로아는데 save() 레코드 저장 (insert, update) findOne() primary key로 레코드 한건 찾기 findAll() 전체 레코드 불러오기. 정렬(sort), 페이징(pageable) 가능 count() 레코드 갯수 delete() 레코드 삭제이렇게사용하면 em.persist 처럼 사용안하고 그냥 save만 불러서 쓸수이ㅣㅆ는거아닌가요?제가 아직 잘몰라서 ㅠㅠ햇갈립니다
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
failed to lazily initialize a collection of role 에러질문
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]컨트롤러@RestController @RequestMapping("/api/members") @RequiredArgsConstructor @Slf4j public class PetController { private final PetService petService; @PostMapping("/pets") @PreAuthorize("isAuthenticated()") public ResponseEntity<Long> createPet( @RequestBody PetCreateRequestDto petCreateRequestDto, @AuthenticationPrincipal PrincipalDetails principalDetails){ Member member = principalDetails.getMember(); Long savedId = petService.addPetToMember(petCreateRequestDto, member); return ResponseEntity.ok(savedId); } } 서비스@Service @RequiredArgsConstructor public class PetService { private final PetRepository petRepository; @Transactional public Long addPetToMember(PetCreateRequestDto request, Member member) { Pet pet = Pet.builder() .nickname(request.nickname()) .gender(request.gender()) .age(request.age()) .photoUrl(request.photoUrl()) .description(request.description()) .likesCount(0) .region(request.region()) .neighborhood(request.neighborhood()) .build(); pet.setPetOwner(member); Pet savePet = petRepository.save(pet); return savePet.getId(); } }연관관계 편의 메서드(Pet 엔티티에 있음)public void setPetOwner(Member member){ this.member = member; member.getPets().add(this); // 해당 코드에서 오류발생 }해당 코드에서 failed to lazily initialize a collection of role: com.gdsc.petwalk.domain.member.entity.Member.pets: could not initialize proxy - no Session가 발생하는 이유가 뭘까요?제가 하고싶었던 건 member를 먼저 만들고, 1:N에서 N쪽인 pet에 member를 add해주고 save하면 될거라 생각했는데 아무리 생각해봐도 모르겠네요... Pet에서 Member는 @ManyToOne(fetch = FetchType.LAZY)입니다
- 해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
인텔리제이 명령어 창에 persist() 및 수정 동시에 하는 경우
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]1. 명령어 창에 persist() 및 수정 코드 동시에 기입한 경우 2. 명령어 창에 수정 코드만 기입한 경우 위와 같이 persist()와 수정을 동시에 한 경우는 저장 쿼리만 나갈 뿐더러 실제 데이터베이스에도 수정된 결과가 반영이 되지 않은 것을 알 수 있었습니다하지만 수정 코드만 적은 경우는 select 및 update 쿼리가 올바르게 나갈 뿐더러 데이터베이스에도 수정된 결과가 잘 반영이 되어있는 것을 알 수 있었습니다이렇게 동작한 이유가 정확히 어떤 매커니즘으로 이렇게 된 것인지 궁금합니다 !
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
데이터 베이스 쿼리 실행 시점 관련 문의
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]이전에 데이터베이스에 반영되는 실행 시점은em.flush()트랜잭션 커밋JPQL 쿼리 실행으로 알고 있습니다. 그런데 11:45초 쯤부터 em.persist(team) 을 실행할때 id 값을 갖고 오기위해 new Member 이전에 insert문이 실행된다면 위의 경우가 아닌 것으로 판단되는데 em.persist 호출해도 쓰기 지연 SQL에 쌓이는 것이 아닌 바로 쿼리를 실행하게 되는 건가요?
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
1:n 양방향, n:1 양방향의 차이에 대해서,,
제 머릿속에는? ㅠ 1:n 이든 N:1이든. 양방향이 된 순간 서로의 필드에 참조가능한 필드가 생기기 때문에 결국에는 같은걸 말하는것 같은데많은 블로그 글에서는 1:N은 양방향은 jpa 스펙에 없다. 라고하는 글들이 많아서.. 헷갈립니다..
- 해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
저장되는 메모리 위치
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]임베디드 타입을 만들 때 자바 클래스로 만들고, @Embeddable 애노테이션을 붙여서 DB 테이블의 값 타입으로 매핑하는데 그럼 여타 값 타입과 마찬가지로 스택 영역에 저장되는지, 아니면 클래스로 구현되어 힙 영역에 저장되는지 궁금합니다.그리고 임베디드 타입 자체를 클래스가 아닌 구조체나 열거형으로 만들 수 없는지도 궁금합니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
준영속 상태와 1차캐시에 올리지 않은(persist 하지 않은) 상태의 차이
detach 메서드를 통해 준영속 상태로 전환 한다고 하였는데,강의에서는 준영속 상태란 더이상 영속성 컨텍스트에서 관리되지 않는 상태라고 하였는데 persist하기 전과 동일한 상태가 되는 것 인지 아니면 다른 차이가 있는지 궁금합니다.
- 미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
@OneToMany , @ManyToOne 질문 있습니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요 JPA 강의를 수강 후 게시판에 이미지 첨부를 구현하기 위해 공부하고 있습니다.1.Board와 Image는 1:N 관계2.연관관계 주인은 Board3.cascade = CascadeType.ALL로 옵션을 주어 부모 엔티티(board)가 영속화될 때, 자식 엔티티(Image)도 함께 영속화되도록 했습니다.위와 같이 설계를 한뒤 코드를 작성했습니다.근데, Image와 Board 테이블에 모든 데이터가 다 들어가는데 Image 테이블의 board_id가 계속 null값이 들어갑니다. 이 부분이 잘 이해가 안되는데 원인이 무엇일까요? 코드는 아래와 같습니다 ! @Entity @Getter @Setter @NoArgsConstructor public class Board { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "board_id") private Long id; @Column(name="title") private String title; @Column(name="content") private String content; @Column(name="writer") private String writer; @Column(name="date") private String date; /* @OneToMany(mappedBy = "board") @Nullable private List<Image> images = new ArrayList<>(); */ @OneToMany(mappedBy = "board",cascade = CascadeType.ALL) private List<Image> images = new ArrayList<>(); @Builder public Board(String writer,String title,String content,String date,List<Image> images){ this.writer=writer; this.title=title; this.content=content; this.date=date; this.images=images; }@Entity @Getter @Setter @NoArgsConstructor public class Image { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String fileName; @ManyToOne @JoinColumn(name = "board_id") private Board board; private String url; @Builder public Image(String fileName,String url){ this.fileName=fileName; this.url =url; } }@Service @Transactional @RequiredArgsConstructor public class BoardService { private final BoardRepository boardRepository; LocalDateTime currentTime = LocalDateTime.now(); // 현재 시간을 가져옴 /** * Create */ @Transactional public Long saveBoard(BoardSaveRequest request,List<Image> images){ Board board= Board.builder() .writer(request.writer()) .content(request.content()) .title(request.title()) .date(String.valueOf(currentTime)) .images(images) .build(); board.setImages(images); //cascade = CascadeType.PERSIST : 보드 생성 -> image add boardRepository.save(board); return board.getId(); } @RestController @RequestMapping("/board") @RequiredArgsConstructor @Slf4j public class BoardController { private final BoardService boardService; private final ImageService imageService; /** * 게시글 생성 * @param writer * @param title * @param content * @param images * @return 생성된 게시글 id */ @PostMapping() public ResponseEntity<ApiUtil.ApiSuccessResult<Long>> createBoard( @RequestPart(value="writer") String writer, @RequestPart(value="title") String title, @RequestPart(value="content") String content, @RequestPart(value = "images", required = false) List<MultipartFile> images) throws IOException { BoardSaveRequest boardSaveRequest= BoardSaveRequest.builder() .writer(writer) .content(content) .title(title).build(); List<Image> listImage = imageService.saveImage(images); Long saveId = boardService.saveBoard(boardSaveRequest,listImage); return ResponseEntity.ok().body(ApiUtil.success(HttpStatus.CREATED,saveId)); } @Service @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class ImageService { public List<Image> saveImage(List<MultipartFile> multipartFiles) throws IOException { List<Image> images = new ArrayList<>(); for(MultipartFile m : multipartFiles){ m.transferTo(new File("/Users/parkjihyeon/Desktop/imageFolder/"+m.getOriginalFilename())); Image image= Image.builder() .fileName(m.getOriginalFilename()) .url("/Users/parkjihyeon/Desktop/imageFolder/"+m.getOriginalFilename()) .build(); images.add(image); } return images; } }