一对多问题(非主键关联),列表显示错误

A onetomany B,B有id,code,name,group,language,A 的b字段指向了B的code,列表显示错误b的名称错误(由于对应group下的code才是正确的)。
请问这种情况是需要联合主键吗?该如何使用?

你好,能举个例子吗?

xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://jmix.io/schema/ui/window"
        xmlns:c="http://jmix.io/schema/ui/jpql-condition"
        caption="msg://AEntityBrowse.caption"
        focusComponent="aEntitiesTable">
    <data readOnly="true">
        <collection id="aEntitiesDc"
                    class="com.company.jmixpm.entity.AEntity">
            <fetchPlan extends="_base">
                <property name="tagDict" fetchPlan="_base"/>
                <property name="typeDict" fetchPlan="_base"/>
                <property name="language" fetchPlan="_base"/>
            </fetchPlan>
            <loader id="aEntitiesDl">
                <query>
                    <![CDATA[select e from AEntity e]]>
                </query>
            </loader>
        </collection>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
        <screenSettings id="settingsFacet" auto="true"/>
    </facets>
    <actions>
        <action id="lookupSelectAction"
                caption="msg:///actions.Select"
                icon="LOOKUP_OK"
                primary="true"
                shortcut="${COMMIT_SHORTCUT}"/>
        <action id="lookupCancelAction"
                caption="msg:///actions.Cancel"
                icon="LOOKUP_CANCEL"/>
    </actions>
    <dialogMode height="600"
                width="800"/>
    <layout expand="aEntitiesTable" spacing="true">
        <filter id="filter"
                dataLoader="aEntitiesDl">
            <properties include=".*"/>
        </filter>
        <groupTable id="aEntitiesTable"
                    width="100%"
                    dataContainer="aEntitiesDc">
            <actions>
                <action id="create" type="create"/>
                <action id="edit" type="edit"/>
                <action id="remove" type="remove"/>
            </actions>
            <columns>
                <column id="code"/>
                <column id="name"/>
                <column id="tagDict"/>
                <column id="typeDict"/>
                <column id="language"/>
            </columns>
            <simplePagination/>
            <buttonsPanel id="buttonsPanel"
                          alwaysVisible="true">
                <button id="createBtn" action="aEntitiesTable.create"/>
                <button id="editBtn" action="aEntitiesTable.edit"/>
                <button id="removeBtn" action="aEntitiesTable.remove"/>
            </buttonsPanel>
        </groupTable>
        <hbox id="lookupActions" spacing="true" visible="false">
            <button action="lookupSelectAction"/>
            <button action="lookupCancelAction"/>
        </hbox>
    </layout>
</window>

A entity:

package com.company.jmixpm.entity;

import io.jmix.core.DeletePolicy;
import io.jmix.core.entity.annotation.JmixGeneratedValue;
import io.jmix.core.entity.annotation.OnDeleteInverse;
import io.jmix.core.metamodel.annotation.JmixEntity;

import javax.persistence.*;
import java.util.UUID;

@JmixEntity
@Table(name = "A_ENTITY")
@Entity
public class AEntity {
    @JmixGeneratedValue
    @Column(name = "ID", nullable = false)
    @Id
    private UUID id;

    @Column(name = "CODE", nullable = false)
    private String code;


    @Column(name = "NAME", nullable = false)
    private String name;

    @OnDeleteInverse(DeletePolicy.DENY)
    @JoinColumn(name = "TAG", referencedColumnName = "code")
    @ManyToOne(fetch = FetchType.LAZY)
    private DictEntity  tagDict;

    @OnDeleteInverse(DeletePolicy.DENY)
    @JoinColumn(name = "TYPE", referencedColumnName = "code")
    @ManyToOne(fetch = FetchType.LAZY)
    private DictEntity  typeDict;

    @OnDeleteInverse(DeletePolicy.DENY)
    @JoinColumn(name = "LG", referencedColumnName = "code")
    @ManyToOne(fetch = FetchType.LAZY)
    private LanguageEntity language;

    public UUID getId() {
        return id;
    }

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

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LanguageEntity getLanguage() {
        return language;
    }

    public void setLanguage(LanguageEntity language) {
        this.language = language;
    }

    public DictEntity getTagDict() {
        return tagDict;
    }

    public void setTagDict(DictEntity tagDict) {
        this.tagDict = tagDict;
    }

    public DictEntity getTypeDict() {
        return typeDict;
    }

    public void setTypeDict(DictEntity typeDict) {
        this.typeDict = typeDict;
    }
}

Dict entity:

package com.company.jmixpm.entity;

import io.jmix.core.DeletePolicy;
import io.jmix.core.entity.annotation.JmixGeneratedValue;
import io.jmix.core.entity.annotation.OnDeleteInverse;
import io.jmix.core.metamodel.annotation.JmixEntity;

import javax.persistence.*;
import java.util.UUID;

@JmixEntity
@Table(name = "DICT_ENTITY", uniqueConstraints = {
        @UniqueConstraint(name = "IDX_DICT_UNQ", columnNames = {"code"})
})
@Entity
public class DictEntity {
    @JmixGeneratedValue
    @Column(name = "ID", nullable = false)
    @Id
    private UUID id;

    @Column(name = "CODE", nullable = false)
    private String code;


    @Column(name = "NAME", nullable = false)
    private String name;

    @Column(name = "GROUP", nullable = false)
    private String group;

    @OnDeleteInverse(DeletePolicy.DENY)
    @JoinColumn(name = "LG", referencedColumnName = "code")
    @ManyToOne(fetch = FetchType.LAZY)
    private LanguageEntity languageEntity;

    public UUID getId() {
        return id;
    }

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

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGroup() {
        return group;
    }

    public void setGroup(String group) {
        this.group = group;
    }

    public LanguageEntity getLanguageEntity() {
        return languageEntity;
    }

    public void setLanguageEntity(LanguageEntity languageEntity) {
        this.languageEntity = languageEntity;
    }
}

lgentity:

package com.company.jmixpm.entity;

import io.jmix.core.entity.annotation.JmixGeneratedValue;
import io.jmix.core.metamodel.annotation.JmixEntity;

import javax.persistence.*;
import java.util.UUID;

@JmixEntity
@Table(name = "LANGUAGE_ENTITY", uniqueConstraints = {
        @UniqueConstraint(name = "IDX_LANGUAGE_UNQ", columnNames = {"code"})
})
@Entity
public class LanguageEntity {
    @JmixGeneratedValue
    @Column(name = "ID", nullable = false)
    @Id
    private UUID id;

    @Column(name = "CODE", nullable = false)
    private String code;

    @Column(name = "NAME", nullable = false)
    private String name;

    public UUID getId() {
        return id;
    }

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

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

PS:

  1. 列表语言可以根据A的code显示对应的lg 的name
  2. dictTag和typeDict列表显示的不正确,因为a存的dict code在dictentity种不唯一(例如A的TAG字段存的test,在dictEntity表中code=test的有两条不同的数据,他们的group不同,因此我需要在锁定一下group才能让其匹配正确的name)

请问这个问题有什么方案吗?

你好,这里的DictEntity的主键并不是code,而是code+group的组合主键是吧?

主键有id,但是用的是code,code+group+lg才能确定唯一;因为要兼容老数据,所以看时候有方案处理

那怎么判断AEntity里面的 tagDict 是哪个group,哪个lg呢?

业务层面有固定的值,比如我开发当前这个功能就取group=‘A’,lg当前系统语言的。
目前就是不知道这个条件该如何加

好的,如果只是列表展示问题的话,可以用 ColumnGenerator 实现的。

这个应该是可以的,但是就意味着界面加载又加了很多单独查表的,感觉很不友好。一对多的entity这块是否能配置来处理这个问题?

这个没有办法的,因为在A实体内存的code对应多个dict实体。只能通过再一次查询确认具体对应哪一个。

如果担心效率问题,可以用 实体和查询的缓存 :: Jmix 文档 ,另外,如果dict实体的数量相对稳定,也可以自己写service在内存管理,提高查询速度。

发现不仅列表,编辑时候下拉选项也是匹配不上。
由于本质上列表查询,该字段已经赋予了框架查的字典对象,它已经有了id,code,group。在下拉选项中,找不到对用的option

只能在onAfterShow中,重新赋予一对多对象值