JPA много для многих с дополнительной колонкой
У меня есть следующая проблема, которую мне нужно решить.
Основная проблема заключается в том, что я хочу добавить дополнительную колонку в отношение JoinTable для ManyToMany в JPA. В моем случае у меня есть следующие сущности.
Тема - это простой объект, который имеет много RemoteDocument (один RemoteDocument может ссылаться на многие темы, следовательно, это должно быть отношение ManyToMany). Объект RemoteDocument читается только потому, что он может быть прочитан только из Oracle Materialized View, но любое изменение этого материализованного представления запрещено. Поэтому я хочу сохранить заказ RemoteDocuments, связанный с какой-то темой. На самом деле я могу сделать что-то подобное с дополнительным объектом:
@Entity
public class Topic {
@Id
private Long id;
@Basic
private String name;
@OneToMany
private Set<TopicToRemoteDocument> association;
}
@Entity
public class RemoteDocument {
@Id
private Long id;
@Basic
private String description;
}
@Entity
public class TopicToRemoteDocument {
@OneToOne
private Topic topic;
@OneToOne
private RemoteDocument remoteDocument;
@Basic
private Integer order;
}
В этом случае дополнительный объект TopicToRemoteDocument помогает мне заменить ассоциацию ManyToMany с OneToMany и добавить дополнительный порядок полей.
Но я хочу иметь отношение ManyToMany, но с настроенным дополнительным столбцом в таблице соединений
Ответы
Ответ 1
Использовать список вместо set вместе с аннотацией @OrderColumn
и JPA автоматически позаботится о порядке:
@MappedSuperclass
public class BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
public Long getId(){
return id;
}
public void setId(final Long id){
this.id = id;
}
}
@Entity
public class Topic extends BaseEntity{
@ManyToMany(mappedBy = "topics")
@OrderColumn
private List<Document> documents = new ArrayList<Document>();
public List<Document> getDocuments(){
return documents;
}
public void setDocuments(final List<Document> documents){
this.documents = documents;
}
}
@Entity
public class Document extends BaseEntity{
@ManyToMany
@OrderColumn
private List<Topic> topics = new ArrayList<Topic>();
public List<Topic> getTopics(){
return topics;
}
public void setTopics(final List<Topic> topics){
this.topics = topics;
}
}
Сгенерированный DDL (с использованием гибернации и HSQL):
create table Document (
id bigint generated by default as identity (start with 1),
primary key (id)
);
create table Document_Topic (
documents_id bigint not null,
topics_id bigint not null,
topics_ORDER integer not null,
documents_ORDER integer not null,
primary key (documents_id, topics_ORDER)
);
create table Topic (
id bigint generated by default as identity (start with 1),
primary key (id)
);
alter table Document_Topic
add constraint FK343B5D0B481100B2
foreign key (documents_id)
references Document;
alter table Document_Topic
add constraint FK343B5D0B558627D0
foreign key (topics_id)
references Topic;
Ответ 2
Я бы постарался избежать использования List
, если вы не разрешаете дубликаты.
Существует аннотация @OrderColumn
, которая автоматически делает это. Вы пробовали это?
@Entity
public class Topic {
@Id
private Long id;
@Basic
private String name;
@OneToMany
@OrderColumn
private Set<TopicToRemoteDocument> association;
}
Ответ 3
Один метод, который полезен при создании объекта класса сопоставления много-ко многим, заключается в атрибуте id в классе вместе с обозначением @ManyToOne, который делает этот класс действительным как составной класс ключей:
@Entity
@Table(name = "market_vendor")
public class MarketVendor implements Serializable
{
@Id
@ManyToOne
@JoinColumn(name = "market_id")
private Market market;
@Id
@ManyToOne
@JoinColumn(name = "vendor_id")
private Vendor vendor;
@Basic
@Column(name="active")
private boolean active;
public MarketVendor(Market market, Vendor vendor, boolean active)
{
this.market = market;
this.vendor = vendor;
this.active = active;
}
}
Это позволяет вам иметь составной первичный ключ, определенный в одном классе, без необходимости иметь отдельный класс первичного ключа. Вы также должны сделать класс сериализуемым.