问题
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
回答1:
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
回答2:
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
来源:https://stackoverflow.com/questions/21783345/jpa-eclips-link-2-5-1-onetomany-not-working-on-columns-not-primary-key