본문 바로가기
Java

스프링부트 JPA 사용기

by NaHyungMin 2020. 11. 1.

특징.

1캐시, 2캐시가 존재. 1캐시는 로컬 영역. 2캐시는 글로벌 영역으로 처리.

 

데이터베이스에 직접 데이터를 넣기 전 메모리로 정보를 가지고 있는다.

때문에 가지고 있는 정보를 먼저 조회 후 데이터가 없으면 데이터베이스에 연결.

 

장점.

데이터베이스와 커넥션을 줄일 수 있다.(통신 비용 감소)

데이터베이스를 잘 몰라도 정보를 조회할 수 있다.(컬렉션을 사용하는 방법으로 운영하는 듯.)

 

단점.

어노테이션 고수가 되야 한다.

어렵거나 최적화 쿼리는 짜기 어렵다.

제공되는 기능이 많다보니 헬적화 인듯.

타인 혹은 타사에게 제공하는 라이브러리를 구현해본 사람은 알겠지만 결국 어노테이션(애트리뷰트) 혹은 원래 기능이 필요하다. 때문에 자체적 쿼리도 구현할 수 있도록 제공 하는 등 아무래도 최적화하기엔 프로시저 접근 방식이 나을수도.

 

혹여나 구현해볼 사람을 위해 구현했던 방법을 남긴다.

 

 

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jpa</groupId>
    <artifactId>sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>sample</name>
    <description>JPA project for Spring Boot</description>

    <properties>
        <java.version>8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.3.7.Final</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 

구조.

 

controller.TestJpaRestController

package com.jpa.sample.user.controller;

import com.jpa.sample.user.entity.MemberVo;
import com.jpa.sample.user.repository.MemberRepository;
import com.jpa.sample.user.service.MemberService;
import com.jpa.sample.user.test.MemberRepositoryTests;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("memberTest")
public class TestJpaRestController {

    //JPA 검색 정보
    //https://kouzie.github.io/spring/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-JPA/#paging-%EC%B2%98%EB%A6%AC-%EB%94%9C%EB%A0%88%EB%A7%88

    private final MemberService memberService;
    private final MemberRepository memberRepository;

    public TestJpaRestController(MemberService memberService, MemberRepository memberRepository){
        this.memberService = memberService;
        this.memberRepository = memberRepository;
    }

    @GetMapping(produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<List<MemberVo>> getAllMembers() {
        List<MemberVo> members = memberService.findAll();

        return new ResponseEntity<>(members, HttpStatus.OK);
    }

    @GetMapping(value= "/{no}", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<MemberVo> getMember(@PathVariable("no") long no) {
        Optional<MemberVo> member = memberService.findById(no);

        return new ResponseEntity<>(member.get(), HttpStatus.OK);
    }

    @DeleteMapping(value= "/{no}", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Void> deleteMember(@PathVariable("no") long no) {
        memberService.deleteById(no);

        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @PutMapping(value= "/{no}", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<MemberVo> updateMember(@PathVariable("no") Long no, MemberVo member) {
        memberService.updateById(no, member);

        return new ResponseEntity<>(member, HttpStatus.OK);
    }

    @PostMapping
    public ResponseEntity<MemberVo> save(MemberVo member){
        //테스트 코드, 테스트를 하지 않는다면 memberRepository 주입을 제거해도 좋다.
        /*MemberRepositoryTests test = new MemberRepositoryTests(memberRepository);
        test.testInsert();

        return new ResponseEntity<>(member, HttpStatus.OK);*/

        //정상 코드
        MemberVo newMember = memberService.save(member);

        return new ResponseEntity<>(newMember, HttpStatus.OK);
    }

    @RequestMapping(value = "/saveMember", method = RequestMethod.GET)
    public ResponseEntity<MemberVo> save(HttpServletRequest req, MemberVo member)
    {
        MemberVo newMember = memberService.save(member);

        return new ResponseEntity<>(newMember, HttpStatus.OK);
    }
}

 

entity.MemberVo

 

package com.jpa.sample.user.entity;

import lombok.*;

import javax.persistence.*;

@AllArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "member")
public class MemberVo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    /*
        1. AUTO : (persistence provider가) 특정 DB에 맞게 자동 선택
        2. IDENTITY : DB의 identity 컬럼을 이용
        3. SEQUENCE : DB의 시퀀스 컬럼을 이용
        4. TABLE : 유일성이 보장된 데이터베이스 테이블을 이용
     */
    private Long idx;

    private String id;
    /*@Column(length = 20, nullable = false)
    @Column(columnDefinition = "TEXT", nullable = true)*/
    private String name;

    /*@CreationTimestamp
    private Timestamp regdate;
    @UpdateTimestamp
    private Timestamp updatedate;*/

    @Builder
    public MemberVo(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public MemberVo() {

    }
}

 

repository.MemberRepository

 

package com.jpa.sample.user.repository;

import com.jpa.sample.user.entity.MemberVo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface MemberRepository extends JpaRepository<MemberVo, Long> {
    public List<MemberVo> findById(String id);

    public List<MemberVo> findByName(String name);

    //like검색도 가능
    public List<MemberVo> findByNameLike(String keyword);
}

 

service.MemberService

 

package com.jpa.sample.user.service;

import com.jpa.sample.user.entity.MemberVo;
import com.jpa.sample.user.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
public class MemberService {

    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }

    public List<MemberVo> findAll()
    {
        List<MemberVo> members = new ArrayList<>();
        memberRepository.findAll().forEach(e -> members.add(e));
        return members;
    }

    public Optional<MemberVo> findById(Long mbrNo)
    {
        Optional<MemberVo> member = memberRepository.findById(mbrNo);
        return member;
    }

    public void deleteById(Long mbrNo)
    {
        memberRepository.deleteById(mbrNo);
    }

    public MemberVo save(MemberVo member)
    {
        memberRepository.save(member);
        //memberRepository.saveAndFlush(member);
        //memberRepository.flush();
        return member;
    }

    public void updateById(Long mbrNo, MemberVo member)
    {
        Optional<MemberVo> e = memberRepository.findById(mbrNo);

        if (e.isPresent())
        {
            e.get().setIdx(member.getIdx());
            e.get().setId(member.getId());
            e.get().setName(member.getName());

            memberRepository.save(member);
            //memberRepository.saveAndFlush(member);
        }
    }
}

 

test.MemberRepositoryTests

 

package com.jpa.sample.user.test;

import com.jpa.sample.user.entity.MemberVo;
import com.jpa.sample.user.repository.MemberRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MemberRepositoryTests {

    private final MemberRepository memberRepository;

    public MemberRepositoryTests(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Test
    public void testInsert() {
        MemberVo member = new MemberVo();
        member.setId("Test User");
        member.setName("테스트 유저에요!!");
        memberRepository.save(member);
    }
}

 

application.properties

# API 호출시, SQL 문을 콘솔에 출력한다.
spring.jpa.show-sql=true

# DDL 정의시 데이터베이스의 고유 기능을 사용합니다.
# ex) 테이블 생성, 삭제 등
spring.jpa.generate-ddl=true

# MySQL 을 사용할 것.
spring.jpa.database=mysql

# MySQL 설정

spring.datasource.url=jdbc:mysql://nahyungmin:1234/jpa_kr?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# MySQL 상세 지정
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
#spring.jpa.open-in-view=false
#spring.jpa.hibernate.ddl-auto=create
#spring.jpa.properties.hibernate.format_sql=true
#spring.datasource.initialization-mode=always
spring.datasource.dbcp2.validation-query=SELECT 1

logging.level.web=DEBUG
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.BasicBinder=TRACE

 

만약 초보개발자들이 이 글을 본다면 JPA도 좋지만 결국 RDBMS를 공부해야 한다.

JPA도 테이블을 만들 때, 기본 키를 제외한 유니크, 인덱스, 특정 문자열만 넣는 제약을 걸어야 한다면

더 어려워 질수도 있을거라 생각한다.

 

'Java' 카테고리의 다른 글

자바 ExecutorService  (0) 2020.11.03
자바 쓰레드 Future  (0) 2020.11.03
인텔리제이 클래스 다이어그램  (0) 2020.09.17
자바 Post 한글 깨짐  (0) 2020.08.21
자바 mybatis <>, > 등 쓰기  (0) 2020.08.21