Problem using association with an entity from app component

Hi,

We’re using:
CUBA Platform version: 7.2.3
CUBA Studio plugin version: 13.2-191
IntelliJ version: CUBA Studio 2019.2
Database : Oracle 12c in Main Data Stores | Oracle 11g in Additional Data Stores

We have a strange problem defining an association with an entity from an app component. Cuba Studio allows defining the corresponding relation without any problem:
CS1

But, runnig the aplication, we get next error as entity ‘portes’ doesn’t exist (Entity ‘portes’ belongs to the app component) :

com.haulmont.cuba.web.sys.remoting.LocalServiceAccessException: Unable to connect to middleware. Middleware block 'arvia-core' failed to start. See exception cause for details.
	at com.haulmont.cuba.web.sys.remoting.LocalServiceProxy$LocalServiceInvocationHandler.createServiceAccessException(LocalServiceProxy.java:193)
	at com.haulmont.cuba.web.sys.remoting.LocalServiceProxy$LocalServiceInvocationHandler.invoke(LocalServiceProxy.java:108)
	at com.sun.proxy.$Proxy30.healthCheck(Unknown Source)
	at com.haulmont.cuba.web.AppUI.init(AppUI.java:330)
	at com.vaadin.ui.UI.doInit(UI.java:738)
	at com.vaadin.server.communication.UIInitHandler.getBrowserDetailsUI(UIInitHandler.java:209)
	at com.vaadin.server.communication.UIInitHandler.synchronizedHandleRequest(UIInitHandler.java:67)
	at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40)
	at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1578)
	at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:425)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.serviceAppRequest(CubaApplicationServlet.java:329)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.service(CubaApplicationServlet.java:215)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108)
	at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74)
	at com.haulmont.cuba.web.sys.CubaHttpFilter.doFilter(CubaHttpFilter.java:93)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodValidationPostProcessor' defined in class path resource [com/haulmont/cuba/spring.xml]: Cannot resolve reference to bean 'cuba_BeanValidation' while setting bean property 'beanValidation'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_BeanValidation': Unsatisfied dependency expressed through field 'messages'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_Messages': Unsatisfied dependency expressed through field 'messageTools'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_MessageTools': Unsatisfied dependency expressed through field 'metadata'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_Metadata': Unsatisfied dependency expressed through field 'tools'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_MetadataTools': Unsatisfied dependency expressed through field 'dynamicAttributesTools'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_DynamicAttributesTools': Unsatisfied dependency expressed through field 'dynamicAttributes'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_DynamicAttributesService': Unsatisfied dependency expressed through field 'dynamicAttributesManagerAPI'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_DynamicAttributesManager': Unsatisfied dependency expressed through field 'persistence'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_Persistence': Unsatisfied dependency expressed through field 'transactions'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cuba_Transactions': Unsatisfied dependency expressed through field 'transactionManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in class path resource [com/haulmont/cuba/spring.xml]: Cannot resolve reference to bean 'entityManagerFactory' while setting bean property 'entityManagerFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [com/haulmont/cuba/spring.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.7.3.6-cuba): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [areavianants] failed.
Internal Exception: Exception [EclipseLink-7250] (Eclipse Persistence Services - 2.7.3.6-cuba): org.eclipse.persistence.exceptions.ValidationException
Exception Description: [class com.company.areavianants.entity.AreaVianants] uses a non-entity [class com.company.territori.entity.Portes] as target entity in the relationship attribute [field porta].
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1699)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1444)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:228)
	at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:722)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:535)
	at org.springframework.context.support.AbstractRefreshableConfigApplicationContext.afterPropertiesSet(AbstractRefreshableConfigApplicationContext.java:154)
	at com.haulmont.cuba.core.sys.CubaClassPathXmlApplicationContext.<init>(CubaClassPathXmlApplicationContext.java:42)
	at com.haulmont.cuba.core.sys.CubaCoreApplicationContext.<init>(CubaCoreApplicationContext.java:32)
	at com.haulmont.cuba.core.sys.AppContextLoader.createApplicationContext(AppContextLoader.java:125)
	at com.haulmont.cuba.core.sys.AppContextLoader.createApplicationContext(AppContextLoader.java:43)
	at com.haulmont.cuba.core.sys.AbstractAppContextLoader.initAppContext(AbstractAppContextLoader.java:65)
	at com.haulmont.cuba.core.sys.AbstractWebAppContextLoader.contextInitialized(AbstractWebAppContextLoader.java:86)
	at com.haulmont.cuba.core.sys.AppContextLoader.contextInitialized(AppContextLoader.java:52)
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4685)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5146)

The app component project has an additional data store that it was configured on the main project:
CS2

Is there anything wrong?. Is it necessary specify in other way that this entity belongs to he app component? Is it necessary to create another entity which extends from this entity?..

We worked with App components in other projects on the same way and we didn’t find this problem.

Regards,
Xavier

Hi

There is a bug in Studio. It doesn’t recognize the Portes entity as an entity from additional data store. Because it belongs to app component.
The YouTrack issue created.

For your application to work correctly you should setup cross data store reference manually. So the porta field will be transient (non-persistent) attribute that relates to portaId system persistent field with the type of the Portes identifier. If Portes has UUID identifier then attributes declaration looks like this (getters and setters omitted, though required):

@SystemLevel
@Column(name = "PORTA_ID")
private UUID portaId;

@Transient
@MetaProperty(related = "portaId")
private Portes porta;

See Data Stores - CUBA Platform. Developer’s Manual

Hi, Alexander.

Thank you for your quickly and detailed answer.

When we registered the topic, we didn’t expose the original situation, a many-to-many association relation, because we began to do multiple tests before registering the topic on this forum to excluding any circumstance and reproducing the example exposed on the documentation.

Originally, we defined next relation:

@JoinTable(name = "AREAVIANANTS_PORTES_LINK",
            joinColumns = @JoinColumn(name = "AREA_VIANANTS_ID"),
            inverseJoinColumns = @JoinColumn(name = "PORTES_ID"))
    @ManyToMany
    protected List<Portes> portes;

We have next doubt. It is possible to define a MANY-TO-MANY relation to an entity? Because on the documentation indicates:

References between entities from different data stores can automatically maintain TO-ONE references between entities from different data stores, if they are properly defined.

If it’s possible? How is it the right way to declare this MANY-TO-MANY relation to entities from different data stores?.

Regards,
Xavier

There is no easy way to implement MANY-TO-MANY relation between entities from different data stores.

It can be implemented with two additional entities and TO-ONE relations between entities from different data stores.

The test project attached, where such relations implemented.

EntityA and EntityB entities are in the different data stores. For the EntityA, an A2BRef binding entity is created, which will be in the opposite data store and will be associated with the EntityB with the @ManyToMany relation. A similar model created for the EntityB.
With this data model, the @ManyToMany relationship is duplicated in each data store. EntityChangeEvent is used for synchronization.
With this model, the UI and linking (link / exclude) actions in the user interface work correctly. Except that for a new entity an immediate association with an entity from another data store will not work. These actions must be performed in different transactions.

Class Diagram1

Test project setup (PostgreSQL required):

  1. Create two empty databases named DataStoreA and DataStoreB.
  2. For DataStoreA run script: modules/core/db/init_astore/postgres/10.create-db.sql
  3. For DataStoreB run script: modules/core/db/init_bstore/postgres/10.create-db.sql
  4. Run application

x-db-link-test.zip (87.2 KB)

1 Like

Hi, Alexander

I’m very grateful for your detailed explanation and the attached test project.
We’ll try to implement this solution approach in our project.

Thank you very much.

Regards,

Xavier

Hi, Alexander.
Finally, our scenario doesn’t require a MANY-TO-MANY association and we only need a MANY-TO-ONE relation.

We defined following classes corresponding to your diagram:

ENTITY A

package com.company.areavianants.entity;

import com.haulmont.cuba.core.entity.StandardEntity;
import com.haulmont.cuba.core.entity.annotation.OnDelete;
import com.haulmont.cuba.core.global.DeletePolicy;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.List;

@Table(name = "AREAVIANANTS_AREA_VIANANTS")
@Entity(name = "areavianants_AreaVianants")
public class AreaVianants extends StandardEntity {
    private static final long serialVersionUID = 6135072034264975246L;

    @NotNull
    @Column(name = "DESCRIPCIO", nullable = false, length = 50)
    protected String descripcio;

    @NotNull
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "DISTINTIU_ID")
    protected DistintiuArea distintiu;

    @OnDelete(DeletePolicy.CASCADE)
    @OneToMany(mappedBy = "areaVianants")
    protected List<AreaVianantsPortes> portes;

    public List<AreaVianantsPortes> getPortes() {
        return portes;
    }

    public void setPortes(List<AreaVianantsPortes> portes) {
        this.portes = portes;
    }

    public DistintiuArea getDistintiu() {
        return distintiu;
    }

    public void setDistintiu(DistintiuArea distintiu) {
        this.distintiu = distintiu;
    }

    public String getDescripcio() {
        return descripcio;
    }

    public void setDescripcio(String descripcio) {
        this.descripcio = descripcio;
    }
}

ENTITY B2AREF

package com.company.areavianants.entity;

import com.company.territori.entity.Portes;
import com.haulmont.chile.core.annotations.MetaProperty;
import com.haulmont.cuba.core.entity.StandardEntity;
import com.haulmont.cuba.core.entity.annotation.SystemLevel;

import javax.persistence.*;

@Table(name = "AREAVIANANTS_PORTES")
@Entity(name = "areavianants_AreaVianantsPortes")
public class AreaVianantsPortes extends StandardEntity {
    private static final long serialVersionUID = -1225321084933635444L;

    @SystemLevel
    @Column(name = "ID_PORTAL")
    private Integer portaId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "AREA_VIANANTS_ID")
    protected AreaVianants areaVianants;

    @Transient
    @MetaProperty(related = "portaId")
    private Portes portal;

    public AreaVianants getAreaVianants() {
        return areaVianants;
    }

    public void setAreaVianants(AreaVianants areaVianants) {
        this.areaVianants = areaVianants;
    }
    public Integer getPortaId() {
        return portaId;
    }

    public void setPortaId(Integer portaId) {
        this.portaId = portaId;
    }
    public Portes getPortal() {
        return portal;
    }

    public void setPortal(Portes portal) {
        this.portal = portal;
    }
}

With this definition, Cuba Studio recognizes the relation with the entity ‘Portes’ that belongs to the app component and it’s stored in the additional data store. As it reflects the view created for editing AreaVianants records:
imatge

We’ve just found a problem with the edit screen creation of the AreaVianants entity, because Cuba Studio doesn’t include automatically the table component in the form to introduce the attribute portes values, because one AreaVianants entity has many portes values.

Step 1:
imatge

Step 2: Edit screen doesn’t contains a table to introduce the different portes values:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
        caption="msg://editorCaption"
        focusComponent="form"
        messagesPack="com.company.areavianants.web.screens.areavianants">
    <data>
        <instance id="areaVianantsDc"
                  class="com.company.areavianants.entity.AreaVianants"
                  view="areaVianants-view-edit">
            <loader/>
        </instance>
    </data>
    <dialogMode height="600"
                width="800"/>
    <layout expand="editActions" spacing="true">
        <form id="form" dataContainer="areaVianantsDc">
            <column width="250px">
                <textField id="descripcioField" property="descripcio"/>
                <pickerField id="distintiuField" property="distintiu">
                    <actions>
                        <action id="lookup" type="picker_lookup"/>
                        <action id="clear" type="picker_clear"/>
                    </actions>
                </pickerField>
            </column>
        </form>
        <hbox id="editActions" spacing="true">
            <button action="windowCommitAndClose"/>
            <button action="windowClose"/>
        </hbox>
    </layout>
</window>

imatge

Alexander, It has some relation with the Cuba Studio bug you mentioned about relation with entities from an additional data store from an application component.? Any workarround?

Regards,
Xavier

1 Like

Hi

No, this behavior doesn’t relate to the bug mentioned above.
You need to use COMPOSITION attribute type instead of ASSOCIATION in your ONE-TO-MANY relation.

Hi, Alexander.

It’s true, you’re completely right.

We’ve linked our error with the problem with entities from additional data store that belong to app component.

Thank you very much.

Regards,
Xavier