일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- MessageQueue
- ORM
- stream api
- 자바8
- 그리드
- spring
- JQuery
- jqGrid
- Javascript
- JPA
- 대용량 업로드
- 엑셀 업로드
- 자동배포
- 보안
- QueryDSL
- 스트림
- mssql
- apache.poi
- sqlserver
- Stream
- rabbitmq
- 자동빌드
- 제이쿼리그리드
- DevOps
- docker
- mom
- poi
- Jenkins
- ci/cd
- java
- Today
- Total
개발 메모장
[JPA] JPA의 정의와 사용 예제 본문
#. JPA 란 무엇인가?
- ORM(Object-Relational Mapping) 기술 표준으로 사용되는 인터페이스의 모음으로 실제 구현된 것이 아니라 구현된 클래스와 매핑을 해주기 위해 사용되는 프레임워크입니다.
- JPA를 구현한 대표적인 오픈소스로는 Hibernate가 있습니다.
- 최초엔 javax로 제공되었지만 Eclipse Foundation으로 이전되면서 jakarta로 변경되었기에 추후 QueryDSL 등 jpa 관련된 Dependency를 추가할 때 확인해야 합니다.
#. JPA의 장점
- 반복적인 CRUD SQL을 처리해 줍니다.
- 개발자가 SQL에 대한 생각을 최소화하여 다른 개발에 집중할 수 있게 합니다.
- JPA로 구현하기 어려운 SQL의 경우 직접 작성할 수 있도록 네이티브 SQL을 지원합니다.
(@Query를 이용해 쿼리를 작성할 수 있습니다.)
@Query("SELECT u FROM User u WHERE u.age > :age")
List<User> findByAgeGreaterThan(int age);
#. JPA의 단점
- 디버깅이 어렵고 JPA를 이용한 개발이 복잡하게 느낄 수 있습니다.
- DBMS별 구현의 차이가 있을 수 있기에 이식성 문제가 발생할 수 있습니다.
- 객체지향 모델과 RDB 간 추상화 계층으로 인한 성능 오버헤드가 발생할 수 있기에 서비스의 처리량 및 성능에 따라 큰 차이를 발생시킬 수 있습니다.
#. Application.yml 설정
- JPA 사용을 원활히 하기 위해 가상 DB인 H2, 쿼리 내용을 console에 출력해 주는 설정을 해주도록 하겠습니다.
spring:
# H2 세팅하기
h2:
console:
enabled: true
path: /h2-console
# DB 세팅하기
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:~/test
username: sa
password:
# JPA 세팅
jpa:
hibernate:
ddl-auto: update # option type: create, create-drop, update, validate, none
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect # DB 엔진 설정
show_sql: true # sql 쿼리를 보여줌
format_sql: true
use_sql_comments: true # 쿼리의 추가정보
# JPA 사용 시 파라미터 확인을 위한 세팅
logging:
level:
org:
hibernate:
orm:
jdbc:
bind: trace
type: trace
SQL : debug
#. pom.xml 설정
- 프로젝트 생성 시 추가한 의존성으로 중요한 것은 JPA와 DB로 사용할 H2라고 보면 되겠습니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
#. Entity 생성
- @NoArgsConstructor을 이용해 기본 생성자를 생성할 수 있으며, 아래와 같이 직접 생성도 가능합니다.
- @JsonIgnoreProperties는 Jackson 라이브러리에서 제공하며 JSON 직렬화 및 역직렬화 시 무시할 속성 지정을 할 때 사용합니다. 인자로 있는 것은 Hibernate나 JPA를 사용하는 경우 엔티티 객체를 JSON으로 변환할 때 불필요한 루프를 방지하기 위해 사용합니다.
- hibernateLazyInitializer는 Hibernate의 프록시 객체가 초기화되는 데 사용되는 속성이며, handler는 자바 Proxy Handler 객체입니다.
- 롬복의 @builder를 사용해 입력받은 데이터를 처리할 수 있으며, 어노테이션이 안될 경우 아래와 같이 builder를 직접 만들어 사용할 수 있습니다.
- 롬복의 @getter, @setter로 처리도 가능하나 getter, setter도 만들었습니다.
- getter, setter는 Alt + Shift + s를 눌러 중단부 generate getters and setters로 생성합니다.
@NoArgsConstructor
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@Entity
@Table(name = "MEMBER")
public class MemberEntity {
// 기본 생성자
MemberEntity() {
}
// 빌더
public static MemberEntity builder() {
return new MemberEntity();
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Column(name = "NAME")
private String name;
@Column(name = "email")
private String email;
@Column(name = "NICKNAME")
private String nickname;
@Column(name = "AGE")
private int age;
@Column(name = "BIRTHDAY")
private Date birthday;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void MemberEntityBuilder() {
// 기본 생성자
}
public MemberEntity id(Long id) {
this.id = id;
return this;
}
public MemberEntity name(String name) {
this.name = name;
return this;
}
public MemberEntity email(String email) {
this.email = email;
return this;
}
public MemberEntity nickname(String nickname) {
this.nickname = nickname;
return this;
}
public MemberEntity age(int age) {
this.age = age;
return this;
}
public MemberEntity birthday(Date birthday) {
this.birthday = birthday;
return this;
}
public MemberEntity build() {
MemberEntity member = new MemberEntity();
member.setId(this.id);
member.setName(this.name);
member.setEmail(this.email);
member.setNickname(this.nickname);
member.setAge(this.age);
member.setBirthday(this.birthday);
return member;
}
@Override
public String toString() {
return "MemberEntity [id=" + id + ", name=" + name + ", email=" + email + ", nickname=" + nickname + ", age="
+ age + ", birthday=" + birthday + "]";
}
}
#. Repository 생성
- @Repository를 추가해 줍니다.
- extends로 JpaRepository를 추가해 jpa를 사용할 수 있게 합니다.
@Repository
public interface MemberRepository extends JpaRepository<MemberEntity, Long> {
}
#. Service 생성
- @RequiredArgsConstructor를 추가하여 final 필드를 가진 생성자를 생성하고 필드를 초기화하는 생성자를 컴파일 시점에 생성해 줍니다.
- 총 5가지의 기능(데이터 생성, 데이터 수정, 전체 조회, id로 조회, 삭제)을 구현합니다.
@Service
@RequiredArgsConstructor
public class MemberService {
@Autowired
private final MemberRepository memberRepository = null;
// member 생성
public MemberEntity createMember(MemberEntity memberEntity) {
MemberEntity saveMember = memberRepository.save(memberEntity);
return null;
}
// member 수정
public MemberEntity updateMember(MemberEntity memberEntity) {
MemberEntity updatedMember = null;
try {
// member id로 유무 확인
MemberEntity existMember = getMember(memberEntity.getId());
if (!ObjectUtils.isEmpty(existMember)) {
updatedMember = memberRepository.save(memberEntity);
}
} catch (Exception e) {
System.err.println(e.toString());
} finally {
return updatedMember;
}
}
// member 전체 조회
public List<MemberEntity> getMembers() {
return memberRepository.findAll();
}
// member id로 조회
public MemberEntity getMember(Long id) {
return memberRepository.getReferenceById(id);
}
// member id로 삭제
public void deleteMember(Long id) {
memberRepository.deleteById(id);
}
}
#. Controller 생성
- MemberEntity에서 만들어둔 builder를 이용해 입력받은 파라미터를 처리합니다.
@RestController
@RequiredArgsConstructor
public class MemberController {
@Autowired
private final MemberService memberService = new MemberService();
@PostMapping("/create")
public ResponseEntity<MemberEntity> create(String name, String email, String nickname, int age, String birthday) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = format.parse(birthday);
MemberEntity member = MemberEntity.builder()
.name(name)
.email(email)
.nickname(nickname)
.age(age)
.birthday(date)
.build();
MemberEntity savedMember = memberService.createMember(member);
return new ResponseEntity<>(savedMember, HttpStatus.OK);
}
@PutMapping("/update")
public ResponseEntity<MemberEntity> update() throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = format.parse("1999-08-02");
MemberEntity member = MemberEntity.builder()
.id(3l)
.name("김홀란")
.email("holransloveda@good.com")
.nickname("king scorer")
.age(23)
.birthday(date)
.build();
MemberEntity updatedMember = memberService.updateMember(member);
if (!ObjectUtils.isEmpty(updatedMember)) {
return new ResponseEntity<>(updatedMember, HttpStatus.OK);
} else {
return new ResponseEntity<>(member, HttpStatus.NOT_FOUND);
}
}
@GetMapping("/list")
public ResponseEntity<List<MemberEntity>> getAll() {
List<MemberEntity> members = memberService.getMembers();
return new ResponseEntity<>(members, HttpStatus.OK);
}
@GetMapping("/{id}")
public ResponseEntity<MemberEntity> get(@PathVariable("id") Long id) {
MemberEntity member = memberService.getMember(id);
return new ResponseEntity<>(member, HttpStatus.OK);
}
@DeleteMapping("/{id}")
public ResponseEntity<Long> delete(@PathVariable("id") Long id) {
memberService.deleteMember(id);
return new ResponseEntity<>(id, HttpStatus.OK);
}
#. 데이터 생성하기
- create 메서드의 경우 post로 받기 때문에 postman을 이용해 처리해 보도록 하겠습니다.

- 실질적으로 jpa가 처리한 sql와 받은 파라미터에 대해 console을 확인해 볼 수 있습니다.

- H2 Console에 접속하여 확인해 보면 아래와 같이 조회가 가능합니다.
- H2 Console은 localhost:포트에 application.yml > h2 > console > path에 지정한 /h2-console 붙여 사용하면 됩니다.

#. 위와 같은 방법으로 URI만 변경하여 처리해 보면 예상하는 결과를 가져올 수 있습니다.
#. JPA에 대한 기본을 간략하게 알아보았습니다.
#. 처음 또는 오랜만에 접하시는 분들께 도움이 되었으면 좋겠습니다.
===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!
===========================================================
'ORM' 카테고리의 다른 글
[QueryDSL] QueryDSL Projection (0) | 2024.02.01 |
---|---|
[QueryDSL] QueryDSL 사용방법(2) (1) | 2024.01.31 |
[QueryDSL] QueryDSL 사용방법(1) (1) | 2024.01.30 |
[QueryDSL] QueryDSL 정의 및 설정방법 (0) | 2024.01.08 |
[myBatis] myBatis 사용방법 (1) | 2023.12.26 |