Composite PK with associations to different datastores

Hello!

I have a legacy app that I’m trying to migrate to Cuba. The database model consists of multiple schemas.

Schema A:

  • Reviewer, Speciality.

Schema B:

  • Person.

Reviewer has a composite PK: speciality_id, person_id.

“Generale model” produced the following code:

@Table(name = "reviewer")
@Entity(name = "asdf$Reviewer")
public class Reviewer extends BaseGenericIdEntity<ReviewerCompKey>
{
    // ...

    @EmbeddedId
    protected ReviewerCompKey id;

    // ...
}

@MetaClass(name = "asdf$ReviewerCompKey")
@Embeddable
public class ReviewerCompKey extends EmbeddableEntity
{
  // ...
    
  @Column(name = "person_id", nullable = false, length = 30)
  protected String personId;

  @Column(name = "speciality_id", nullable = false, length = 36)
  protected String specialityId;

  // ...
}

I’ve tried the recipe from References between entities from different data stores, but it looks like it’s not for composite PK case.

What is the proper way to add associations from Reviewer to Person and Speciality?

Hi,
You can create to_one association using Studio.
In my test application, I have an entity with Composite PK of three columns in additional datastore. Studio has generated the link to the entity as follows:

  1. A couple of attributes has been created
    image
  2. The code

    @Transient
    @MetaProperty(related = "linkToCPKId")
    protected Mysqlconstra02CpkEntity linkToCPK;

    @SystemLevel
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "strKey", column = @Column(name = "LINK_TO_C_P_K_ID_STR_KEY")),
        @AttributeOverride(name = "intKey", column = @Column(name = "LINK_TO_C_P_K_ID_INT_KEY")),
        @AttributeOverride(name = "dblKey", column = @Column(name = "LINK_TO_C_P_K_ID_DBL_KEY"))
    })
    protected Mysqlconstra02CpkEntityCompKey linkToCPKId;
...

It looks like I’m forced to have some synthetic entity that consists of two properties - Person and Speciality together.

I want to have my Reviewer entity as follows:

@Table(name = "reviewer")
@Entity(name = "asdf$Reviewer")
public class Reviewer extends BaseGenericIdEntity<ReviewerCompKey>
{
  // ...

  @Transient
  // ???
  protected Person person;
  
  @Transient
  // ???
  protected Speciality speciality;
  
  @EmbeddedId
  protected ReviewerCompKey id;
  
  // ...
}

Is this possible with Cuba? What should I put instead of ??? to let Cuba know that, for example, Person’s Id should be placed to id.personId and respectively Speciality’s Id - to id.specialityId?

If I specify:

@Transient
@MetaProperty(related = "personId")
protected Person person;

then Cuba throws the following exception in browse screen for Reviewer entity:

com.haulmont.cuba.core.global.RemoteException: Property 'personId' not found in asdf$Reviewer

Hi,
Let me bring an example of how an entity with composite PK could be created.
Composite PK entities are represented by two entities:

  1. EmbeddableEntity which consists of PK - fields.
@NamePattern("%s %s|keyOne,keyTwo")
@MetaClass(name = "formattertest$KeyEntity")
@Embeddable
public class KeyEntity extends EmbeddableEntity {
    private static final long serialVersionUID = -6154461586339023936L;

    @Column(name = "KEY_ONE")
    protected String keyOne;

    @Column(name = "KEY_TWO")
    protected String keyTwo;

...
  1. BaseGenericIdEntity with embedded ID
@NamePattern("%s|utilityField")
@Table(name = "FORMATTERTEST_CPK_ENTITY")
@Entity(name = "formattertest$CPKEntity")
public class CPKEntity extends BaseGenericIdEntity<KeyEntity> {
    private static final long serialVersionUID = -6805482344993664086L;

    @EmbeddedId
    protected KeyEntity id;

    @Column(name = "UTILITY_FIELD")
    protected String utilityField;

    @Override
    public KeyEntity getId() {
        return id;
    }

    @Override
    public void setId(KeyEntity id) {
        this.id = id;
    }
....

You can create and setup such Composite_PK_Entity by Studio. Using Studio, you can associate the entity with others by any type of association. You can create screens for it. Everything should work well. For “imported” entities (created during model generation) - too.

So it seems there is no need to make the properties @Transient and bind them programmatically.

When the entity is in additional datastore only links to it are allowed as Studio must not modify additional datastore scheme. The link is created in that case as were described in my previous post.

Finally got it working:

@EmbeddedId
protected ReviewerCompKey id;

@Override
public ReviewerCompKey getId()
{
  return id;
}

@Override
public void setId(ReviewerCompKey id)
{
  this.id = id;
}

@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@PrimaryKeyJoinColumn(name = "SPECIALITY_ID", referencedColumnName = "UID")
protected Speciality speciality;

@Transient
@MetaProperty(related = "personId")
protected Person person;


@SystemLevel
@NotNull
@Column(name = "PERSON_ID", nullable = false, length = 30)
protected String personId;

public void setSpeciality(Speciality speciality)
{
  this.speciality = speciality;

  getId().setSpecialityId(speciality != null ? speciality.getId() : null);
}

public Speciality getSpeciality()
{
  return speciality;
}

public void setPerson(Person person)
{
  this.person = person;

  getId().setPersonId(person != null ? person.getId() : null);
}

public Person getPerson()
{
  return person;
}

public void setPersonId(String personId)
{
  this.personId = personId;

  getId().setPersonId(personId);
}

public String getPersonId()
{
  return personId;
}

Thanks for the help!

2 Likes