예전에는 복합 외래키를 사용하려면 클래스에 @JoinColumns 어노테이션을 설정하고 각각의 필드에 @JoinColumn을 사용하여야 했다.
지금은 각각의 필드에 @JoinColumn만 사용하면 된다.
기준은 항상 자신이 기준이다.
@ManyToMany는 존재하긴 하지만 없다고 생각하면 된다.
이유는 @ManyToMany는 중간에 존재하는 테이블을 숨기고 이어진 두 테이블을 다중성으로 연결하는 것인데 중간에 날짜 등과 같은 column이 추가되는 경우가 있어 중간 테이블을 만들고 @OneToMany와 @ManyToOne으로 묶는 것이 좋다.
toone에서 eager를 쓰는 이유는 상대 테이블에서 하나 아니면 존재하지 않기 때문에 즉시 가져와도 부담이 되지 않아서 기본 전략이 eager이다.
tomany는 상대가 몇 개인지 모르기 때문에 lazy전략을 사용한다.
관계에서 실선과 점선 차이는 실선은 식별관계 외래키를 기본키에 사용하겠다는 것이고, 점선은 비식별관계 외래키를 기본키에 사용하지 않겠다는 의미이다.
@OneToOne(cascade = CascadeType.PERSIST)
@OneToMany(cascade = CascadeType.ALL)
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
public enum CascadeType {
ALL, /* PERSIST, MERGE, REMOVE, REFRESH, DETACH */
PERSIST, // cf.) EntityManager.persist()
MERGE, // cf.) EntityManager.merge()
REMOVE, // cf.) EntityManager.remove()
REFRESH, // cf.) EntityManager.refresh()
DETACH // cf.) EntityManager.detach()
}
영속성 전이는 cascade를 설정하는 것이고 이 것을 설정하면 따로 persist() 메서드를 사용하여 등록을 하지 않아도 된다.
단방향 일대다 관계에서는 다른 테이블에 외래키가 있으면 연관관계 처리를 위해 추가적인 update쿼리가 갯수만큼 실행된다.
이를 해결하기 위해서는 양방향 관계로 만들어 줘야 한다.
대부분의 경우에는 단방향으로 잡아도 되지만 OneToMany일 경우에는 양방향으로 처리하는 것이 좋다.
해당 테이블에 외래키가 있는 경우는 단방향을 설정할 수 있지만 대상 테이블에 외래키가 존재하는 경우 해당 Entity에서는 단방향을 지정할 수 없다 JPA에서 지원하지 않음 이런 경우에는 외래키를 해당 테이블이 소유하게 하거나 양방향을 만들어야 한다.
식별관계에서는 외래 키에 @MapsId를 설정해 주어야 한다.
식별관계에서 외래키가 복합 기본키에 포함되는 경우에는 @MapsId(field이름)로 필드 이름과 맵핑을 해주어야 한다.
repository는 spring의 기능이다.
jpa repository를 만들기 위해서는 JpaRepository 인터페이스를 확장해야 하는데 제네릭의 첫 인자는 엔티티의 타입이고, 두 번째 인자는 기본 키의 타입을 적어주어야 한다.
Spring Data Repository 추상화의 목표는 다음과 같다. 상용구 코드의 양을 크게 줄이기 위해 다양한 지속성 저장소에 대한 데이터 액세스 계층을 구현하는데 필요하다.

@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable>
extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAllById(Iterable<ID> ids);
<S extends T> List<S> save(Iterable<S> entities);
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
// ...
}
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable<? extends T> entities);
// ...
}
여기서 @NoRepositoryBean은 Repository를 스캔할 때 어노테이션이 붙은 것은 제외하겠다는 의미이다.
// insert / update
<S extends T> S save(S entity);
// select * from Items where item_id = {id}
Optional<T> findById(ID id);
// select count(*) from Items;
long count();
// delete from Items where item_id = {id}
void deleteById(ID id);
// ...
public interface ItemRepository {
// select * from Items where item_name like '{itemName}'
List<Item> findByItemNameLike(String itemName);
// select item_id from Items
// where item_name = '{itemName}'
// and price = {price} limit 1
boolean existsByItemNameAndPrice(String itemName, Long price);
// select count(*) from Items where item_name like '{itemName}'
int countByItemNameLike(String itemName);
// delete from Items where price between {price1} and {price2}
void deleteByPriceBetween(long price1, long price2);
}
인터페이스로 선언해도 사용이 가능한 이유는 SimpleJpaRepository라는 JpaRepository 구현체를 spring data jpa에서 지원을 해주기 때문이다.
@Query("select i from Item i where i.price > ?1")
List<Item> getItemsHavingPriceAtLeast(long price);
@Query(value = "select * from Items where price > ?1", nativeQuery = true)
List<Item> getItemsHavingPriceAtLeast2(long price);
@Modifying
@Query("update Item i set i.itemName = :itemName where i.itemId = :itemId")
int updateItemName(@Param("itemId") Long itemId, @Param("itemName")String itemName);
@Query 어노테이션에서 nativeQuery 속성을 기본값 false로 두면 JPQL로 실행이 되고 true로 주면 native query로 실행이 된다.
여기서 JPQL은 entity에 대해서 query를 수행하는 것이고, native query는 database를 기반으로 sql을 실행한다.
@Query 어노테이션에서 ?순서를 주지 않고 직접 주고 싶을때는 query안에서 :fieldName을 주고 parameter에서 @Param(fieldName)을 붙여주면 된다.