🌿 기본 환경: IDE: STS4, Language: Java

 

 

JPA entity로 생성한 테이블에 trigger를 통해 데이터를 INSERT하고자 할 경우,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DELIMITER //
 
CREATE TRIGGER tempSeqTrigger BEFORE INSERT ON tempUser
FOR EACH ROW
BEGIN
  DECLARE sequence_value INT;
  
  INSERT INTO tempSeq VALUES (NULL);
  SET sequence_value = LAST_INSERT_ID();
  SET NEW.id = CONCAT(NEW.type, LPAD(sequence_value, 6'0'));
END//
 
DELIMITER ;
 
 
 

🚨 다음과 같은 Error 발생

java.sql.SQLIntegrityConstraintViolationException: Column '...' cannot be null

 

 

발생 원인

INSERT 시, JPA에서 setId를 통해 PK값을 주입 전에 trigger가 실행되어 trigger에서 null exception을 발생시킴

* 실행순서 Trigger → JPA insert
트리거에서 NEW.user_id 값을 설정하려고 하지만, JPA는 이전에 생성된 userEntity 객체를 사용하여 저장을 시도하므로 user_id 컬럼이 null인 상태로 반환됨

 

 

해결 방법

@PrePersist를 사용하여 JPA를 통해 userEntity를 저장하기 전에 해당 컬럼에 값을 할당해야 함

1
2
3
4
5
6
    @PrePersist
    public void prePersist() {
        String uuid = UUID.randomUUID().toString();
        this.id = uuid;
    }
 
 
 

 

🌿 기본 환경: IDE: STS4, Language: Java

 

 

JPA에서 Custom ID를 부여하기 위해서는 테이블 작업 이후, 추가적인 과정이 필요

 

MySQL에서는 Sequence를 제공하지 않으므로 그에 대한 대안으로, sequence 테이블을 만들고 trigger와 연동시키는 과정이 필요

1. Sequence table 생성

1
2
3
4
CREATE TABLE tempSeq (
  sequence_value INT AUTO_INCREMENT PRIMARY KEY
);
 
 
 

 

2. Trigger를 선언하여 seq를 사용하는 테이블과 연결

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DELIMITER //
 
CREATE TRIGGER tempSeqTrigger BEFORE INSERT ON tempUser
FOR EACH ROW
BEGIN
  DECLARE sequence_value INT;
  
  INSERT INTO tempSeq VALUES (NULL);
  SET sequence_value = LAST_INSERT_ID();
  SET NEW.id = CONCAT(NEW.type, LPAD(sequence_value, 6'0'));
END//
 
DELIMITER ;
 
 
 

 * CREATE TRIGGER tempSeqTrigger BEFORE INSERT ON tempUser

- tempUser(seq를 사용하고자하는 entity)에 insert하기 전 사용할 trigger 선언

 * DECLARE sequence_value INT

- SEQ 테이블의 seq column값 선언

 * INSERT INTO tempSeq VALUES (NULL)

- SEQ 테이블에 null값 insert

 * SET sequence_value = LAST_INSERT_ID()

- 직전에 삽입된 행의 자동 증가 값(시퀀스 값)을 반환하는 함수

 * SET NEW.id = CONCAT(NEW.type, LPAD(sequence_value, 6, '0'));

- tempUser 테이블의 type과 sequence_value값을 결합

- LPAD: 지정한 길이(6)만큼 값이 부여되지 않을 경우 지정한 값('0')으로 채움

 

3. JPA에서 insert 작업 전 @PrePersist 선언

1
2
3
4
5
6
    @PrePersist
    public void prePersist() {
        String uuid = UUID.randomUUID().toString();
        this.id = uuid;
    }
 
 
 

 * @PrePersist

- Entity가 저장되기 전에 자동으로 호출되며, UUID 값을 생성하고 id 필드에 할당

 

⭐ INSERT 발생 시, @PrePersist 실행 후, Trigger 동작으로 custom id 부여

 

 

 

추가 자료

@PrePersist를 선언해야하는 이유

 

[해결 방법] java.sql.SQLIntegrityConstraintViolationException: Column '...' cannot be null

🌿 기본 환경: IDE: STS4, Language: Java JPA entity로 생성한 테이블에 trigger를 통해 데이터를 INSERT하고자 할 경우, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 DELIMITER // CREATE TRIGGER tempSeqTrigger BEFORE INSERT ON tempUser FOR EACH ROW

hj0216.tistory.com

 

🟦 기본 환경: IDE: IntelliJ, Language: Java

 

1
2
3
4
5
6
7
8
@Repository
public interface TestDAO extends JpaRepository<TestOne, String> {
 
    @Query("SELECT t1, t2 FROM TestOne t1 LEFT JOIN ue.testTwo t2")
    List<Object[]> findAllWithTestOneTwo();
 
}
 
 
 

다음 소스코드를 실행하게 될 경우,

TestTwo entity에 저장된 데이터가 없더라도 null값으로 조회가 가능(LEFT JOIN)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    public List<SampleDTO> testList() {
        List<Object[]> queryResult = testDAO.findAllWithTestOneTwo();
 
        List<SampleDTO> resultList = new ArrayList<>();
 
        for (Object[] result : queryResult) {
            TestOne testOne = (TestOne) result[0];
            TestTwo testTwo = (TestTwo) result[1];
 
            SampleDTO sampleDTO;
            if (userLike == null) {
                sampleDTO = new SampleDTO(testOne);
            } else {
                sampleDTO = new SampleDTO(testOne, testTwo);
            }
            resultList.add(sampleDTO);
 
        }
 
        return resultList;
    }
 
 
 

단, TestTwo의 데이터가 null값으로 넘어올 경우 sampleDTO에 값을 주입하기 위해 TestTwo 내부 필드에 대해 getter를 호출하게 되고 이에 NullPointerException이 발생하므로, null값인 경우에 대한 추가적인 로직이 필요

 

'Java > JPA' 카테고리의 다른 글

[JPA_Basic] Persistence Context 장점  (0) 2023.07.29
[JPA] @PrePersist  (0) 2023.06.25
[SpringBoot_JPA_Basic] JPA 1차 cache  (0) 2023.06.16
[JPA_Basic] JPA persist, find, remove, update  (1) 2023.06.15
[SpringBoot_JPA_1] @PathVariable  (0) 2023.06.03

🟦 기본 환경: IDE: IntelliJ, Language: Java

 

 

SpringBoot의 MainApplication에서 양방향 참조 관계가 있는 Entity의 데이터를 호출할 경우,

🚨 다음과 같은 Error 발생

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)

 

 

발생 원인

Entity 객체간 양방향 참조관계가 있을 경우,  엔티티를 반환하여 JSON으로 변경할 경우, serialize(직렬화) 과정을 거치는데, 이때 객체간 상호 참조를 통해 무한 재귀 발생

 

 

해결 방법

1. 양방향 참조 관계의 필드에 @JsonIgnore 선언

🚨 해당 필드값이 null값으로 세팅되어 JSON으로 출력되지 않음

2. 부모 클래스측에 @JsonManagedReference를, 자식측에 @JsonBackReference를 Annotation에 추가

@JsonManagedReference
: 참조가 되는 앞부분을 의미, 정상적으로 직렬화를 수행
@JsonBackReference
: 참조가 되는 뒷부분을 의미, 직렬화를 수행하지 않음

3. Entity 대신 DTO를 만들어 return

 

 

 

참고 자료

 

[JPA] 스프링부트 Could not write JSON: Infinite recursion 에러 해결

에러 내용 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recur

ahn3330.tistory.com

 

JPA에서 순환참조를 해결하는 방법 | @JsonIgnore, @JsonManagedReference, @JsonBackReference

JPA는 ORM이기 때문에 RDB를 관리하는데 있어서 양방향 참조를 필요로 한다. 물론 필수는 아니지만 Entity는 본질적인 데이터를 표현하는 것이기 때문에 그 관계에 대하여 명세해주는 것이 원칙이라

binarycube.tistory.com

 

20190720 [문제해결] Infinite recursion (StackOverflowError)

JPA 엔티티, JSON 변환 에러 org.springframework.http.converter.HttpMessageNotWritableException Could not write JSON Infinite recursion (StackOverflowError) nested exception is com.fasterxml.jackson.databind.JsonMappingException 원인 JPA 연관관

pasudo123.tistory.com

 

🟦 기본 환경: IDE: IntelliJ, Language: Java

 

 

SpringBoot의 MainApplication에서 다음 Source Code를 실행할 경우,

1
2
3
4
5
6
7
@GetMapping("/")
    public List<Item> list() {
        List<Item> items = itemStoryService.itemList();
        System.out.println(items[0])
        return items;
    }
 
 
 

🚨 다음과 같은 Error 발생

java: array required, but java.util.List<> found

 

 

발생 원인

List data type에서 인덱스로 직접 접근

 

 

해결 방법

List는 인덱스로 직접 접근하는 것이 아니라 get() 메서드를 사용하여 요소에 접근

1
2
3
4
5
6
7
@GetMapping("/")
    public List<Item> list() {
        List<Item> items = itemStoryService.itemList();
        System.out.println(items.get(0))
        return items;
    }