Spring JPA
by jsh
Spring JPA
- Spring JPA
ORM
- Object-Relational Mapping(객체와 관계형 데이터베이스 Mapping, 객체와 DB의 Table이 Mapping을 이루는 것)
- 객체가 Table이 되도록 Mapping 시켜주는 Framework 이다.
- 프로그램의 복잡도를 줄이고 JAVA Object와 Query를 분리할 수 있으며 Transaction 처리나기타 Database 관련 작업들을 좀 더 편리하게 처리 할 수 있는 방법
- SQL Query가 아닌 직관적인 Code(Method)로서 Data를 조작 할 수 있다.
- before : SELECT * FROM USER;
위 Query를 ORM을 이용하면 다음과 같다.
- after : user.findAll()
- before : SELECT * FROM USER;
JPA
- Java Persistence API(JAVA ORM 기술에 대한 API 표준 명세)
- 한마디로 ORM을 사용하기 위한 Interface를 모아둔 것이라고 볼 수 있다.
- Java Application에서 관계형 DataBase를 사용하는 방식을 정의한 Interface이다.
- ORM에 대한 Java API 규격이며 Hibernate, OpenJPA등이 JPA를 구현 한 구현체이다.
- Hibernate 이외에도 EclipseLink, DataNucleus, OpenJPA, TopLink 등이 있다.
결국 Interface이기 때문에 JPA를 사용하기 위해서는 JPA를 구현한 Hibernate, EclipseLink, DataNucleus, OpenJPA, TopLink같은 ORM Framework를 사용해야 한다.
Hibernate
- JPA를 사용하기 위한 ORM Framework 중 하나.
- Hibernate는 JPA명세의 구현체이다.
javax.persistence.EntityManager와 같은 JPA의 Interface를 직접 구현한 library 이다.
장점
- 생산성이 뛰어나고 유지보수가 용이하다.
- 객체 지향적인 코드로 인해 직관적이고 비즈니스 로직에 좀 더 집중할 수 있게 도와준다.
- 객체 지향적으로 데이터를 관리할 수 있기 때문에 전체 프로그램 구조를 일관되게 유지 할 수 있다.
- SQL을 직접적으로 작성하지 않고 객체를 사용하여 동작하기 때문에 유지보수 비용이 적다.
- DB 컬럼이 추가 될 때마다 Table 수정이나 SQL 수정하는 과정이 줄어들고 값을 할당하거나 변수 선언등의 부수적인 코드 또한 급격히 줄어든다.
- 각각의 객체에 대한 코드를 별도로 작성하여 코드의 가독성이 증가한다.
- DBMS에 대한 종속성이 줄어든다.
- DBMS가 변경된다 하더라도 Source, Query, 구현 방법, 자료형 Type등을 변경 할 필요가 없다.
- 개발자는 Object에만 집중하면 되고 DBMS를 교체하는 작업에도 비교적 적은 리스크와 시간이 소요 된다.
단점
- JPA의 장점을 살려 잘 사용하려면 학습 비용이 높은 편이다.
- 복잡한 Query를 사용해야 할 때에 불리하다.
- 업무 비즈니스가 매우 복잡한 경우 JPA로 처리하기 어렵고 통계처리와 같은 복잡한 쿼리 자체를 ORM으로 표현하는데 한계가 있다.
- 결국 기존 DataBase 중심으로 되어 있는 환경에서는 JPA를 사용하기도 어렵고 힘을 발휘하기 어렵다.
- 잘못 사용 할 경우 실제 SQL문을 직접 작성하는 것보다는 성능이 비교적 떨어 질 수 있다.
- 대용량 데이터 기반의 환경에서도 튜닝이 어려워 상대적으로 기존 방식보다 성능이 떨어질 수 있다.
Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Gradle
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Entity생성
package com.jsh.jpaExample.entity;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.time.LocalDateTime;
@NoArgsConstructor
@Setter
@Getter
@Table(name = "book")
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String category;
@Column(nullable = false)
private long sellCount;
@CreationTimestamp
@Column(nullable = false, length = 20, updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(length = 20)
private LocalDateTime updatedAt;
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", category='" + category + '\'' +
", sellCount=" + sellCount +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}
Repository생성
package com.jsh.jpaExample.repository;
import com.jsh.jpaExample.entity.Book;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
public Book findBookByName(String name);
public Book findBookByCategory(String category);
}
Service생성
package com.jsh.jpaExample.service;
import com.jsh.jpaExample.entity.Book;
import java.util.List;
public interface BookService {
public Book findBookByName(String name);
public Book findBookByCategory(String category);
public Book save(Book book);
public List<Book> findBookAll();
public void delete(Long id);
}
package com.jsh.jpaExample.service;
import com.jsh.jpaExample.entity.Book;
import com.jsh.jpaExample.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("bookService")
public class BookServiceImpl implements BookService{
@Autowired
private BookRepository repository;
@Override
public List<Book> findBookAll() {
return (List<Book>) repository.findAll();
}
@Override
public void delete(Long id) {
repository.deleteById(id);
}
@Override
public Book findBookByName(String name) {
return repository.findBookByName(name);
}
@Override
public Book findBookByCategory(String category) {
return repository.findBookByCategory(category);
}
@Override
public Book save(Book book) {
return repository.save(book);
}
}
RestController생성
package com.jsh.jpaExample.controller;
import com.jsh.jpaExample.entity.Book;
import com.jsh.jpaExample.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@Slf4j
public class BookController {
@Autowired
private BookService service;
@GetMapping("/book")
public List<Book> findAllBook(){
List<Book> books = service.findBookAll();
books.forEach(System.out::println);
return books;
}
@PostMapping("/book")
public Book save(@RequestBody Book book){
Book resBook = service.save(book);
log.info(resBook.toString());
return resBook;
}
@PutMapping("/book/{bookId}")
public Book updateBook(@PathVariable("bookId") Long bookId, @RequestBody Book book){
book.setId(bookId);
Book resBook = service.save(book);
log.info(resBook.toString());
return resBook;
}
@DeleteMapping("/book/{bookId}")
public void deleteBook(@PathVariable("bookId") Long bookId){
service.delete(bookId);
}
}
POST
curl POST http://localhost:8081/book -h ‘{Content-Type: application/json}’ -d ‘{“name”: “Korean Book”, “category”: “1”}’
# Hibernate Query
Hibernate:
/* insert com.jsh.jpaExample.entity.Book
*/ insert
into
book
(category, created_at, name, sell_count, updated_at)
values
(?, ?, ?, ?, ?)
# Insert 한 데이터
11:37 INFO c.j.j.controller.BookController - Book{id=3, name='Korean Book', category='1', sellCount=0, createdAt=2021-04-19T11:37:50.820, updatedAt=2021-04-19T11:37:50.820}
GET
curl GET http://localhost:8081/book
# Hibernate Query
Hibernate:
/* select
generatedAlias0
from
Book as generatedAlias0 */ select
book0_.id as id1_0_,
book0_.category as category2_0_,
book0_.created_at as created_3_0_,
book0_.name as name4_0_,
book0_.sell_count as sell_cou5_0_,
book0_.updated_at as updated_6_0_
from
book book0_
# SELECT 된 데이터
Book{id=3, name='Korean Book', category='1', sellCount=0, createdAt=2021-04-19T11:37:50.820, updatedAt=2021-04-19T11:37:50.820}
PUT
curl PUT http://localhost:8081/book -h ‘{Content-Type: application/json}’ -d ‘{ “name”: “Korean Book2”, “category”: “1” }’
# Hibernate Query
Hibernate:
/* load com.jsh.jpaExample.entity.Book */ select
book0_.id as id1_0_0_,
book0_.category as category2_0_0_,
book0_.created_at as created_3_0_0_,
book0_.name as name4_0_0_,
book0_.sell_count as sell_cou5_0_0_,
book0_.updated_at as updated_6_0_0_
from
book book0_
where
book0_.id=?
Hibernate:
/* update
com.jsh.jpaExample.entity.Book */ update
book
set
category=?,
name=?,
sell_count=?,
updated_at=?
where
id=?
# Update 된 데이터
13:03 INFO c.j.j.controller.BookController - Book{id=3, name='Korean Book2', category='1', sellCount=0, createdAt=null, updatedAt=2021-04-19T13:03:11.224}
DELETE
curl DELETE http://localhost:8081/book -h ‘{Content-Type: application/json}’ -d ‘{ “name”: “Korean Book2”, “category”: “1” }’
# Hibernate Query
Hibernate:
select
book0_.id as id1_0_0_,
book0_.category as category2_0_0_,
book0_.created_at as created_3_0_0_,
book0_.name as name4_0_0_,
book0_.sell_count as sell_cou5_0_0_,
book0_.updated_at as updated_6_0_0_
from
book book0_
where
book0_.id=?
Hibernate:
/* delete com.jsh.jpaExample.entity.Book */ delete
from
book
where
id=?
Subscribe via RSS