STS 로 Spring Boot 프로젝트 만들기. 6) MYSQL 연동하기 및
"Cannot load driver class: com.mysql.cj.jdbc.Driver spring boot" 에러와
"No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer " 해결하기
- 제가 읽은 책에서는 MYSQL 연동하는 과정이 없어서 따로 해 보았습니다.
- 기존에 집에서 서버로 사용하는 노트북에 MariaDB가 돌아가고 있어 MariaDB로 진행하지만, 해당 내용에서는 MYSQL과 같습니다.
- 5편(https://shanepark.tistory.com/162)에 이어지는 글입니다. 코드가 연속성이 있어 코드 전문을 계속 올리긴 하지만 해당 글만 봐서는 다소 따라하기가 힘들 수 있습니다. 필요한 부분만 찾아 보시거나 막히는 부분이 있다면 앞의 글들을 확인 하시는 것을 추천드립니다. 혹은 깃허브 Repository https://github.com/Shane-Park/springBootStudy 에서 프로젝트의 전체 코드를 확인 하실 수 있습니다.
이번에는 HSQLDB의 가짜(?) 데이터 베이스가 아닌 실제 MYSQL 서버와 연동 해서 작업을 해 보도록 하겠습니다.
앞서 저는 몇달전 JDBC 와 MYSQL 연동하기 (https://shanepark.tistory.com/85?category=1197959) 를 하며 만들어두었던 데이터베이스를 재활용 해볼 예정입니다. 혹시 이 글을 읽는 분중에 본인만의 MYSQL 서버가 아직 구축이 되어 있지 않은 분들은 아마 없을텐데, 혹시나 서버 구축부터 시작해야 하는 분은 아래 링크를 먼저 타서 서버 구축을 먼저 해 주셔야 합니다.
https://shanepark.tistory.com/category/Database/MYSQL%2C%20MariaDB
1. Dependency 설정
전편에서 위에 보이듯이 이미 jpa 의존성은 추가를 해 두었는데요.
mysql 커넥터 추가가 필요합니다.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2. properties 파일 설정
application.properties 파일에 데이터베이스 접속 관련 정보를 입력 해 주어야 합니다.
spring.datasource.url=jdbc:mysql://주소입력:3306/test?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=유저네임입력
spring.datasource.password=비밀번호입력
# spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database=mysql
spring.jpa.show-sql=true
3. Entity 만들기. 저는 기존의 alba 테이블을 재활용 하는데, 가지고있는 테이블이 있으신 분들은 Member나 Emplyer 등등 본인이 사용하시던 테이블 그대로 사용 하시면 되겠습니다.
package com.shane.boot;
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.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@Entity
@Table(name="alba")
public class Alba {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="al_id")
String al_id;
@Column(name="al_name")
String al_name;
@Column(name="al_age")
int al_age;
@Column(name="al_zip")
String al_zip;
@Column(name="al_addr1")
String al_addr1;
@Column(name="al_addr2")
String al_addr2;
@Column(name="al_hp")
String al_hp;
@Column(name="gr_code")
String gr_code;
@Column(name="al_gen")
String al_gen;
@Column(name="al_mail")
String al_mail;
@Column(name="al_career")
String al_career;
@Column(name="al_spec")
String al_spec;
@Column(name="al_desc")
String al_desc;
@Column(name="al_img")
String al_img;
}
Getter, Setter 만들다 지쳐 lombok을 pom.xml에 추가 했습니다.
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
뭔가 Entity도 더 쉽게 만들 방법이 있지 않을까 싶습니다만 일단 만들었습니다.
4. Repository 생성
package com.shane.boot.repositories;
import com.shane.boot.Alba;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AlbaRepository extends JpaRepository<Alba, String>{
}
DB 에 접근 할 수 있도록 Repository를 생성해줍니다.
5. Service 생성
package com.shane.boot.service;
import com.shane.boot.repositories.AlbaRepository;
@Service
public class AlbaService {
@Autowired
AlbaRepository albaRepository;
public List<Alba> findAll(){
List<Alba> list = albaRepository.findAll();
System.out.println(list);
return list;
}
public Alba findOne(String al_id){
Alba alba = albaRepository.getById(al_id);
System.out.println(alba);
return alba;
}
}
repository에서 데이터를 받아와 전달하면서, 해당 내용들을 출력 해 주는 서비스도 하나 만들어 봅니다.
6. RestController 생성
package com.shane.boot.controller;
import java.util.List;
import com.shane.boot.Alba;
import com.shane.boot.service.AlbaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/restapi/alba")
public class AlbaRest {
@Autowired
AlbaService albaService;
@GetMapping
public List<Alba> getAlbaList(){
return albaService.findAll();
}
@GetMapping("/{al_id}")
public Alba getAlba(@PathVariable("al_id") String al_id){
return albaService.findOne(al_id);
}
}
JSON 형태로 조회한 값을 반환해줄 RestController 도 하나 만들어줍니다.
Cannot load driver class: com.mysql.cj.jdbc.Driver spring boot 에러가 발생한다면?
# spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
저는 application.properties에서 위 초록색 부분을 주석 처리 해서 해결 했습니다.
알아서 driver를 찾기 때문에 굳이 명시를 해주지 말라고 합니다.
이제 서버를 켜서 확인 해 봅니다.
restapi/alba 로 요청을 보내니 , Console에 System.out.println로 찍어둔 것 때문에 객체들을 출력 합니다. lombok으로 @ToString 어노테이션을 걸었는데도 ToString이 제대로 동작하진 못했네요. JSON데이터 반환 된 것도, 갯수는 맞는데 내용물이 하나도 보이지 않습니다.
outline을 확인해보니 롬복이 적용되지 않았습니다.
Lombok Extention을 설치 해 줍니다.
이제야 getter와 setter들이 정상적으로 보이기 시작합니다. 스프링 부트가 뭐 알아서 다 해주다보니 롬복도 알아서 해줄 줄 알았던게 문제였습니다.
lombok이 정상적으로 적용 된 후에는 API가 정상적으로 작동합니다!
이번에는 ID로 검색을 해보려고 하는데, 또 다른 에러가 발생합니다.
"No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer "
(to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
println 은 작동하는걸로 봐서, 서비스까지는 문제가 없었는데. 컨트롤러쪽에가 문제가 발생 한 것으로 보입니다.
에러에서 안내하는대로 SerializationFeature.FAIL_ON_EMPTY_BEANS 를 disable 해보겠습니다.
application.properties에 다음 한 줄을 추가합니다.
spring.jackson.serialization.fail-on-empty-beans=false
그러고 다시 서버를 켜서 접속하면,
이제 정상적으로 데이터를 받아옵니다. 맨 아래 보이는 hibernateLazyInitializer 라는 녀석이 눈에 띕니다.
spring.jackson.serialization.fail-on-empty-beans=false 는 에러를 해결하는게 아니라 숨기는 것 이라는 말이 있습니다.
그래서 이번에는 application.properties 에 달았던 spring.jackson.serialization.fail-on-empty-beans=false 를 삭제하고,
Entity 에 @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 어노테이션을 달아 서 다시 서버를 실행 해 보았습니다.
문제가 말끔하게 해결 되었습니다. 지연 로딩에 관한 문제라는데, 몇가지 컬럼들이 다른 컬럼과 관계를 맺고 있어서 그런게 아니었나 싶습니다. LAZY속성에 대해 다음번에 공부를 해 보면 알 수 있지 않을까 싶습니다.
마지막으로 수정된 코드들 한번씩 다시 올려 드리겠습니다.
Alba.java
package com.shane.boot;
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 com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@Entity
@Table(name="alba")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Alba {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="al_id")
String al_id;
@Column(name="al_name")
String al_name;
@Column(name="al_age")
int al_age;
@Column(name="al_zip")
String al_zip;
@Column(name="al_addr1")
String al_addr1;
@Column(name="al_addr2")
String al_addr2;
@Column(name="al_hp")
String al_hp;
@Column(name="gr_code")
String gr_code;
@Column(name="al_gen")
String al_gen;
@Column(name="al_mail")
String al_mail;
@Column(name="al_career")
String al_career;
@Column(name="al_spec")
String al_spec;
@Column(name="al_desc")
String al_desc;
@Column(name="al_img")
String al_img;
}
AlbaRepository.java
package com.shane.boot.repositories;
import com.shane.boot.Alba;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AlbaRepository extends JpaRepository<Alba, String>{
}
AlbaService.java
package com.shane.boot.service;
import java.util.List;
import com.shane.boot.Alba;
import com.shane.boot.repositories.AlbaRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AlbaService {
@Autowired
AlbaRepository albaRepository;
public List<Alba> findAll(){
List<Alba> list = albaRepository.findAll();
System.out.println(list);
return list;
}
public Alba findOne(String al_id){
Alba alba = albaRepository.getById(al_id);
System.out.println(alba);
return alba;
}
}
AlbaRest.java
package com.shane.boot.controller;
import java.util.List;
import com.shane.boot.Alba;
import com.shane.boot.service.AlbaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/restapi/alba")
public class AlbaRest {
@Autowired
AlbaService albaService;
@GetMapping
public List<Alba> getAlbaList(){
return albaService.findAll();
}
@GetMapping("/{al_id}")
public Alba getAlba(@PathVariable("al_id") String al_id){
return albaService.findOne(al_id);
}
}
이상입니다. 아래 링크를 통해 이번 포스팅에 작성된 코드와, 해당 commit을 확인하실 수 있습니다.
https://github.com/Shane-Park/springBootStudy/commit/4469976c3712555a5e2335f8ac83500850dace7e
'Programming > JPA ⁄ Spring' 카테고리의 다른 글
Spring 에서 페블 템플릿 Pebble template 사용하기 1 ) 기본 사용 (0) | 2021.08.12 |
---|---|
SpringBoot + PostgreSQL + Hibernate ) 간단한 게시판 만들기 (0) | 2021.08.08 |
STS 로 Spring Boot 프로젝트 만들기. 5) 데이터 베이스 사용하기. CRUD 예제 (0) | 2021.07.09 |
Visual Studio Code에 Spring Boot 필요한 Plugin들 설치하기 (2) | 2021.07.09 |
STS 로 Spring Boot 프로젝트 만들기. 4) Thymeleaf 조금 더 활용해보기 (0) | 2021.07.09 |