본문 바로가기

프로그래밍

jpa entity와 관련된 문제들


Cascade, fetch


@ManyToMany(cascade={CascadeType.ALL}, fetch = FetchType.EAGER)

데이터가 변경될때 변경의 방법에 대한 범위의 제한을 cascade를 통해 설정가능하다.

다음과 같은 cascade 옵션이 존재한다.

  • CascadeType.RESIST – 엔티티를 생성하고, 연관 엔티티를 추가하였을 때 persist() 를 수행하면 연관 엔티티도 함께 persist()가 수행된다.  만약 연관 엔티티가 DB에 등록된 키값을 가지고 있다면 detached entity passed to persist Exception이 발생한다.
  • CascadeType.MERGE – 트랜잭션이 종료되고 detach 상태에서 연관 엔티티를 추가하거나 변경된 이후에 부모 엔티티가 merge()를 수행하게 되면 변경사항이 적용된다.(연관 엔티티의 추가 및 수정 모두 반영됨)
  • CascadeType.REMOVE – 삭제 시 연관된 엔티티도 같이 삭제됨
  • CascadeType.DETACH – 부모 엔티티가 detach()를 수행하게 되면, 연관된 엔티티도 detach() 상태가 되어 변경사항이 반영되지 않는다.
  • CascadeType.ALL – 모든 Cascade 적용

데이터를 받아올때 fetch를 통해 데이터 적재 시점을 선택가능하다.

eager인 경우 즉시, lazy인 경우 필요한 시점에 데이터를 적재한다.



직렬화될때 무한 참조의 문제



먼저 이 문제의 대부분은 게으름 또는 서투름에 있을 수 있다고 생각된다.

나는 jpa entity를 생성할때 Lombok의 @Data annotation을 사용하는데, 이 경우 보다 많은 문제가 생기는 것 같다.

하지만 lombok의 편의성을 버리는 것 또한 그다지 마음에 들지 않아 다른 해결 방법을 찾는 쪽을 선택하게 되었다.


모델을 작성할때 편의에 따라 모델을 상호참조하게 하는 경우가 있는데, 이 경우 json으로 response를 보낼때 무한 참조를 하게 된다.

두 가지 방법으로 위의 문제를 해결할 수 있고 각각의 방법마다 장단점이 있다.


1. @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")


모델에 위의 annotation을 붙여주는 것으로 두번째 참조를 하는 경우 id만을 반환하도록 만들어준다.

또, @Data의 경우 Lombok이 필드 데이터를 강제 생성하게 되므로 

@EqualsAndHashCode(exclude={"a","b"})

@ToString(exclude={"a","b"})

를 같이 사용하여 필드를 자동생성하는 문제의 발생을 회피하는 것도 생각해 보도록 하자


2. @JsonSerialize(using = CustomSerializer.class)
직접 데이터를 직렬화하는 Serializer를 구현하여 문제를 해결한다.
단순히 데이터를 보내지 않도록 하기 위해서는 @JsonIgnore를 이용하여 해당 컬럼을 블럭할 수 있으니 Serializer를 구현할 필요까지는 없다.
Serializer의 구현은 데이터를 스스로 가공하는 방법을 구현할수 있는 강력한 수단이므로 경우에 따라 적절한 방법을 택하도록 하자.


Serializer의 구현은 다음을 참조하자

public class CustomSerializer  extends JsonSerializer<Customer> {

    @Override

    public void serialize(Customer value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {


        gen.writeStartObject();

        gen.writeNumberField("id", value.getId());

        gen.writeStringField("name", value.getName());

        gen.writeEndObject();

  }

}



또한 @JsonInclude(JsonInclude.Include.NON_NULL)을 통해 null인 property는 제외하고 가공할 수 있다.

이 경우 javascript에서는 undefined로 해당 필드가 있는지 확인해야 한다.




컬럼 데이터 유형


@Column(columnDefinition = "BLOB")

String으로 선언된 컬럼은 varchar(255)를 기본으로 데이터베이스의 필드로 생성된다.

해당 컬럼의 유형을 지정하고 싶을때 columnDenifition을 설정한다.




List 컬럼 정렬


@OrderColumn(name = "order")

List형 데이터는 jpa에서 순서를 가지고 있는 데이터가 아니기 때문에 순서를 가져야 하는 경우 OrderColumn을 지정하여 순서를 지정할 수 있다.




@EmbeddedId


primary key가 두개 이상의 컬럼인 경우, @EmbeddedId를 사용하여 이를 해결할 수 있다,


public class MultiId implements Serializable {

    private static final long serialVersionUID = 1L;

    private int id;

    private Double uu;

}



@EmbeddedId

    private MultiId multiId;