'Hibernate inserting NULL values into @ManyToOne @JoinColumn when given field is clearly set

I've got a weird issue here that I've been debugging for 2 days now and which I can't get my head around.

Basically I've got two entities: Company and EkasaCashRegister.
The EkasaCashRegister entity references Company entity via a @ManyToOne mapping.
Both those entities have tax_id column in the database and it's declared using @JoinColumn annotation.

The problem is that whenever I try to persist the EkasaCashRegister entity, even though it has the company field set to a managed instance of Company entity, Hibernate attempts to write NULL into the tax_id column of the ekasa_cash_registers table.

Entities look like this:

@Entity
@Table(name = "companies")
public class Company
{
    @NotNull
    @EmbeddedId
    private CompanyIdentity identity;

    @NotBlank
    @Size(max = 15)
    @Column(name = "tax_id", nullable = false, updatable = false, unique = true, length = 15)
    private String taxId;

    // other fields and boilerplate code omitted

    @Override
    public String toString()
    {
        return "Company{" +
                "identity=" + identity +
                ", taxId='" + taxId + '\'' +
                '}';
    }
}

@Embeddable
public class CompanyIdentity implements Serializable
{
    @NotBlank
    @Column(name = "company_country_code", nullable = false, updatable = false, length = 2)
    protected String companyCountryCode;

    @NotBlank
    @Column(name = "company_registration_number", nullable = false, updatable = false, length = 15)
    protected String companyRegistrationNumber;

    // boilerplate code omitted

    @Override
    public String toString()
    {
        return "CompanyIdentity{" +
                "companyCountryCode='" + companyCountryCode + '\'' +
                ", companyRegistrationNumber='" + companyRegistrationNumber + '\'' +
                '}';
    }
}
@Entity
@Table(name = "ekasa_cash_registers")
public class EkasaCashRegister
{
    @Id
    @NotNull
    @Column(name = "cash_register_code")
    private Long cashRegisterCode;

    @NotNull
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "tax_id", referencedColumnName = "tax_id", nullable = false, updatable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Company company;

    // other fields and boilerplate code omitted

    public EkasaCashRegister(Company company, Long cashRegisterCode)
    {
        Objects.requireNonNull(company, "company=null");
        Objects.requireNonNull(cashRegisterCode, "cashRegisterCode=null");

        this.company = company;
        this.cashRegisterCode = cashRegisterCode;
    }

    @Override
    public String toString()
    {
        return "EkasaCashRegister{" +
                "cashRegisterCode=" + cashRegisterCode +
                ", company=" + company +
                '}';
    }
}

Now here is the actual code used to persist the EkasaCashRegister entity:

var newCashRegister = new EkasaCashRegister(
        pos.getBranch().getCompany(), // returns managed instance of Company from @ManyToOne mapping on Branch
        cashRegisterCode
);

// setters for some further attributes have been omitted

System.out.println("newCashRegister:");
System.out.println(newCashRegister.toString());

em.persist(newCashRegister);

Which outputs:

newCashRegister:
EkasaCashRegister{cashRegisterCode=88812345678900001, company=Company{identity=CompanyIdentity{companyCountryCode='SK', companyRegistrationNumber='36684449'}, taxId='2022251528'}}
17:57:12.999 DEBUG SQL                                      : 
    insert 
    into
        ekasa_cash_registers
        (created_by, created_date, last_modified_by, last_modified_date, auth_data, auth_data_exp, auth_data_pass, tax_id, id_data, cash_register_code) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
17:57:12.999 TRACE BasicBinder                              : binding parameter [1] as [VARCHAR] - [root]
17:57:12.999 TRACE BasicBinder                              : binding parameter [2] as [TIMESTAMP] - [2022-04-05T15:57:12.954669Z]
17:57:12.999 TRACE BasicBinder                              : binding parameter [3] as [VARCHAR] - [root]
17:57:12.999 TRACE BasicBinder                              : binding parameter [4] as [TIMESTAMP] - [2022-04-05T15:57:12.954669Z]
17:57:13.004 TRACE BasicBinder                              : binding parameter [5] as [OTHER] - [*redacted:authData*]
17:57:13.007 TRACE BasicBinder                              : binding parameter [6] as [TIMESTAMP] - [2023-03-16T09:28:42Z]
17:57:13.007 TRACE BasicBinder                              : binding parameter [7] as [VARCHAR] - [*redacted:identityData*]
17:57:13.007 TRACE BasicBinder                              : binding parameter [8] as [VARCHAR] - [null]
17:57:13.064 TRACE BasicBinder                              : binding parameter [9] as [OTHER] - [*redacted:authDataPass*]
17:57:13.066 TRACE BasicBinder                              : binding parameter [10] as [BIGINT] - [88812345678900001]
17:57:13.071 WARN  SqlExceptionHelper                       : SQL Error: 0, SQLState: 23502
17:57:13.071 ERROR SqlExceptionHelper                       : ERROR: null value in column "tax_id" of relation "ekasa_cash_registers" violates not-null constraint
  Detail: Failing row contains (88812345678900001, null, *redacted:identityData*, *redacted:authData*, *redacted:authDataPass*, 2023-03-16 10:28:42, root, 2022-04-05 17:57:12.954669, root, 2022-04-05 17:57:12.954669).

Of which the following is important:

  1. EkasaCashRegister#company#taxId is clearly NOT NULL:
EkasaCashRegister{cashRegisterCode=88812345678900001, company=Company{identity=CompanyIdentity{companyCountryCode='SK', companyRegistrationNumber='36684449'}, taxId='2022251528'}}
  1. Yet Hibernate binds the tax_id as NULL:
17:57:13.007 TRACE BasicBinder                              : binding parameter [8] as [VARCHAR] - [null]
  1. And then it complains the database rejected that NULL value
ERROR: null value in column "tax_id" of relation "ekasa_cash_registers" violates not-null constraint

I would be really glad if someone could advise me on what I'm doing wrong here.
Thanks in advance.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source