본문 바로가기
Spring

Spring 기초 8 (SpringBoot 의 JPA)

by sehunbang 2024. 1. 21.

1. SpringBoot의 JPA

 

메모장 프로젝트 JPA 설정

build.gradle

 

// JPA 설정
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

추가

dependencies {
// JPA 설정
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

 

application.properties

: Hibernate 설정

1/. show_sql, format_sql, use_sql_comments 옵션

Hibernate가 DB에 요청하는 모든 SQL을 보기좋게 출력 해줍니다.

2/. ddl-auto

  • create : 기존 테이블 삭제 후 다시 생성합니다. (DROP + CREATE)
  • create-drop : create와 같으나 종료시점에 테이블을 DROP 합니다.
  • update : 변경된 부분만 반영합니다.
  • validate : Entity와 테이블이 정상 매핑되었는지만 확인합니다.
  • none: 아무것도 하지 않습니다.
spring.datasource.url=jdbc:mysql://localhost:3306/memo
spring.datasource.username=root
spring.datasource.password=Aa1213811!
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

 

Memo.java

클래스 를 Entity 로 변경

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Getter
@Setter
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
@NoArgsConstructor
public class Memo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "username", nullable = false)
    private String username;
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;

    public Memo(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }

    public void update(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }
}

 

SpringBoot 는 EntityManagerFactory 랑  EntityManager 를 자동 생성해줌

 

그냥 기본 java 프로젝트 에서는 persistance.xml 에서 성정 했어야 했음

(복습)

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             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">
    <persistence-unit name="memo">
        <class>com.sparta.entity.Memo</class>
        <properties>
            <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="jakarta.persistence.jdbc.user" value="root"/>
            <property name="jakarta.persistence.jdbc.password" value="Aa1213811!"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/memo"/>

            <property name="hibernate.hbm2ddl.auto" value="update" />

            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

Spring 에서는 이거는 인제 필요 없음.

 

 

application.properties 에서 있는 정보들로 다 자동으로 생성 해줌 

 

그럼 EntityManagerFactory 랑  EntityManager 를 어떻게 사용하나?

 

@Transactional 애노테이션을 걸면 사용 할수 있다.

클래스 위에다가 혹을 메소드 위에다가.

@Transactional(readOnly = true) 에서는 데이터 수정 x 

조회 (SELECT) 할때 Transactional 필수는 아니지만 필요 할때는 있다.

 

@Transactional 는 덯어 씌우기가 가능 하다,

@Transactional(readOnly = true)
.....
@Transactional // 수정할때 덮어쓰기

 

 

EntityManagerFactory 랑  EntityManager 직접 사용.

 

@PersistenceContext

@PersistenceContext

 

원래 기존 java 에서는 autowired 사용.

 

테스트 1 (성공)

@Test
@Transactional
@Rollback(value = false) // 테스트 코드에서 @Transactional 를 사용하면 테스트가 완료된 후 롤백하기 때문에 false 옵션 추가
@DisplayName("메모 생성 성공")
void test1() {
    Memo memo = new Memo();
    memo.setUsername("Robbert");
    memo.setContents("@Transactional 테스트 중!");

    em.persist(memo);  // 영속성 컨텍스트에 메모 Entity 객체를 저장합니다.
}

 

테스트 2 (실패 Transactional 이 없기 때문에)

@Test
@Disabled
@DisplayName("메모 생성 실패")
void test2() {
    Memo memo = new Memo();
    memo.setUsername("Robbie");
    memo.setContents("@Transactional 테스트 중!");

    em.persist(memo);  // 영속성 컨텍스트에 메모 Entity 객체를 저장합니다.
}

 

MemoRepository( 우리가 전에 만들었던 repo 클래스 , mvc 기억하시나요?)

에 함수 추가

package com.sparta.memo.memoRepositary;

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Component;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
@Component
public class memoRepository {
    Memo memo; // memo
    private JdbcTemplate jdbcTemplate;
    @Autowired
    public void setmemoRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;// repository
    }

    
.......
    @Transactional
    public Memo createMemo(EntityManager em) {
        Memo memo = em.find(Memo.class, 1);
        memo.setUsername("Robbie");
        memo.setContents("@Transactional 전파 테스트 중!");

        System.out.println("createMemo 메서드 종료");
        return memo;
    }
}

 

테스트 3 MemoRepository 안에 있는 거로 JPA 하기.

@Test
@Transactional
@Rollback(value = false)
@DisplayName("트랜잭션 전파 테스트")
void test3() {
    memoRepository.createMemo(em);
    System.out.println("테스트 test3 메서드 종료");
}

 

자식 메소트 @Transactional 이 부모 의  @Transactional 과 합쳐진다.

 @Transactional 는 default 가 Propagation.REQUIRED.

 

그래서 부모가 @Transactional 가 있으면 부모 가 끝나기 전까진 

Hibernate 업데이트 안함.