AntonioJSG (16) [Avatar] Offline
#1
Hi,

My application is built on Glassfish 2.1 and Eclipselink 1.1.2

I have two entities:

InventarioMaestro with a OneToMany relationship to InventarioSku
Each one has a compound key.

There is a Foreign Key restriction in the database on insert-restrict.

These are the classes (I omit the get-set methods):

public class InventarioMaestro implements Serializable, PickListElements {
@EmbeddedId
protected InventarioMaestroPK inventarioMaestroPK;
@Basic(optional = false)
@Column(name = "nombre")
private String nombre;
@Basic(optional = false)
@Column(name = "unidad_medida")
private String unidadMedida;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "inventarioMaestro", fetch = FetchType.EAGER)
private List<InventarioSku> inventarioSkuCollection;


public class InventarioMaestroPK implements Serializable {
@Basic(optional = false)
@Column(name = "codigo_upc")
private String codigoUpc;
@Basic(optional = false)
@Column(name = "id_empresa")
private String idEmpresa;

public class InventarioSku implements Serializable, PickListElements {
@EmbeddedId
protected InventarioSkuPK inventarioSkuPK;
@Column(name = "unidad_medida")
private String unidadMedida;
@Column(name = "cantidad_unidades_upc")
private double cantidadUnidadesUpc;
@Column(name="codigo_alterno")
private String codigoAlterno;
@Column(name="uso_suspendido")
private String usoSuspendido;
@JoinColumns({@JoinColumn(name = "codigo_upc", referencedColumnName = "codigo_upc", insertable=true, updatable=true), @JoinColumn(name = "id_empresa", referencedColumnName = "id_empresa", insertable=false, updatable=false)})
@ManyToOne(optional=false, fetch=FetchType.EAGER)
private InventarioMaestro inventarioMaestro;

public class InventarioSkuPK implements Serializable {
@Basic(optional = false)
@Column(name = "codigo_sku")
private String codigoSku;
@Basic(optional = false)
@Column(name = "id_empresa")
private String idEmpresa;


In the web application I build InventarioSku and:
- build it´s primary key,
- get the InventarioMaestro object and assign to InventarioSku

Finally, I send InventarioSku to a stateless session bean for a persist operation.
This is the code:

public MgTransactionRC insertInventarioSku(InventarioSku pObjeto) {
MgTransactionRC outcome = new MgTransactionRC();
InventarioSku objetoSearch = em.find(InventarioSku.class, pObjeto.getInventarioSkuPK());
if (objetoSearch != null) {
outcome.setSuccessful(false);
outcome.setErrorCode(MgTransactionRC.msgObjectExists);
return outcome;
}
em.persist(pObjeto);
outcome.setSuccessful(true);
outcome.setErrorCode(MgTransactionRC.msgPersistSuccess);
outcome.setObjeto(pObjeto);
return outcome;
}

I get the following error from EclipseLink

Exception [EclipseLink-4002] (Eclipse Persistence Services - 1.1.2.v20090612-r4475): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: org.postgresql.util.PSQLException: ERROR: insert or update on table "mg_inventario_sku" violates foreign key constraint "FK_inventario_sku" Detail: Key (codigo_upc,id_empresa)=(,se001) is not present in table "mg_inventario_maestro". Error Code: 0 Call: INSERT INTO mg_inventario_Sku (cantidad_unidades_upc, uso_suspendido, codigo_alterno, unidad_medida, codigo_sku, id_empresa) VALUES (?, ?, ?, ?, ?, ?) bind => [1.0, 0, 7506105600204, UNIDAD, 7506105600204, se001] Query: InsertObjectQuery(com.mobilges.server.sales.model.InventarioSku[inventarioSkuPK=com.mobilges.server.sales.model.InventarioSkuPK[codigoSku=7506105600204, idEmpresa=se001]])

From the error I can see that the field upc_code in InventarioSKU is never assigned.
The strange thing is that the field id_empresa is assigned, but the field upc_code is not.
I debug the stateless session bean and just before the persist operation I verified that all values both in InventarioSKU and InventarioMaestro are correctly assigned, I mean, codigo_upc in both entities are not null and are the same.

Is there an error in the relationship mapping ?

Thank you.
AntonioJSG (16) [Avatar] Offline
#2
Re: Entity with ManyToOne relatinship can not be persisted
Hello,

I have found the solution to my problem. It is because one of the JoinFields is not part of the primary key on the "Many Side" of the relationship.

I must explain starting from the Database Design.

Table InventarioMaestro
Primary Key( idEmpresa, codigoUpc)

Table InventarioSKU
Primary Key( idEmpresa, codigoSKU)
Field codigoUPC, varchar

There is a foreign key from InventarioSku to InventarioMaestro:

ALTER TABLE inventarioSKU ADD CONSTRAINT FK_inventario_sku
FOREIGN KEY (codigo_upc, id_empresa) REFERENCES
InventarioMaestro (codigo_upc, id_empresa)
ON DELETE RESTRICT ON UPDATE RESTRICT

As you can see, on table InventarioSKU the field codigoUpc is not part of the primary key but is used to reference InventarioMaestro.

Then, on the Mapping you can see that codigoUpc is not part of the ID:

public class InventarioSkuPK implements Serializable {
@Basic(optional = false)
@Column(name = "codigo_sku")
private String codigoSku;
@Basic(optional = false)
@Column(name = "id_empresa")
private String idEmpresa;

So, when I try to merge InventarioMaestro, the field codigoUpc is not assigned in InventarioSku, as showed in the EclipseLink database error.

The solution is to add codigoUpc to the class InventarioSkuPK.

This means that if you plan to make OneToMany relationships, be sure that all fields used in JoinColumns are part of the primaryKey mappings or ID mappings.

The funny part is that the code I showed initially worked to show data, I mean, the application was able to retrieve data from Database and instantiate objects and show the "many" objects related. But it did not work for persisting to the database.

Maybe is a good warning to include in any book !!

Regards,

Antonio