Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- resttemplate
- 코드로배우는스프링웹프로젝트
- 데비안
- /etc/network/interfaces
- 이터레이터
- 서버설정
- 코드로배우는스프링부트웹프로젝트
- 구멍가게코딩단
- 티스토리 쿠키 삭제
- 처음 만나는 AI 수학 with Python
- Kernighan의 C언어 프로그래밍
- network configuration
- baeldung
- 자료구조와 함께 배우는 알고리즘 입문
- 선형대수
- 스프링 시큐리티
- 목록처리
- 리눅스
- iterator
- 처음 만나는 AI수학 with Python
- 알파회계
- 페이징
- GIT
- 친절한SQL튜닝
- d
- 자료구조와함께배우는알고리즘입문
- 자바편
- 스프링부트핵심가이드
- 네트워크 설정
- ㅒ
Archives
- Today
- Total
bright jazz music
JpaRepository + @Query, nativeQuery = true 본문
package org.zerok.ex2.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import org.zerok.ex2.entity.Memo;
import java.util.List;
public interface MemoRepository extends JpaRepository<Memo, Long> {
//JpaRepository를 사용할 때는 엔티티 타입 정보(여기서는 Memo클래스)와 @Id 타입을 지정한다.
//SpringDataJpa는 인터페이스 선언만으로도 자동으로 bean으로 등록한다.
//(내부적으로는 인터페이스 타입에 맞는 객체를 생성해서 빈으로 등록한다.)
//선언이 끝났으면 test폴더-repository패키지생성-MemoRepositoryTests클래스를 작성해서 진행
List<Memo> findByMnoBetweenOrderByMnoDesc(Long from, Long to);
//쿼리메소드와 @Query를 사용하기 위해 선언. 이 메소드의 이름 자체가 질의문이 된다.
void deleteMemoByMnoLessThan(Long num);
@Query("select m from Memo m order by m.mno desc")
List<Memo> getListDesc();
@Transactional
@Modifying
@Query("update Memo m set m.memoText = :memoText where m.mno = :mno")
int updateMemoText(@Param("mno") Long mno, @Param("memoText") String memoText);
@Transactional
@Modifying
@Query("update Memo m set m.memoText = :#{#param.memoText} " + //:, #, {}는 붙여 써야 한다. :#{}이렇게. 띄어 쓰면 오류발생
"where m.mno = :#{#param.mno} ") //한 칸 띄어쓰기 주의.
int updateMemoText2(@Param("param") Memo memo);
@Query(value = "select m from Memo m where m.mno > :mno",
countQuery = "select count(m) from Memo m where m.mno > :mno" )
Page<Memo> getListWithQuery(Long mno, Pageable pageable);
@Query(value = "select m.mno, m.memoText, CURRENT_DATE from Memo m " +
"where m.mno > :mno",
countQuery = "select count(m) from Memo m where m.mno > :mno" )
Page<Object[]> getListWithQueryObject(Long mno, Pageable pageable);
@Query(value="select * from tbl_memo where mno > 0", nativeQuery = true)
List<Object[]> getNativeResult();
//DB의 테이블명을 그대로 사용해야 한다. tbl_memo를 그냥 memo라고 썼다가 오류발생하였다.
}
}
package org.zerok.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.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.test.annotation.Commit;
import org.zerok.ex2.entity.Memo;
import javax.transaction.Transactional;
import java.util.List;
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());
/*
* testClass()메서드는 MemoRepository 인터페이스 타입의 실제 객체가 어떤 것인지 확인한다.
* 스프링이 내부적으로 해당 클래스를 자동생성(AOP)하는데, 이 때 선언한 적 없는 클래스의 이름이
* com.sun.proxy.$Proxy110 와 같이 콘솔에 출력된다.(동적 프록시 방식으로 생성됨)
*
*
* */
}
@Test //등록작업 테스트
public void testInsertDummies(){
IntStream.rangeClosed(1,100).forEach(i -> {
Memo memo = Memo.builder().memoText("Sample..." + i).build();
memoRepository.save(memo);
});
//한 번에 여러 개의 객체를 저장하도록 작성. 100개의 Memo 객체를 생성하고 MemoRepository를 이용해서 insert.
// 두 번 실행하면 테이블에 행이 200번까지 만들어진다.
}
@Test //조회작업 테스트: findById()사용
public void testSelect(){
//DB에 존재하는 mno
Long mno = 100L;
Optional<Memo> result = memoRepository.findById(mno);
//100번이 존재하지 않더라도 오류를 반환하지 않음. 물론 객체도 반환되지 않음.
System.out.println("==============================");
if(result.isPresent()) {
Memo memo = result.get();
System.out.println(memo);
}
/*
* 조회 작업의 테스트는 findById()나 getOne()을 이용해서 엔티티 객체를 조회할 수 있다.
* findById()와 getOne()은 동작하는 방식이 조금 다르다.
* DB를 먼저 이용하는지, 필요한 순간까지 미루는지에 대한 차이이다.
*
* findById()는 DB를 먼저 이용해서 값을 가져온다.
* findById()를 실행한 순간에 이미 sql은 처리되었고
*
* findById()의 경우 java.util 패키지의 Optional 타입으로 반환되기 때문에
* 한 번 더 결과가 존재하는지 체크하는 형태로 작성하게 된다.
* "==============================" 부분은 SQL 처리 이후에 실행된다.
* */
}
@Transactional //트랜잭션 처리를 위해 사용하는 어노테이션. 없으면 오류 반환: no session
@Test //조회작업 테스트: getOne()사용
public void testSelect2(){
Long mno = 101L;
Memo memo = memoRepository.getOne(mno);
//리턴 값은 해당 객체이지만 필요한 순간까지 sql을 실행하지 않는다.
//해당 값이 테이블에 존재하지 않으면 오류 반환: Unable to find org.zerok.ex2.entity.Memo with id 100
//getOne()은 depricated됨. getReferenceById()로 대체. testSelect3()에서 사용
System.out.println("==============================");
System.out.println(memo);//실제 객체를 사용하는 순간에 SQL이 동작한다.
}
@Transactional //트랜잭션 처리를 위해 사용하는 어노테이션. 없으면 오류 반환: no session
@Test //조회작업 테스트: getOne()사용
public void testSelect3(){
Long mno = 101L;
Memo memo = memoRepository.getReferenceById(mno); //getOne() 대신에 사용
//리턴 값은 해당 객체이지만 필요한 순간까지 sql을 실행하지 않는다.
//해당 값이 테이블에 존재하지 않으면 오류 반환 : Unable to find org.zerok.ex2.entity.Memo with id 100
//getOne()은 depricated됨. getReferenceById()로 대체. testSelect3()에서 사용
System.out.println("==============================");
System.out.println(memo);//실제 객체를 사용하는 순간에 SQL이 동작한다.
}
@Test
public void testUpdate(){ //수정작업
Memo memo = Memo.builder().mno(100L).memoText("Update Text").build();
System.out.println(memoRepository.save(memo));
/*
* 수정작업은 등록작업과 동일하게 save()를 이용해서 처리한다.
* 내부적으로 해당 엔티티의 @Id 값이 일치하는지 확인해서 insert 혹은 update 작업을 처리한다.
*
* 여기서는 100번의 Memo객체를 만들고, save()를 호출한다.
* 호출결과를 보면 내부적으로 select 쿼리로 해당 번호의 Memo 객체를 확인하고
* 이를 update하는 것을 볼 수 있다.
*
* JPA는 엔티티 객체들을 메모리 상에 보관하려고 하기 때문에 특정한 엔티티 객체가 존재하는지
* 확인하는 select가 먼저 실행되고 해당 @Id를 가진 엔티티 객체가 있다면 update를,
* 그렇지 않다면 insert를 실행한다.
* */
}
@Test
public void testDelete(){
Long mno = 100L;
memoRepository.deleteById(mno);
/*
* 삭제 작업도 위와 동일한 개념이 적용된다. 삭제하려는 번호(mno)의 엔티티 객체가 있는지
* 먼저 확인(select)하고 이를 삭제 시도한다.
* deleteById()의 리턴 타입은 void이고 만일 해당 데이터가 존재하지 않으면
* org.springframework.dao.EmptyResultAccessException 예외를 발생시킴.
* */
}
@Test
public void testPageDefault(){
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("Total 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);
}
}
@Test
public void testSort(){
Sort sort1 = Sort.by("mno").descending();
Sort sort2 = Sort.by("memoText").ascending();
Sort sortAll = sort1.and(sort2); //and를 이용한 연결. memo는 desc로, memoText는 asc로 정렬
Pageable pageable = PageRequest.of(0, 10, sortAll);
Page<Memo> result = memoRepository.findAll(pageable);
result.get().forEach(memo -> {
System.out.println(memo);
});
}
@Test
public void testQueryMethod(){
List<Memo> list = memoRepository.findByMnoBetweenOrderByMnoDesc(70L, 80L);
for (Memo memo : list){
System.out.println(memo);
}
}
@Commit
@Transactional
@Test
public void testDeleteQueryMethod(){
memoRepository.deleteMemoByMnoLessThan(20L); //10번 전까지의 데이터를 삭제
/*
* @Transactional과 @Commit 어노테이션 함께 사용.
* select문으로 해당 엔티티 객체들을 가져오는 작업과 각 엔티티를 삭제하는 작업이 같이 이뤄지기 때문.
*
* @Commit은 최종 결과를 커밋하기 위해서 사용. 이를 적용하지 않으면 쿼리가 기본적으로 롤백 처리되어
* 결과가 반영되지 않는다.
*
* deleteBy는 실제 개발에는 일반적으로 사용되지 않는다. 그 이유는 SQL을 이용하듯 한 번에 삭제가
* 이뤄지는 것이 아니라 각 엔티티 객체를 하나씩 삭제하기 때문이다. 여기의 예제의 경우는 10번을 삭제하는 것이다.
* 실제 개발에는 deleteBy등의 쿼리메소드보다는 @Query 어노테이션을 이용해서 이와 같은 비효율을 개선한다.
*
* */
}
@Test
public void queryAnnotationTest(){
memoRepository.getListDesc(); //@Query("select m from Memo m order by m.mno desc") List<Memo> getListDesc();
}
@Test
public void queryAnnotationTest2(){
memoRepository.updateMemoText(95L, "new text!!");
}
@Test
public void queryAnnotationTest3(){
Memo memo = Memo.builder().memoText("update memo3 !!!!").mno(94L).build();
int n = memoRepository.updateMemoText2(memo);
System.out.println("==> " + n);
}
@Test
public void queryAnnotationTest4(){
Pageable pageable = PageRequest.of(0, 10, Sort.by("memoText").descending());
Page<Memo> pagelist = memoRepository.getListWithQuery(100L, pageable);
pagelist.forEach(r -> {
System.out.println(r);
});
}
@Test
public void queryAnnotationTest5(){
Pageable pageable = PageRequest.of(0, 10, Sort.by("memoText").descending());
Page<Object[]> listWithQueryObject = memoRepository.getListWithQueryObject(50L, pageable);
listWithQueryObject.forEach(r -> {
Arrays.stream(r).forEach(c -> {
System.out.println(c.toString());
});
System.out.println("----------------------------------------");
});
}
@Test
public void
testNativeObject(){
List<Object[]> listNativeQueryObject = memoRepository.getNativeResult();
System.out.println(listNativeQueryObject);
listNativeQueryObject.forEach(r -> {
Arrays.stream(r).forEach(c -> {
System.out.println(c.toString());
});
System.out.println("----------------------------------------");
});
}
}
@Test
public void queryAnnotationTest2()
@Test
public void queryAnnotationTest3()
@Test
public void queryAnnotationTest4()
@Test
public void queryAnnotationTest5(){
@Test
public void
testNativeObject()
//MemoRepository.java
@Query(value="select * from tbl_memo where mno > 0", nativeQuery = true)
List<Object[]> getNativeResult();
위의 쿼리를 사용해서 아래의 결과를 도출한다.
**** 주의 ****
아래와 같은 오류 발생 시.
2022-06-25 08:24:36.090 ERROR 16600 --- [ Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper : (conn=629) Table 'bootex.memo' doesn't exist
could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
증상:
원인:
@Query(value="select * from memo where mno > 0", nativeQuery = true)
List<Object[]> getNativeResult();
//위와 같이 코드가 쓰여 있다. 이를 그대로 적용하면 위와 같은 오류가 발생한다.
// DB에 bootex.memo 테이블이 없기 때문에 resultSet을 추출하지 못했다는 것이다.
// 즉 실습용으로 생성한 bootex라는 DB에 memo 테이블이 없다는 뜻이다.
// 실제로 DB를 확인해 보면 memo 테이블은 존재하지 않는다.
// 대신 tbl_memo라는 이름의 테이블이 존재한다.
// 이는 우리가 Memo.java 클래스에서 @Table(name="tbl_memo") 코드를 사용하여
// 테이블 이름을 tbl_memo로 생성하였기 때문이다.
// 아무래도 책에서 테이블 명을 오기한 듯하다.
// 어쨌든 위 쿼리의 memo를 tbl_memo로 변경해주면 @test가 정상 구동한다.
// memo ==> tbl_memo 변경 후의 쿼리
@Query(value="select * from tbl_memo where mno > 0", nativeQuery = true)
List<Object[]> getNativeResult();
'Framework > Spring' 카테고리의 다른 글
guestbook : 02. 기본 화면 레이아웃 구성 (0) | 2022.06.28 |
---|---|
Thymeleaf 타임리프 기본 + Simple Sidebar (0) | 2022.06.28 |
JpaRepository + 페이징, 쿼리메소드 (0) | 2022.06.23 |
JPA 인터페이스 생성 + CRUD 테스트 (insert, findById, getOne, getReferenceById save, deleteById) (0) | 2022.06.12 |
JPA 활용 + 클래스 생성 및 테이블 생성 (0) | 2022.06.09 |
Comments