복붙노트

[SPRING] Hibernate를 통한 엔티티 저장시 org.hibernate.WrongClassException

SPRING

Hibernate를 통한 엔티티 저장시 org.hibernate.WrongClassException

이 질문에서 나는 Hibernate 4.3.4.Final과 Spring ORM 4.1.2.RELEASE로 작업하고있다.

다음과 같이 Set of CardInstances를 보유하는 User 클래스가 있습니다.

@Entity
@Table
public class User implements UserDetails {

    protected List<CardInstance> cards;

    @ManyToMany
    public List<CardInstance> getCards() {
        return cards;
    }

    // setter and other members/methods omitted 
}

@Table
@Entity
@Inheritance
@DiscriminatorColumn(name = "card_type", discriminatorType = DiscriminatorType.STRING)
public abstract class CardInstance<T extends Card> {

    private T card;

    @ManyToOne
    public T getCard() {
        return card;
    }
}

@Table
@Entity
@Inheritance
@DiscriminatorOptions(force = true)
@DiscriminatorColumn(name = "card_type", discriminatorType = DiscriminatorType.STRING)
public abstract class Card {
    // nothing interesting here
}

몇 가지 유형의 카드가 있는데, 각각 카드 기본 클래스와 CardInstance 기본 클래스를 다음과 같이 확장합니다.

@Entity
@DiscriminatorValue("unit")
public class UnitCardInstance extends CardInstance<UnitCard> {
    // all types of CardInstances extend only the CardInstance<T> class
}

@Entity
@DiscriminatorValue("leader")
public class LeaderCardInstance extends CardInstance<LeaderCard> {

}
@Entity
@DiscriminatorValue("unit")
public class UnitCard extends Card {
}

@Entity
@DiscriminatorValue("leader")
public class LeaderCard extends AbilityCard {
}

@Entity
@DiscriminatorValue("hero")
public class HeroCard extends UnitCard {
    // card classes (you could call them the definitions of cards) can
    // extend other types of cards, not only the base class
}

@Entity
@DiscriminatorValue("ability")
public class AbilityCard extends Card {
}

UnitCardInstance 또는 HeroCardInstance를 cards 컬렉션에 추가하고 엔티티를 저장하면 모든 것이 잘 동작합니다. 그러나 컬렉션에 AbilityCardInstance를 추가하고 엔티티를 저장하면 org.hibernate.WrongClassException으로 실패합니다. 게시물 끝에 정확한 예외 + 메시지를 추가했습니다.

몇 가지 질문을 읽었습니다. 기본 클래스의 컬렉션으로 작업하는 동안 게으른로드가 문제가되는 것 같습니다. 여기에 카드를 추가하고 저장하기 전에 User 엔터티를로드하는 방법은 다음과 같습니다.

User user = this.entityManager.createQuery("FROM User u " +
                "WHERE u.id = ?1", User.class)
                .setParameter(1, id)
                .getSingleResult();

        Hibernate.initialize(user.getCards());

        return user;

"카드"에 대한 데이터베이스 항목 "cardinstances"에 대한 데이터베이스 항목

org.hibernate.WrongClassException: Object [id=1] was not of the specified subclass [org.gwentonline.model.cards.UnitCard] : Discriminator: leader

이 문제를 해결하는 방법에 대한 단서를 미리 알려 주셔서 감사합니다. 추가 정보가 필요하면 기꺼이 내 질문을 업데이트 할 것입니다!

해결법

  1. ==============================

    1.@ManyToOne에 대한 JavaDocs의 첫 번째 단락에 따르면 :

    @ManyToOne에 대한 JavaDocs의 첫 번째 단락에 따르면 :

    그러나 귀하의 경우 @ManyToOne은 유형이 generic이고 generic 형식 정보가 컴파일 유형에서 지워집니다. 따라서, deserialize 할 때, Hibernate는 필드의 정확한 타입을 알지 못한다.

    해결 방법은 targetEntity = Card.class를 @ManyToOne에 추가하는 것입니다. Card는 추상적이고 @Inheritance와 @DiscriminatorColumn 어노테이션을 가지고 있기 때문에, Hibernate는 모든 가능한 방법으로 실제 필드 타입을 해석해야한다. 이 작업은 Card 테이블의 판별 자 값을 사용하여 올바른 클래스 인스턴스를 생성합니다. 또한 Java 코드에서 유형 안전성을 유지합니다.

    따라서 일반적으로 런타임에 필드 유형이 완전히 알려지지 않을 때마다 @ManyToOne 및 @OneToMany와 함께 targetEntity를 사용하십시오.

  2. ==============================

    2.나는 그 문제를 해결했다.

    나는 그 문제를 해결했다.

    근본적인 원인은이 디자인에 있습니다.

    @Table
    @Entity
    @Inheritance
    @DiscriminatorColumn(name = "card_type", discriminatorType = DiscriminatorType.STRING)
    public class CardInstance<T extends Card> {  
        protected T card;
    }
    
    @Entity
    @DiscriminatorValue("leader")
    public class LeaderCardInstance extends CardInstance<LeaderCard> {
    }
    

    런타임시 클래스의 제네릭 유형에 대한 정보는 Java에 없습니다. 자세한 내용은이 질문을 참조하십시오. Java generics - type erasure - 언제 어떻게됩니까?

    즉, 최대 절전 모드에서는 CardInstance 클래스의 실제 유형을 판별 할 수 없습니다.

    이것에 대한 해결책은 단순히 generic 형식을 제거하고 모든 클래스를 확장 (구현)하고 다음과 같은 하나의 클래스를 사용하는 것입니다.

    @Table
    @Entity
    @Inheritance
    @DiscriminatorColumn(name = "card_type", discriminatorType = DiscriminatorType.STRING)
    public class CardInstance {
        Card card;
    }
    

    회원 카드가 카드 유형에 대한 모든 정보를 가지고 있기 때문에 가능합니다 (그리고 더 좋은 디자인입니다).

    나는 이것이 같은 문제에 부딪치게되면 사람들에게 도움이되기를 바랍니다.

  3. from https://stackoverflow.com/questions/31257027/org-hibernate-wrongclassexception-on-saving-an-entity-via-hibernate by cc-by-sa and MIT license