java.lang.NullPointerException: Cannot invoke "jakarta.persistence.EntityManager.persist(Object)" because "this.em" is null
SpringBoot 환경에서 JPA 사용 테스트 코드를 첫 실행하는데 시작부터 오류가 발생했다. 분명히 공부 자료와 동일한 코드를 사용하고 있는데 어떤 차이점이 있는 것일까?
내가 아는 지식선에서 저 문장에서 말하는 오류를 해석해보면, 일단 Entity Manager가 null이다. Entity Manager Factory가 EM을 생성하지 않았나? 아니면 EMF조차도 생성되지 않은것인가? 왜 null이 되었을까.
우선적으로 교재에서의 주입한 외부 라이브러리를 살펴보았다.
// JPA 구현체인 hibernate
implementation 'org.hibernate:hibernate-core:6.1.7.Final'
Java
복사
Hibernate 라이브러리를 주입하면서 해당 기능을 사용하게 된다. 버전의 문제일까?
dependencies {
// MySQL
implementation 'mysql:mysql-connector-java:8.0.28'
// JPA 구현체인 hibernate //
implementation 'org.hibernate:hibernate-core:6.1.7.Final'
// JDBC hibernate 주입시켜서 필요없어짐
//implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
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'
}
Java
복사
음 hibernate의 버전의 문제가있는건가? ChatGPT에게 도움을 요청해보았다.
Q/A
나는 여기서 Spring의 버전과 해당 라이브러리의 호환의 문제로 잘못 생각했었다. 따라서 그 부분을 찾아보고자 아래 블로그를 통해서 또 버전마다 특징적으로 변한것들 특히 javax에서 jakarata라는 패키지명이 변했던 과정에 대해서 알게 되었고 Java8 버전과 그 이후 버전들과 패키지명이 다른 이유를 여기서 알게 되었다.
하지만 이것으로 해결 되는 문제가 아니었고, 생각보다 잘못된 접근이었다.
앗 놓친 부분
ChatGPT의 권고에 따라서 해당 라이브러리를 추가하니 정상적으로 작동되었다. 그런데 타이밍좋게 멘토님을 만나게되었고, 해당 부분에 대한 정확한 피드백이 오고갈 수 있었다.
우선, 교재에서의 코드를 잘 살펴봐야 한다.
// JPA 구현체인 hibernate
implementation 'org.hibernate:hibernate-core:6.1.7.Final'
Java
복사
분명히 JPA의 구현체인 hibernate라고 명시되어 있다. 구현체? 저 부분을 간단하게 생각했던 문제이다. 구현체라하면 바로 인터페이스 또는 부모클래스가 추상화 메소드를 가지고 있을 때, 자손 클래스는 그 메소드의 구현체를 실제로 정의하면서 기능을 구체화 시킨다. 이 말은 현재 내 dependencies에서는 구현체인 hibernate-core만 들어간 상태이며, 부모 클래스가 없는, 이는 곧 추상 클래스가 없는 상태인 것이다. 이 말은 곧 Spring Boot 자체가 없던 것이다. 이에 따라 아래처럼 Spring Boot JPA 를 넣어주면서 해결이 되었다.
dependencies {
// MySQL
implementation 'mysql:mysql-connector-java:8.0.28'
// JPA 구현체인 hibernate
implementation 'org.hibernate:hibernate-core:6.1.7.Final'
// JPA 구현체의 추상 클래스, 부모인 Spring Boot
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// JDBC hibernate 주입시켜서 필요없어짐
//implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
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'
}
Java
복사
이것은 내가 프로젝트 생성 할 때, Spring Boot를 주입하면서 생성하지 않아서 잊고 있었던 것으로 파악된다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
멘토님과 너무 좋은 피드백과 소통이 있었고 다시한번 추상클래스와 구현체에 대한 정확한 이해를 한번 더 복습할 수 있었다. 여기서 또한 Java의 기본기가 얼마나 중요하고 Spring Framework를 공부하는데서도 얼마나 기본적인 바탕이 깔려 있는지 체험하게 되었다.
더하여 DI와 IoC에 대한 결합과 관련된 내용도 멘토님께 내가 설명해보면서 그 내용에 대한 설명해보는 연습을 통해 한번 더 정확하게 정리하는 시간을 가질 수 있었고, 부정확한 부분에 대해서 멘토님의 쉬운 피드백을 통해 더 정확한 내용이 보강되어 정확히 내 지식 범위로 들어오고 있음을 느낄 수 있었다.