본문 바로가기
Spring/JPA

[JPA] JPA Repository설정 및 CRUD

by J4J 2021. 3. 22.
300x250
반응형

안녕하세요. J4J입니다.

 

이번 포스팅은 JPA Repository설정 및 CRUD에 대해 적어보는 시간을 가져보려고 합니다.

 

아직 환경설정을 하지 않으신 분들은 이전 포스팅 참고해주시길 바랍니다.

2021.03.21 - [Spring/JPA] - [JPA] JPA 환경설정

 

 

 

 

학교를 주제로 하여 해당 내용을 테스트해보도록 하겠습니다.

 

데이터베이스에는 학교라는 테이블을 만들고 스프링에서는 학교 클래스를 생성하여 기본적인 CRUD작업을 수행해보겠습니다.

 

프로젝트 구성

 

[ 1. MySQL에 학교 테이블 생성 ]

 

create database jpa;

use jpa;

create table school (
    school_id int auto_increment,
    name varchar(50),
    region varchar(50),
    ranking int,
    primary key(school_id)
)

 

 

[ 2. School 엔티티 생성 (com.spring.jpa.dto.School) ]

 

package com.spring.jpa.dto;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity // 영속성 컨텍스트에 의해 관리되는 클래스를 의미
@Table(name = "school") // 엔티티에 매핑되는 DB 테이블 명
public class School implements Serializable { // Serializable을 상속받지 않으면 Entity가 여러개일 때 에러 발생
	@Id // 기본키가 될 변수를 의미
	@GeneratedValue(strategy = GenerationType.IDENTITY) // DB의 auto_increment로 값을 저장하는 것을 의미 
	@Column(name = "school_id") // DB에서는 컬럼명이 school_id
	int id;
	
	private String name;
	private String region;
	private int ranking;
}

 

@Entity: 영속성 컨텍스트에 의해 관리되는 클래스를 의미, 단순하게 테이블에 매핑되는 객체라고 생각

@Table: 엔티티에 매핑되는 데이터베이스 테이블 명

@Id: 기본키가 될 변수를 의미

@GeneratedValue: 데이터베이스에서 auto_increment로 자동 생성을 해주기 위해 사용

@Column: 데이터베이스의 컬럼명과 변수명이 동일하지 않을 때 매핑해주기 위해 사용

 

 

[ 3. SchoolRepository 생성 (com.spring.jpa.repository.SchoolRepository) ]

 

package com.spring.jpa.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.spring.jpa.dto.School;

public interface SchoolRepository extends JpaRepository<School, Integer> { // 제네릭 타입: <엔티티 클래스, 엔티티클래스의 기본키>

}

 

Repository를 위와 같이 생성할 경우 JPA에서 기본적으로 제공해주는 메서드들을 사용할 수 있습니다.

 

추가적인 메서드를 사용하고 싶다면 조건에 맞게 메서드를 정의해주면 사용할 수 있습니다.

 

관련된 내용은 다음 포스팅에서 작성하겠습니다.

 

 

반응형

 

 

Insert

 

단위 테스트를 통해 Insert를 해보겠습니다.

 

package com.spring.jpa;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import com.spring.jpa.config.RootContext;
import com.spring.jpa.dto.School;
import com.spring.jpa.repository.SchoolRepository;

import lombok.extern.slf4j.Slf4j;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = RootContext.class)
@Slf4j
public class JPATest {
	
	@Autowired
	SchoolRepository schoolRepository;

	@Test
	public void Insert() {
		School school = new School();
		school.setName("청아예술 고등학교");
		school.setRegion("서울");
		school.setRanking(1);
		
		schoolRepository.save(school); // Insert
	}
}

 

Insert를 위해 사용되는 메서드 명은 save입니다.

 

엔티티를 생성하여 데이터를 저장하고 save메서드에 집어넣을 경우 Insert작업이 수행되는데 정확하게는 merge문과 같은 Upsert가 수행됩니다.

 

Upsert라는 것은 테이블에 조건과 동일한게 존재하지 않으면 데이터를 생성하고, 존재한다면 데이터 업데이트를 진행한다는 의미입니다. 여기서 조건은 "기본키에 해당하는 값들이 모두 같은가?"가 되겠습니다

 

위의 코드를 실행할 경우 다음과 같은 로그가 출력되며 데이터가 저장되는 것을 확인할 수 있습니다.

 

INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@4d41cee, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@3712b94, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@2833cc44, org.springframework.test.context.support.DirtiesContextTestExecutionListener@33f88ab, org.springframework.test.context.transaction.TransactionalTestExecutionListener@27a8c74e, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@2d8f65a4, org.springframework.test.context.event.EventPublishingTestExecutionListener@1b68ddbd]
INFO : org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
INFO : org.springframework.data.repository.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 105ms. Found 1 JPA repository interfaces.
INFO : org.hibernate.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: jpa-mysql]
INFO : org.hibernate.Version - HHH000412: Hibernate Core {5.4.10.Final}
INFO : org.hibernate.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
INFO : org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
INFO : org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
INFO : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'jpa-mysql'
Hibernate: 
    /* insert com.spring.jpa.dto.School
        */ insert 
        into
            school
            (name, ranking, region) 
        values
            (?, ?, ?)
INFO : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'jpa-mysql'

 

 

JPA Insert

 

 

Select

 

package com.spring.jpa;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import com.spring.jpa.config.RootContext;
import com.spring.jpa.dto.School;
import com.spring.jpa.repository.SchoolRepository;

import lombok.extern.slf4j.Slf4j;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = RootContext.class)
@Slf4j
public class JPATest {
	
	@Autowired
	SchoolRepository schoolRepository;

	@Test
	public void Select() {
		List<School> schools = schoolRepository.findAll(); // Select
		log.info(schools.toString());
	}
}

 

 

Select를 위해 사용되는 메서드 명은 find입니다.

 

그리고 기본적으로 제공되는 메서드에는 findAll과 findById 등이 있습니다.

 

 

 

findAll은 모든 데이터를 조회하고 findById는 기본키에 해당하는 값을 파라미터로 집어넣어 해당하는 데이터를 조회합니다.

 

실행할 경우 다음과 같은 로그가 출력됩니다.

 

INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@4d41cee, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@3712b94, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@2833cc44, org.springframework.test.context.support.DirtiesContextTestExecutionListener@33f88ab, org.springframework.test.context.transaction.TransactionalTestExecutionListener@27a8c74e, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@2d8f65a4, org.springframework.test.context.event.EventPublishingTestExecutionListener@1b68ddbd]
INFO : org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
INFO : org.springframework.data.repository.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 101ms. Found 1 JPA repository interfaces.
INFO : org.hibernate.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: jpa-mysql]
INFO : org.hibernate.Version - HHH000412: Hibernate Core {5.4.10.Final}
INFO : org.hibernate.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
INFO : org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
INFO : org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
INFO : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'jpa-mysql'
Hibernate: 
    /* select
        generatedAlias0 
    from
        School as generatedAlias0 */ select
            school0_.school_id as school_i1_0_,
            school0_.name as name2_0_,
            school0_.ranking as ranking3_0_,
            school0_.region as region4_0_ 
        from
            school school0_
INFO : com.spring.jpa.JPATest - [School(id=1, name=청아예술 고등학교, region=서울, ranking=1)]
Hibernate: 
    select
        school0_.school_id as school_i1_0_0_,
        school0_.name as name2_0_0_,
        school0_.ranking as ranking3_0_0_,
        school0_.region as region4_0_0_ 
    from
        school school0_ 
    where
        school0_.school_id=?
INFO : com.spring.jpa.JPATest - School(id=1, name=청아예술 고등학교, region=서울, ranking=1)
INFO : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'jpa-mysql'

 

 

728x90

 

 

Update

 

package com.spring.jpa;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import com.spring.jpa.config.RootContext;
import com.spring.jpa.dto.School;
import com.spring.jpa.repository.SchoolRepository;

import lombok.extern.slf4j.Slf4j;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = RootContext.class)
@Slf4j
public class JPATest {
	
	@Autowired
	SchoolRepository schoolRepository;

	@Test
	public void Update() {
		List<School> schools = schoolRepository.findAll();
		
		for(int i=0; i<schools.size(); i++) { 
			schools.get(i).setRegion("경기");
			schoolRepository.save(schools.get(i)); // Update
		}
		
		log.info(schoolRepository.findAll().toString());
	}
}

 

 

안타깝게도 JPA는 업데이트만을 위한 메서드가 존재하지 않습니다.

 

하지만 업데이트를 할 수 없는 것은 아닙니다.

 

위에서 말씀드린 것처럼 save메서드는 Upsert처리가 되기 때문에 데이터를 변경한 뒤 save메서드를 이용하여 업데이트를 할 수 있습니다.

 

실행할 경우 다음과 같은 로그가 출력되고 데이터가 변경된 것을 확인할 수 있습니다.

 

INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@4d41cee, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@3712b94, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@2833cc44, org.springframework.test.context.support.DirtiesContextTestExecutionListener@33f88ab, org.springframework.test.context.transaction.TransactionalTestExecutionListener@27a8c74e, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@2d8f65a4, org.springframework.test.context.event.EventPublishingTestExecutionListener@1b68ddbd]
INFO : org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
INFO : org.springframework.data.repository.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 96ms. Found 1 JPA repository interfaces.
INFO : org.hibernate.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: jpa-mysql]
INFO : org.hibernate.Version - HHH000412: Hibernate Core {5.4.10.Final}
INFO : org.hibernate.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
INFO : org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
INFO : org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
INFO : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'jpa-mysql'
Hibernate: 
    /* select
        generatedAlias0 
    from
        School as generatedAlias0 */ select
            school0_.school_id as school_i1_0_,
            school0_.name as name2_0_,
            school0_.ranking as ranking3_0_,
            school0_.region as region4_0_ 
        from
            school school0_
Hibernate: 
    /* load com.spring.jpa.dto.School */ select
        school0_.school_id as school_i1_0_0_,
        school0_.name as name2_0_0_,
        school0_.ranking as ranking3_0_0_,
        school0_.region as region4_0_0_ 
    from
        school school0_ 
    where
        school0_.school_id=?
Hibernate: 
    /* update
        com.spring.jpa.dto.School */ update
            school 
        set
            name=?,
            ranking=?,
            region=? 
        where
            school_id=?
Hibernate: 
    /* select
        generatedAlias0 
    from
        School as generatedAlias0 */ select
            school0_.school_id as school_i1_0_,
            school0_.name as name2_0_,
            school0_.ranking as ranking3_0_,
            school0_.region as region4_0_ 
        from
            school school0_
INFO : com.spring.jpa.JPATest - [School(id=1, name=청아예술 고등학교, region=경기, ranking=1)]
INFO : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'jpa-mysql'

 

 

JPA Update

 

 

 

 

Delete

 

package com.spring.jpa;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import com.spring.jpa.config.RootContext;
import com.spring.jpa.repository.SchoolRepository;

import lombok.extern.slf4j.Slf4j;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = RootContext.class)
@Slf4j
public class JPATest {
	
	@Autowired
	SchoolRepository schoolRepository;

	@Test
	public void Delete() {
		schoolRepository.deleteById(1); // Delete
		// schoolRepository.deleteAll(); // Delete All
		
		log.info(schoolRepository.findAll().toString());
	}
}

 

 

Delete를 위해 사용되는 메서드 명은 delete입니다.

 

그리고 기본적으로 제공되는 메서드에는 deleteById, deleteAll 등이 있습니다.

 

Select와 동일하게 deleteById는 기본키 값을 파라미터로 집어넣으면 해당하는 데이터 값을 삭제하고 deleteAll은 모든 데이터를 삭제 처리합니다.

 

위의 코드를 실행하면 다음과 같은 로그가 출력되고 데이터가 삭제되는 것을 확인할 수 있습니다.

 

INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@4d41cee, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@3712b94, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@2833cc44, org.springframework.test.context.support.DirtiesContextTestExecutionListener@33f88ab, org.springframework.test.context.transaction.TransactionalTestExecutionListener@27a8c74e, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@2d8f65a4, org.springframework.test.context.event.EventPublishingTestExecutionListener@1b68ddbd]
INFO : org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
INFO : org.springframework.data.repository.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 113ms. Found 1 JPA repository interfaces.
INFO : org.hibernate.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: jpa-mysql]
INFO : org.hibernate.Version - HHH000412: Hibernate Core {5.4.10.Final}
INFO : org.hibernate.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
INFO : org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
INFO : org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
INFO : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'jpa-mysql'
Hibernate: 
    select
        school0_.school_id as school_i1_0_0_,
        school0_.name as name2_0_0_,
        school0_.ranking as ranking3_0_0_,
        school0_.region as region4_0_0_ 
    from
        school school0_ 
    where
        school0_.school_id=?
Hibernate: 
    /* delete com.spring.jpa.dto.School */ delete 
        from
            school 
        where
            school_id=?
Hibernate: 
    /* select
        generatedAlias0 
    from
        School as generatedAlias0 */ select
            school0_.school_id as school_i1_0_,
            school0_.name as name2_0_,
            school0_.ranking as ranking3_0_,
            school0_.region as region4_0_ 
        from
            school school0_
INFO : com.spring.jpa.JPATest - []
INFO : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'jpa-mysql'

 

 

JPA Delete

 

 

 

 

이상으로 JPA Repository설정 및 CRUD에 대해 간단하게 알아보는 시간이었습니다.

 

읽어주셔서 감사합니다.

728x90
반응형

댓글