Changelog Generation Error - column type is undefined

Hi:

I’m on Jmix 1.5.5, migrating from Cuba 7.2. Jmix Studio version 2.3.1-241. I have migrated a Cuba 7.2 project and tested it, then converted it into an add-on.

I then migrated a second Cuba 7.2 project that uses that add-on in a Composite project. After some pain, I managed to get everything to build.

When I go to execute the job, I get:

Changelog Generation Error:
java.lang.RuntimeException:
java.lang.illegalStateException:
The column type is undefined.
Table - Customers; Column - CUS_ID

My Customers object indeed does not contain a field named “CUS_ID” as it is "ID. Everywhere I reference it in both the main and add-on projects, I have inserted @JoinColumn references like this:

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "CUS_ID", referencedColumnName = "ID")
    protected Customers cus;

I did this just in case Liquibase or Eclipeselink didn’t get the association correct.

The only place that I do actually have a field named CUS_ID is in an EmbeddedId in my add-on:

@JmixEntity(name = "filemaint_JobdefsCompKey")
@Embeddable
public class JobdefsCompKey implements Serializable {
    private static final long serialVersionUID = -8702137759981849222L;

    @Column(name = "CUS_ID")
    protected Long cus;
    @Column(name = "JOB_ID")
    protected Long job;
    <getters and setters>
@JmixEntity
@Table(name = "JOBDEFS")
@Entity(name = "filemaint_Jobdefs")
public class Jobdefs {
    private static final long serialVersionUID = 2913012589319426949L;

    @EmbeddedId
    protected JobdefsCompKey id;

    @MapsId("cus")
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "CUS_ID", referencedColumnName = "ID")
    protected Customers customer;
    <more fields, getters and setters>

I don’t reference the jobdefs entity in this main application at all.

How do I go about isolating what is wrong and fixing it?

Hi Eric,

Could you reproduce the problem on a test project?

Regards,
Konstantin

Not sure. It’s a composite project with an add-on.

I will give it a shot and get back to you if I can recreate it.

Hi

Please check if you have reference to non-existent CUS_ID column in index specified for Customers entity.
Something like the following

@Table(name = "CUSTOMERS", indexes = {
        @Index(name = "IDX_CUSTOMERS_CUSTOMER", columnList = "CUS_ID")
})

Such case can cause exception like you described above.

Unfortunately, no. I do not have any @Index definitions at all. This is customers:

@JmixEntity
@Table(name = "CUSTOMERS")
@Entity(name = "rentals_Customers")
public class Customers  {

    @Id
    @GeneratedValue(generator = "IDGEN")
    @SequenceGenerator(name = "IDGEN", sequenceName = "IDGEN", allocationSize = 1)
    protected Long id;

(and then a bunch of fields).

I have two entities that reference it:

@JmixEntity
@AttributeOverrides({
        @AttributeOverride(name = "id", column = @Column(name = "ORDNUM"))
})
@Table(name = "ORDERS")
@Entity(name = "rentals_Orders")
public class Orders  {

    @Id
    @GeneratedValue(generator = "ORDGEN")
    @SequenceGenerator(name="ORDGEN", sequenceName = "ORDGEN", allocationSize=1)
    protected Integer id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "CUS_ID", referencedColumnName = "ID")
    protected Customers cus;

and

@JmixEntity
@Table(name = "LISTS")
@Entity(name = "rentals_Lists")
public class Lists  {

    @Id
    @GeneratedValue(generator = "IDGEN")
    @SequenceGenerator(name = "IDGEN", sequenceName = "IDGEN", allocationSize = 1)
    protected Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "CUS_ID", referencedColumnName = "ID")
    protected Customers cus;

I don’t have any other references to CUS_ID in my application.

image

Could you please provide the full sources of the Customers entity?
As we have Table - Customers hint in the error message it is 99% that the issue cause is in the Customers entity definition.
Feel free to use forum private messages.

I’am afraid we don’t have any clue with the input already provided.

Sure:

package com.paslists.rentals.entity;

import io.jmix.core.metamodel.annotation.InstanceName;
import io.jmix.core.metamodel.annotation.JmixEntity;

import javax.persistence.*;
import java.math.BigDecimal;

@JmixEntity
@Table(name = "CUSTOMERS")
@Entity(name = "rentals_Customers")
public class Customers  {

    @Id
    @GeneratedValue(generator = "IDGEN")
    @SequenceGenerator(name = "IDGEN", sequenceName = "IDGEN", allocationSize = 1)
    protected Long id;

    @Column(name = "ACCTREP", length = 50)
    protected String acctrep;

    @Column(name = "ADDRESS_1", nullable = false, length = 35)
    protected String address1;

    @Column(name = "ADDRESS_2", length = 35)
    protected String address2;

    @Column(name = "BILLRADE")
    protected Boolean billrade;

    @Column(name = "CANPAFID", length = 11)
    protected String canpafid;

    @Column(name = "CHARSET", length = 1)
    protected String charset;

    @Column(name = "CITY", length = 25)
    protected String city;

    @InstanceName
    @Column(name = "COMPANY", nullable = false, length = 35)
    protected String company;

    @Column(name = "COMPVERDELIV")
    protected String compverdeliv;

    @Column(name = "CONTACT", length = 35)
    protected String contact;

    @Column(name = "COUNTRY", length = 25)
    protected String country;

    @Column(name = "CREDITSTATUS", length = 1)
    protected String creditstatus;

    @Column(name = "CREDLIMIT", precision = 10, scale = 2)
    protected BigDecimal credlimit;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "CUS_ORG_ID")
    protected Customers cusOrg;

    @Column(name = "CUSTOMTAG", length = 8)
    protected String customtag;

    @Column(name = "DELIVCC", length = 512)
    protected String delivcc;

    @Column(name = "DELIVEMAIL", length = 512)
    protected String delivemail;

    @Column(name = "DELIVERVIA", length = 1)
    protected String delivervia;

    @Column(name = "DIVISION", length = 35)
    protected String division;

    @Column(name = "DUPPGM", length = 20)
    protected String duppgm;

    @Column(name = "EMAIL", length = 40)
    protected String email;

    @Column(name = "FAX", length = 20)
    protected String fax;

    @Column(name = "FEMALETHRES")
    protected Long femalethres;

    @Column(name = "FTPADDR", length = 512)
    protected String ftpaddr;

    @Column(name = "FTPPWD", length = 80)
    protected String ftppwd;

    @Column(name = "FTPUSER", length = 80)
    protected String ftpuser;

    @Column(name = "MALETHRES")
    protected Long malethres;

    @Column(name = "MPEMAIL", length = 512)
    protected String mpemail;

    @Lob
    @Column(name = "NOTES")
    protected String notes;

    @Column(name = "PAFEMAIL", length = 80)
    protected String pafemail;

    @Column(name = "PAFID", length = 10)
    protected String pafid;

    @Column(name = "PAFID2", length = 10)
    protected String pafid2;

    @Column(name = "PHONE", length = 20)
    protected String phone;

    @Column(name = "PTID", length = 10)
    protected String ptid;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "RADEMASTER")
    protected Customers rademaster;

    @Column(name = "RESPPWD", length = 20)
    protected String resppwd;

    @Column(name = "RESPSCHEMA", length = 20)
    protected String respschema;

    @Column(name = "RPTEMAIL", length = 512)
    protected String rptemail;

    @Column(name = "SALESREP_FK")
    protected Long salesrepFk;

    @Column(name = "STATE", length = 2)
    protected String state;

    @Column(name = "STATSPGM", length = 20)
    protected String statspgm;

    @Column(name = "STATUS", length = 1)
    protected String status;

    @Column(name = "TAPESTATBKR", length = 1)
    protected Boolean tapestatbkr;

    @Column(name = "TAPESTATCHG", length = 1)
    protected Boolean tapestatchg;

    @Column(name = "TAPESTATMLR", length = 1)
    protected Boolean tapestatmlr;

    @Column(name = "TAXABLE", nullable = false)
    protected Boolean taxable = false;

    @Column(name = "UPDREQEMAIL", length = 512)
    protected String updreqemail;

    @Column(name = "WEBSITE", length = 40)
    protected String website;

    @Column(name = "ZIP", length = 6)
    protected String zip;

    @Column(name = "ZIP4", length = 4)
    protected String zip4;

    public void setRademaster(Customers rademaster) {
        this.rademaster = rademaster;
    }

    public Customers getRademaster() {
        return rademaster;
    }

    public void setTaxable(Boolean taxable) {
        this.taxable = taxable;
    }

    public Boolean getTaxable() {
        return taxable;
    }

    public void setCompverdeliv(String compverdeliv) {
        this.compverdeliv = compverdeliv;
    }

    public String getCompverdeliv() {
        return compverdeliv;
    }

    public void setBillrade(Boolean billrade) {
        this.billrade = billrade;
    }

    public Boolean getBillrade() {
        return billrade;
    }

    public void setTapestatbkr(Boolean tapestatbkr) {
        this.tapestatbkr = tapestatbkr;
    }

    public Boolean getTapestatbkr() {
        return tapestatbkr;
    }

    public void setTapestatchg(Boolean tapestatchg) {
        this.tapestatchg = tapestatchg;
    }

    public Boolean getTapestatchg() {
        return tapestatchg;
    }

    public void setTapestatmlr(Boolean tapestatmlr) {
        this.tapestatmlr = tapestatmlr;
    }

    public Boolean getTapestatmlr() {
        return tapestatmlr;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public String getCharset() {
        return charset;
    }

    public void setCreditstatus(String creditstatus) {
        this.creditstatus = creditstatus;
    }

    public String getCreditstatus() {
        return creditstatus;
    }

    public void setDelivervia(String delivervia) {
        this.delivervia = delivervia;
    }

    public String getDelivervia() {
        return delivervia;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getStatus() {
        return status;
    }

    public String getZip4() {
        return zip4;
    }

    public void setZip4(String zip4) {
        this.zip4 = zip4;
    }

    public String getZip() {
        return zip;
    }

    public void setZip(String zip) {
        this.zip = zip;
    }

    public String getWebsite() {
        return website;
    }

    public void setWebsite(String website) {
        this.website = website;
    }

    public String getUpdreqemail() {
        return updreqemail;
    }

    public void setUpdreqemail(String updreqemail) {
        this.updreqemail = updreqemail;
    }

    public String getStatspgm() {
        return statspgm;
    }

    public void setStatspgm(String statspgm) {
        this.statspgm = statspgm;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public Long getSalesrepFk() {
        return salesrepFk;
    }

    public void setSalesrepFk(Long salesrepFk) {
        this.salesrepFk = salesrepFk;
    }

    public String getRptemail() {
        return rptemail;
    }

    public void setRptemail(String rptemail) {
        this.rptemail = rptemail;
    }

    public String getRespschema() {
        return respschema;
    }

    public void setRespschema(String respschema) {
        this.respschema = respschema;
    }

    public String getResppwd() {
        return resppwd;
    }

    public void setResppwd(String resppwd) {
        this.resppwd = resppwd;
    }

    public String getPtid() {
        return ptid;
    }

    public void setPtid(String ptid) {
        this.ptid = ptid;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getPafid2() {
        return pafid2;
    }

    public void setPafid2(String pafid2) {
        this.pafid2 = pafid2;
    }

    public String getPafid() {
        return pafid;
    }

    public void setPafid(String pafid) {
        this.pafid = pafid;
    }

    public String getPafemail() {
        return pafemail;
    }

    public void setPafemail(String pafemail) {
        this.pafemail = pafemail;
    }

    public String getNotes() {
        return notes;
    }

    public void setNotes(String notes) {
        this.notes = notes;
    }

    public String getMpemail() {
        return mpemail;
    }

    public void setMpemail(String mpemail) {
        this.mpemail = mpemail;
    }

    public Long getMalethres() {
        return malethres;
    }

    public void setMalethres(Long malethres) {
        this.malethres = malethres;
    }

    public String getFtpuser() {
        return ftpuser;
    }

    public void setFtpuser(String ftpuser) {
        this.ftpuser = ftpuser;
    }

    public String getFtppwd() {
        return ftppwd;
    }

    public void setFtppwd(String ftppwd) {
        this.ftppwd = ftppwd;
    }

    public String getFtpaddr() {
        return ftpaddr;
    }

    public void setFtpaddr(String ftpaddr) {
        this.ftpaddr = ftpaddr;
    }

    public Long getFemalethres() {
        return femalethres;
    }

    public void setFemalethres(Long femalethres) {
        this.femalethres = femalethres;
    }

    public String getFax() {
        return fax;
    }

    public void setFax(String fax) {
        this.fax = fax;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getDuppgm() {
        return duppgm;
    }

    public void setDuppgm(String duppgm) {
        this.duppgm = duppgm;
    }

    public String getDivision() {
        return division;
    }

    public void setDivision(String division) {
        this.division = division;
    }

    public String getDelivemail() {
        return delivemail;
    }

    public void setDelivemail(String delivemail) {
        this.delivemail = delivemail;
    }

    public String getDelivcc() {
        return delivcc;
    }

    public void setDelivcc(String delivcc) {
        this.delivcc = delivcc;
    }

    public String getCustomtag() {
        return customtag;
    }

    public void setCustomtag(String customtag) {
        this.customtag = customtag;
    }

    public Customers getCusOrg() {
        return cusOrg;
    }

    public void setCusOrg(Customers cusOrg) {
        this.cusOrg = cusOrg;
    }

    public BigDecimal getCredlimit() {
        return credlimit;
    }

    public void setCredlimit(BigDecimal credlimit) {
        this.credlimit = credlimit;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getContact() {
        return contact;
    }

    public void setContact(String contact) {
        this.contact = contact;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCanpafid() {
        return canpafid;
    }

    public void setCanpafid(String canpafid) {
        this.canpafid = canpafid;
    }

    public String getAddress2() {
        return address2;
    }

    public void setAddress2(String address2) {
        this.address2 = address2;
    }

    public String getAddress1() {
        return address1;
    }

    public void setAddress1(String address1) {
        this.address1 = address1;
    }

    public String getAcctrep() {
        return acctrep;
    }

    public void setAcctrep(String acctrep) {
        this.acctrep = acctrep;
    }

    public Long getId() {
        return id;
    }

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

Here is the actual postgresql table definition:

                       Table "sbowner_test.customers"
    Column    |          Type          | Collation | Nullable |   Default
--------------+------------------------+-----------+----------+-------------
 id           | bigint                 |           | not null |
 company      | character varying(35)  |           | not null |
 cus_org_id   | bigint                 |           |          |
 division     | character varying(35)  |           |          |
 address_1    | character varying(35)  |           | not null |
 address_2    | character varying(35)  |           |          |
 city         | character varying(25)  |           |          |
 state        | character(2)           |           |          |
 zip          | character varying(6)   |           |          |
 zip4         | character(4)           |           |          |
 contact      | character varying(35)  |           |          |
 phone        | character varying(20)  |           |          |
 fax          | character varying(20)  |           |          |
 email        | character varying(40)  |           |          |
 website      | character varying(40)  |           |          |
 credlimit    | numeric(10,2)          |           |          | 0
 country      | character varying(25)  |           |          |
 mpemail      | character varying(512) |           |          |
 ptid         | character varying(10)  |           |          |
 statspgm     | character varying(20)  |           |          |
 salesrep_fk  | bigint                 |           |          |
 updreqemail  | character varying(512) |           |          |
 duppgm       | character varying(20)  |           |          |
 tapestatmlr  | boolean                |           |          |
 tapestatbkr  | boolean                |           |          |
 tapestatchg  | boolean                |           |          |
 malethres    | double precision       |           |          |
 femalethres  | double precision       |           |          |
 pafid        | character varying(10)  |           |          |
 customtag    | character varying(8)   |           |          |
 status       | character(1)           |           |          | 'A'::bpchar
 respschema   | character varying(20)  |           |          |
 resppwd      | character varying(20)  |           |          |
 notes        | text                   |           |          |
 pafid2       | character varying(10)  |           |          |
 delivemail   | character varying(512) |           |          |
 delivcc      | character varying(512) |           |          |
 delivervia   | character(1)           |           |          |
 ftpuser      | character varying(80)  |           |          |
 ftppwd       | character varying(80)  |           |          |
 ftpaddr      | character varying(512) |           |          |
 compverdeliv | character(1)           |           |          |
 rptemail     | character varying(512) |           |          |
 creditstatus | character(1)           |           |          |
 charset      | character(1)           |           |          |
 canpafid     | character varying(11)  |           |          |
 acctrep      | character varying(50)  |           |          |
 rademaster   | bigint                 |           |          |
 pafemail     | character varying(80)  |           |          |
 billrade     | boolean                |           |          |
 taxable      | boolean                |           |          |
 ptmergeid    | character varying(20)  |           |          |
Indexes:
    "cus_pk" PRIMARY KEY, btree (id)
    "cus_cus_fk_i" btree (cus_org_id)
    "cus_customtag_ndx" btree (customtag)
    "customers_salesrep_ndx" btree (salesrep_fk)
Foreign-key constraints:
    "cus_cus_fk" FOREIGN KEY (cus_org_id) REFERENCES customers(id) DEFERRABLE
    "cust$rademaster$fk" FOREIGN KEY (rademaster) REFERENCES customers(id)
Referenced by:
    TABLE "custrels" CONSTRAINT "cr_cus_fk" FOREIGN KEY (cus_id) REFERENCES customers(id) DEFERRABLE
    TABLE "custreps" CONSTRAINT "crp_cus_fk" FOREIGN KEY (cus_id) REFERENCES customers(id) DEFERRABLE
    TABLE "customers" CONSTRAINT "cus_cus_fk" FOREIGN KEY (cus_org_id) REFERENCES customers(id) DEFERRABLE
    TABLE "customers" CONSTRAINT "cust$rademaster$fk" FOREIGN KEY (rademaster) REFERENCES customers(id)
    TABLE "lists" CONSTRAINT "fk_lists_on_mgr" FOREIGN KEY (mgr_id) REFERENCES customers(id) DEFERRABLE
    TABLE "liststest" CONSTRAINT "fk_liststest_on_cus" FOREIGN KEY (cus_id) REFERENCES customers(id) DEFERRABLE
    TABLE "liststest" CONSTRAINT "fk_liststest_on_own" FOREIGN KEY (own_id) REFERENCES customers(id) DEFERRABLE
    TABLE "orders" CONSTRAINT "fk_orders_cus" FOREIGN KEY (cus_id) REFERENCES customers(id) DEFERRABLE
    TABLE "invtemplatehdr" CONSTRAINT "invtemplatehdr$cus$fk" FOREIGN KEY (cus_id) REFERENCES customers(id)
    TABLE "jobdefs" CONSTRAINT "jobdefs_cus_fk" FOREIGN KEY (cus_id) REFERENCES customers(id) DEFERRABLE
    TABLE "lists" CONSTRAINT "list_owner_fk" FOREIGN KEY (own_id) REFERENCES customers(id) DEFERRABLE
    TABLE "lists" CONSTRAINT "lst_cus_fk" FOREIGN KEY (cus_id) REFERENCES customers(id) DEFERRABLE
    TABLE "mergedeliv" CONSTRAINT "mergedeliv$cus$fk" FOREIGN KEY (cus_id) REFERENCES customers(id)
    TABLE "radeprices" CONSTRAINT "radeprices_cus_fk" FOREIGN KEY (cus_id) REFERENCES customers(id)
    TABLE "webusers" CONSTRAINT "webusers_customer_fk" FOREIGN KEY (customer_fk) REFERENCES customers(id) DEFERRABLE
    TABLE "webusers" CONSTRAINT "webusers_owner_fk" FOREIGN KEY (owner_fk) REFERENCES customers(id) DEFERRABLE
Triggers:
    "biur$customers" BEFORE INSERT OR UPDATE ON customers FOR EACH ROW EXECUTE FUNCTION "biur$customers$customers"()

Thank you

Unfortunately I don’t see any issues here.

I would ask you to enable DEBUG logging for
liquibase.ext.intellij.snapshot.IntellijSnapshotGenerator
com.haulmont.jmixstudio
categories (Help → Diagnostic Tools → Debug Log Settings…) and run “Generate Liquibase Changelog” action.
Then provide idea.log file

As requested. I hope you can find something.

idea.zip (21.9 KB)

You don’t need to add :debug to the category name, when enabling debug logging
Just add one category per line in the dialog
Screenshot 2024-08-02 at 19.11.10

Please fix this and perform the steps once again

Sorry about that. Here it is.

idea.zip (37.7 KB)

Thank you

But unfortunately there is still nothing helpful in the logs.

I’ve improved logging to get more details about this mysterious CUSTOMERS.CUS_ID column.
Could you please install latest NIGHTLY1384 version of Jmix Studio plugin and try again? Then attach idea.log.
You can install NIGHTLY build using custom repository URL (see Installation :: Jmix Documentation) or download it manually https://plugins.jetbrains.com/plugin/14340-jmix/versions/jmix_nightly and install from disk.

Sorry for the delay. I got tied up yesterday. I have installed your nightly build and re-run the Generate Changelog function. Here are the new logs.

idea.zip (51.1 KB)

I looked through it. The only possible reference to Customers.CUS_ID is in the embedded key for a table named Jobdefs in my add-on (filemaint-addon).

2024-08-06 10:15:20,667 [  36288]   FINE - #liquibase.ext.intellij.snapshot.IntellijSnapshotGenerator - Found foreign key: FK_CUSTOMERS_ON_CUIDJOID, foreign key table: CUSTOMERS, primary key table: JOBDEFS, foreign key columns: CUSTOMERS.CUS_ID, CUSTOMERS.JOB_ID, primary key columns: JOBDEFS.CUS_ID, JOBDEFS.JOB_ID

There are no indexes defined in Jmix on the Indexes tab for Customers or Jobdefs. It’s correct in the database:

                        Table "sbowner_test.jobdefs"
      Column      |          Type           | Collation | Nullable | Default
------------------+-------------------------+-----------+----------+---------
 cus_id           | bigint                  |           |          |
 job_id           | bigint                  |           |          |
 mpp_type         | character(1)            |           |          |
 australian       | character(1)            |           |          |
 canadian         | character(1)            |           |          |
 apofpo           | character(1)            |           |          |
 ak_hi            | character(1)            |           |          |
 pr_vi            | character(1)            |           |          |
 territories      | character(1)            |           |          |
 cons_bus         | character(1)            |           |          |
 companies        | character(1)            |           |          |
 initials         | character(1)            |           |          |
 fname            | character(1)            |           |          |
 blank            | character(1)            |           |          |
 titles           | character(1)            |           |          |
 seedpath         | character varying(80)   |           |          |
 middle           | character(1)            |           |          |
 dma              | boolean                 |           |          |
 pander           | boolean                 |           |          |
 survive          | character(1)            |           |          |
 layoutpath       | character varying(80)   |           |          |
 deflisttype      | character(1)            |           |          |
 sexselomit       | character(1)            |           |          |
 sexmale          | boolean                 |           |          |
 sexfemale        | boolean                 |           |          |
 sexambig         | boolean                 |           |          |
 sexunk           | boolean                 |           |          |
 prisons          | boolean                 |           |          |
 pwexits          | character varying(512)  |           |          |
 specinst         | character varying(3200) |           |          |
 buyersup         | boolean                 |           |          |
 mcase            | character(1)            |           |          |
 radebuyersupdays | double precision        |           |          |
 rademlgsupdays   | double precision        |           |          |
 geosel           | character varying(4000) |           |          |
 charset          | character(1)            |           |          |
 radesuptype      | character(1)            |           |          |
 reversename      | character(1)            |           |          |
 matchkey         | character(1)            |           |          |
Indexes:
    "jdf_uk" UNIQUE CONSTRAINT, btree (cus_id, job_id)
Foreign-key constraints:
    "jobdefs_cus_fk" FOREIGN KEY (cus_id) REFERENCES customers(id) DEFERRABLE
Triggers:
    biur_jobdefs BEFORE INSERT OR UPDATE ON jobdefs FOR EACH ROW EXECUTE FUNCTION "biur_jobdefs$jobdefs"()

It’s got this in the Entity (leaving out other fields):

@JmixEntity
@Table(name = "JOBDEFS")
@Entity(name = "filemaint_Jobdefs")
public class Jobdefs {
    private static final long serialVersionUID = 2913012589319426949L;

    @EmbeddedId
    protected JobdefsCompKey id;

    @MapsId("cus")
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "CUS_ID", referencedColumnName = "ID")
    protected Customers customer;

This is the embedded key:

@JmixEntity(name = "filemaint_JobdefsCompKey")
@Embeddable
public class JobdefsCompKey implements Serializable {
    private static final long serialVersionUID = -8702137759981849222L;

    @Column(name = "CUS_ID")
    protected Long cus;
    @Column(name = "JOB_ID")
    protected Long job;

Could this be the issue somehow?

I know embedded keys like this are frowned upon. It’s an old system.

Hi

Yes, this foreign key you found in logs seems to be the cause. Strange point is that it is foreign key defined for CUSTOMERS table, referencing JOBDEFS. And we don’t see attribute in the Customers entity that references Jobdefs.

Also I would like to admit, that the issue caused not by the existing database schema, but project data model. For some reason non-existent foreign key generated for the Customers entity, as I described above.

I’m afraid there’s only one option left. If it is possible, you provide the whole composite project’s data model (e.g. via forum private messages), so we can check where the process goes wrong.

We plan to improve Liquibase changelog generation process, so issues like that doesn’t block the whole process, but just skips certain enity or attribute. But can’t say when this will be available.

We can do that. How do I extract it for you? Just zip up the entire project?

Yes, the entire project archive is the best option.

But I’am not sure about forum file size limit. So maybe you’ll need to upload zip to some file storage and send a link.

Private Message sent with link to Google Drive. Please let me know if you have any issues downloading it.

I’ve downloaded the project, thank you.

As I see, you have another Customers entity in your add-on. And this one has reference to Jobdefs entity. So the foreign key I mentioned above is generated for this reference.
In general it is definitely not valid case when you have such entity duplicates.

OK. I can fix that in the filemaint-addon.

But what is the correct way to handle this use case? My File Maintenance add-on allows me to put file maintenance screens in all my apps. But not every app uses every entity.

How am I supposed to achieve this without duplicating entities?