Clave compuesta JPA con ManyToOne obteniendo org.hibernate.PropertyAccessException: no se pudo establecer un valor de campo por medio del reflector

Tengo una clave compuestaContractServiceLocationPK hecho de tres id (contractId, locationId, serviceId) de tipo largo en una clase incorporable. La clase que usa esta clave compuesta,ContractServiceLocation, asigna estos identificadores, utilizando@MapsId anotación, a sus objetos. Así es como se ve (setters / getters eliminados y propiedades irrelevantes):

Contrato

@Entity
@Table(name = "Contract")
public class Contract implements Serializable {

    public Contract() {
    }

    @Id
    @GeneratedValue
    private long id;
    @OneToMany(mappedBy = "contract", cascade = CascadeType.ALL, fetch= FetchType.EAGER)
    Collection<ContractServiceLocation> contractServiceLocation;
}

ContractServiceLocationPK

@Embeddable
public class ContractServiceLocationPK implements Serializable {

    private long contractId;
    private long locationId;
    private long serviceId;
}

ContractServiceLocation

@Entity
@Table(name="Contract_Service_Location")
public class ContractServiceLocation implements Serializable {

    @EmbeddedId
    ContractServiceLocationPK id;

    @ManyToOne(cascade = CascadeType.ALL)
    @MapsId("contractId")
    Contract contract;

    @ManyToOne(cascade = CascadeType.ALL)
    @MapsId("locationId")
    Location location;

    @ManyToOne(cascade = CascadeType.ALL)
    @MapsId("serviceId")
    Service service;    

    BigDecimal price;
}

Cuando intento persistir un objeto de tipo ContractServiceLocation de cualquier manera (directamente o mediante contrato) obtengo:

Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.test.model.ContractServiceLocationPK.contractId
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1187)
    at com.test.MainTest.main(MainTest.java:139)
Caused by: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.test.model.ContractServiceLocationPK.contractId
    at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:134)
    at org.hibernate.mapping.Component$ValueGenerationPlan.execute(Component.java:441)
    at org.hibernate.id.CompositeNestedGeneratedValueGenerator.generate(CompositeNestedGeneratedValueGenerator.java:121)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:117)
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
    ... 1 more
Caused by: java.lang.NullPointerException
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(Unknown Source)
    at sun.reflect.UnsafeLongFieldAccessorImpl.set(Unknown Source)
    at java.lang.reflect.Field.set(Unknown Source)
    at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:122)
    ... 12 more

Mi suposición es que JPA / Hibernate espera un objeto Contract en lugar de una variable larga, pero si cambio las variables en incrustables de largo a su tipo, obtengoThe type of the ID mapped by the relationship 'contract' does not agree with the primary key class of the target entity.. Si intento usar la clase id en lugar de incrustable, entoncesmappedby en el mapeo OneToMany de Contract que obtengoIn attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship.. ¿Qué debo hacer para hacer una clave compuesta con múltiplesManyToOne mapeos?

EDITAR: Agregué un fragmento donde trato de persistir los elementos:

    Service service = new Service();
    // Set all service properties       
    Contract contract = new Contract();
    // Set all contract properties
    Location location = new Location();
    // Set all location properties
    ContractServiceLocation csl = new ContractServiceLocation();
    csl.setContract(contract);
    csl.setLocation(location);
    csl.setService(service);
    Collection<ContractServiceLocation> cslItems = new ArrayList<>();
    cslItems.add(csl);

    em.getTransaction().begin();
    em.persist(location);
    em.persist(service);
    em.persist(csl);
    em.persist(contract);
    em.getTransaction().commit();

La razón por la que se ve así en lugar de estar en algún DAO es porque estoy generando la base de datos y probando los elementos primero antes de continuar con el desarrollo del resto de la aplicación.

EDITAR 2: He reescrito mis modelos y ahora todo parece funcionar, excepto en Eclipse, obtengo un error persistente. Así es como se ven las cosas actualmente:

Contrato: sin cambios (excepto que se eliminó la carga Eager)

ContractServiceLocationPK: ahora es una clase de ID

public class ContractServiceLocationPK implements Serializable {

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)      
    @JoinColumn(name = "contract_id")
    private Contract contract;
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)      
    @JoinColumn(name = "location_id")
    private Location location;
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)      
    @JoinColumn(name = "service_id")
    private Service service;

    //getters and setters
    //overridden equals() and hashCode()
}

ContractServiceLocation

@Entity
@Table(name="Contract_Service_Location")
@IdClass(ContractServiceLocationPK.class)
public class ContractServiceLocation implements Serializable {

    @Id
    Contract contract;

    @Id
    Location location;

    @Id
    Service service;    

    BigDecimal price;
        //getters and setters
        //overridden equals() and hashCode()
}

Esto parece funcionar correctamente por ahora. Crea una clave compuesta y mantiene una relación de muchos a uno con todas las propiedades compuestas. Sin embargo hay algo raro. En contrato marcas de eclipsemappedBy sobre el@OneToMany anotación para elContractServiceLocation colección con el mensaje de errorIn attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship.. Supongo que esto se debe a que elContract propiedad definida enContractServiceLocation no tiene un@ManyToOne anotación, pero eso se define en la clase compuesta. ¿Me topé con la trampa "JPA no conforme pero trabajando con Hibernate" o qué está pasando aquí?

Respuestas a la pregunta(2)

Su respuesta a la pregunta