1

I have these tables:

CREATE TABLE company (
    id VARCHAR(256) NOT NULL,
    tenantId VARCHAR(256) NOT NULL,
    fieldName VARCHAR(256) NOT NULL,
    PRIMARY KEY (id, tenantId, fieldName)
);

CREATE TABLE employee (
    tenantId VARCHAR(256) NOT NULL,
    companyFieldName VARCHAR(256) NOT NULL,
    companyId VARCHAR(256) NOT NULL,
    fieldName VARCHAR(256) NOT NULL,
    PRIMARY KEY (tenantId, companyFieldName, companyId, fieldName),
    CONSTRAINT fkCompany FOREIGN KEY (tenantId, companyFieldName, companyId) REFERENCES employee (tenantId, fieldName, id)
);
  • One company can have many employees
  • A company's primary key is a composite key consisting of 3 fields
  • An employees' primary key consists of the company's composite key (i.e the foreign key), plus another field specific to company.

Based on this related question:

JPA how to make composite Foreign Key part of composite Primary Key

I have created the following entities:

@Embeddable
public class CompanyIdentity implements Serializable
{
    @NotBlank
    private String tenantId;

    @NotBlank
    private String fieldName;

    @NotBlank
    private String id;

    //getters, setters, equals and hashcode ommited
}

@Entity
public class Company implements Serializable
{
    @EmbeddedId
    private CompanyIdentity companyIdentity;

    @OneToMany(mappedBy = "company")
    private Set<Employee> employees;

    //getters, setters, equals and hashcode ommited
}

@Embeddable
public class EmployeeIdentity implements Serializable
{
    @NotNull
    private CompanyIdentity companyIdentity;

    // This *not* the fieldName in the CompanyIdentity, this is a property
    // specific to an employee, it just happens to have the same name
    @NotBlank
    private String fieldName; 

    //getters, setters, equals and hashcode ommited
}

public class Employee implements Serializable
{
    @EmbeddedId
    private EmployeeIdentity employeeIdentity;

    @MapsId("companyIdentity")
    @JoinColumns({
        @JoinColumn(name = "tenantId", referencedColumnName = "tenantId"),
        @JoinColumn(name = "companyFieldName", referencedColumnName = "fieldName"),
        @JoinColumn(name = "companyId", referencedColumnName = "id")
    })
    @ManyToOne
    @Cascade(value={org.hibernate.annotations.CascadeType.ALL})
    private Company company;

    //getters, setters, equals and hashcode ommited
}

I want to save a company with a single employee, and have it write a row to the Company table and Employee table, but whenever I run the following, I only ever see a row getting written to the Company table and never the Employee table?

I'm not sure if below is the right approach or not, or maybe the entities above are not correct?

public interface CompanyRepository extends CrudRepository<Company, String> {}
final Company company = new Company();
final CompanyIdentity companyIdentity = new CompanyIdentity("company-tenant-id", "company-field-name", "company-id");

company.setCompanyIdentity(companyIdentity);

final Employee employee = new Employee();

final EmployeeIdentity employeeIdentity = new EmployeeIdentity();
employeeIdentity.setFieldName("employee-field-name");
employeeIdentity.setCompanyIdentity(companyIdentity);

employee.setEmployeeIdentity(employeeIdentity);

employee.setCompany(company);

final Set<Employee> employees = new HashSet<>();
employees.add(employee);

company.setEmployees(employees);

companyRepository.save(company); //only saves company, not employee?

Many thanks!

rmf
  • 625
  • 2
  • 9
  • 39

1 Answers1

1

Your are saving the company with the company repository but the company doens´t have a cascade annotation.

@OneToMany(mappedBy = "company")
private Set<Employee> employees;

Should be:

@OneToMany(mappedBy = "company")
@Cascade(value={org.hibernate.annotations.CascadeType.ALL})
private Set<Employee> employees;
Klaas
  • 80
  • 8
  • Thanks @Klaas, looks good. But I am getting a constraint violation when I try to save a second company+employee with a different tenantId (or different fieldName/id): `Unique index or primary key violation: \"FKCOMPANYFIELDNAME_INDEX_1 ON PUBLIC.COMPANY(FIELDNAME) VALUES ('a-company-field-name', 1)\"; SQL statement:\ninsert into company`. Any ideas here? In the Employee table, the combination of the four fields: tenantId+companyFieldName+companyId+fieldName should serve as the primary key, so I should be able to add another company+employee by changing just one of these. – rmf Feb 06 '20 at 12:06
  • In this case the setting of the foreign keys in the Employee Class does not work. Example number 2 in https://docs.oracle.com/javaee/6/api/javax/persistence/EmbeddedId.html could work for your case. – Klaas Feb 06 '20 at 12:31
  • Thanks, actually it was how my foreign key was defined SQL in my original question, I've updated it now, looks better. – rmf Feb 06 '20 at 16:57