JPA 질문 모음

김민지·2022년 10월 24일
0

JPA

목록 보기
10/27

제가 처음에 flush()와 commit()의 차이점을 잘 모르겠어서
다른 글들을 읽어봤는데도 제가 궁금한 것이 풀리지 않아 질문드립니다.
플러시를 하는 순간 쓰기 지연 SQL 저장소에서 DB에 반영이 된다고 하셨는데, 그런데 또 다른 글들을 보니 플러시를 한다고 해서 실제 DB에 반영되는 것이 아니라 커밋을 해야지 실제 DB에 반영이 된다고 읽었습니다.
그래서 인위적으로 커밋을 없애고 플러시만 하게 만들었더니 실제 DB는 안 바뀌고 insert문만 나왔습니다.
Q. 혹시 플러시를 했을 경우 실제 DB에 반영이 안되고
이렇게 반영될 예정이다? 그런 상태로 되는 건가요???

실제 DB에 반영하려면 commit이 발생해야 합니다.
가령 flush로 쿼리가 발생했지만 다음 로직에서 예외가 발생했다면 어떻게 되어야 할까요?
이전 상태로 돌려야 할것입니다. 이 때 롤백이 될 것이고, DB에는 반영이 되지 않을 것입니다.


예제 코드에서
persist하고 중간에 JPQL로 조회 쿼리를 수행하면 이전 SQL이 flush가 되서 결과값이 나온다고 하셨는데,
실제로 DB에 COMMIT이 된 상태가 아니고, 단순히 INSERT 쿼리만 날린 상태인데 SELECT가 되는건가요?

DB는 같은 트랜잭션 범위에서 insert 한 다음에 commit하지 않아도 해당 데이터의 읽기가 가능합니다^^!
대신에 다른 트랜잭션에서는 앞서 설명한 insert 내용을 커밋 전까지 볼 수 없습니다.


IDENTITY 전략을 사용하는 경우에는 PK값을 확인하려면 디비에 직접 들어가서 확인을 한다고 하셨고, JPA를 이용해 영속성 컨텍스트에서 관리할 때, persist를 호출하는 시점에 실제 쿼리를 날려준다는 것까지는 이해를 했습니다.
이때 플러시 과정이 일어나는지 궁금합니다.

IDENTITY 전략의 경우 em.persist 즉시 해당 엔티티를 데이터베이스에 플러시 합니다. 왜냐하면 영속성 컨텍스트에 저장하려면 PK가 필수이기 때문입니다.


@Entity적용시

  • 기본생성자는 필수
  • final클래스, enum,interface,inner클래스에는 사용할 수 없음
  • 저장할필드에 final을 사용하면안된다
    -> 이런 제약조건이 붙는 이유는 뭔가요?

하이버네이트 내부에서 엔티티들을 생성하거나 상속받아 프록시 객체를 만드는 등, 여러가지 작업을 해야 합니다. 그래서 하이버네이트 내부 처리를 위해 필요한 제약입니다.

값을 저장할 필드에 final을 붙이면 안되는 것은, final 을 붙이면 객체를 생성한 이후 이 값을 변경할수가 없기 떄문입니다. final 을 처음 할당한 값을 절대 변경할 수 없다는 자바 문법입니다.


em.createQuery()로 객체 대상으로 쿼리날리는게 jpql이고
em.persist는 jpql이 아닌가요? 그냥 바로 sql을 날리는건가요?
jpql 호출시에 flush가 발생하는데
em.persist할때도 flush가 발생하나요?


@Transactional이 붙은 메서드의 마지막쿼리는 나가지 않는다

@Test
	@Transactional
	public void Parent_Child(){
		Member member = Member.builder().name("김길동").build();
		em.persist(member);
		System.out.println("-=====");
		em.flush();
		em.clear();
		Member member1 = em.find(Member.class, member.getId());
		member1.setName("이름바꿈");
		System.out.println("====");
	}

@Transactioanl이 붙은 메서드의 마지막 쿼리는 날아가지 않아요
@Commit까지 추가해야 마지막쿼리가 출력이되더라구요
그 이유가뭔가요..? 마지막 쿼리까지 다 날리고 나서 롤백을 시켜야 논리적으로 맞는 것 같은데..

  • 트랜잭션이 끝나면 flush를 하게 되지만 테스트 특성상 트랜잭션이 끝나면 flush 대신 rollback을 진행하는 것 같습니다.
    변경감지로 인한 update 쿼리를 보고 싶으시다면 @Commit을 붙여주시거나 flush()를 메서드 종료 직전에 호출해주세요.

int와 같은 원시타입에 null을 입력할 수 없잖아요
그래서 nullable = false를 추가하는게 안전하대요
근데 전 왜 그게 안전한건지 모르겠어요
만약에 에러가 터졌을때 저는 에러내용을 보고 null을 넣어서 에러가 난것을 파악할 수 있을거예요
근데 그게 nullable=false인데 null을 넣어서 에러가 난것인지, int와 같은 원시타입에 null을 넣어서 에러가 난것인지에 따라 차이가 있을까요? 에러출력내용은 비슷하게 뜰텐데요..ㅜㅜ

@Column 애노테이션은 특성 상 데이터베이스 테이블의 해당 컬럼에 대한 정보에 대한 표시를 해주는 측면도 있습니다. 데이터베이스 테이블의 조건이 not null이기 때문에 우리 애플리케이션에서도 해당 컬럼의 not null 조건을 표시해주는 게 좋습니다. 이것은 명확하다는 측면에서 이점이 있습니다.

예를 들어 엔티티에는 int로 되어 있었고, int에는 null이 들어갈 수 없으니까 nullable 조건을 빼주었다고 가정하겠습니다. 추후에 다른 사람이 요구사항 등으로 해당 엔티티를 수정할 일이 생겼습니다. 그때 int 타입으로 되어있는 column에서 타입을 편의를 위해 Integer로 바꾸었다고 가정하면, 말씀하신 nullable 조건을 명시해주지 않았던 것이 원인으로 장애가 발생할 수도 있습니다. 바꾸는 사람은 해당 컬럼이 nullable하다고 생각하고 코드를 변경할 것이기 때문입니다.

혼자 하는 게 아닌, 다른 사람과 일하는 경우가 많은 개발자로서 명확한 코드를 짜는 게 중요하다고 생각합니다


프록시를 사용할때 외래키를 직접관리하지 않는 일대일관계는 지연로딩으로 설정해도 즉시로딩된다
p220


@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor
@IdClass(ChildId.class)
public class IdenChild {
    @Id
    @ManyToOne
    @JoinColumn(name = "parent_id")
    public Parent parent;
    
    @Id
    private Long child_id;
    //private ChildId childId; Property of @IdClass not found in entity jpaStudy.ex.entity.IdenChild: child_id

    private String name;
}

jpa 책 259p에있는 내용이에요 강의내용엔 없는것같아서 질문을 드려도될지 모르겠네요..
1. 결국 Parent타입인데 어떻게 id 어노테이션을 선언할 수 있는지 궁금해요
2. idclass가 하는일을 정확히 잘 모르겠어요
ChildId.class에 있는 필드들을 다 가져와요
childid에있는 필드명과 동일한 child의 필드들은 id를 중복선언할 수 있게 돼요 < 이런일을 하게 되나요?
3. 어떻게 Parent타입과 String타입을 매핑할 수 있게 되는건가요?


에러 : detached entity passed to perist

에러분석
generatedvalue로 id값이 생성됐는데 전 그걸까먹고 id값을 setting해줬어요
그랬더니 detached entity passed to perist 라는 오류가떴어요
준영속엔티티를 영속화시키려고해서 발생하는오류잖아요?
왜 이런 오류가뜨는걸까요? id생성전략은 sequence예요
sequence는 다음과 같이 동작해요
<<persist 를 호출하면 sequence 를 가져옵니다.
가져온 Sequence 를 id 에 할당하고 (영속성 상태), transaction 이 commit 될 때, insert 쿼리를 날립니다.>>
근데 이러면 제가 set을 해도 새로운 id에 덮어씌여져야하는거아닌가요?

자동 생성을 위임을 했는데 직접적으로 값을 넣어주면 JPA는 식별자가 있는데 해당 엔티티가 영속화가 아니니까 DETACHED~가 뜨는 것

  • 엔티티의 아이디 부여를 하이버네이트 내부에서 관리하고 있는데,
    이미 아이디가 할당된 엔티티를 persist 하거나 사용자가 이를 강제로 바꿀경우
    이를 detached 한 엔티티라고 간주하기 때문에 그런 메시지를 출력합니다.

상속매핑에서 조인전략을 사용할시에 외래키 참조 무결성 제약조건을 활용할수있다는데
어떻게 활용한다는거죠?
@foriengkey
어노테이션활용하는걸말하는걸까요?


@Test
	@Transactional
	@Commit
	public void 값타입컬렉션의insert쿼리확인(){
		Address address1 = new Address(null, "2");
		Address address2 = new Address("3", "4");
		Address address3 = new Address("12", "123");
		List<Address> addressList = new ArrayList<>();
		addressList.add(address1);
		addressList.add(address2);
		addressList.add(address3);
		Member member = new Member();
		member.setAddressList(null);
		em.persist(member);
		//변경
		em.flush();
		address1 = new Address("213", "!23");
		addressList.remove(0);
		addressList.add(address1);

컬럼에 null을 입력할 수 없다는데.. 입력해도 테스트를 통과해요 제가 어떤부분을 잘못알고있는건가요?
같은값을 중복해서 저장할 수 없다는데 2번째 3번째 줄을 보면 잘 저장하고 있지 않나요..?

ElementCollection 쿼리 방법

public void 임베디드값조회(){
   List list = em.createQuery("select m.addressList from Member m").getResultList();
   for (Object o : list) {
      System.out.println(o);
   }
}

다음과 같은 오류가 뜹니다
not an entity [select m.addressList from jpaStudy.ex.entity.Member m]
근데 m.addressList가 엔티티는 당연히 아니잖아요?
왜 저 문장에 오류가 있는건가요? addresslist는 강의에 나온 address의 list버전입니다.

Profile 어노테이션이란?

  • 런타임 환경을 설정할 수 있는 기능
  • 런타임 환경을 설정한다는게 뭘까? 언제필요한거지?
  • 로컬로 애플리케이션을 돌릴 때와 테스트를 돌릴 때 그리고 실제 운영을 하기위한 배포를 할 때, 각기 다른 설정을 주고 싶을 수가 있다. 또는 각 설정에 맞는 빈을 가져와서 사용하고 싶을 수가 있다.
    예를 들면, 배포를 할 때에는 실제 실무에서 사용하는 DB에다가 연결하고 싶을 것이고 테스트나 로컬 시에는 인메모리 DB를 사용하고 싶을 수도 있다.

@PostConstruct

  • 의존성 주입이 이루어진 후 초기화를 수행하는 메서드이다.

사용 이유

1) 생성자가 호출되었을 때, 빈은 초기화되지 않았음(의존성 주입이 이루어지지 않았음)
이럴 때 @PostConstruct를 사용하면 의존성 주입이 끝나고 실행됨이 보장되므로 빈의 초기화에 대해서 걱정할 필요가 없다.

2) bean 의 생애주기에서 오직 한 번만 수행된다는 것을 보장한다. (어플리케이션이 실행될 때 한번만 실행됨)
따라서 bean이 여러 번 초기화되는 걸 방지할 수 있다.

AOP

  • 주요 핵심 기능과 핵심 기능 구현을 위한 부가적인 기능 구현을 분리하여 각각의 관점별로 묶어서 개발하는 방식을 말합니다.

출처
https://bepoz-study-diary.tistory.com/371
https://zorba91.tistory.com/223

profile
안녕하세요!

0개의 댓글