김영한님의 강의를 들으면서, 정리하면서 모르는 것은 보태보았습니다!
스프링을 거치지 않은 순수 JPA 기술이다.
@Entity
@NoArgsConstructor
public class Entity {
@Id
private String id;
}
@Entity
: 이 어노테이션이 있어야 JPA가 있음public
/protected
기본 생성자 필수서비스계층
EntityManager를 주입
@Transactional
@Configuration
@RequiredArgsConstructor
public class JpaConfig {
private final EntityManager em;
@Bean
public Service service() {
return new ServiceImpl(repository());
}
@Bean
public Repository repository() {
return new JpaRepository(em);
}
}
persist()
em.persist(Entity entity)
find()
Entity entity = em.find(Entity.class, pk)
: 조회타입, pk값
Optional.ofNullable(entity)
으로 return해주는게 null방지createQuery()
String jpql = "select e from Entity e where e.pk = :pk";
TypedQuery<Entity> query = em.createQuery(jpql, Entity.class);
query.setParameter("pk", pk);
return query.getResultList();
JPQL
query.setParameter("pk", pk);
:pk
부분을 바인딩한다)Entity entity = em.find(Entity.class, pk);
entity.setPk(requestDto.getPk);
entity.setPk(값)
@Repository
예외 변환 AOP의 적용 대상이 된다.
스프링과 JPA를 함께 사용하는 경우, 스프링은 PersistenceExceptionTranslator
라는 JPA예외변환기를 등록
-> 예외변환AOP프록시는 JPA예외가 발생하면 JPA예외변환기를 통해 발생한 예외를 스프링데이터접근예외로 변환
즉
EntityManager(PersistenceException 발생) -> JpaRepository 예외 전달 -> 예외변환AOP프록시(PersistenceException을 DataAccessException으로 변환) -> 서비스계층에 스프링예외(DataAccessException) 전달. 스프링예외추상화에 의존
JpaRepository
인터페이스
public interface Repository extends JpaRepository<Entity, entity타입> {
}
여기서 메소드를 구현하여 사용할 수 있다.
@Repository
@RequiredArgsConstructor
public class RepositoryImpl implements Repository {
private final EntityManager em;
...
}
여기서 @Override하여 사용할 수 있다.
find...By
, read...By
, query...By
, get...By
...
에 식별내용이 들어감(생략가능)find...ByFieldLessThanEqual(Integer field)
field
보다 작거나 같은것을 조회 (<= )find...ByFieldLike(String field)
field
like검색조합 -> find...ByFieldLikeAndField2LessThanEqual(String field, Integer field2)
count...By
: long타입
exists...By
: boolean타입
delete...By
, remove...By
: long타입
findDistinct
, findEntityDistinctBy
findFirst3
, findFirst
, findTop
, findTop3
@Query("select e from Entity e where e.pk lik :pk")
List<Entity> findEntities(@Param("pk") Long pk);
파라미터를 명시적으로 바인딩해야함
엔티티 객체를 쿼리하는 방식. 테이블이 대상이 아닌 엔티티를 대상으로 날림
JPQL 대신 직접 SQL 작성 가능
SQL문을 DB에 직접 날림
메서드 이름으로 쿼리를 실행하면 1)조건이 많을때 메서드이름이 너무 길어지고, 2)조인과같은 복잡한 조건으 사용할 수 없다.
따라서, 복잡해지면 직접 JPQL 쿼리를 작성하는 것이 좋다.
동적 쿼리 문제를 해결해줌.
컴파일 시점에 자동 생성
QEntity 클래스로 생성된다 -> 버전관리(Git)에 포함되지 않는 것이 좋음!
IntelliJ Idea 컴파일 -> src/main/generated에 파일 생성
gradle 컴파일 -> gradle clean 명령(build.gradle) 설정으로 자동 삭제 가능
clean { delete file('src/main/generated') }
@Configuration
@RequiredArgsConstructor
public class QuerydslConfig {
private final EntityManager em;
@Bean
public EntityService entityService() {
return new EntityService1(entityRepository());
}
@Bean
public EntityRepository entityRepository() {
return new RepositoryImpl(em);
}
}
JPAQueryFactory
가 필요하다. JPQL을 만들기때문에 EntityManager가 필요함.
JPAQueryFactory를 스프링 빈으로 등록해서 사용해도 됨.
@Repository
@Transcational
public class RepositoryIml implements Repository {
private final EntityManager em;
private final JPAQueryFactory query;
public RepositoryImpl(EntityManager em){
this.em = em;
this.query = new JPAQueryFactory(em);
}
}
List<Entity> result = query
.select(entity)
.from(entity)
.where(likeField1(field1))
.fetch();
...
priavate BooleanExpression likeField1(String field1){
if (StringUtils.hasText(field1) {
return entity.field1.like("%"+field1+"%");
}
return null;
}
booleanBuilder
를 사용해 원하는 where조건을 자바코드로 넣어줄 수 있음.
where 파라미터에 null을 입력하면 해당 조건은 무시됨.
파라미터에 여러가지 변수를 넣어 줄 수 있다(조건)
likeField()등을 다른 쿼리를 작성할 때 재사용 할 수 있음 -> 쿼리 조건 부분모듈화 가능
DI, OCP를 지키기위해 어댑터를 도입하고 더많은 코드를 유지하는 방법을 보통 사용하기 때문이다.
클래스 의존관계
Service -> Repository(i) -> RepositoryImpl -> JpaRepositoryImpl -> 스프링데이터JpaRepository
런타임 객체의존관계
Service -> Repositoryimpl -> 스프링데이터JPA 프록시
Service가 직접 스프링데이터Repository를 구현하여 구조를 단순화한다.
클래스 의존관계
Service -> 스프링데이터JpaRepository
항상 나은 선택을 해야함.
추상화도 비용이 든다!
Repository를 분리한다.
Service -> 스프링데이터JPA Repository Interface + Querydsl Repository
public interface JpaRepository extends JpaRepository<Entity, pk>{
... 기본CRUD/단순조회쿼리
}
@Repository
public class QueryRepository {
private final JPAQueryFactory query;
public QueryRepository(EntityManager em) {
this.query = new JPAQueryFactory(em);
}
... 복잡쿼리
}
Config
@Configuration
@RequiredArgsConstructor
public class Config {
private final EntityManager em;
private final JpaRepository repository;
@Bean
public Service service() {
return new Service(repository, queryRepository());
}
@Bean
public QueryRepository queryRepository() {
return new QueryRepository(em);
}
}
서비스
@Service
@RequiredArgsConstructor
public class ServiceImpl implements Service {
private final JpaRepository jpaRepository;
private final QueryRepository queryRepository;
...
}