2019. 11. 14. 13:26ㆍ[정리] 기능별 개념 정리/JPA
1. Query 어노테이션을 사용하여 만든 쿼리는 반드시 SQL 문을 날린다 (같은 파라미터 일지라도)
코드
@Repository
public interface TeamRepository extends JpaRepository<TeamEntity, Long> {
Optional<TeamEntity> findById(long teamId);
}
TeamEntity teamEntity = teamRepository.findById(teamId).get();
teamEntity = teamRepository.findById(teamId).get();
teamEntity = teamRepository.findById(teamId).get();
teamEntity = teamRepository.findById(teamId).get();
teamEntity = teamRepository.findById(teamId).get();
결과
Hibernate: select teamentity0_.id as id1_1_0_, teamentity0_.name as name2_1_0_ from team_entity teamentity0_ where teamentity0_.id=?
코드
@Repository
public interface TeamRepository extends JpaRepository<TeamEntity, Long> {
@Query("SELECT te FROM TeamEntity te WHERE te.id = :teamId")
Optional<TeamEntity> findById(long teamId);
}
TeamEntity teamEntity = teamRepository.findById(teamId).get();
teamEntity = teamRepository.findById(teamId).get();
teamEntity = teamRepository.findById(teamId).get();
teamEntity = teamRepository.findById(teamId).get();
teamEntity = teamRepository.findById(teamId).get();
결과
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.id=?
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.id=?
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.id=?
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.id=?
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.id=?
@Query 가 달린 경우는 JPQL 을 직접 DB에 때려보겠다는 의미이기 때문이다.
2. JPA 인터페이스가 기본 만들어주는 쿼리일지라도 SQL 문은 중복해서 발생한다. (특정 칼럼이 unqiue 제약이 걸려있어도 같은 결과를 낸다.)
코드
@Repository
public interface TeamRepository extends JpaRepository<TeamEntity, Long> {
Optional<TeamEntity> findByName(String teamName);
}
TeamEntity teamEntity = teamRepository.findByName(teamName).get();
teamEntity = teamRepository.findByName(teamName).get();
teamEntity = teamRepository.findByName(teamName).get();
teamEntity = teamRepository.findByName(teamName).get();
teamEntity = teamRepository.findByName(teamName).get();
결과
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.name=?
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.name=?
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.name=?
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.name=?
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_ where teamentity0_.name=?
해석
영속성 컨텍스트가 1차 캐싱 하는건 맞는데, @Id 가 달려있는 필드가 key, Entity 가 value 로 되어있는 맵핑 테이블을 통해 캐싱하기 때문.
3. findAll 메소드도 중복해서 발생한다.
코드
@Repository
public interface TeamRepository extends JpaRepository<TeamEntity, Long> {
Optional<TeamEntity> findByName(String teamName);
}
List<TeamEntity> teamEntities = teamRepository.findAll();
teamEntities = teamRepository.findAll();
teamEntities = teamRepository.findAll();
teamEntities = teamRepository.findAll();
teamEntities = teamRepository.findAll();
결과
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_
Hibernate: select teamentity0_.id as id1_1_, teamentity0_.name as name2_1_ from team_entity teamentity0_
4. 트랙잭션이 없어도 하위 컴포넌트에서 findById 쿼리를 사용하는 경우가 있다면 중복을 알아서 제거해준다.
코드
@Service
public class TeamService {
@Autowired
private TeamRepository teamRepository;
@Autowired
private MemberRepository memberRepository;
public Team findById(long teamId){
TeamEntity teamEntity = teamRepository.findById(teamId).get();
List<MemberEntity> memberEntities = memberRepository.findByTeamId(teamId);
return EntityConverter.convert(teamEntity, Team.class);
}
}
teamService.findById(team.getId());
teamService.findById(team.getId());
teamService.findById(team.getId());
teamService.findById(team.getId());
teamService.findById(team.getId());
결과
Hibernate: select teamentity0_.id as id1_1_0_, teamentity0_.name as name2_1_0_ from team_entity teamentity0_ where teamentity0_.id=?
Hibernate: select memberenti0_.id as id1_0_, memberenti0_.name as name2_0_, memberenti0_.team_id as team_id3_0_ from member_entity memberenti0_ left outer join team_entity teamentity1_ on memberenti0_.team_id=teamentity1_.id where teamentity1_.id=?
TeamService findById done
Hibernate: select memberenti0_.id as id1_0_, memberenti0_.name as name2_0_, memberenti0_.team_id as team_id3_0_ from member_entity memberenti0_ left outer join team_entity teamentity1_ on memberenti0_.team_id=teamentity1_.id where teamentity1_.id=?
TeamService findById done
Hibernate: select memberenti0_.id as id1_0_, memberenti0_.name as name2_0_, memberenti0_.team_id as team_id3_0_ from member_entity memberenti0_ left outer join team_entity teamentity1_ on memberenti0_.team_id=teamentity1_.id where teamentity1_.id=?
TeamService findById done
Hibernate: select memberenti0_.id as id1_0_, memberenti0_.name as name2_0_, memberenti0_.team_id as team_id3_0_ from member_entity memberenti0_ left outer join team_entity teamentity1_ on memberenti0_.team_id=teamentity1_.id where teamentity1_.id=?
TeamService findById done
Hibernate: select memberenti0_.id as id1_0_, memberenti0_.name as name2_0_, memberenti0_.team_id as team_id3_0_ from member_entity memberenti0_ left outer join team_entity teamentity1_ on memberenti0_.team_id=teamentity1_.id where teamentity1_.id=?
TeamService findById done
영속성 컨텍스트
강의 : https://www.youtube.com/watch?v=xqEVS8LzxZM
앤티티 매니저
- 스프링에서 앤티티 매니저 팩토리가 사용자의 요청당 엔티티 매니저를 하나 할당 해준다.
- 엔티티 매니터를 통해서 영속성 컨텍스트에 접근 할 수 있다.
J2SE, 생 자바 환경: 앤티티 매니저와 영속성 컨텍스트의 관계는 1:1
J2EE, 스프링 환경 : 앤티티 매니저와 영속성 컨텍스트의 관계는 N:1
em.persist 하면 저장 되는게 아니다.
em.persist 하면 POJO 데이터를 영속성 컨텍스트에 저장하는 것일 뿐이다.
em.persist 하면 영속성 컨텍스트안에 있는 '쓰기 지연 SQL 저장소'에 SQL 문을 만들어서 저장해둔다.
transaction.commit() 을 해야 DB 에 쿼리가 날라간다.
JPA 의 전제 : JPA 의 데이터의 변경은 무조건 트랜잭션 안에서 해야한다.
영속성 컨텍스트가 1차 캐싱 하는건 맞는데, @Id 가 달려있는 필드가 key, Entity 가 value 로 되어있는 맵핑 테이블을 통해 캐싱하기 때문.
영속성 컨텍스트를 플러시하는 방법
1. 직접 호출 : em.flush()
2. 자동 호출 : transaction.commit()
3. 자동 호출 : JPQL 호출
플러시 동작
1. 더티 체킹
2. 쓰기 지연 SQL 저장소에 Update 쿼리 등록
3. 쓰기 지연 SQL 저장소에 있는 쿼리를 DB에 호출
*) 영속성 컨텍스트를 비우는 동작은 아니다.
주의 : JPQL 이 호출 되면 영속성 컨텍스트가 플러시가 된다.
왜냐하면 JPQL 은 DB 에 직접 SQL 을 하는 것이기 때문에 영속성 컨텍스트를 DB 에 반영시켜야 데이터 정합성이 깨지지 않기 때문
@Query 가 달린 경우도 JPQL 이다.
스프링 프레임워크에서 영속성 컨텍스트는 어떻게 동작하는가??
트랜잭션 범위에 영속성 컨텍스트를 할당한다.
- @Transactional 이 생성된다 = 영속성 컨텍스트가 생성된다.
- @Transactional 이 종료된다 = 영속성 컨텍스트를 날린다.
- @Transactional 이 같다 = 항상 같은 영속성 컨텍스트를 참조한다.
* 트랜잭션 전파 : 현재 Transaction 에서 다른 Transaction 메소드를 호출할 때 발생하는 것. 최초에 호출된 트랜잭션 이름으로 이후 호출되는 트랜잭션에 전파되어진다. 즉 후에 호출하는 트랜잭션 이름과 처음 호출된 트랜잭션의 이름은 같다.
* 트랜잭션의 최대 약점 : 트랜잭션이 끝나면 준영속 상태로 바뀌어 버린다. -> 지연 로딩이 안된다.
* 트랜잭션이 롤백될 경우 DB 의 데이터는 롤백되지만 객체는 롤백이 되지 않을 수 있으므로 조심한다.
'[정리] 기능별 개념 정리 > JPA' 카테고리의 다른 글
Persist vs Merge (0) | 2020.02.04 |
---|---|
JPA : Query 발생 시점 (0) | 2019.06.17 |
JPA : Embeddable (0) | 2019.06.17 |
@EntityGraph : FetchType.EAGER 일 경우 Select 쿼리 줄이기 (1) | 2019.06.15 |
JPA fetch 맵핑별 기본값 (0) | 2019.06.14 |