JPA에서는 이전에 서블릿이나 스프링에서 사용하던 DAO 클래스와 비슷한 역할을 하는 클래스를 우리가 정의해야 한다.  Jpa의 표준 명세라는 것이 있고 우리가 그것을 가져다 쓰는 방식으로 구현을 하게 되는 것이다. 그래서 간단한 쿼리 같은 것은 메서드 하나로 뚝딱이지만 복잡한 쿼리는 이러한 메서드들로 해결이 되지 않는다는 문제점이 있다. 하지만 JPA 입문 초반에는 복잡한 쿼리에 대한 고민보다는 이러한 표준 명세에 대하여 익히는 것을 우선으로 하되 JpaRepository API 인터페이스 공부를 제대로 잡고 하는 것이 필요하다. 여기서는 '진짜 심플한 예제'부터 시작한다. 해당 엔터티를 하나 정의하고 그에 대한 엔터티 Repository를 만든다. 

 

명명 방식은 다음과 같다.

  • 엔터티명 + Repository (클래스명은 자유이긴한데 일반적으로 이렇게 정의)

이렇게 우리가 정의한 클래스는 JpaRepository를 상속 받아서 써야 한다.

  • 이 때 상속을 받게 되면 기본적으로 부모 클래스에서 이미 정의된 메서드를 사용할 수 있다. 
  • JpaRepository의 클래스 계층구조는 다음과 같다.

요런 계층 구조

  • CrudRepository에서는 existsById, findById, save, saveAll, findAll, count, deleteById, delete, deleteAll 등의 메서드를 정의하고 있다. 말 그대로 Crud에 관련한 기본 메서드 정의이다.
  • PagingAndSortingRepository 
    • 페이징 처리를 위한 메서드를 제공
  • QueryByExampleExecutor 
    • 어떤 명세를 인자로 받으면 그에 맞는 동적으로 쿼리를 제공해준다. 

사용자 정의 메소드

  • 위에서 상속하는 클래스가 제공하는 메서드를 쓰지 않고 JPA가 약속한 규칙에 따라 사용자가 메서드를 정의해서 써야 한다. 주로 목적은 테이블 칼럼에 맞추고 Where절 부분에 해당하는 조건을 파라미터로 받기 위한 것이다. 
  • 메서드 이름은 CamelCase로 입력해야 한다.  
    • 예시)   
      • findBy○○○(String ○○○)
      • findBy○○○Null
      • findAllByOrderBy○○○
      • findAllByOrderBy○○○Desc
      • findBy○○○In(String[] strings)
  • ItemRepository.java
public interface ItemRepository extends JpaRepository<Item, String>{


 // 여기다가 이제 메서드 정의만 추가 ↓↓↓↓↓↓↓
 
 
 
 }
  • m9 
    • where 조건이 붙은 쿼리를 정의하는 느낌으로 구성할 수 있다. 
    • findByName("마우스") ----> ItemRepository에 Item findByName(String name);  추가
      • 아래의 표현은 결과가 동일하다.  
      • Item findByName(String name); 
      • Item findByNameIs(String name);
      • Item findByNameEquals(String name); 
    • itemRepo.findByPrice(100000) ---->  ItemRepository에 Item findByPrice(int price); 추가
@GetMapping("/item/m9")
	public String m9(Model model) {
		//findByName으로 자동으로 구현 
		// Item item = itemRepo.findByName("마우스");
		// Item item = itemRepo.findByPrice(100000);
		// 메서드 이름 패턴이 중요하다. 
		// 가장 중요하게 생각하는 패턴이 > findBy이다. 
		// findBy컬럼명
		// - By : Po
		//Item item = itemRepo.findByNameIs("키보드");
		Item item = itemRepo.findByNameEquals("키보드");
		model.addAttribute("result", item);
		return "item/result";
}
  • m10
    • where절에 And 혹은 Or 사용
    • where절 다중조건
    • itemRepo.findByColorAndOwnerAndPrice("black", "홍길동", 150000);
@GetMapping("/item/m10")
public String m10(Model model) {
    // And, Or
    //Item item = itemRepo.findByColorAndOwner("yellow", "홍길동");
    //Item item = itemRepo.findByColorAndOwnerAndPrice("black", "홍길동", 150000);
    Item item = itemRepo.findByColorOrPrice("gold", 300000);
    //where item0_.color=? and item0_.owner=? and item0_.price=?

    model.addAttribute("result", item);
    return "item/result";
}
  • m11
    • findBy에 정렬 추가 
      • 파라미터 두번째 인자 Sort.by
      • 첫번째 인자로 오름, 내림차순을 정하고, properties를 가변인자로 받아서 정렬 기준을 정한다. 

내용

  • Like 쿼리 : 패턴 매칭
    • findBy○○○Like(패턴)
    • findBy○○○IsLike(패턴)
    • findBy○○○NotLike(패턴)
    • findBy○○○IsNotLike(패턴)
    • 패턴으로 %, _ 등을 넣어서 찾을 수 있다.
  • StartingWith, EndingWith, Containing : ~로 시작거나 끝나거나 포함하는걸로 매칭
    • findBy○○○[Is]StartingWith(검색어)
    • findBy○○○[Is]EndingWith(검색어)
    • findBy○○○[Is]Containing(검색어)
@GetMapping("/item/m11")
	public String m11(Model model) {
		
		//List<Item> list = itemRepo.findByColor("white");
		//List<Item> list= itemRepo.findByColor("white", Sort.by(Sort.Direction.ASC, "price"));
		//color를 조건 price를 기준으로 order by를 했다.
		//List<Item> list= itemRepo.findByColorAndPrice("yellow", 95000);
		//List<Item> list= itemRepo.findByColorOrOwner("white", "홍길동");
		//- findByNameLike()
		//- findByNameIsLike()
		//- findByNameNotLike()  // 이거 빼고 찾아줘 ~ 
		//- findByNameIsNotLike()
				
		//- %, _를 직접 명시 
		//List<Item> list= itemRepo.findByNameLike("키보드");
		//List<Item> list= itemRepo.findByNameLike("%키보드");
		// - findByName[Is]StartingWith
		// - findByName[Is]EndingWith
		// - findByName[Is]Containing
		// List<Item> list= itemRepo.findByNameStartingWith("마우스");
		List<Item> list= itemRepo.findByNameEndingWith("마우스");
		model.addAttribute("list", list);
		return "item/result";
}
  • m12
    • findBy○○○[Is]Null, findBy○○○[Is]NotNull
      • 컬럼값이 null인 레코드 검색
    • findBy○○○[Is]Empty, findBy○○○[Is]NotEmpty
      • 컬럼값이 null이거나 빈문자열인 레코드 검색
    • 정렬 
      • ex) List<Item> list = itemRepo.findAll(Sort.by(Sort.Direction.DESC, "price")); 
      • findAll에 Sort를 받도록 오버로딩 되어 있다.  
      • List<T> findAll(Sort sort);
      • 좀 더 가독성 있는 메서드명 버전
        • List<Item> list = itemRepo.findAllByOrderByColor();
        • List<Item> list = itemRepo.findAllByOrderByColorDesc();
          • 정렬조건 추가
        • List<Item> list = itemRepo.findByOwnerOrderByColorDesc("홍길동");
          • 조건 추가
    • 비교
      • findBy○○○[IS]GreaterThan, [IS]LessThan, [Is]Between, [Is]GreaterThanEqual, [Is]LessThanEqual
        • List<Item> list = itemRepo.findByPriceGreaterThan(100000);
        • List<Item> list = itemRepo.findByPriceGreaterThan(100000, Sort.by("price"));
        • List<Item> list = itemRepo.findByPriceLessThan(100000, Sort.by("price"));
        • List<Item> list = itemRepo.findByPriceBetween(90000, 120000);
        • List<Item> list = itemRepo.findByOrderdateBetween("2023-06-25", "2023-06-27");
    • In, NotIn
      • Where color in ('yellow', 'blue')
      • List 형식으로 인자로 넣는다.
      • findBy○○○In
      • findBy○○○NotIn
List<String> colors = new ArrayList<String>();
colors.add("yellow");
colors.add("blue");		
List<Item> list = itemRepo.findByColorIn(colors);

List<Item> list = itemRepo.findByOwnerNotIn(new String[]{"홍길동", "아무개"});
@GetMapping("/item/m12")
	public String m12(Model model) {
		// [Is]Null, [Is]NotNull
		// - 컬럼값이 null인 레코드 검색 
		
		// [Is]Empty, [Is]NotEmpty
		// - 컬럼값이 null이거나 빈문자열인 레코드 검색
		
		//List<Item> list = itemRepo.findByOwnerNull();
		//List<Item> list = itemRepo.findByOwnerEmpty();
		//List<Item> list = itemRepo.findByOwnerNotNull();
		
		// 정렬 
		// List<Item> list = itemRepo.findAll(Sort.by(Sort.Direction.DESC, "price")); // > findAll
		
		// List<Item> list = itemRepo.findAll(Sort.by("name")); // 오름차순
		
		// List<Item> list = itemRepo.findAllByOrderByColor();
		
		// List<Item> list = itemRepo.findAllByOrderByColorDesc();
		
		// List<Item> list = itemRepo.findByOwnerOrderByColorDesc("홍길동");
		
		//	where item0_.owner=? order by item0_.color desc
		
		
		// [IS]GreaterThan, [IS]LessThan, [Is]Between
		// [Is]GreaterThanEqual, [Is]LessThanEqual
		
		// List<Item> list = itemRepo.findByPriceGreaterThan(100000);
		
		// List<Item> list = itemRepo.findByPriceGreaterThan(100000, Sort.by("price"));
		
		// List<Item> list = itemRepo.findByPriceLessThan(100000, Sort.by("price"));
		
		// List<Item> list = itemRepo.findByPriceBetween(90000, 120000);
		
		// List<Item> list = itemRepo.findByOrderdateBetween("2023-06-25", "2023-06-27");
		
		//IgnoreCase
		//- 특정 컬럼의 대소문자를 구분하지 않고 검색
		// List<Item> list = itemRepo.findByColor("white");
		
		// List<Item> list = itemRepo.findByColorIgnoreCase("White");
		
		
		// In, NotIn
		// - Where color in ('yellow', 'blue')
		
//		List<String> colors = new ArrayList<String>();
//		colors.add("yellow");
//		colors.add("blue");
//		
//		List<Item> list = itemRepo.findByColorIn(colors);
		
		//List<Item> list = itemRepo.findByOwnerIn(new String[]{"홍길동", "아무개"});
		
		List<Item> list = itemRepo.findByOwnerNotIn(new String[]{"홍길동", "아무개"});
		 
		model.addAttribute("list", list);
		
		return "item/result";
	}
  • m13, m14
    • JPA 도메인 클래스 컨버터(Domain Class Converter)
    • PK를 URL Path로 넣거나 쿼리스트링으로 리퀘스트 했을 뿐인데 그걸 베이스로 쿼리를 만들어준다.
    • 컨트롤러 메서드 파라미터 객체에 결과 값이 바로 채워진다. 
    • 코드량이 상당히 줄어든다. 값 조회 용도로만 쓰자
    • 두가지 방식이 있다.
      • RequestParam 방식
      • PathVariable 방식
@GetMapping("/item/m13")
public String m13(Model model, @RequestParam("name") Item result) {

    // PK를 리퀘스트 했을 뿐인데 그걸 베이스로 쿼리를 만들어서 아이템에 넣어주는 작업도 해주는 것이다. 
    // 도메인 클래스 컨버터(Domain Class Converter)
    // - PK를 넘겨서, 바로 Entity를 조회할 수 있다. 

    System.out.println(result);
    model.addAttribute("result", result);
    // item/m13?name=키보드
    //"마우스" > PK  
    // Optional<Item> result = itemRepo.findById(name);
    // model.addAttribute("result", result.get()); // Optional은 꺼내야 한다.

    return "item/result";
}
// 이 떄는 @PathVariable 이나 @RequestParam를 생략하면 안된다. 
@GetMapping("/item/m14/{name}")
public String m14(Model model, @PathVariable("name") Item result) {

    //item/m13?name=마우스
    //item/m13/마우스
    model.addAttribute("result", result);

    return "item/result";
}
  • m15
    • First, Top 등의 최상위 한개, 상위 몇개, 범위 지정등의 쿼리를 정의할 수 있다. 
    • findFirstBy, findTopBy는 레코드 하나를 반환한다.
      • findFirstBy, findTopBy는 결과는 동일
    • findTop숫자By 등의 범위 지정 쿼리도 가능하다.
@GetMapping("/item/m15")
public String m15(Model model) {

    // First
    // Top
    // 둘다 첫번쨰거 가져오는데 First는 무조건하나를 가져오는데 ★ Top은 원하는 범위를 선택한다.
    // Item result = itemRepo.findFirstByOrderByPriceAsc();
    // Item result = itemRepo.findTopByOrderByPriceAsc();
    List<Item> list = itemRepo.findTop3ByOrderByPriceDesc();
    model.addAttribute("list", list);
    return "item/result";
}
  • m16
    • PageRequest : 페이징 기능을 담당한다. 
    • 레코드가 여러개 있을 때 페이징을 하는데 PageRequest.of 메서드로 기능을 처리한다. page로 몇 페이지, size로 몇개씩 가져올 것인가를 지정하고 Sort.by를 넣어서 정렬기준을 정할 수 있다. 
    • 반환값은 PageRequest라는 객체를 반환하며 findPageListBy라는 메서드로 엔터티 형식 객체로 바꿔준다. 
    • List<Item> findPageListBy(PageRequest pageRequest); 이건 ItemRepository에 이렇게 정의가 필요하다. 

밖에서 사용법
of 정의

요청URL : http://localhost:8092/item/m16?page=1
@GetMapping("/item/m16")
	public String m16(Model model, int page) {
		
		// PageRequest 이게 페이징을 담당한다. 
		PageRequest pageRequest = PageRequest.of(page, 5, Sort.by("name"));
		// Page는 0부터다 !!! 
		List<Item> list = itemRepo.findPageListBy(pageRequest);
		model.addAttribute("list", list);
		
		return "item/result";
}
  • m17, m18
    • @Query라는 애너테이션을 사용한다. @Query의 value에 들어가는 부분이 JPQL을 작성할 수 있는데 nativeQuery=true로 지정하면 SQL을 직접 정의할 수 있다. 이렇게 셋팅하면 JPQL과는 성격이 다름
    • @Query(value="select * from Item where color = :color", nativeQuery=true)
    • : 을 써서 파라미터 바인딩이 가능한데 :#을 쓰는 경우도 있다.
@GetMapping("/item/m17")
	public String m17(Model model) {
		
		//@Query 
		// - 사용자 쿼리 작성
		// - 쿼리 메소드 키워드로 작성 불가능 쿼리 > 직접 SQL 작성 
		// select * from Item 
		List<Item> list = itemRepo.findAllItem(); // 메서드 이름이 중요하지 않다. 쿼리를 내맘대로 짠다. 쿼리를 내가 만든다. 
		model.addAttribute("list", list);
		return "item/result";
}

@GetMapping("/item/m18")
	public String m18(Model model, String color) {
		
		List<Item> list = itemRepo.findAllItemByColor(color);
		model.addAttribute("list",list);
		return "item/result";
}
  • ItemRepository.java 부분
// 이렇게 붙여주면 된다. 쿼리가 길고 복잡하면 이방법으로 쓴다. 
@Query(value="select * from Item", nativeQuery=true)
List<Item> findAllItem();

// 이 쿼리를 JPQL 이라고 한다 > Java Persistence Language 
@Query(value="select * from Item where color = :color", nativeQuery=true)
List<Item> findAllItemByColor(String color);

'JPA' 카테고리의 다른 글

스프링 부트 + JPA(1)  (0) 2023.07.18

JPA는 Java Persistent API의 약자이고 자바 진영에서 ORM(object-relational mapping) 기술 표준으로 사용되는 인터페이스이다. JPA를 구현하는 기술로 대표적인 오픈소스는 Hibernate가 있다. 레거시 프로젝트에서는 많이 사용하지 않으나 해외에서나 아니면 서비스업 회사나 스타트업 회사에서는 많이 사용한다고 한다. JPA, Mybatis의 통계적인 사용 비율이 3:7이라고는 말이 전해져 내려오나 실제로 그런지는 잘 모르겠다. 

  • ORM(Object Relational Mapping)
    • Entity 객체외 Database 테이블을 서로 매핑해서, SQL 쿼리가 아닌 Java 메소드를 사용해서 데이터를 조작한다.
    • DB의 데이터 구조를 자바의 객체 구조로 취급한다. 

프로젝트 셋팅 

STS4)
New > New Spring Starter Project
- Name : boot-jpa
- Type : Maven
- Java Version : 11 
- Packaging : Jar
- Group : com.test
- Aritifact : boot-jpa
- Package : com.test.jpa

Dependency 추가
- Spring Web
- Lombok
- Mabatis Framework
- Oracle Driver
- Spring boot Devtools
- Thymeleaf
- Spring JPA

DB 셋팅(Oracle 11g)

create table Item (
    name varchar2(30) primary key,
    price number not null,
    color varchar2(30) not null,
    owner varchar2(30) null,
    orderdate date default sysdate not null
);

insert into Item (name, price, color, owner, orderdate) values ('키보드', 100000, 'black', '홍길동', sysdate - 1);
insert into Item (name, price, color, owner, orderdate) values ('무접점 고급 키보드', 120000, 'white', null, sysdate - 1.5);
insert into Item (name, price, color, owner, orderdate) values ('기계식 키보드', 150000, 'black', '홍길동', sysdate - 2);
insert into Item (name, price, color, owner, orderdate) values ('멤브레인 키보드', 80000, 'white', '홍길동', sysdate - 2.5);
insert into Item (name, price, color, owner, orderdate) values ('마우스', 50000, 'silver', '홍길동', sysdate - 3);
insert into Item (name, price, color, owner, orderdate) values ('버티컬 마우스', 90000, 'silver', '아무개', sysdate - 3.5);
insert into Item (name, price, color, owner, orderdate) values ('게이밍 마우스', 120000, 'black', '아무개', sysdate - 4);
insert into Item (name, price, color, owner, orderdate) values ('고급 볼 마우스', 95000, 'yellow', '아무개', sysdate - 3);
insert into Item (name, price, color, owner, orderdate) values ('노트북', 1100000, 'white', '아무개', sysdate - 4.5);
insert into Item (name, price, color, owner, orderdate) values ('노트북 가방', 120000, 'blue', null, sysdate - 5);
insert into Item (name, price, color, owner, orderdate) values ('노트북 받침대', 95000, 'yellow', '하하하', sysdate - 5.5);
insert into Item (name, price, color, owner, orderdate) values ('노트북 파우치', 95000, 'yellow', '하하하', sysdate - 5);


select * from Item;

commit;

application.properties

# Server port
server.port=8092

# Oracle settings
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=hr
spring.datasource.password=java1234

# 히카리cp가 있어서 이건 필요가 없다. >>> 이게 commons dbcp이다. 
# 아래것이 히카리CP이다.  

# Hikari CP settings
spring.datasource.hikari.connection-timeout=60000
spring.datasource.hikari.maximum-pool-size=5
spring.datasource.hikari.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.hikari.jdbc-url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.hikari.username=hr
spring.datasource.hikari.password=java1234


# logging > 이게 log4j관련이다. 
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n
logging.level.org.hibernate.SQL=debug

logging.level.org.hibernate.type.descriptor.sql=trace
logging.level.=error


#Thymeleaf
spring.thymeleaf.cache=false

# JPA
spring.jpa.database=oracle

spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
# jpa가 자동으로 만들어주는 쿼리를 보여주라 ~ !

# DB마다 자기들만의 구문이 있다. 이걸 넣어줘야 한다. 
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect

프로젝트 파일 구조 

src/main/java
	com.test.controller
    	> ItemController
	com.test.domain
    	> Item.java
	com.test.jpa
    	> BootJpaApplication.java
	com.test.repository
    	> ItemRepository.java
        
src/main/resources
	templates
    	> item
        	> result.html
  • BootJpaApplication.java
    • controller 부분은 ComponentScan
    • Entity는 EntityScan
    • EnableJpaRepositories에 Repository 정의
@SpringBootApplication
@ComponentScan(basePackages = "com.test.controller")
@EntityScan(basePackages ="com.test.domain")
@EnableJpaRepositories(basePackages="com.test.repository")
public class BootJpaApplication {
	public static void main(String[] args) {
		SpringApplication.run(BootJpaApplication.class, args);
	}
}
  • com.test.repository > ItemRepository.java
  • JpaRepository<Item, String> 
    • 첫번째는 제네릭에 사용하려는 객체, 두번째는 PK 타입의 자료형을 쓴다. 
  • @Repository라는 어노테이션이 없어도 IoC된다. 이유는 JpaRepository를 상속했기 때문에 가능하다. 
public interface ItemRepository extends JpaRepository<Item, String>{
	
	
}
  • domain 패키지에는 엔터티를 정의한다. 
    • com.test.domain > Item.java
  • domain에 정의된 엔터티를 가져다가 사용하는 DAO역할을 하는 것이 필요하다. 이것을 repository 패키지에서 클래스를 정의한다.
    • com.test.repository > ItemRepository.java(I)
    • ItemRepository는 JpaRepository<Item, String>를 상속받아서 정의 한다. 
    • JpaRepository의 내부에 정의된 메서드들은 findAll, findAllById, saveAndFlush, deleteAllInBatch 등등 이상한 것들이 있다. findAll만 주로 많이 쓰는 듯하다. 
    • ItemRepository에 뭔가의 약속된 형식의 메서드명을 지어서 사용한다. Overriding은 아니지만 JPA에서 이 뭔가의 규칙에 맞게 처리를 해준다.
      • findBy○○○
      • findBy○○○Null
      • findAllByOrderBy○○○
      • findAllByOrderBy○○○Desc
      • findBy○○○In
  • 아래 예제 진행하다가 JpaRepository 자체가 상속 및 인터페이스 구조라는 것을 확인하고 검색한 이미지

아니 .. 이런구조가..

  • CrudRepository 
    • existsById, findById, save, saveAll, findAll, count, deleteById, delete, deleteAll 등의 메서드를 가지고 있다.
    • 이것을 상속받아서 내가 구현하는 Repository에서 별다른 메서드 정의없이 사용할 수 있다.
    • 단, 이런데에 해당하지 않는 Column에 따른 재정의가 필요한 메서드 정의시에는 정의를 해야 하는 것이다.
  • item/result.html
<!DOCTYPE html>
<html xmlns:th="https://thymeleaf.org">
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>Spring Boot<small>JPA</small></h1>
	
	<div th:text="${bool}"></div>
	
	<div th:text="${count}"></div>
	
	<div class="message" title="결과">
		<!-- 다른애 도움없이 얘 혼자 쓸 수 있다. -->
		<th:block th:if="${result != null}">
		
			<div th:text="${result != null}"></div>
			<div th:text="${result.name}"></div>
			<div th:text="${#numbers.formatInteger(result.price, 0, 'COMMA')}"></div>
			<div th:text="${result.color}"></div>
			<div th:text="${result.owner}"></div>
			<div th:text="${result.orderdate}"></div>
			
		</th:block>
	</div>
	
	<hr>
	
	<table class="vertical" th:if="${list != null}">
		<tr>
			<th>이름</th>
			<th>가격</th>
			<th>색상</th>
			<th>소유자</th>
			<th>주문날짜</th>
		</tr>
		
		
		<tr th:each="item : ${list}">
			<td th:text="${item.name}"></td>
			<td th:text="${#numbers.formatInteger(item.price, 0, 'COMMA')}"></td>
			<td th:text="${item.color}"></td>
			<td th:text="${item.owner}"></td>
			<td th:text="${item.orderdate}"></td>
		</tr>
		
	</table>
	
</body>
</html>
  • com.test.domain > Item.java
    • lombok Builder 패턴 사용
    • @Entity  > 테이블 정의 >>> 객체화 
    • @NoArgsConstructor, @AllArgsConstructor 생성자 정의
    • @Id 
      • Primary Key 역할을 하는 칼럼에 붙인다.
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Item {
	// 엔티티가 참조하는 테이블의 컬럼 > 멤버 정의
	// PK는 꼭 붙여야 한다. 
	@Id
	private String name;
	
	private int price;
	private String color;
	private String owner;
	
	@Column(insertable=false, updatable=false) // 얘를 업데이트하는 대상에서 뺴버린다.
	private String orderdate; // 이 컬럼은 insert를 안해도 된다. default가 있다. 
}
  • ItemController.java 클래스 내부 
    • @EnableJpaRepositories를 BootJpaApplication에 정의됨에 따라 스캔이 가능해짐
    • 이걸 사용하는 쪽(Controller나 Service로직)에서 @Autowired 하여 쓴다.
  • JPA는 쿼리들을 메서드화 시켰다.
    • insert(DB) > (JPA) > save(Method)
    • <S extends T> S save(S entity)
  • m1 > 객체 setter로 정의 > 레코드 Insert : [C]RUD
@Autowired
private ItemRepository itemRepo;

@GetMapping("/item/m1")
public String m1(Model model) {
    System.out.println("m1");
    //[C]RUD
    // - 레코드 추가하기 
    Item item = new Item();
    item.setName("잉크젯 프린터");
    item.setPrice(250000);
    item.setColor("yellow");
    item.setOwner("홍길동");
    // save > (JPA) > insert
    Item result = itemRepo.save(item);
    // Hibernate: insert into item (color, orderdate, owner, price, name) values (?, ?, ?, ?, ?)
    // orderdate date default sysdate not null > db엔 이렇게 되어 있다. 
    model.addAttribute("result", result);

    return "item/result";
}

결과

  • m2 > 객체 Builder 패턴으로 정의 > 레코드 Insert  : [C]RUD
    • Builder 패턴 사용으로 인한 객체 값 셋팅은 기존 생성자 방식보다 가독성이 좋다. 
	@GetMapping("/item/m2")
	public String m2(Model model) {
		System.out.println("m2");
		//[C]RUD
		// - 레코드 추가하기 
		//빌더 패턴(Builder Pattern)
		// 생성자 패턴, 빌더 패턴 
		// - OOP에서 겍체를 생성하는 패턴 중 하나
		Item item2 = new Item(null, 0, null, null, null);		
		// 얘는 멤버를 자유롷게 넣을 수 있어서 필요없으면 뺴고 안넣어도 된다. 가독성있다.
		Item item = Item.builder()
				.name("레이저 프린터")
				.price(300000)
				.color("black")
				.owner("아무개")
				.build();
		// save > (JPA) > insert
		Item result = itemRepo.save(item);
		// Hibernate: insert into item (color, orderdate, owner, price, name) values (?, ?, ?, ?, ?)
		// orderdate date default sysdate not null > db엔 이렇게 되어 있다. 
		model.addAttribute("result", result);
		return "item/result";
	}
  • m3 > 단일 레코드 읽기 > findById("뭘로?") : C[R]UD
    • Optional<T> findById(ID id); 리턴형이 Optional이라는 것 주의! 
      • .get()으로 값을 꺼낸다는 것
    • itemRepo.getById("마우스") : 이것도 되지만 Deprecated 된 메서드이다. > 비권장
      • 이건 T getById(ID id);
	@GetMapping("/item/m3")
	public String m3(Model model) {
		//C[R]UD
		// - 단일 레코드 읽기
		//	select item0_.name as name1_0_0_, item0_.color as color2_0_0_, item0_.orderdate as orderdate3_0_0_, item0_.owner as owner4_0_0_, item0_.price as price5_0_0_ from item item0_ where item0_.name=?
		//	Hibernate: select item0_.name as name1_0_0_, item0_.color as color2_0_0_, item0_.orderdate as orderdate3_0
		// 내부적으로 쿼리로 동작한다. 메소드가 더이상 안쓰인다. 
		//Item item = itemRepo.getById("마우스"); // 얘는 더이상 안쓰임
		//model.addAttribute("result", item);
		Optional<Item> item = itemRepo.findById("프린터"); // ★ 얘는 가장 많이 쓰인다. 레코드 찾을 때
		System.out.println(item.isPresent());
		model.addAttribute("result", item.get());
		return "item/result";
	}

결과

  • m4 > isPresent() : C[R]UD 검색한 결과가 존재하는지 확인하는데 사용할 수 있다. 
  • Optional의 isPresent()
@GetMapping("/item/m4")
	public String m4(Model model) {

		Optional<Item> item = itemRepo.findById("프린터");
		
		if(item.isPresent()) {
			item.get().setPrice(240000);
			Item result = itemRepo.save(item.get());
			model.addAttribute("result", result);
		}
		
		return "item/result";
}

 

결과

  • m5 > delete(엔터티) > :  CRU[D] 해당 레코드를 찾고 그것을 인자로 넣어서 삭제를 한다. 
  • Optional로 찾으니까 get()으로 꺼내서 사용
@GetMapping("/item/m5")
public String m5(Model model) {
    //CRU[D]
    Optional<Item> item = itemRepo.findById("노트북");
    itemRepo.delete(item.get());

    return "item/result";
}
  • m6 > 다중 레코드 조회 > findAll() > C[R]UD 
  • findAll()은 JpaRepository에서 정의된 메서드로 이건 ItemRepository에 따로 정의할 필요가 없다.
@GetMapping("/item/m6")
	public String m6(Model model) {
		//Query Methods
		//Repository에 구현된 메서드가 쿼리 메서드다.
		// 다중 레코드 조회
		List<Item> list = itemRepo.findAll();
		model.addAttribute("list", list);
		return "item/result";
}
  • m7 > 존재유무 확인 > existById(뭐로?) : C[R]UD 
@GetMapping("/item/m7")
public String m7(Model model) {
    // 존재 유무 확인
    boolean bool = itemRepo.existsById("마우스");
    model.addAttribute("bool", bool);
    return "item/result";
}

결과

  • m8 > count() > 이거나 existById() 이런것들은 CrudRepository 에 정의된 것을 사용한 것이다.
@GetMapping("/item/m8")
	public String m8(Model model) {
		// 카운트
		long count = itemRepo.count();
		model.addAttribute("count", count);
		return "item/result";
}

결과

이제부터는 사용자 정의 findBy○○○ 메서드 

여기서부터는 다음 글에 이어서 진행

 

소스코드는 https://github.com/ngotic/SpringBootBasic/tree/main/boot-jpa

'JPA' 카테고리의 다른 글

스프링 부트 + JPA(2)  (0) 2023.07.19

+ Recent posts