Spring발행일 2024. 7. 13.원본 https://blog.naver.com/jword_/223510374114 ↗

자바 JPA(Hibernater)적용하기

자바 JPA(Hibernater)적용하기 — #자바 #JAVA #자바Hibernate #자바하이버네이트 #자바JPA적용 #JAVAJPA적용 #개발자의도구...

#Spring#Naver Blog

#자바 #JAVA #자바Hibernate #자바하이버네이트 #자바JPA적용 #JAVAJPA적용 #개발자의도구들

🚨📝 📖 📒

​

AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용을 작성하였습니다

\* 현재 Spring 프로젝트를 진행 중입니다. 전체적인 목차를 보시려면 여기​를 눌러주세요.

사실 Hibernate는 JPA의 원조이다

JPA스펙에 대해 공부한바 있는데요. 사실 이 스펙은 Hibernate를 보고 만들어진 스펙입니다. JAVA에서는 이렇게 사용자가 만든 라이브러리가 공식 스펙으로 되는 경우가 많습니다.

​

Red hat에서 개발한 오픈소스 ORM 프레임워크였다가, 기술이 좋아서 JAVA가 이를 참고하여 표준 JPA명세를 만들었습니다.

​

그렇다고 JPA와 Hibernate가 완전 동일하다고느 ㄴ할 수 없습니다. JPA는 단순 명세(스펙)를 나열한 것이고, 그 명세대로 구현된 것이 Hibernate라고 보면 됩니다. (싸실은 반대지만... ). 그러니 JPA를 구현한 또 다른 프레임워크가 존재한다는 것이죠.

Hibernate 적용하기

\* gradle 프로젝트 기준으로 설명하겠습니다.

환경 샛탕

의존성 추가

build.gradle

// JPA APIimplementation 'jakarta.persistence:jakarta.persistence-api:3.1.0' // JPA API​// JPA 구현체implementation 'org.hibernate:hibernate-core:6.2.5.Final' // Hibernate Core (JPA 구현체)
💡JAVA JPA의 버전 Isuue(javax vs jakarta)​ 만약 import에 javax가 들어가 있따면 JPA버전이 2.2이하인 경우입니다. 예전에 사용하던 버전으로 여전히 많은 프로젝트에서 사용되기는 하지만, 최신 버전을 사용하려면 JPA 2.3 이상의 버전을 사용하세요.​ 최신 버전에는 jakarta. 가 import됩니다. ex) import jakarta.persistence.\* ;​

파일 설정

project내부 direcotry경로/src/main/resource/META-INF(없으면 만들기)

persistence.xml

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence\_2\_2.xsd" version="2.2">​ <!-- EntityManager를 Hibernater로 등록--> <persistence-unit name="onlyJPAUnitt" transaction-type="RESOURCE\_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>​ <properties> <!-- DB연결 --> <property name="javax.persistence.jdbc.url" value="jdbc:mariadb://localhost:3306/http?useSSL=false"/> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.password" value="pw"/> <property name="javax.persistence.jdbc.driver" value="org.mariadb.jdbc.Driver"/> <property name="hibernate.dialect" value="org.hibernate.dialect.MariaDBDialect"/> <property name="hibernate.show\_sql" value="false"/> <property name="hibernate.format\_sql" value="true"/>​ <!-- Configuration for connection pool (HikariCP)--> <property name="hibernate.hikari.connectionTimeout" value="20000"/> <property name="hibernate.hikari.minimumIdle" value="10"/> <property name="hibernate.hikari.maximumPoolSize" value="10"/> <property name="hibernate.hikari.idleTimeout" value="300000"/> <property name="hibernate.hikari.poolName" value="SpringBootJPAHikariCP"/> </properties> </persistence-unit></persistence>​

주석으로 관련 설정들을 달아뒀으니 여러분들의 환경에 따라 변경하시며 됩니다!

🚧 위 설정 파일은 yaml으로 대체되었으니 여기를 참고해주세요.

Entity 정의

jPA는 기본적으로 ORM입니다. 이는 JAVA에서 Obejct를 만들면 JPA가 알아서 내가 연결한 DB의 Table과 maaping을 해줍니다.

​

우리는 그럼 Class를 만들면 되겠죠? 어떻게 만드는지 아래 코드를 봅시다.

package com.example.webserver.entity;​​import jakarta.persistence.\*;​import java.time.LocalDateTime;import java.util.List;​@Entitypublic class BoardEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="id") private long boardId;​ @Column(name = "title", nullable = false) private String title;​ @Column(name = "writer", nullable = false) private String writer;​ @Column(name = "writing\_time", nullable = false) private LocalDateTime writingTime;​ @Column(name = "reading\_count", nullable = false) private Integer readingCount;​ @Column(name = "text\_content", nullable = false) private String textContent;​ @OneToMany(mappedBy = "board") private List<CommentEntity> comments;}​
package com.example.webserver.entity;​import jakarta.persistence.\;import lombok.Data;import lombok.extern.slf4j.Slf4j;​import java.time.LocalDateTime;​@Entity@Slf4j@Table(name = "comments")@Datapublic class CommentEntity {@Id@GeneratedValue(strategy = GenerationType.IDENTITY*)@Column(name="id")private long commentId;​@ManyToOne@JoinColumn(name="board\_id")private BoardEntity board;​@Column(name="writer")private String Writer;​@Column(name="writing\_time")private LocalDateTime writingTime;​@Column(name="text\_content")private String textContent;}
  • @Entoty로 선언된 class는 DB의 테이블에 매칭이됩니다
  • table에는 각 column이 존재하죠? 이는 class의 feild에 매핑됩니다.
  • @id는 table의 primary Key를 지정하는데 사용됩니다. ( clas는 반드시 하나의 @id feild가 존재 해야 합니다. )
  • @GenerateValue를 사용하여 AUTO INCREMENT로 column 속성을 변경할 수 있습니다.
  • @oneToMany / @ManyToOne을 사용하여 1:M 관계를 정의합니다. (comment에 boardId가 외래키로 참조 중)

이미지

이미지

boards / comments

실제 DB의 모습은 이렇습니다.

​

더 많고 자세한 어노테이션(@)은 JPA스펙 글에 정리를 해두었씁니다.(쭈기적으로 업데이트 해볼게요.

​

\* 추가정보

  • @table을 @Entity와 사용하면 테이블 이름을 다르게 지정하거나 스키마 등의 추가 설정이 가능합니다.
  • 나머지는 그때 그때 찾아보세요!

EntityManger 설정

JPA의 경우 EntityMager를 꼭 구현해줘야 하는데, 저희는 Hibernater를 사용하고 있으니, EntityMager로 Hibernater에서 구현된 객체를 사용하겠다고 알려줘야 합니다. 사실 이건 위의 xml에서 적용된 부분입니다.

<persistence-unit name="onlyJPAUnit" transaction-type="RESOURCE\_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

persistenc-unit으로 이미 Hibernater를 적용했고 이름도 "onlyJPAUnit"으로 지정해두었네요. 우리는 이제 EntityMager를 불러서 사용하면됩니다.

​

기본적으로 MSC 모델을 사용하고 있기 때문에 Model에 해당하는 Repository를 구현 해봅시다.

package com.example.webserver.repository;​import com.example.webserver.entity.BoardEntity;​import java.util.List;import java.util.Optional;​public interface BoardRepository { List<BoardEntity> findAll(); Optional<BoardEntity> findById(long id); void removeById(long id); void save(BoardEntity board);}​
package com.example.webserver.repository;​import com.example.webserver.entity.BoardEntity;import lombok.extern.slf4j.Slf4j;import jakarta.persistence.\*;​import java.util.List;import java.util.Optional;​@Slf4jpublic class BoardRepositoryImpl implements BoardRepository { EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("onlyJPAUnit"); EntityManager entityManager = entityManagerFactory.createEntityManager();​ @Override }​

interface로 선언 후 상속받아서 이런 패턴으로 구현해주면 됩니다!

  • EntityManger를 선언하려면 EntityMagerFactory(JPA꺼)에서 hibernater에서 만든 EntityManger를 불러옵니다. (xml 파일에서 설정한 unit이름을 넣어요)
  • 이후 EntityManger를 선언해주면 됩니다. (.createEntityManager() )

CRUD 연산 사용해보기

기본적으로 JPA는 CRUD연산을 지원해줍니다. 근데 복잡한건 지원해주지 않아요. 이때는 JPQL을 사용해야 하는데, 이부분은 나중에 따로 다뤄보도록 하겠습니다.

package com.example.webserver.repository;​import com.example.webserver.entity.BoardEntity;​import java.util.List;import java.util.Optional;​public interface BoardRepository { List<BoardEntity> findAll(); Optional<BoardEntity> findById(long id); void removeById(long id); void save(BoardEntity board);}​

아까 Repository에서 만들어둔 CRUD 연산만을 구현해 보겠습니다.

package com.example.webserver.repository;​import com.example.webserver.entity.BoardEntity;import lombok.extern.slf4j.Slf4j;import jakarta.persistence.\;​import java.util.List;import java.util.Optional;​@Slf4jpublic class BoardRepositoryImpl implements BoardRepository { EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("onlyJPAUnit"); EntityManager entityManager = entityManagerFactory.createEntityManager();​ @Override public List<BoardEntity> findAll() { return null; } @Override public Optional<BoardEntity> findById(long id) { return Optional.of*(entityManager.find(BoardEntity.class, id)); } @Override public void removeById(long id) { entityManager.remove(id); }; @Override public void save(BoardEntity board){ entityManager.persist(board); };}​
  • persist(Entity)는 매핑되는 Entity class를 테이블에 저장해 줍니다. (C)
  • find(Entity.class, id)는 id를 통해 Entity를 조회해주는 메서드입니다.(R)
  • remove(id) 는 id를 통해 Entity를 제거해주는 메서드입니다. (D)
  • findAll()은 전체 BoardEntity를 조회하는건데, 이는 기본적으로 지원해주지 않습니다.

​

총 정리

이제 JPA에 대해 조금은 감이 오시리라 생각됩니다. 오늘 내용을 정리해보자면 다음과 같습니다.

​

  • 결국 JPA는 DB와의 연결을 통해 데이터를 쉽게 조작하기 위해 만들어졌다.
  • JPA는 method를 미리 구현해 두어 DB에 쿼리를 대신 날려준다. 이 말은 사용자가 직접 query작성 없이도 쉽게 데이터를 조작할 수 있음을 의미한다.
  • JPA는 기본적인 CRUD를 method만을 지원한다. 더 복잡한 Query를 만들려면 JPQL을 사용할 수 있다.(다른 것도 있어요)