Chave JPA Composite com ManyToOne obtendo org.hibernate.PropertyAccessException: não foi possível definir um valor de campo pelo configurador de reflexão de
Eu tenho uma chave compostaContractServiceLocationPK
composto de três IDs (contractId
, locationId
, serviceId
) do tipo long em uma classe incorporável. A classe que usa essa chave composta,ContractServiceLocation
, mapeia esses IDs, usando@MapsId
anotação, para seus objetos. Veja como fica (setters / getters removidos e propriedades 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;
}
Ao tentar persistir um objeto do tipo ContractServiceLocation de qualquer forma (diretamente ou através de contrato), recebo:
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
Minha suposição é que o JPA / Hibernate espera um objeto Contract em vez de uma variável longa, mas se eu alterar as variáveis em incorporáveis de long para o tipo delas, receboThe type of the ID mapped by the relationship 'contract' does not agree with the primary key class of the target entity.
. Se eu tentar usar a classe id em vez de incorporável,mappedby
no mapeamento OneToMany do contrato, receboIn attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship.
. O que devo fazer para criar uma chave composta com váriosManyToOne
mapeamentos?
EDITAR: Adicionado um trecho em que tento persistir os itens:
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();
A razão pela qual parece isso, em vez de estar em algum DAO, é porque estou gerando o banco de dados e testando os itens antes de começar a desenvolver o restante do aplicativo.
EDIT 2: Reescrevi meus modelos e agora tudo parece funcionar, exceto no Eclipse, recebo um erro persistente. Veja como estão as coisas atualmente:
Contrato - Nenhuma alteração (exceto remoção do carregamento Ansioso)
ContractServiceLocationPK - Agora é uma classe 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()
}
Isso parece funcionar corretamente por enquanto. Ele cria uma chave composta e mantém um relacionamento muitos-para-um com todas as propriedades compostas. No entanto, há algo estranho. Marcas de eclipse no contratomappedBy
no@OneToMany
anotação para oContractServiceLocation
coleção com a mensagem de erroIn attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship.
. Estou assumindo que isso ocorre porque oContract
propriedade definida emContractServiceLocation
não tem um@ManyToOne
anotação, mas isso é definido na classe composta. Eu tropecei na armadilha "JPA não compatível, mas trabalhando com o Hibernate" ou o que está acontecendo aqui?