带有子类和泛型的双向多对一 [英] Bidirectional many-to-one with subclasses and generics

查看:84
本文介绍了带有子类和泛型的双向多对一的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在为实现两个类层次结构之间的双向多对一映射而苦苦挣扎.

I'm struggling a bit to implement a bi-directional many-to-one mapping between two class hierachies.

我有以下内容:

超类Queue,带有子类AQueueBQueueCQueue. 超类Element,带有子类AElementBElementCElement.

Superclass Queue, with subclasses AQueue, BQueue, CQueue. Superclass Element, with subclasses AElement, BElement, CElement.

AQueue具有AElement的列表,BQueue具有BElement的列表,依此类推,AElement具有AQueue的列表,依此类推.

AQueue has a list of AElement, BQueue has a list of BElement, and so on, AElement has a AQueue and so on.

我尝试过这样的事情:

@Entity
@Inheritance(strategy InheritanceType.SINGLE_TABLE)
public abstract class Queue<T extends Element> {
    @OneToMany(mappedBy="queue")
    private List<T> elements = new ArrayList<>();
    //...
}

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Element<T extends Element> {
    @ManyToOne
    @JoinColumn("queue_id")
    private Queue<T> queue;
}

只能让Hibernate抱怨mappedBy reference an unknown target entity property.

是否有一种方法可以使用泛型在超类中映射这种关系,还是我必须选择每个子类对都保持一个关系?

Is there a way to map such a relationship in the superclasses using generics, or do I have to opt for each of the subclass pairs maintaining one relationship each?

推荐答案

以下是对我有用的Spring Boot测试(跳过样板)(Hibernate 5.0):

Here's a Spring Boot test (to skip the boilerplate) that works for me (Hibernate 5.0):

import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.transaction.Transactional;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.*;

@SpringBootTest
@RunWith(SpringRunner.class)
public class JpaTest {

    @Autowired
    private EntityManager em;

    @Test
    @Transactional
    public void test() {
        QueueA queueA = new QueueA(1L);
        ElementA elementA = new ElementA(1L, queueA);
        queueA.getElements().add(elementA);
        em.persist(queueA);
        em.persist(elementA);

        QueueB queueB = new QueueB(2L);
        ElementB elementB = new ElementB(2L, queueB);
        queueB.getElements().add(elementB);
        em.persist(queueB);
        em.persist(elementB);

        List queues = em.createQuery("SELECT q FROM Queue q").getResultList();

        assertThat(queues).containsOnly(queueA, queueB);

        List elements = em.createQuery("SELECT e FROM Element e").getResultList();

        assertThat(elements).containsOnly(elementA, elementB);
    }
}

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
abstract class Queue<T extends Element<T>> {

    @Id
    private Long id;

    @OneToMany(mappedBy = "queue", targetEntity = Element.class)
    private List<T> elements = new ArrayList<>();

    public Queue(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public List<T> getElements() {
        return elements;
    }

    public void setElements(List<T> elements) {
        this.elements = elements;
    }
}

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
abstract class Element<T extends Element<T>> {

    @Id
    private Long id;

    @ManyToOne(targetEntity = Queue.class)
    private Queue<T> queue;

    public Element(Long id, Queue<T> queue) {
        this.id = id;
        this.queue = queue;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Queue<T> getQueue() {
        return queue;
    }

    public void setQueue(Queue<T> queue) {
        this.queue = queue;
    }
}

@Entity
class QueueA extends Queue<ElementA> {

    public QueueA(Long id) {
        super(id);
    }
}

@Entity
class ElementA extends Element<ElementA> {

    public ElementA(Long id, Queue<ElementA> queue) {
        super(id, queue);
    }
}

@Entity
class QueueB extends Queue<ElementB> {

    public QueueB(Long id) {
        super(id);
    }
}

@Entity
class ElementB extends Element<ElementB> {

    public ElementB(Long id, Queue<ElementB> queue) {
        super(id, queue);
    }
}

要修复的重要问题是:

  • 使所有泛型正确并相互正确引用
  • @OneToMany/@ManyToOne关系中设置targetEntity.否则,Hibernate不能仅从泛型字段中确定确定实际引用的类是什么.在这里,通过指定targetEntity = Element.class/targetEntity = Queue.class表示我们希望Hibernate将其映射到整个实体类层次结构.
  • Make all the generics correct and correctly reference each other
  • Set targetEntity in the @OneToMany/@ManyToOne relationships. Otherwise Hibernate cannot determine just from the generic field for sure what should be the actual referenced class. Here, by specifying targetEntity = Element.class/targetEntity = Queue.class we're saying that we want Hibernate to map it to the whole entity class hierarchy.

这篇关于带有子类和泛型的双向多对一的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆