Question

I have these two entities:

Anagrafica

  @Entity
    @Access(AccessType.FIELD)
    @Table(name = "S_MC_CC_USER")
    @SequenceGenerator(name = "SEQ_ID", sequenceName = "SEQ_ID", allocationSize = 1)
    public class Anagrafica implements Serializable{
        private static final long serialVersionUID = 332466838544720886L;

        @EmbeddedId
        private AnagraficaId anagraficaId;

        @Column(name = "USER_ID")
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ID")
        private Long userId;

        @OneToMany(cascade = CascadeType.ALL)
        @JoinColumn(name = "USER_ID", updatable = false, insertable = false)
        private List<Mobile> mobiles;

/**
     * La classe di dominio che modella la chiave primaria di un {@link Anagrafica}
     * 
     * @author Massimo Ugues
     * 
     */
    @Embeddable
    static public class AnagraficaId implements Serializable {
        private static final long serialVersionUID = -54640203292300521L;

        @Column(name = "ANAG_UTENTE")
        private String bt;

        @Column(name = "COD_ABI")
        private String abi;

        public AnagraficaId() {
            super();
        }

Mobile

@Entity
@Table(name = "S_MOBILE")
@SequenceGenerator(name = "SEQ_MOBILE", sequenceName = "SEQ_MOBILE", allocationSize = 1)
public class Mobile implements Serializable{
    private static final long serialVersionUID = 5999493664911497370L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_MOBILE_DEVICE_REGISTRY")
    @Column(name = "ID_MOBILE")
    private Long mobileId;

    @Column(name = "DEVICE_TOKEN")
    private String deviceToken;

    @Column(name = "DATA_INSERIMENTO")
    @Temporal(TemporalType.TIMESTAMP)
    private Calendar dataInserimento = Calendar.getInstance();

With eclispe-link 2.1.2 all works great, but with eclispe-link 2.5.1 I got this exception:

Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [persistence-unit] failed.
Internal Exception: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The @JoinColumns on the annotated element [field mobiles] from the entity class [class com.intesasanpaolo.domain.entities.sub.Anagrafica] is incomplete. When the source entity class uses a composite primary key, a @JoinColumn must be specified for each join column using the @JoinColumns. Both the name and the referencedColumnName elements must be specified in each such @JoinColumn.
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createPredeployFailedPersistenceException(EntityManagerSetupImpl.java:1954)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:1945)
    at org.eclipse.persistence.jpa.PersistenceProvider.createContainerEntityManagerFactory(PersistenceProvider.java:322)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:288)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1571)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1509)
    ... 40 more

The problem is the OneToMany association based on a foreign key that is not primary key. Since I cannot change the database model how can I make it work?

Kind regards Massimo

Was it helpful?

Solution

The reason it worked in a prior version was that it EclipseLink doesn't look at the fields in the mapping, but with JPA adding derived Id support, EclipseLink now validates the number of foreign keys match the number of ID fields.

James' answer here JPA @JoinColumn issues while joining on non primary key columns explains it that you'll need to use a descriptorCustomizer to change the JPA mapping. So you would either not map the field in JPA (mark it as @Transient) and then add a mapping in the customizer, or have the JPA mapping to use all primary key fields and then change the mapping in the customizer to only use the USER_ID->USER_ID fields.

EclipseLink customizers are shown here: http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_customizer.htm

OTHER TIPS

Ok, this is the Customizer I created:

public void customize(ClassDescriptor descriptor) throws Exception {
        // handle the oneToManyMapping to non foreign keys
        ManyToManyMapping mapping = (ManyToManyMapping) descriptor.getMappingForAttributeName("mobileDevices");
        ExpressionBuilder builder = new ExpressionBuilder();
        mapping.setSelectionCriteria(builder.getField("USER_ID").equal(builder.getParameter("USER_ID")));

        // handle the insert statement
        mapping.setInsertCall(new SQLCall(""));     
    }

As suggested from Chris this works great with the selection. I had to modify the Insert Call since eclipse-link tried to create and insert statement on a mapping table that I haven't. The problem now is on the delete: when I try to delete the collection from the source association (i.e. Cliente) as described here

Cliente.ClienteId id = new Cliente.ClienteId(abi, bt);
Cliente cliente = clienteRepository.findOne(id);            
cliente.setMobileDevices(null); 

I need eclipse link to delete the orphan. The dml generated is the following:

DELETE FROM S_MC_CC_CLIENTI_S_MOBILE_DEVICE_REGISTRY WHERE ((mobileDevices_ID_MOBILE_DEVICE_REGISTRY = 13) AND ((ANAG_UTENTE = '71576493') AND (COD_ABI = '01025')))

Since I haven't the mapping table I modified the customizer adding a setDeleteCall statement :

mapping.setDeleteCall(new SQLCall("DELETE FROM S_MOBILE_DEVICE_REGISTRY WHERE USER_ID = #USER_ID"));

In this way eclipse link generates 2 dml:

DELETE FROM S_MOBILE_DEVICE_REGISTRY WHERE USER_ID = NULL 
DELETE FROM S_MOBILE_DEVICE_REGISTRY WHERE (ID_MOBILE_DEVICE_REGISTRY = 13)

The first is the translation of my SQLCall, but without the correct parameter: any idea how to generate only the correct delete statement?

Kind regards. Massimo

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top