I am using a composite key in a composition M:N relationship, but the datagrid behavior is very strange.
Following the steps indicated in this topic I cannot get it to work correctly.
This is an example:
Book
package com.company.compositeprimarykey.entity;
import io.jmix.core.entity.annotation.JmixGeneratedValue;
import io.jmix.core.metamodel.annotation.Composition;
import io.jmix.core.metamodel.annotation.InstanceName;
import io.jmix.core.metamodel.annotation.JmixEntity;
import jakarta.persistence.*;
import java.util.Set;
@JmixEntity
@Table(name = "CPK_BOOK")
@Entity(name = "cpk_Book")
public class Book {
@JmixGeneratedValue
@Column(name = "ID", nullable = false)
@Id
private Long id;
@InstanceName
@Column(name = "TITLE", nullable = false)
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Composition
@OneToMany(mappedBy = "book")
private Set<BookAuthor> comments;
public Set<BookAuthor> getComments() {
return comments;
}
public void setComments(Set<BookAuthor> comments) {
this.comments = comments;
}
}
Author
package com.company.compositeprimarykey.entity;
import io.jmix.core.entity.annotation.JmixGeneratedValue;
import io.jmix.core.metamodel.annotation.InstanceName;
import io.jmix.core.metamodel.annotation.JmixEntity;
import jakarta.persistence.*;
import java.util.Set;
@JmixEntity
@Table(name = "CPK_AUTHOR")
@Entity(name = "cpk_Author")
public class Author {
@JmixGeneratedValue
@Column(name = "ID", nullable = false)
@Id
private Long id;
@InstanceName
@Column(name = "NAME", nullable = false)
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(mappedBy = "author")
private Set<BookAuthor> comments;
public Set<BookAuthor> getComments() {
return comments;
}
public void setComments(Set<BookAuthor> comments) {
this.comments = comments;
}
}
BookAuthorKey
package com.company.compositeprimarykey.entity;
import io.jmix.core.metamodel.annotation.JmixEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotNull;
import java.util.Objects;
@JmixEntity(name = "cpk_BookAuthorKey")
@Embeddable
public class BookAuthorKey {
@Column(name = "BOOK_ID", nullable = false)
@NotNull
private Long bookId;
@Column(name = "AUTHOR_ID", nullable = false)
@NotNull
private Long authorId;
public Long getBookId() {
return bookId;
}
public void setBookId(Long bookId) {
this.bookId = bookId;
}
public Long getAuthorId() {
return authorId;
}
public void setAuthorId(Long authorId) {
this.authorId = authorId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BookAuthorKey that = (BookAuthorKey) o;
if (!Objects.equals(bookId, that.bookId)) return false;
return Objects.equals(authorId, that.authorId);
}
@Override
public int hashCode() {
int result = bookId != null ? bookId.hashCode() : 0;
result = 31 * result + (authorId != null ? authorId.hashCode() : 0);
return result;
}
}
BookAuthor
package com.company.compositeprimarykey.entity;
import io.jmix.core.metamodel.annotation.JmixEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
@JmixEntity
@Table(name = "CPK_BOOK_AUTHOR")
@Entity(name = "cpk_BookAuthor")
public class BookAuthor {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "bookId", column = @Column(name = "BOOK_ID")),
@AttributeOverride(name = "authorId", column = @Column(name = "AUTHOR_ID"))
})
private BookAuthorKey id;
public BookAuthorKey getId() {
return id;
}
public void setId(BookAuthorKey id) {
this.id = id;
}
@MapsId("bookId")
@JoinColumn(name = "BOOK_ID", nullable = false)
@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
private Book book;
@MapsId("authorId")
@JoinColumn(name = "AUTHOR_ID", nullable = false)
@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
private Author author;
@Column(name = "TEXT", nullable = true)
private String text;
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Database Schema
CREATE TABLE CPK_BOOK(
ID BIGINT NOT NULL,
TITLE VARCHAR(255) NOT NULL,
CONSTRAINT CPK_BOOK PRIMARY KEY (ID)
);
CREATE TABLE CPK_AUTHOR(
ID BIGINT NOT NULL,
NAME VARCHAR(255) NOT NULL,
CONSTRAINT CPK_AUTHOR PRIMARY KEY (ID)
);
CREATE TABLE CPK_BOOK_AUTHOR(
BOOK_ID BIGINT NOT NULL,
AUTHOR_ID BIGINT NOT NULL,
TEXT VARCHAR(255),
CONSTRAINT PK_I PRIMARY KEY (BOOK_ID,AUTHOR_ID),
CONSTRAINT FK_BOOKAUTHOR_BOOK FOREIGN KEY (BOOK_ID) REFERENCES CPK_BOOK(ID) ON UPDATE CASCADE ON DELETE NO ACTION,
CONSTRAINT FK_BOOKAUTHOR_AUTHOR FOREIGN KEY (AUTHOR_ID) REFERENCES CPK_AUTHOR(ID) ON UPDATE CASCADE ON DELETE NO ACTION
);
The views have been generated automatically with “IntelliJ IDEA Community”.
I have 3 authors created, A1, A2 and A3. I create a book, B1, and add an author’s text, B1A1. At the moment it works well.
Now I am trying to add another text to book B1. Text B1A2 by author A2. I press the create button and instead of creating it modifies the previous one.
When I save the book and edit it again, the two authors’ texts appear.
Now the create button works. I try to add another text, B1A3, but when I save it does not appear in the grid.
If I save the book and edit it again the new text B1A3 appears.
I’ve tried several ways, even without @Composition, but the result is the same.
Furthermore, if you continue creating, editing or deleting texts, the behavior becomes more strange. For example, several lines appear with the same text.
Does JMIX V2.1.x support composite keys? I am doing something wrong?
Thanks.