저번에 spring에서 jpa를 활용하여 로그인 및 회원가입을 구현했다. 근데 jpa를 잘 모르고 쓰다보니 이게 맞는건지.. mybatis와 별 차이가 없는데? 이런 생각이 들어 강의를 찾아보았고 이에 대한 정보를 정리하고자 한다.
[Spring] JPA를 활용한 로그인 구현
https://mjoo1106.tistory.com/entry/Spring-JPA%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EA%B5%AC%ED%98%84 [Spring] JPA를 활용한 회원가입 구현 앞서 MyBatis를 활용해서..
mjoo1106.tistory.com
JPA란?
JPA는 JAVA ORM 기술 표준으로 사용되는 인터페이스 모음이다. 대표적인 오픈소스로는 Hibernate가 있다. 말이 너무 어렵다 그래서 ORM은 무엇일까..?
ORM(Object-Relational Mapping)
아마 이 때까지는 RDB(Relational DataBase)의 테이블에 직접 연결(매핑)해서 사용했을 것이다. 그런데 ORM은 객체와 RDB 데이터를 자동으로 연결(매핑)해준다. 객체 지향 프로그래밍은 클래스를 사용하고, RDB는 테이블을 사용한다.
그래서 왜 JPA를 사용해야할까?
2016년 6월부터 현재까지의 트랜드를 조사해보았다. 과거에는 mybatis를 많이 사용했지만 jpa관심이 계속 증가하더니 2021년 부터는 mybatis를 이겼다. 사실 이 이유 하나로만으로도 jpa를 쓸 이유는 충~~분하다. 왜? 취미로 프로그래밍을 배우는 사람은 해당되지 않지만 그 외는 취업을 목표로 두고 있기 때문이다.
두 번째 이유는 설계에 있다고 생각한다. 기존에는 RDB에 있는 물리적 테이블을 대상으로 쿼리를 날렸다. 이 경우 해당 데이터베이스에 종속적으로 설계되는 경우가 빈번하다. 반면 JPA는 엔티티 객체를 대상으로 쿼리를 작성하므로 이와 반대되는 성격을 지녔다. 그 외에도 JPA를 사용하는 이점이 많지만, 가장 중요한 이유 두 가지만 나타내보았다.
C(Create) R(Read) U(Update) D(Delete)
이번 실습에서는 완전 기초부터 할 예정이므로 SPRING을 사용하지 않고 진행해보겠다. 우선 실험 환경은 MySQL, Intellij, JDK 11, gradle을 사용했다. (DB는 H2, Oracle 등 다양한 것을 사용해도 된다. 실제 코드는 한 줄만 다르다.)
1. Project Create
2. build.gradle
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation 'mysql:mysql-connector-java:8.0.28'
implementation 'org.hibernate:hibernate-entitymanager:5.3.10.Final'
implementation 'javax.xml.bind:jaxb-api:2.3.0'
}
dependencies를 위와 같이 수정한다. hibernate는 jpa의 대표적인 오픈소스이다. 그리고 mysql을 사용하지 않는 분들은 3 번째 줄만 변경하면 된다.
3. persistence.xml
Spring으로 진행했을 때는 application.properties 에서 슉슉 진행했지만, 이번에는 JAVA 환경에서만 진행하기 때문에 persistence.xml 을 통해 DB와 매핑한다. 이 때 경로는 main > resources > META-INF > persistence.xml이다.
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
<persistence-unit name="hello">
<class>hellojpa.Member</class>
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="your_password"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/{schema}?serverTimezone=UTC&characterEncoding=UTF-8"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<!-- 쿼리 보여줘 -->
<property name="hibernate.show_sql" value="true"/>
<!-- 쿼리 예쁘게 포맷해줘 -->
<property name="hibernate.format_sql" value="true"/>
<!-- insert 등으로 왜 쿼리가 발생했는지 -->
<property name="hibernate.use_sql_comments" value="true"/>
</properties>
</persistence-unit>
</persistence>
필요한 부분만 설명하자면 첫 번째로 persistence-unit name을 반드시 지정해주어야 한다. (이름은 아무거나) 그 후 <class>hellojpa.Member</class>를 지정한 이유는 Member라는 클래스를 나중에 만들 것인데, 인식을 하지 못해서 추가해줬다. (Maven에서는 문제가 발생하지 않는 것으로 파악) 그 후 mysql 설정을 해주는 코드이다. h2, oracle 등을 사용하는 경우에는 검색 후 properties의 첫 번째 줄과 다섯 번째 줄만 변경해주면 된다. 그리고 4 번째 줄의 schema는 mysql의 경우 데이터베이스 이름이다.
3. Main
세팅은 끝났으므로 main 함수를 만들어준다. 그 전에 패키지를 아무 이름이나 하나 생성한다. 경로는 main > java > (생성한 패키지 이름) > (생성한 클래스 이름)
JpaMain
package hellojpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;
public class JpaMain {
public static void main(String[] args){
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em=emf.createEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
try {
// 나중에 CRUD code 입력
tx.commit();
} catch (Exception e){
tx.rollback();
} finally{
em.close();
}
emf.close();
}
}
main 함수 안의 요소들을 하나씩 살펴보자
> emf
emf의 경우 앞서 persistence.xml을 만들었을 때 지정해준 이름이 있을 것이다. 이를 활용하여 mysql과 연결하는 것으로 애플리케이션에서 딱 한 번만 사용한다.
> em
entity를 관리하는 역할을 수행한다.
> tx
transaction 또한 반드시 지정해야 한다. commit, rollback 옵션이 있는데 두 가지 요소에 대해 왜 사용하는지 간략하게 설명하겠다.
commit은 간단하게 항공사 예제로 설명하겠다. A라는 사용자가 43D 자리를 예약하고 있다. 이 때 B라는 사용자가 동시에 43D 자리를 예약한다면 어떤 상황이 발생할까? 특정 예외처리가 없다면 동시에 예약될 수 있다. 즉, 이런 동시성 문제를 해결하기 위해 commit을 사용하는데 commit은 query를 처리하는 동안 lock을 수행하여 다른 작업이 들어오지 못하고 작업이 끝나면 다음 작업을 진행할 수 있다. 이를 통해 항공사 문제도 해결할 수 있을 것이다. rollback은 말 그대로 오류가 났을 때 rollback하는 것이라 넘어가겠다.
코드는 try 구문 안에서 작성할 것이다. 이제 CRUD 각각의 요소 하나씩 작성할 것인데, 이 전에 현재 데이터베이스가 비어있으므로 하나 만들어주고 이를 매핑할 Class를 하나 만들자!
4. MySQL
mysql -u root -p
Enter password:
mysql> create database jpatest;
mysql> use jpatest;
mysql> create table Member( id bigint not null primary key, name varchar(255));
select * from member; 를 입력했을 때 Empty set 이라고 나오면 성공적으로 생성된 것이다.
5. Member
아까 만든 main 함수와 동일한 위치에 Member Class를 하나 만들어준다.
package hellojpa;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
/*
@Table(name="") 으로 수동으로 매핑 가능하다.
@Column(name="")으로 거기서 지정한 이름이랑 다르게 지정했을 때 매핑해서 사용함
*/
public class Member {
@Id
private Long id;
private String name;
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;
}
}
@Id를 통해 PK라는 것을 알려준다. Table과 Column의 이름을 다르게 지정하고 싶은 경우에는 @Table, @Column을 통해 수동으로 매핑해주면 사용 가능하다.
6. Create
try {
Member member = new Member();
member.setId(1L);
member.setName("HelloA");
em.persist(member);
tx.commit();
}
삽입은 위 코드가 끝이다. 실제로 실행시켜보면 빨간 줄이 많이 뜰 것인데 그럼 성공한 것이다.
7. Read
try {
Member findMember=em.find(Member.class, 1L);
System.out.println("findMember.id = "+findMember.getId());
System.out.println("findMember.Name = "+findMember.getName());
tx.commit();
}
em.find(Member.class, 1L)에서 1L은 Id가 1인 것을 조회하라는 뜻이다.
8. Update
try {
Member findMember=em.find(Member.class,1L);
findMember.setName("HelloGTA");
tx.commit();
}
9. Delete
try {
Member findMember=em.find(Member.class, 1L);
em.remove(findMember);
tx.commit();
}
10. JPQL
아마 앞의 CRUD를 보면서 이렇게 간단할 수가 있나? 라는 생각이 든다.. 점점 JPA 빠져들고 있다...
그런데 복잡한 조회는 어떤 식으로 접근할까? 라는 고민이 들 수도 있다. 이 때 사용하는 것이 JPQL이다. JPQL은 SQL을 추상화한 객체 지향 쿼리 언어이다. SQL 문법과 유사해서 기존에 SQL 문을 사용해 본 사람이라면 쉽게 적응할 수 있다. 즉, SQL은 DB TABLE을 대상으로 쿼리문을 날리고 JPQL은 엔티티 객체를 대상으로 쿼리를 날린다는 차이점이 있다. 둘 다 사용할 수 있다. 앞선 로그인 예제에서는 SQL 문을 사용하고 있다. 그러나 JPA를 쓰는 것으로 결정했다면 당장 JPQL을 연습해야 한다.
그래서 조금 더 복잡하게 짜보겠다. 첫 번째부터 다섯번째까지 이름을 조회하라는 쿼리문을 어떻게 작성할까?
try {
List<Member> result=em.createQuery("select m from Member as m", Member.class)
.setFirstResult(1)
.setMaxResults(5)
.getResultList();
for(Member member : result){
System.out.println("member.name = "+member.getName());
}
}
JPA에 관련해서 진짜 기초만 알아보았다. 이렇게 블로그에 정리해보니 조금 감이 오는 것 같다. 확실히 개념을 적립해서 로그인, 회원가입 예제를 수정해보겠다!
'SPRING' 카테고리의 다른 글
[JAVA] JPA 연관관계 매핑 (0) | 2022.07.25 |
---|---|
[JAVA] JPA 실습(Create table and PK/FK 설정) (0) | 2022.07.19 |
[Spring] JPA를 활용한 로그인 구현 (2) | 2022.07.15 |
[Spring] JPA를 활용한 회원가입 구현 (0) | 2022.07.14 |
[Spring] MyBatis를 활용한 Login 구현 - 2 (0) | 2022.07.06 |