Jmix 2.1.0 Entity Relation Composition missing related Uuid

Hi,
I’m using Jmix 2.1 and I had created a composition relationship, using the wizard Spesa has many RigaSpesa

When I create a RigaSpesa from Spese detail-view the new RigaSpesa entity has no reference to Spesa, so Spesa_id is empty.

But is the same also for other composition relationship in the model

Spesa:

package com.company.cantieri.entity;

import io.jmix.core.MetadataTools;
import io.jmix.core.entity.annotation.JmixGeneratedValue;
import io.jmix.core.metamodel.annotation.Composition;
import io.jmix.core.metamodel.annotation.DependsOnProperties;
import io.jmix.core.metamodel.annotation.InstanceName;
import io.jmix.core.metamodel.annotation.JmixEntity;
import io.jmix.core.metamodel.datatype.DatatypeFormatter;
import jakarta.persistence.*;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;

import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@JmixEntity
@Table(name = "SPESA", indexes = {
        @Index(name = "IDX_SPESA_UTENTE", columnList = "UTENTE_ID"),
        @Index(name = "IDX_SPESA_CANTIERE", columnList = "CANTIERE_ID")
})
@Entity
public class Spesa {
    @JmixGeneratedValue
    @Column(name = "ID", nullable = false)
    @Id
    private UUID id;

    @Column(name = "DATA_")
    private LocalDate data;

    @JoinColumn(name = "UTENTE_ID")
    @ManyToOne(fetch = FetchType.LAZY)
    private User utente;

    @JoinColumn(name = "CANTIERE_ID")
    @ManyToOne(fetch = FetchType.LAZY)
    private Cantiere cantiere;

    @Composition
    @OneToMany(mappedBy = "spesa")
    private List<RigaSpesa> rigaSpesa;

    public Cantiere getCantiere() {
        return cantiere;
    }

    public void setCantiere(Cantiere cantiere) {
        this.cantiere = cantiere;
    }

    public User getUtente() {
        return utente;
    }

    public void setUtente(User utente) {
        this.utente = utente;
    }

    public LocalDate getData() {
        return data;
    }

    public void setData(LocalDate data) {
        this.data = data;
    }

    public UUID getId() {
        return id;
    }

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

    @CreatedBy
    @Column(name = "CREATED_BY")
    private String createdBy;

    @CreatedDate
    @Temporal(TemporalType.DATE)
    @Column(name = "CREATED_DATE")
    private Date createdDate;

    @LastModifiedBy
    @Column(name = "LAST_MODIFIED_BY")
    private String lastModifiedBy;

    @LastModifiedDate
    @Temporal(TemporalType.DATE)
    @Column(name = "LAST_MODIFIED_DATE")
    private Date lastModifiedDate;

    public List<RigaSpesa> getRigaSpesa() {
        return rigaSpesa;
    }

    public void setRigaSpesa(List<RigaSpesa> rigaSpesa) {
        this.rigaSpesa = rigaSpesa;
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public String getLastModifiedBy() {
        return lastModifiedBy;
    }

    public Date getLastModifiedDate() {
        return lastModifiedDate;
    }

    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public void setLastModifiedBy(String lastModifiedBy) {
        this.lastModifiedBy = lastModifiedBy;
    }

    public void setLastModifiedDate(Date lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }

    @InstanceName
    @DependsOnProperties({"utente", "data"})
    public String getInstanceName(MetadataTools metadataTools, DatatypeFormatter datatypeFormatter) {
        return String.format("%s - %s",
                metadataTools.format(utente),
                datatypeFormatter.formatLocalDate(data));
    }
}

RigaSpesa

package com.company.cantieri.entity;

import io.jmix.core.DeletePolicy;
import io.jmix.core.FileRef;
import io.jmix.core.entity.annotation.JmixGeneratedValue;
import io.jmix.core.entity.annotation.OnDeleteInverse;
import io.jmix.core.metamodel.annotation.JmixEntity;
import jakarta.persistence.*;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;

import java.time.OffsetDateTime;
import java.util.UUID;

@JmixEntity
@Table(name = "RIGA_SPESA", indexes = {
        @Index(name = "IDX_RIGA_SPESA_SPESA", columnList = "SPESA_ID")
})
@Entity
public class RigaSpesa {
    @JmixGeneratedValue
    @Column(name = "ID", nullable = false)
    @Id
    private UUID id;

    @Column(name = "IMPORTO")
    private Double importo;

    @Column(name = "PERSONE")
    private Double persone;

    @Column(name = "ALLEGATO", length = 1024)
    private FileRef allegato;

    @Column(name = "NOTE")
    @Lob
    private String note;

    @CreatedBy
    @Column(name = "CREATED_BY")
    private String createdBy;

    @CreatedDate
    @Column(name = "CREATED_DATE")
    private OffsetDateTime createdDate;

    @LastModifiedBy
    @Column(name = "LAST_MODIFIED_BY")
    private String lastModifiedBy;

    @LastModifiedDate
    @Column(name = "LAST_MODIFIED_DATE")
    private OffsetDateTime lastModifiedDate;

    @OnDeleteInverse(DeletePolicy.CASCADE)
    @JoinColumn(name = "SPESA_ID")
    @ManyToOne(fetch = FetchType.LAZY)
    private Spesa spesa;

    public Spesa getSpesa() {
        return spesa;
    }

    public void setSpesa(Spesa spesa) {
        this.spesa = spesa;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    public FileRef getAllegato() {
        return allegato;
    }

    public void setAllegato(FileRef allegato) {
        this.allegato = allegato;
    }

    public Double getPersone() {
        return persone;
    }

    public void setPersone(Double persone) {
        this.persone = persone;
    }

    public Double getImporto() {
        return importo;
    }

    public void setImporto(Double importo) {
        this.importo = importo;
    }

    public OffsetDateTime getLastModifiedDate() {
        return lastModifiedDate;
    }

    public void setLastModifiedDate(OffsetDateTime lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }

    public String getLastModifiedBy() {
        return lastModifiedBy;
    }

    public void setLastModifiedBy(String lastModifiedBy) {
        this.lastModifiedBy = lastModifiedBy;
    }

    public OffsetDateTime getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(OffsetDateTime createdDate) {
        this.createdDate = createdDate;
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

    public UUID getId() {
        return id;
    }

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

Spesa detail-view:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<view xmlns="http://jmix.io/schema/flowui/view"
      title="msg://spesaDetailView.title"
      focusComponent="form">
    <data>
        <collection id="cantieresDc" class="com.company.cantieri.entity.Cantiere">
            <fetchPlan extends="_base"/>
            <loader id="cantieresDl" readOnly="true">
                <query>
                    <![CDATA[select e from Cantiere e where e.attivo=true]]>
                </query>
            </loader>
        </collection>
        <instance id="spesaDc"
                  class="com.company.cantieri.entity.Spesa">
            <fetchPlan extends="_base">
                <property name="utente" fetchPlan="_base"/>
                <property name="cantiere" fetchPlan="_base">
                </property>
                <property name="rigaSpesa" fetchPlan="_base"/>
            </fetchPlan>
            <loader/>
            <collection id="rigaSpesaDc" property="rigaSpesa"/>
        </instance>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
    </facets>
    <actions>
        <action id="saveAction" type="detail_saveClose"/>
        <action id="closeAction" type="detail_close"/>
    </actions>
    <layout>
        <formLayout id="form" dataContainer="spesaDc">
            <datePicker id="dataField" property="data"/>
            <entityPicker id="utenteField" property="utente">
                <actions>
                    <action id="entityLookup" type="entity_lookup"/>
                    <action id="entityClear" type="entity_clear"/>
                </actions>
            </entityPicker>
            <entityComboBox id="cantiereField" property="cantiere" itemsContainer="cantieresDc"/>
            <datePicker id="createdDateField" property="createdDate"/>
            <datePicker id="lastModifiedDateField" property="lastModifiedDate"/>
            <textField id="uidField" property="id"/>
        </formLayout>
        <hbox id="buttonsPanel" classNames="buttons-panel">
            <button action="rigaSpesaDataGrid.create"/>
            <button action="rigaSpesaDataGrid.edit"/>
            <button action="rigaSpesaDataGrid.remove"/>
        </hbox>
        <dataGrid id="rigaSpesaDataGrid" dataContainer="rigaSpesaDc" width="100%" height="100%">
            <actions>
                <action id="create" type="list_create"/>
                <action id="edit" type="list_edit"/>
                <action id="remove" type="list_remove"/>
            </actions>
            <columns>
                <column property="importo"/>
                <column property="persone"/>
                <column property="allegato"/>
                <column property="note"/>
            </columns>
        </dataGrid>
        <hbox id="detailActions">
            <button id="saveAndCloseBtn" action="saveAction"/>
            <button id="closeBtn" action="closeAction"/>
        </hbox>
    </layout>
</view>

1 Like

Hi,

Try opening the ReigaSpesa details view in a dialog mode. You need to set it for create and edit actions in the rigaSpesaDataGrid:

                <action id="create" type="list_create">
                    <properties>
                        <property name="openMode" value="DIALOG"/>
                    </properties>
                </action>
                <action id="edit" type="list_edit">
                    <properties>
                        <property name="openMode" value="DIALOG"/>
                    </properties>
                </action>
1 Like

Does this have a simple solution without using DIALOG mode?
I believe that it should work with both variant of open mode.

Composition detail views cannot be opened by navigation, because when you return from them to the root details view, it will be recreated and the whole context will be lost.