일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 배열
- View
- db
- JDBC
- mysql
- MVC
- Eclipse
- API
- jquery
- string
- 문자열
- Uipath
- rpa
- git
- 이클립스
- Oracle
- SpringBoot
- Java
- Board
- Database
- spring
- 조건문
- React
- Array
- Thymeleaf
- jsp
- Scanner
- Controller
- 상속
- html
- Today
- Total
유정잉
🎀 스프링 부트 [ JPA, Repository, @Query ] 본문
- JPA를 통해서 관리하게 되는 객체(이하 엔티티객체(Entity Object))를 위한 엔티티 클래스
- 엔티티 객체들을 처리하는 기능을 가진 Repository
[ @GeneratedValue(strategy = GenerationType.IDENTITY) ]
AUTO(default) : JPA 구현체(스프링부트에서는 Hibernate)가 생성 방식을 결정
IDENTITY : 사용하는 데이터베이스가 키 생성을 결정 MySQL이나 MariaDB의 경우 auto increment 방식을 이용
SEQUENCE : 데이터베이스의 sequence를 이용해서 키를 생성. @SequenceGenerator와 같이 사용
TABLE : 키 생성 전용 테이블을 생성해서 키 생성 @TableGenerator와 함께 사용
package org.zerock.ex2.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "tbl_memo")
@ToString
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Memo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long mno;
@Column(length = 200, nullable = false)
private String memoText;
}
[ MySql 연동을 위한 application.properties 코드 ]
spring.application.name=ex2
# MySQL ?????? ?? ??
spring.datasource.url=jdbc:mysql://localhost:3306/yujung
spring.datasource.username=root
spring.datasource.password=00000000
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
## JPA (Hibernate) ??
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.hibernate.format_sql=true
# Open-in-view ????
spring.jpa.open-in-view=false
[ CRUD ]
insert : save(엔티티 객체)
select : findById(키 타입), getOne(키 타입)
update : save(엔티티 객체)
delete : deleteById(키 타입), delete(엔티티 객체)
package org.zerock.ex2.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import org.zerock.ex2.entity.Memo;
import java.util.Optional;
import java.util.stream.IntStream;
@SpringBootTest
public class MemoRepositoryTests {
@Autowired
MemoRepository memoRepository;
@Test
public void testClass(){
System.out.println(memoRepository.getClass().getName());
}
@Test
public void tesInsertDummies(){
IntStream.rangeClosed(1,100).forEach(i->{
Memo memo = Memo.builder().memoText("Sample..."+i).build();
memoRepository.save(memo);
});
}
@Transactional
@Test
public void testSelect(){
//데이터 베이스에 존재하는 memo
Long mno = 100L;
Optional<Memo> result = memoRepository.findById(mno);
System.out.println("====================================");
if(result.isPresent()){
Memo memo = result.get();
System.out.println(memo);
}
}
@Test
public void testUpdate(){
Memo memo = Memo.builder().mno(100L).memoText("Update Text").build();
System.out.println(memoRepository.save(memo));
}
@Test
public void testDelete(){
Long mno = 100L;
memoRepository.deleteById(mno);
}
}
[ 페이징/정렬 limits ]
JPA에서 페이징 처리와 정렬은 findAll()이라는 메서드 사용. 단 한가지 주의할 사항은 리턴 타입을 Page<T> 타입으로 지정하는 경우에는 반드시 파라미터를 Pageable 타입을 이용해야 한다는 점이다.
PageRequest클래스의 생성자는 특이하게 protected로 선언되어 new를 이용할 수 없다. 객체를 생성하기 위해서는 static한 of()를 이용해서 처리한다. page, size, Sort라는 정보를 이용해서 객체를 생성한다.
JPA를 이용할 때 페이지 처리는 반드시 '0'부터 시작한다.
of(int page, int size) : 0부터 시작하는 페이지 번호와 개수, 정렬이 지정되지 않음
of(int page, int size, Sort sort) : 페이지 번호와 개수, 정렬 관련 정보
of(int page, int size, Sort.Direction direction, String...props) : 0부터 시작하는 페이지번호와 개수, 정렬의 방향과 정렬기준필드들
package org.zerock.ex2.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.zerock.ex2.entity.Memo;
@SpringBootTest
public class MemoRepositoryTests {
@Autowired
MemoRepository memoRepository;
@Test
public void testPageDefault() {
//1페이지 10개
Pageable pageable = PageRequest.of(0,10);
Page<Memo> result = memoRepository.findAll(pageable);
System.out.println(result);
}
}
[ Page<엔티티타입>을 이용해서 주로 사용하는 메서드들 ]
10개씩 페이징을 처리하기 떄문에 전체 페이지 수는 10페이지이고, 99개의 데이터가 존재하는 등 필요한 정보를 가져온다.
실제 페이지의 데이터를 처리하는 것은 getContent()를 이용해서 List<엔티티 타입>으로 처리하거나 Stream<엔티티 타입>을 반환하는 get()을 이용할 수 있다.
package org.zerock.ex2.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
import org.zerock.ex2.entity.Memo;
@SpringBootTest
public class MemoRepositoryTests {
@Autowired
MemoRepository memoRepository;
@Test
public void testPageDefault() {
//1페이지 10개
Pageable pageable = PageRequest.of(0,10);
Page<Memo> result = memoRepository.findAll(pageable);
System.out.println(result);
System.out.println("------------------------------------");
System.out.println("Total Pages : " + result.getTotalPages()); //총 몇 페이지
System.out.println("Total Count : " + result.getTotalElements()); //전체 페이지
System.out.println("Page Number : " + result.getNumber()); //현재 페이지 번호 0부터 시작
System.out.println("Page Size : " + result.getSize()); //페이지당 데이터 개수
System.out.println("has next Page ? :" + result.hasNext()); //다음 페이지 존재 여부
System.out.println("first page ? :" + result.isFirst()); //시작 페이지(0) 여부
System.out.println("------------------------------------");
for (Memo memo : result.getContent()) {
System.out.println(memo);
}
}
}

[ 정렬 조건 추가하기 ]
정렬은 Sort 타입을 파라미터로 전달할 수 있다. Sort는 한 개 혹은 여러 개의 필드 값을 이용해서 순차적으로 정렬(asc)이나 역순으로 정렬(desc)을 지정할 수 있다.
Sort 객체의 and()를 이용해서 여러 개의 정렬 조건을 다르게 지정할 수 있다. memoText는 asc로 하고, mno는 desc로 지정할 수 있다.
package org.zerock.ex2.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;
import org.zerock.ex2.entity.Memo;
@SpringBootTest
public class MemoRepositoryTests {
@Autowired
MemoRepository memoRepository;
@Test
public void testSort() {
Sort sort1 = Sort.by("mno").descending();
Sort sort2 = Sort.by("memoText").ascending();
Sort sortAll = sort1.and(sort2); //and()를 이용한 페이징 정렬 연결
Pageable pageable = PageRequest.of(0,10, sort1);
Page<Memo> result = memoRepository.findAll(pageable);
result.get().forEach(memo -> {
System.out.println(memo);
});
}
}
[ 쿼리 메서드(Query Methods) 기능과 @Query ]
다양한 검색조건을 위한 쿼리 메서드. 특정한 범위의 Memo객체를 검색하거나, like 처리가 필요한 경우, 여러 검색 조건이 필요하다.
- 쿼리 메서드 : 메서드의 이름 자체가 쿼리의 구문으로 처리되는 기능
- @Query : SQL과 유사하게 엔티티 클래스의 정보를 이용해서 쿼리를 작성하는 기능
- Querydsl 등의 동적 쿼리 처리 기능
Memo 객체의 mno값이 70부터 80사이의 객체들을 구하고 mno의 역순으로 정렬하고 싶다면 다음과 같은 인터페이스 추가한다.
package org.zerock.ex2.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.zerock.ex2.entity.Memo;
import java.util.List;
public interface MemoRepository extends JpaRepository<Memo, Long> {
List<Memo> findByMnoBetweenOrderByMnoDesc(Long from, Long to);
}
mno을 기준으로 between 구문을 사용하고 order by가 적용해서 Test 작성
@Test
public void testQueryMethods(){
List<Memo> list = memoRepository.findByMnoBetweenOrderByMnoDesc(70L, 80L);
for(Memo memo : list){
System.out.println(memo);
}
}
[ deleteBy로 시작하는 삭제 처리 ]
deleteBy로 메서드 이름을 시작하면 특정한 조건에 맞는 데이터를 삭제하는 것도 가능하다.
@Transactional과 @commit이라는 어노테이션 같이 사용.
deleteBy는 별로 좋은 방법이 아님 !! -> @Query를 이용하는 것이 효율적
package org.zerock.ex2.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.zerock.ex2.entity.Memo;
import java.util.List;
public interface MemoRepository extends JpaRepository<Memo, Long> {
void deleteMemoByMnoLessThan(Long num);
}
@Commit
@Transactional
@Test
public void testDeleteQueryMethods() {
memoRepository.deleteMemoByMnoLessThan(10L);
}
[ @Query 어노테이션 ]
JPA가 제공하는 쿼리 메서드는 검색과 같은 기능을 작성할 때는 편리하지만, 나중에 조인을 사용하는 등 복잡한 조건을 처리해야 하는 경우는 불편함이 더 많다. 그래서 간단한 처리만 쿼리 메서드를 이용하고, @Query를 이용하는 경우가 더 많다.
@Query는 메서드의 이름과 상관없이 메서드에 추가한 어노테이션을 통해서 원하는 처리가 가능하다.
- 필요한 데이터만 선별적으로 추출하는 기능 가능
- 데이터베이스에 맞는 순수한 SQL(Native SQL)을 사용하는 기능
- insert, update, delete와 같은 select가 아닌 DML 등을 처리하는 기능(@Modifying과 함께 사용)
public interface MemoRepository extends JpaRepository<Memo, Long> {
@Query("SELECT m FROM Memo m WHERE m.memoText LIKE %:keyword%")
List<Memo> findByMemoTextContaining(@Param("keyword") String keyword);
@Query(value = "SELECT * FROM tbl_memo WHERE memo_text LIKE %:keyword%", nativeQuery = true)
List<Memo> findByMemoTextContainingNative(@Param("keyword") String keyword);
@Query("SELECT m FROM Memo m WHERE m.memoText LIKE %:keyword% AND m.mno > :mno")
List<Memo> findByMemoTextAndMnoGreaterThan(@Param("keyword") String keyword, @Param("mno") Long mno);
@Query("SELECT m FROM Memo m WHERE m.memoText LIKE %:keyword%")
Page<Memo> findByMemoTextContainingWithPagination(@Param("keyword") String keyword, Pageable pageable);
@Modifying
@Query("UPDATE Memo m SET m.memoText = :memoText WHERE m.mno = :mno")
int updateMemoTextByMno(@Param("memoText") String memoText, @Param("mno") Long mno);
}
[ 기본적인 JPQL 쿼리 ]
JPQL(Java Persistence Query Language)은 객체 지향 쿼리 언어로, 엔티티 객체를 대상으로 쿼리를 작성합니다.
- Memo 엔티티에서 memoText에 특정 키워드가 포함된 레코드를 검색합니다.
- :keyword는 메서드 매개변수로 전달된 값을 바인딩합니다.
- @Param("keyword") String keyword: @Query에 사용된 매개변수 :keyword와 메서드 매개변수를 바인딩합니다.
public interface MemoRepository extends JpaRepository<Memo, Long> {
@Query("SELECT m FROM Memo m WHERE m.memoText LIKE %:keyword%")
List<Memo> findByMemoTextContaining(@Param("keyword") String keyword);
}
[ 네이티브 SQL 쿼리 ]
네이티브 SQL 쿼리는 데이터베이스 벤더에 종속적인 SQL 쿼리를 직접 작성하여 사용할 수 있습니다.
- 네이티브 SQL 쿼리를 사용하여 tbl_memo 테이블에서 memo_text에 특정 키워드가 포함된 레코드를 검색합니다.
- nativeQuery = true를 설정하여 네이티브 쿼리를 사용하도록 지정합니다.
public interface MemoRepository extends JpaRepository<Memo, Long> {
@Query(value = "SELECT * FROM tbl_memo WHERE memo_text LIKE %:keyword%", nativeQuery = true)
List<Memo> findByMemoTextContainingNative(@Param("keyword") String keyword);
}
[ 복잡한 JPQL 쿼리 ]
- memoText에 특정 키워드가 포함되고 mno가 주어진 값보다 큰 레코드를 검색합니다
- @Param("keyword") String keyword, @Param("mno") Long mno: @Query에 사용된 매개변수 :keyword와 :mno를 메서드 매개변수와 바인딩합니다.
public interface MemoRepository extends JpaRepository<Memo, Long> {
@Query("SELECT m FROM Memo m WHERE m.memoText LIKE %:keyword% AND m.mno > :mno")
List<Memo> findByMemoTextAndMnoGreaterThan(@Param("keyword") String keyword, @Param("mno") Long mno);
}
[ 페이징과 정렬을 포함한 JPQL 쿼리 ]
- memoText에 특정 키워드가 포함된 레코드를 검색합니다
- Page<Memo> findByMemoTextContainingWithPagination(@Param("keyword") String keyword, Pageable pageable):
- Pageable 인터페이스를 사용하여 페이징과 정렬 정보를 전달합니다.
- 반환 타입을 Page<Memo>로 설정하여 페이징 결과를 반환합니다.
public interface MemoRepository extends JpaRepository<Memo, Long> {
@Query("SELECT m FROM Memo m WHERE m.memoText LIKE %:keyword%")
Page<Memo> findByMemoTextContainingWithPagination(@Param("keyword") String keyword, Pageable pageable);
}
[ 업데이트 쿼리 ]
- @Modifying: 데이터베이스에 변경 작업(업데이트, 삭제 등)을 수행할 때 사용합니다.
- @Query("UPDATE Memo m SET m.memoText = :memoText WHERE m.mno = :mno"):
- Memo 엔티티에서 mno가 주어진 값인 레코드의 memoText를 업데이트합니다.
- int updateMemoTextByMno(@Param("memoText") String memoText, @Param("mno") Long mno):
- 업데이트된 레코드 수를 반환합니다.
public interface MemoRepository extends JpaRepository<Memo, Long> {
@Modifying
@Query("UPDATE Memo m SET m.memoText = :memoText WHERE m.mno = :mno")
int updateMemoTextByMno(@Param("memoText") String memoText, @Param("mno") Long mno);
}
'개발자 공부 > 🎀 스프링 공부' 카테고리의 다른 글
🎀 스프링 부트 [ 게시판 만들기 ] (0) | 2024.07.10 |
---|---|
🎀 스프링부트 SpringBoot 파일 생성 (0) | 2024.06.18 |
🎀 스프링 레거시 흐름과 코드 예시 (0) | 2024.06.15 |
⭐️ @PostConstruct와 @PreDestroy 빈의 초기화 + 빈의 소멸 (0) | 2024.05.29 |
⭐️ Controller와 RestController의 차이점 (0) | 2024.05.29 |