Спящий режим и наследование (TABLE_PER_CLASS) - PullRequest
5 голосов
/ 24 августа 2010

Я использую Hibernate для сохранения унаследованных объектов, но я получаю это сообщение при попытке сохранить объекты в базе данных:

org.springframework.dao.InvalidDataAccessResourceUsageException: Could not execute
JDBC batch update; SQL [update Widget set CONTAINER_ID=? where WIDGET_ID=?]; nested 
exception is org.hibernate.exception.SQLGrammarException: 
Could not execute JDBC batch update (...) Caused by: java.sql.BatchUpdateException: Table 
'schema.widget' doesn't exist

Вот классы, которые я использовал для создания таблицы:

@Entity
@Table(name="CONTAINER")
public class Container {
     (...)
     private Set<Widget> widgets;

     @OneToMany(targetEntity = Widget.class)
     @JoinColumn(name="CONTAINER_ID", nullable=true)
     public Set<Widget> getWidgets() {
         return widgets;
     }

     public void setWidgets(Set<Widget> widgets) {
         this.widgets = widgets;
     }
}


@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Widget {
     private long id;
     private int position;

     @Id
     @GeneratedValue(strategy = GenerationType.TABLE)
     @Column(name="WIDGET_ID")
     public long getId() {
         return id;
     }

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

     @Column(name="POSITION")
     public int getPosition() {
         return position;
     }

     public void setPosition(int position) {
         this.position = position;
     }
}


@Entity
@Table(name="TEXT_WIDGET")
public class TextWidget extends Widget {
     (...)
}


@Entity
@Table(name="IMAGE_WIDGET")
public class ImageWidget extends Widget {
     (...)
}

Таким образом, это означает, что Hibernate ищет таблицу «виджет», но она не создана, и это имеет смысл, потому что я выбираю опцию InheritanceType.TABLE_PER_CLASS, тогда только конкретные классы имеют таблицу.В базе данных я вижу таблицы контейнера, text_widget и image_widget.

Затем, когда я пытаюсь выполнить этот код и сохранить контейнер, я получаю вышеуказанную ошибку:

Set<Widget> widgets = new HashSet<Widget>();
widgets.add(textw1); // instance of TextWidget
widgets.add(imgw1); // instance of ImageWidget

Container container1 = new Container();
container1.setWidgets(widgets);

Спасибо за вашепомощь!

1 Ответ

7 голосов
/ 25 августа 2010

Ваша ассоциация должна быть двунаправленной, как указано в документации Hibernate.

2.2.4.1. Таблица на класс

У этой стратегии много недостатков (особенно с полиморфными запросами и ассоциации) объяснил в JPA спецификация, ссылка на Hibernate документация, Hibernate в действии, и много других мест. Спящая работа вокруг большинства из них реализуют это стратегия с использованием запросов SQL UNION. Это обычно используется для верхнего уровня иерархия наследования:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable { ... }            

Эта стратегия поддерживает один ко многим ассоциации при условии, что они Двунаправленный . Эта стратегия делает не поддерживает генератор IDENTITY стратегия: идентификатор должен быть разделен через несколько столов. Как следствие, при использовании этой стратегии, вы должны не используйте ни AUTO, ни IDENTITY.

Таким образом, Hibernate сможет создать соответствующий столбец внешнего ключа в каждой конкретной таблице виджетов. Вот карта, которая на самом деле работает. Для Container:

@Entity
public class Container {
    @Id @GeneratedValue
    private long id;

    @OneToMany(targetEntity = Widget.class, mappedBy = "container", cascade = CascadeType.ALL)
    private Set<Widget> widgets = new HashSet<Widget>();

    public long getId() { return id; }

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

    public Set<Widget> getWidgets() { return widgets; }

    public void setWidgets(Set<Widget> widgets) { this.widgets = widgets; }

    public void addToWidgets(Widget widget) {
        this.getWidgets().add(widget);
        widget.setContainer(this);
    }

    public void removeFromWidgets(Widget widget) {
        this.getWidgets().remove(widget);
        widget.setContainer(null);
    }
}

И абстрактный класс Widget:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Widget {
    @Id @GeneratedValue(strategy = GenerationType.TABLE)
    private long id;

    private int position;

    @ManyToOne
    private Container container;

    public long getId() { return id; }

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

    public int getPosition() { return position; }

    public void setPosition(int position) { this.position = position; }

    public Container getContainer() { return container; }

    public void setContainer(Container container) { this.container = container; }
}

И следующий метод тестирования (запущенный внутри транзакции) просто проходит:

@Test
public void testInsertContainer() {
    TextWidget textw1 = new TextWidget();
    ImageWidget imgw1 = new ImageWidget();

    Container container1 = new Container();
    container1.addToWidgets(textw1); // instance of TextWidget
    container1.addToWidgets(imgw1); // instance of ImageWidget

    session.persist(container1);
    session.flush();

    assertNotNull(textw1.getId());
    assertNotNull(imgw1.getId());
    assertNotNull(container1.getId());
}

И генерирует следующий SQL:

21:59:57.964 [main] DEBUG org.hibernate.SQL - select next_hi from hibernate_unique_key for read only with rs
21:59:57.978 [main] DEBUG org.hibernate.SQL - update hibernate_unique_key set next_hi = ? where next_hi = ?
21:59:58.063 [main] DEBUG org.hibernate.SQL - null
21:59:58.125 [main] DEBUG org.hibernate.SQL - insert into Container (id) values (?)
Hibernate: insert into Container (id) values (?)
21:59:58.140 [main] TRACE org.hibernate.type.LongType - binding '98304' to parameter: 1
21:59:58.145 [main] DEBUG org.hibernate.SQL - insert into ImageWidget (container_id, position, id) values (?, ?, ?)
Hibernate: insert into ImageWidget (container_id, position, id) values (?, ?, ?)
21:59:58.164 [main] TRACE org.hibernate.type.LongType - binding '98304' to parameter: 1
21:59:58.165 [main] TRACE org.hibernate.type.IntegerType - binding '0' to parameter: 2
21:59:58.166 [main] TRACE org.hibernate.type.LongType - binding '32768' to parameter: 3
21:59:58.172 [main] DEBUG org.hibernate.SQL - insert into TextWidget (container_id, position, id) values (?, ?, ?)
Hibernate: insert into TextWidget (container_id, position, id) values (?, ?, ?)
21:59:58.187 [main] TRACE org.hibernate.type.LongType - binding '98304' to parameter: 1
21:59:58.188 [main] TRACE org.hibernate.type.IntegerType - binding '0' to parameter: 2
21:59:58.189 [main] TRACE org.hibernate.type.LongType - binding '32769' to parameter: 3

Но имейте в виду, что стратегия таблицы на класс обеспечивает слабую поддержку полиморфных отношений и может быть не совсем уместной, если у вас много детей Widget (это приведет к в огромном SQL UNION).

Смежный вопрос

...