ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot JPA를 통해 Tibero6와 연동하기
    Tibero DB (Tmax AI Bigdata Academy) 2023. 10. 28. 11:39

    벌써 교내에서 진행하는 TABA 수업의 운체, AI, DB, Tibero 시험들이 모두 끝났다..

    이제 팀별 프로젝트만을 남겨두고 있는데, 백엔드를 공부하고 있기때문에 Spring Boot와

    이번 프로젝트에서 DB를 평소에 사용하던 MySQL이 아닌 Tibero6를 사용해보고 싶어 미리 기록해두려고 한다.

    (이러고 스프링이랑 티베로 안쓰면 ㅠㅠ)

     

     

    📖 참고

    Tibero6를 윈도우나 리눅스 환경에서 설치했다고 가정하고

    연동했던 과정을 기록했습니다.

     

    📒Window 환경에서 Tibero6 설치

    https://codecollector.tistory.com/1613

     

    (Tibero) - Window에 설치 및 연결해보기

    🍳머리말 local환경에서 Tibero6과 TiberoStudio2를 설치하고 연결해보는 설명글입니다. 📕 Tibero6 📔 installer download 설치 가능한 license가 있다고 가정하고 spring boot library에 넣을 tibero6을 설치합니다.

    codecollector.tistory.com

    📒Linux(Ubuntu) 환경에서 Tibero6 설치

    https://kjungw1025.tistory.com/4

     

    티베로 데이터베이스 설치하기 Tibero install in Ubuntu(WSL)

    교내에서 TmaxTibero와 진행하는 교육 프로그램인 TABA(Tmax AI Bigdata Academy)를 들으면서 드디어 Tibero를 설치하는 실습을 진행하게 되었다. https://www.tmaxtibero.com/product/productView.do?prod_cd=tibero 티맥스티베

    kjungw1025.tistory.com

     

     

    📖 tibero6-jdbc.jar file 확인

    우선 JDBC(Java Database Connectivity)란 Java 기반 애플리케이션의 데이터를 데이터베이스에 저장 및 업데이트 또는 java에서 사용할 수 있도록 하는 API이다.

    Tibero에서는 Tibero DB와 연결을 도와주기 위해 JDBC API를 제공하는데, 이를 tbJDBC(Tibero의 Java Database Connectivity)라고 부른다.

    Tibero6를 설치하면서 제공되는 드라이버 파일은 아래 경로에 위치한다.

     

    Window

    C:\TmaxData\tibero6\client\lib\jar

    Linux

    /tibero6/client/lib/jar

     

    위 사진처럼 경로에 들어가면 tibero6-jdbc.jar과 tibero6-jdbc-14.jar파일이 존재한다.

    Tibero 6 Online Manual을 확인해보면,  JDK 버전이 1.4인 경우에는 tibero6-jdbc-14.jar 파일을 사용하고

    JDK 6 이상에서 드라이버 파일은 tibero6-jdbc.jar 파일을 사용하면 된다고 설명하고 있다.

    나의 경우 JDK 17이 설치되어있기 때문에 tibero6-jdbc.jar 파일을 사용할 예정이다. 

    # 터미널에서 jdk 버전 확인
    java --version

     

     

     

    📖 Spring Boot JPA

    📒 프로젝트 생성

    Spring Initializr를 사용해 프로젝트를 생성했다.

    https://start.spring.io/

     

    📒tibero6-jdbc.jar 추가

    위에서 확인한 tibero6-jdbc.jar 파일을, 생성한 스프링부트 폴더안의 src 폴더와 같은 level에

    별도의 libs 폴더를 만들어 libs 폴더안에 넣어준다.

     

    📒build.gradle 설정

    build.gradle안의 dependencies에 추가한 jar의 경로를 넣어 build해준다.

    plugins {
    	id 'java'
    	id 'org.springframework.boot' version '2.7.17'
    	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    }
    
    group = 'taba'
    version = '0.0.1-SNAPSHOT'
    
    java {
    	sourceCompatibility = '17'
    }
    
    configurations {
    	compileOnly {
    		extendsFrom annotationProcessor
    	}
    }
    
    repositories {
    	mavenCentral()
    }
    
    dependencies {
    	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'
    	implementation 'org.springframework.boot:spring-boot-starter-validation'
    	implementation files('libs/tibero6-jdbc.jar')
    	compileOnly 'org.projectlombok:lombok'
    	developmentOnly 'org.springframework.boot:spring-boot-devtools'
    	annotationProcessor 'org.projectlombok:lombok'
    	testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }
    
    tasks.named('test') {
    	useJUnitPlatform()
    }

     

    📒application.yml 설정

    spring:
      datasource:
        driver-class-name: com.tmax.tibero.jdbc.TbDriver
        url: jdbc:tibero:thin:@127.0.0.1:8629:tibero
        username: sys
        password: tibero
    
      jpa:
      	# 문법 정의 : tibero는 oracle 문법을 사용하므로
        database-platform: org.hibernate.dialect.Oracle10gDialect
        hibernate:
          ddl-auto: update
        properties:
          hibernate:
            format_sql: true
            show_sql: true
            highlight_sql: true
            default_batch_fetch_size: 200

     

    📒Entity 작성

    Tibero6 연동을 확인하기 위해 아래 사진처럼 패키지들을 만들어 수행하고자 한다.

    domain 패키지 안에 Member.java 생성

    package taba.tibero6jpa.user.domain;
    
    import lombok.*;
    
    import javax.persistence.*;
    import javax.validation.constraints.NotNull;
    
    @Entity
    @Table(name="member")
    @Getter
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class Member {
        @Id
        @GeneratedValue
        @Column(name = "member_id")
        private Long id;
    
        @NotNull
        @Column(length = 20)
        private String name;
    
        @NotNull
        private String phone;
    
        @Builder
        private Member(@NotNull String name,
                       @NotNull String phone) {
            this.name = name;
            this.phone = phone;
        }
    }

     

    📒DTO 작성

    RequestMemberDto.java

    package taba.tibero6jpa.user.domain;
    
    import lombok.Getter;
    import lombok.RequiredArgsConstructor;
    import lombok.Setter;
    
    import javax.validation.constraints.NotBlank;
    
    @Getter @Setter
    @RequiredArgsConstructor
    public class RequestMemberDto {
        @NotBlank
        private String name;
        @NotBlank
        private String phone;
    }

     

    📒Repository 작성

    package taba.tibero6jpa.user.repository;
    
    import org.springframework.stereotype.Repository;
    import taba.tibero6jpa.user.domain.Member;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import java.util.List;
    
    @Repository
    public class MemberRepository {
        @PersistenceContext // JPA가 제공하는 표준 어노테이션
        private EntityManager em; // Spring이 Entity manager를 만들어서 주입하게 해줌
    
        public void save(Member member) {
            em.persist(member);
        }
    
        public List<Member> findAll() {
            List<Member> result = em.createQuery("select m from Member m", Member.class)
                    .getResultList();
            return result;
        }
    
        public List<Member> findByName(String name) {
            return em.createQuery("select m from Member m where m.name = :name", Member.class)
                    .setParameter("name", name)
                    .getResultList();
        }
    }

     

    📒Service 작성

    package taba.tibero6jpa.user.service;
    
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    import taba.tibero6jpa.user.domain.Member;
    import taba.tibero6jpa.user.domain.RequestMemberDto;
    import taba.tibero6jpa.user.repository.MemberRepository;
    
    import java.util.List;
    
    @Service
    @RequiredArgsConstructor
    public class MemberService {
        private final MemberRepository memberRepository;
    
        @Transactional
        public Long join(RequestMemberDto dto) {
            validateDuplicateMember(dto);
    
            Member member = Member.builder()
                    .name(dto.getName())
                    .phone(dto.getPhone())
                    .build();
            memberRepository.save(member);
    
            return member.getId();
        }
    
        private void validateDuplicateMember(RequestMemberDto dto) {
            List<Member> findMembers = memberRepository.findByName(dto.getName());
            if (!findMembers.isEmpty()) {
                throw new IllegalStateException("이미 존재하는 회원입니다.");
            }
        }
    
        @Transactional(readOnly = true)
        public List<Member> findMembers() {
            return memberRepository.findAll();
        }
    
        @Transactional(readOnly = true)
        public List<Member> findByName(String name) {
            return memberRepository.findByName(name);
        }
    }

     

    📒 실행 및 테이블 생성 확인

    application을 실행하면 터미널상에 아래와 같이 테이블이 생성된 것을 log로 확인할 수 있다.

    그렇다면 Tibero6 DB에도 테이블이 잘 생성되었을까? 확인해보자!

    다음과 같이 성공적으로 테이블이 생성된 것을 확인할 수 있다. 

     

    📖 Thymeleaf 템플릿 엔진을 통해 로컬 환경에서 웹 띄워보기

    사실 위에서 끝내도 되긴하지만,

    간단하게 Thymeleaf를 통해 뷰 템플릿을 작성하고

    컨트롤러로 전달하는 데이터를 이용하여 동적으로 웹 화면을 보여주는 것까지 하고 마무리하려고 한다.

     

    📒controller

    package taba.tibero6jpa.user.controller;
    
    import javax.validation.Valid;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import taba.tibero6jpa.user.domain.Member;
    import taba.tibero6jpa.user.domain.RequestMemberDto;
    import taba.tibero6jpa.user.service.MemberService;
    
    import java.util.List;
    
    @Controller
    @RequiredArgsConstructor
    public class MemberController {
        private final MemberService memberService;
    
        @GetMapping("/members/new")
        public String createForm(Model model) {
            model.addAttribute("memberForm", new RequestMemberDto());
            return "members/createMemberForm";
        }
    
        @PostMapping("/members/new")
        public String create(@Valid RequestMemberDto dto, BindingResult result) {
            if (result.hasErrors()) {
                return "members/createMemberForm";
            }
            memberService.join(dto);
            return "redirect:/";
        }
    
        @GetMapping("/members")
        public String list(Model model) {
            List<Member> members = memberService.findMembers();
            model.addAttribute("members", members);
            return "members/memberList";
        }
    }

     

    📒HTML 작성

    resources > static > templates에 아래 html들을 작성하자

    createMember.html

    <!DOCTYPE HTML>
    <html xmlns:th="http://www.thymleaf.org">
        <body>
            <div>
                <form action="/members/new" method="post">
                    <div>
                        <label for="name">이름</label>
                        <input type="text" id="name" name="name" placeholder="이름을 입력하세요.">
    
                        <label for="phone">전화번호</label>
                        <input type="text" id="phone" name="phone" placeholder="전화번호를 입력하세요.">
                    </div>
                </form>
            </div>
        </body>
    </html>

    memberList.html

    <!DOCTYPE HTML>
    <html xmlns:th="http://www.thymeleaf.org">
        <body>
            <div>
                <div>
                    <table>
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>이름</th>
                                <th>전화번호</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr th:each="member : ${members}">
                                <td th:text="${member.id}"></td>
                                <td th:text="${member.name}"></td>
                                <td th:text="${member.phone}"></td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </body>
    </html>

     

    📒로컬 환경에서 확인하기

    처음 /members에 들어가면 현재 Member 테이블에 값들이 비어있기 때문에 아래와 같이 나온다.

    /members/new에 들어가서 값을 insert해주자

    입력 버튼을 누르면 /members로 리다이렉트 했기 때문에 아래와 같이 값이 잘 적용된 것을 확인할 수 있다.

    터미널에서도 아래와 같이 query문이 잘 동작하는 것을 확인할 수 있다.

    사용자가 input tag에 값을 넣고 입력 버튼을 누르면, Hibernate의 시퀀스 전략으로 nextval이 호출되면서

    name, phone값과 함께 Insert되고,

    /members 페이지로 가면서 Member 테이블에 존재하는 값들 전부를 select하는 과정이다.

     Tibero6 DB에도 값이 문제 없이 Insert된 것을 확인할 수 있다.

     

Designed by Tistory.