Mapping class in jmix

I have a requirement like I need to create an entity with references of other two entities;

Eg : I have a client entity and a Language entity
Now i need to create a ClientLanguage entity to save which all languages that are assigned to a specific client. So new entity should have a composite primary key with client id and language id. I followed usual jpa steps like creating an idClass with overiding equals and hashcode and annotated new mapping class with @IdClass(ClientLanguageId.class) but it throwing More than one @Id field in com.package.ClientLanguage: ClientLanguage#client, ClientLanguage#language

I planned like creating a multiselect list in language page to select client so that i can create ClientLanguage objects with language id and selected client ids and can save to new table that is going to be created

Acutally this is not only related to Language but i need to do same for about 20 objects like ClientRegion, ClientCountry, etc.
Basically how to handle composite key in jmix is the issue

What is the most good way to handle this situation in jmix

In general, you don’t necessarily have to use composite keys in your situation.

You can use just a many-to-many attribute which automatically manages a link table, or, if you need additional attributes in the link table, create a regular entity with a UUID primary key and two foreign keys, plus a unique index on both FKs.

But if you decide to use composite primary key, do it as follows.

Suppose you have User and Language entities, both with UUID primary keys.

  1. Create an Embeddable entity with two attributes of UUID type:

    @JmixEntity
    @Embeddable
    public class UserLangKey {
        @Column(name = "USER_ID", nullable = false)
        @NotNull
        private UUID user;
    
        @Column(name = "LANGUAGE_ID", nullable = false)
        @NotNull
        private UUID language;
    
        public UUID getLanguage() {
            return language;
        }
    
        public void setLanguage(UUID language) {
            this.language = language;
        }
    
        public UUID getUser() {
            return user;
        }
    
        public void setUser(UUID userId) {
            this.user = userId;
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(language, user);
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || ProxyUtils.getUserClass(this) != ProxyUtils.getUserClass(o)) return false;
            UserLangKey entity = (UserLangKey) o;
            return Objects.equals(this.language, entity.language) &&
                    Objects.equals(this.user, entity.user);
        }
    }
    
  2. Create the link entity with the embedded id, add many-to-one attributes and annotate them with @MapsId:

@JmixEntity
@Table(name = "USER_LANG", indexes = {
        @Index(name = "IDX_USER_LANG_USER", columnList = "USER_ID"),
        @Index(name = "IDX_USER_LANG_LANGUAGE", columnList = "LANGUAGE_ID")
})
@Entity
public class UserLang {
    @EmbeddedId
    @AttributeOverrides({
            @AttributeOverride(name = "user", column = @Column(name = "USER_ID")),
            @AttributeOverride(name = "language", column = @Column(name = "LANGUAGE_ID"))
    })
    private UserLangKey id;

    @MapsId
    @JoinColumn(name = "USER_ID", nullable = false)
    @NotNull
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private User user;

    @MapsId
    @JoinColumn(name = "LANGUAGE_ID", nullable = false)
    @NotNull
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Language language;

// other attributes, getters and setters
1 Like