[Spring Data JPA] ์คํ๋ง ๋ฐ์ดํฐ JPA
by rlaehddnd0422Spring Data JPA๋?
์คํ๋ง ํ๋ ์์ํฌ์์ JPA๋ฅผ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํ๋ ํ๋ก์ ํธ์ ๋๋ค.
์ฃผ ๊ธฐ๋ฅ
- CRUD ์ฒ๋ฆฌ๋ฅผ ์ํ ๊ณตํต ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณต
- Repository ๊ณ์ธต์ ๊ตฌํ ํด๋์ค ์์ด ์ธํฐํ์ด์ค๋ง ์์ฑํ๋ฉด ์คํ ์์ ์ ์คํ๋ง ๋ฐ์ดํฐ JPA๊ฐ ๊ตฌํ ๊ฐ์ฒด๋ฅผ ๋์ ์ผ๋ก ์์ฑ ๋ฐ ์ฃผ์
- ๊ณตํต ๋ฉ์๋๋ ์คํ๋ง ๋ฐ์ดํฐ JPA๊ฐ ์ ๊ณตํ๋ org.springframework.date.jpa.repository.JpaRepository ์ธํฐํ์ด์ค์
count, delete, deleteAll, deleteAll, deleteById, existsById, findById, save ๋ฑ์ด ๊ตฌํ๋์ด ์์ต๋๋ค.
์ฌ์ฉ ๋ฐฉ๋ฒ
build.gradle์ ์์กด๊ด๊ณ ์ถ๊ฐ
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
๋ฆฌํฌ์งํ ๋ฆฌ ๊ณ์ธต์ ์ธํฐํ์ด์ค๋ก ์ ์ธํ๊ณ JpaRepository๋ฅผ ์์๋ฐ์ต๋๋ค.
- JpaRepository.interface
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
@Override
List<T> findAll();
@Override
List<T> findAll(Sort sort);
@Override
List<T> findAllById(Iterable<ID> ids);
@Override
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
@Deprecated
default void deleteInBatch(Iterable<T> entities){deleteAllInBatch(entities);}
void deleteAllInBatch(Iterable<T> entities);
void deleteAllByIdInBatch(Iterable<ID> ids);
void deleteAllInBatch();
@Deprecated
T getOne(ID id);
T getById(ID id);
@Override
<S extends T> List<S> findAll(Example<S> example);
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
- JpaRepository ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ๊ณ , ์ ๋ค๋ฆญ์ ๊ด๋ฆฌํ <์ํฐํฐ, ์ํฐํฐID>๋ฅผ ์ฃผ๋ฉด ๋ฉ๋๋ค.
๊ณตํต ์ธํฐํ์ด์ค ๊ธฐ๋ฅ
public interface SpringDataJpaItemRepository extends JpaRepository<Item,Long> {
List<Item> findByItemNameLike(String itemName);
List<Item> findByPriceLessThanEqual(Integer price);
// ์ฟผ๋ฆฌ ๋ฉ์๋ ( ์๋ ๋ฉ์๋์ ๊ฐ์ ๊ธฐ๋ฅ ์ํ )
List<Item> findByItemNameLikeAndPriceLessThanEqual(String itemName, Integer price);
// ์ฟผ๋ฆฌ ์ง์ ์คํ
@Query("select i from Item i where i.itemName like :itemName and i.price <= :price")
List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);
}
์ด๋ ๊ฒ JpaRepository ์ธํฐํ์ด์ค๋ง ์์๋ฐ์๋ Spring Data JPA๊ฐ ํ๋ก์ ๊ธฐ์ ์ ์ฌ์ฉํด์ ๊ตฌํ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํด์ฃผ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๋ ๊ตฌํ ํด๋์ค ์์ด ์ธํฐํ์ด์ค๋ง ๋ง๋ค๋ฉด ๊ธฐ๋ณธ CRUD ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ
์คํ๋ง ๋ฐ์ดํฐ JPA๋ ์ธํฐํ์ด์ค์ ๋ฉ์๋ ์ด๋ฆ๋ง ์ ์ด๋๋ฉด, ๋ฉ์๋ ์ด๋ฆ์ ๋ถ์ํด์ ์ฟผ๋ฆฌ๋ฅผ ์๋์ผ๋ก ๋ง๋ค๊ณ ์คํํด์ฃผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
// ์ฟผ๋ฆฌ ๋ฉ์๋
List<Item> findByItemNameLikeAndPriceLessThanEqual(String itemName, Integer price);
์คํ๋ง ๋ฐ์ดํฐ JPA๊ฐ ์ ๊ณตํ๋ ์ฟผ๋ฆฌ ๋ฉ์๋ ๊ท์น
- ์กฐํ : find..By, read..By, query..By, get..By
- Row ์ COUNT : count..By ( return type : long )
- ์กด์ฌ ์ฌ๋ถ : Exist : exists...By ( return type : boolean )
- ์ญ์ : delete...By, remove...By ( return type : boolean - ์ญ์ ๋ row ์ )
- ์ค๋ณต์๋ Row ์ Distinct : ex) findDistinct, findMemberDistinctBy
-LIMIT : findFirst3, findFirst, findTop, findTop3
๊ณต์ ๋ฌธ์ ์ฐธ์กฐ
Distinct | findDistinctByLastnameAndFirstname | select distinct …โ where x.lastname = ?1 and x.firstname = ?2 |
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is, Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull, Null | findByAge(Is)Null | … where x.age is null |
IsNotNull, NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstname) = UPPER(?1) |
์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ง ์๊ณ ์ง์ JPQL์ ์ฌ์ฉํ๊ณ ์ถ์ ๋๋ @Query ์ ์ง์ JPQL์ ์์ฑํ๋ฉด ๋ฉ๋๋ค.
// ์ฟผ๋ฆฌ ์ง์ ์คํ
@Query("select i from Item i where i.itemName like :itemName and i.price <= :price")
List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);
@Query ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์คํํ๋ ๊ท์น์ด ๋ฌด์๋ฉ๋๋ค.
JPQL์ด ์๋ SQL์ ์์ฑํ๋ ๋ค์ดํฐ๋ธ ์ฟผ๋ฆฌ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด nativeQuery = true ์ต์ ์ถ๊ฐ (@Param ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ ๋ฐ์ธ๋ฉ์ด 0๋ถํฐ ์์)
ํ์ด์ง ๊ธฐ๋ฅ
์คํ๋ง ๋ฐ์ดํฐ JPA๋ ์ฟผ๋ฆฌ ๋ฉ์๋์ ํ์ด์ง๊ณผ ์ ๋ ฌ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
- org.springframework.data.domain.Sort : ์ ๋ ฌ ๊ธฐ๋ฅ
- org.springframework.data.domain.Pageable : ํ์ด์ง ๊ธฐ๋ฅ(๋ด๋ถ์ Sort ํฌํจ)
public interface SpringDataJpaItemRepository extends JpaRepository<Item,Long> {
List<Item> findByItemNameStartingWith(String itemName, Pageable pageable);
ํ๋ผ๋ฏธํฐ์ Pageable์ ์ฌ์ฉํ๋ฉด ๋ฐํํ์ ์ผ๋ก List๋ Page๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
> ๋ฐํ ํ์ ์ด Page์ธ ๊ฒฝ์ฐ ์คํ๋ง ๋ฐ์ดํฐ JPA๋ ํ์ด์ง ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ธฐ ์ํด ๊ฒ์๋ ์ ์ฒด ๋ฐ์ดํฐ ๊ฑด์๋ฅผ ์กฐํํ๋ count์ฟผ๋ฆฌ๋ฅผ ์ถ๊ฐ๋ก ํธ์ถํฉ๋๋ค.
> ๋ฐํ ํ์ ์ด List์ธ ๊ฒฝ์ฐ์๋ count ์ฟผ๋ฆฌ๋ฅผ ํธ์ถํ์ง ์์ต๋๋ค.
ํ์ด์ง ์กฐ๊ฑด, ์ ๋ ฌ ์กฐ๊ฑด์ PageRequest๋ฅผ ํตํด ์ค์ ํ ์ ์์ต๋๋ค.
public List<Item> findByItemNameStartingWithName01(String itemName)
{
Sort sort = Sort.by("itemName").descending();
PageRequest pageable = PageRequest.of(0,10, sort);
List<Item> result = repository.findByItemNameStartingWith("Name01", (Pageable) pageable);
return result;
}
Spring Data JPA ์ ์ฉ
JpaRepository ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ผ๋ฉด ๊ธฐ๋ณธ์ ์ธ CRUD ๊ธฐ๋ฅ๋ค์ ๊ตฌํ๋์ด ์๊ธฐ ๋๋ฌธ์, ๊ฐ๋ฐ์๊ฐ ์ํ๋ ๊ธฐ๋ฅ์ ์ถ๊ฐ์ ์ผ๋ก ์ฟผ๋ฆฌ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉฐ ๊ตฌํํด์ฃผ๋ฉด๋ฉ๋๋ค.
1. findByItemNameLike(String itemName) : itemName๋ง์ ์ฌ์ฉํด ๊ฒ์ํ์ ๋ ์ฌ์ฉํ๋ ์ฟผ๋ฆฌ ๋ฉ์๋
List<Item> findByItemNameLike(String itemName);
์คํ๋๋ JPQL ์ฟผ๋ฆฌ
select i from Item i where i.name like itemName
2. findByPriceLessThanEqual() : price๋ง์ ์ฌ์ฉํด ๊ฒ์ํ์ ๋ ์ฌ์ฉํ๋ ์ฟผ๋ฆฌ ๋ฉ์๋
List<Item> findByPriceLessThanEqual(Integer price);
์คํ๋๋ JPQL ์ฟผ๋ฆฌ
select i from Item i where i.pice <= price
3. findByItemNameLikeAndPriceLessThanEqual() : itemName๊ณผ price๋ฅผ ์ฌ์ฉํด ๊ฒ์ํ์ ๋ ์ฌ์ฉํ๋ ์ฟผ๋ฆฌ ๋ฉ์๋
// ์ฟผ๋ฆฌ ๋ฉ์๋ ( ์๋ ๋ฉ์๋์ ๊ฐ์ ๊ธฐ๋ฅ ์ํ )
List<Item> findByItemNameLikeAndPriceLessThanEqual(String itemName, Integer price);
์กฐ๊ฑด์ด ๋ง์ผ๋ฉด ๋ฉ์๋ ์ด๋ฆ์ด ๊ธธ์ด์ง๊ธฐ ๋๋ฌธ์ ๋ณต์กํด์ง๋ ๊ฒฝ์ฐ ์ง์ JPQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
@Query(value = "select i from Item i where i.itemName like :itemName and i.price <= :price")
List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);
@Param์ ํตํด ์ด๋ฆ ๊ธฐ๋ฐ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ ์ฌ์ฉ
@Param์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์์น ๊ธฐ๋ฐ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ์ด ์ฌ์ฉ๋๋๋ฐ, ์ฝ๋์ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์๋ฅผ ์ํด ์ด๋ฆ ๊ธฐ๋ฐ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํฉ์๋ค.
JpaItemRepositoryV2
@Slf4j
@Repository
@Transactional
@RequiredArgsConstructor
public class JpaItemRepositoryV2 implements ItemRepository {
// ํ๋ก์๋ก ์๋์ฃผ์
๋ฉ๋๋ค.
private final SpringDataJpaItemRepository repository;
@Override
public Item save(Item item) {
return repository.save(item);
}
ํด๋์ค ์์กด๊ด๊ณ
@Override
public Item save(Item item) {
return repository.save(item);
}
@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
Item findItem = repository.findById(itemId).orElseThrow();
findItem.setItemName(updateParam.getItemName());
findItem.setPrice(updateParam.getPrice());
findItem.setQuantity(updateParam.getQuantity());
}
@Override
public Optional<Item> findById(Long id) {
return repository.findById(id);
}
@Override
public List<Item> findAll(ItemSearchCond cond) {
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
if (StringUtils.hasText(itemName) && maxPrice != null) {
// return repository.findByItemNameLikeAndPriceLessThanEqual(itemName, maxPrice);
return repository.findItems("%" + itemName + "%", maxPrice);
} else if (StringUtils.hasText(itemName)) {
return repository.findByItemNameLike("%" + itemName + "%");
} else if (maxPrice != null) {
return repository.findByPriceLessThanEqual(maxPrice);
} else {
return repository.findAll();
}
}
- JpaRepository ์ ๊ตฌํ๋์ด ์๋ CRUD ๊ธฐ๋ฅ๊ณผ ๋ฉ์๋ ์ฟผ๋ฆฌ๋ฅผ ์ด์ฉํด ๋ฆฌํฌ์งํ ๋ฆฌ ๊ณ์ธต ๊ตฌํ
findAll() ๋ฉ์๋์ ๋์ ์ฟผ๋ฆฌ ๋ถ๋ถ์ ์ํฉ์ ๋ฐ๋ผ ๊ฐ๊ฐ ์คํ๋ง ๋ฐ์ดํฐ JPA์ ๋ฉ์๋๋ฅผ ํธ์ถํด์ ์๋นํ ๋นํจ์จ์ ์ธ๋ฐ, ์ด ํ Querydsl์ ์ฌ์ฉํ๋ฉด ๊น๋ํ๊ฒ ๊ฐ์ ์ด ๊ฐ๋ฅํฉ๋๋ค. ์ด ๋ถ๋ถ์ ์ดํ์ Querydsl์ ์ด์ฉํด ๊ฐ์ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
+ Config ์ค์
@Configuration
@RequiredArgsConstructor
public class SpringDataJpaConfig {
private final SpringDataJpaItemRepository springDataJpaItemRepository;
@Bean
public ItemService itemService()
{
return new ItemServiceV1(itemRepository());
}
@Bean
public ItemRepository itemRepository()
{
return new JpaItemRepositoryV2(springDataJpaItemRepository);
}
}
+ ์์ธ ๋ณํ
๋ง์ฐฌ๊ฐ์ง๋ก ์คํ๋ง ๋ฐ์ดํฐ JPA๋ ์์ธ ์ถ์ํ ๋ณํ์ ์ง์ํฉ๋๋ค. ์คํ๋ง ๋ฐ์ดํฐ JPA๋ ํ๋ก์์์ ์์ธ๋ณํ์ ์ฒ๋ฆฌํด์ฃผ์ด @Repository์ ๊ด๊ณ์์ด ์์ธ๊ฐ ๋ณํ๋ฉ๋๋ค.
<์ ๋ฆฌ>
์คํ๋ง ๋ฐ์ดํฐ JPA๋ Querydsl๊ณผ ๋๋ถ์ด ์ค๋ฌด์์ ๊ธฐ๋ณธ์ผ๋ก ์ ํํ๋ ๊ธฐ์ ์ ๋๋ค. JPA์ ์คํ๋ง ๋ฐ์ดํฐ JPA๋ ๊ณต๋ถํ ์์ด ๋ง๊ธฐ ๋๋ฌธ์ ์ด๋ฒ ํฌ์คํ ์์๋ ๋๋ต์ ์ธ ์ฌ์ฉ๋ฒ์ ์์๋ณด์๊ณ , ์ด ์ดํ์ ์์ธํ๊ฒ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
<์ฐธ๊ณ ์๋ฃ>
'๐ Backend > DB Access' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ํธ๋์ญ์ AOP @Transactional ์ฌ์ฉ ์ ์ฃผ์์ฌํญ (1) | 2023.04.20 |
---|---|
[Querydsl] Querydsl ์ด๋? (0) | 2023.04.19 |
[JPA] JPA(Java Persistent API) (0) | 2023.04.17 |
TestCode์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฒ ์ฐ๋ (1) | 2023.04.15 |
[JdbcTemplate] JdbcTemplate ์ ์ฉ (0) | 2023.04.12 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422