Caused by: org.hibernate.MappingException: Unknown collection role - embedabble + criteriaBuilder - java

I'm trying to create List of expressions for my elementCollection defined in an embeddable using the criteria builder, but it's always throwing:
Caused by: org.hibernate.MappingException: Unknown collection role: be.fgov.health.gift.action.domain.potentialdonorfile.patient.GeneralPatientInformation.causesOfDeath
Has anyone got any idea to work around this problem?
This is my code:
The entities :
#Entity
#Table(name = "POTENTIAL_DONOR_FILE")
#SequenceGenerator(name = Domain.SEQUENCE_GENERATOR_NAME, sequenceName = "PDF_SEQUENCE")
//Domain contains the Id
public class PotentialDonorFile extends Domain {
#Embedded
private GeneralPatientInformation generalPatientInformation;
}
#Embeddable
public class GeneralPatientInformation {
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CAUSES_OF_DEATH",
joinColumns = #JoinColumn(name = PDF_ID_JOIN_COLUMN_NAME))
#Column(name = "CAUSE_OF_DEATH")
#Enumerated(EnumType.STRING)
#Fetch(FetchMode.SELECT)
private List<CauseOfDeath> causesOfDeath;
}
The repository layer :
Expression<List<CauseOfDeath>> listExpression = getGeneralPatientInformationPath(potentialDonorFileRoot).get(GeneralPatientInformation_.causesOfDeath);
private Path<GeneralPatientInformation> getGeneralPatientInformationPath(Root<PotentialDonorFile> potentialDonorFileRoot) {
return potentialDonorFileRoot.get(PotentialDonorFile_.generalPatientInformation);
}
So to summarize:
When i'm calling "xxx.get(GeneralPatientInformation_.causesOfDeath);" I'm getting the MappingException.
EDIT:
As test I've moved the ElementCollection to the PotentialDonorFile itself (so outside of the embeddable), and now it seems to be working.. Can't consider this as a solution, but put I've it here as an extra comment.

Related

JpaObjectRetrievalFailureException when saving entity with one-to-many and client-assigned ids

In a simple Spring Boot Application, I'm facing with a JpaObjectRetrievalFailureException when I'm trying to save an entity with one-to-many association and client-assigned ids.
Please take a look on these entities and on this simple repository:
#Entity
#Table(name = "cart")
public class Cart {
#Id
#Column(name = "id")
private UUID id;
#Column(name = "name")
private String name;
#OneToMany
#JoinColumn(name = "cart_id")
private List<Item> items;
// constructors, getters, setters, equals and hashCode ommitted
}
#Entity
#Table(name = "item")
public class Item {
#Id
#Column(name = "id")
private UUID id;
#Column(name = "name")
private String name;
// constructors, getters, setters, equals and hashCode ommitted
}
public interface CartRepository extends JpaRepository<Cart, UUID> {
}
I wrote this test:
#DataJpaTest
class CartRepositoryTest {
#Autowired
private CartRepository cartRepository;
#Test
void should_save_cart() {
// GIVEN
final var cart = new Cart(UUID.randomUUID(), "cart");
final var item = new Item(UUID.randomUUID(), "item");
cart.setItems(List.of(item));
// WHEN
final var saved = cartRepository.save(cart);
// THEN
final var fetched = cartRepository.findById(saved.id());
assertThat(fetched).isPresent();
}
}
When I run the test, call to cartRepository.save(cart) fails with:
Unable to find com.example.testjpaonetomany.domain.Item with id f5658508-f3d0-4d9b-a1f0-17b614753356; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.example.testjpaonetomany.domain.Item with id f5658508-f3d0-4d9b-a1f0-17b614753356
org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find com.example.testjpaonetomany.domain.Item with id f5658508-f3d0-4d9b-a1f0-17b614753356; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.example.testjpaonetomany.domain.Item with id f5658508-f3d0-4d9b-a1f0-17b614753356
at app//org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:379)
at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:235)
at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
at app//org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at app//org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at app//org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at app//org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at app//org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at app//org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at app/jdk.proxy3/jdk.proxy3.$Proxy105.save(Unknown Source)
at app//com.example.testjpaonetomany.repository.CartRepositoryTest.should_save_cart(CartRepositoryTest.java:28)
If I modify my entities by adding #GeneratedValue for ids, and in my test, I replace UUID.randomUUID() by null to delegate to Hibernate the ID generation, the test passes.
How to deal with client-generated ids?
The cause is that you save the parent object only (which is absolutely correct and fine) but still need to explain JPA that the operation should be propagated i.e.
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "cart_id")
private List<Item> items;
As minor improvements I would suggest to put the UUID generation into constructors and establish the relation via the dedicated method i.e.
final var cart = new Cart("cart");
cart.addItem(new Item("item"));
and probably consider using em.persist() instead of repository.save() as it makes a select request first in case of using uuids as #Augusto mentioned

How to ignore hibernate List<>[] arrays methods from being scanned during startup?

I have a spring-boot application with JPA, and I have the following mapping class:
#Entity
public class Entry {
#Id
#GeneratedValue(generator = "ENTRY_SEQ")
#SequenceGenerator(name = "ENTRY_SEQ", sequenceName = "ENTRY_SEQ")
private Long id;
#ManyToOne
#JoinColumn(name = "CONSTR_TABLE_ID", nullable = false)
private ConstraintTable constraintTable;
#OneToMany(mappedBy = "entry", cascade = CascadeType.ALL, orphanRemoval = true)
private List<EntryValue> entryValues = new ArrayList<>();
// default getters and setters
public List<EntryValue>[] createValuesGroupedByColumn() {
return null;
}
}
Every time I start my application it throws the following exception:
Caused by: java.lang.IllegalArgumentException: No PropertyTypeExtractor available for type void
It's caused because List<EntryValue>[] it's not a valid type supported in JavaReflectionManager.toXType, it only supports arrays, collections and simple types. However I would like to have this method and configure hibernate to ignore it.
In this post was suggested to use the #Transient annotation, but it didn't work. I tried different names that don't start with [get|set|is]AttrName (the standard bean convention) and also didn't work. Is there someway I can ignore this method from being scanned by hibernate during initialization?

Error in Join on a relation ManyToOne. Using QueryDSL

I have problems using join with QUeryDSL. I'm trying to receive a list of "Clube" which has a ManyToOne relation with "Federacao".
Could someone help me?
Thanks.
The relation is between the classes below.
#Entity
#Table(name = "federacoes")
#SequenceGenerator(name = "idgen", sequenceName = "federacoes_id_federacao_seq", allocationSize = 1)
#AttributeOverride(name = "id", column = #Column(name = "id_federacao"))
public class Federacao extends AbstractEntity{
private String sigla;
private String uf;
private String nome;
...
}
#Entity
#Table(name = "clubes")
#SequenceGenerator(name = "idgen", sequenceName = "clubes_id_clube_seq", allocationSize = 1)
#AttributeOverride(name = "id", column = #Column(name = "id_clube"))
public class Clube extends AbstractEntity{
private Federacao federacao;
...
#ManyToOne
#JoinColumn(name = "id_federacao")
#Column(name = "federacao")
public Federacao getFederacao() {
return federacao;
}
...
}
I'm using QueryDSL and in the repository class which extends QueryDslRepositorySupport I'm doing this:
...
#PersistenceContext
private EntityManager em;
private static final QClube qClube = QClube.clube;
private static final QFederacao qFederacao = QFederacao.federacao;
#Override
public List<Clube> findAll(FilterClubeDTO filterClubeDTO) {
JPAQuery query = new JPAQuery(em);
return query.from(qClube)
.innerJoin(qFederacao).on(qClube.federacao.eq(qFederacao))
.orderBy(qClube.id.desc())
.list(qClube);
}
I'm receiving this exception:
2016-04-12 12:32:38.485 ERROR 2853 --- [ qtp36627152-15] o.h.hql.internal.ast.ErrorCounter : Path expected for join!
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.ast.QuerySyntaxException: Path expected for join! [select clube
from br.com.cbfm.core.models.Clube clube
inner join Federacao federacao with clube.federacao = federacao
order by clube.id desc]; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Path expected for join! [select clube
from br.com.cbfm.core.models.Clube clube
inner join Federacao federacao with clube.federacao = federacao
order by clube.id desc]
This should work as expected:
JPAQuery query = new JPAQuery(em);
return query.from(qClube)
.innerJoin(qClube.federacao, qFederacao)
.orderBy(qClube.id.desc())
.list(qClube);
The query provided by Meiko is right. I've downloaded your project and found some issues related with the relations of the models.
Don't use the following code,
#ManyToOne
#JoinColumn(name = "id_federacao")
#Column(name = "federacao")
public Federacao getFederacao() {
return federacao;
}
But use,
#ManyToOne
#JoinColumn(name = "federacao")
private Federacao federacao;
Use the name "federacao" instead "id_federacao" because the column you want to join is clubes.federacao and not clubes.id_federacao.
You can't use #Column with #ManyToOne.
That should solve you're problem. I've also modified some little things, but that shouldn't affect the code to work.
The problem was in my mapping. I just fixed it doing it:
Class Clube
#ManyToOne
#JoinColumn(name="id_federacao", nullable=false)
private Federacao federacao;
and Class Federacao
#OneToMany(mappedBy="federacao")
private Set<Clube> clubes;
I have now another problem that I'll post right now.
Thanks everyone.

Spring Boot JPA - OneToMany relationship causes infinite loop

I have a two objects with simple #OneToMany relationship which looks as follows:
parent:
#Entity
public class ParentAccount {
#Id
#GeneratedValue
private long id;
private String name;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "parentAccount")
private Set<LinkedAccount> linkedAccounts;
}
child:
#Entity
public class LinkedAccount {
#Id
#GeneratedValue
private long id;
#ManyToOne(optional = false)
private ParentAccount parentAccount;
private String name;
// empty constructor for JPA
public LinkedAccount() {
}
}
I ma using Spring CrudRepository to operate with these entities. However, when calling ParentAccount parent = parentAccountRepository.findOne(id);, some kind of infinite loop starts happening and hibernate spams this all over the console:
Hibernate: select linkedacco0_.parent_account_id as parent_a6_1_0_, linkedacco0_.id as id1_0_0_, linkedacco0_.id as id1_0_1_, linkedacco0_.aws_id as aws_id2_0_1_, linkedacco0_.key_id as key_id3_0_1_, linkedacco0_.name as name4_0_1_, linkedacco0_.parent_account_id as parent_a6_0_1_, linkedacco0_.secret_key as secret_k5_0_1_ from linked_account linkedacco0_ where linkedacco0_.parent_account_id=?
I tried changed the fetch type to LAZY but then I get this error:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.berrycloud.scheduler.model.ParentAccount.linkedAccounts, could not initialize proxy - no Session
(It seems that it is trying to do the lazy load outside of the transactional context).
This is my CRUD repository:
#Repository
public interface ParentAccountRepository extends CrudRepository<ParentAccount, Long> {
}
Could someone tell me how to resolve this issue? I would prefer the solution with EAGER fetch. Thank you for any tips
EDIT: here is the schema I am using
CREATE TABLE parent_account (
id BIGINT auto_increment,
name VARCHAR(80) null,
PRIMARY KEY (`id`)
);
CREATE TABLE linked_account (
id BIGINT auto_increment,
parent_account_id BIGINT,
name VARCHAR(80) null,
FOREIGN KEY (`parent_account_id`) REFERENCES `parent_account` (`id`),
PRIMARY KEY (`id`)
);
As the first answer suggests:
Do not use Lombok's #Data annotation on #Entity classes.
Reason: #Data generates hashcode(), equals() and toString() methods that use the generated getters. Using the getter means of course fetching new data even if the property was marked with FetchType=LAZY.
Somewhere along the way hibernate tries to log the data with toString() and it crashes.
Problem solved. I was using a custom #toString method in the LinkedAccount which was referencing the ParentAccount. I had no idea that this could cause any problem and therefor I did not include the toString in my question.
Apparently, this was causing an infinite loop of lazy loading and removing this reference fixed the problem.
As user1819111 told, #Data from Lombok is not compatible with #Entity and FetchType=LAZY. I had used Lombok.Data (#Data) and I was getting this error.
As I don't want do create all get/set, I just put the Lombok #Setter and #Getter in your class and all will work fine.
#Setter
#Getter
#Entity
#Table(name = "file")
#SequenceGenerator(name = "File_Sequence", allocationSize=1, sequenceName = "file_id_seq")
public class MyClass{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "File_Sequence")
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "file", cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
private Set<Base2FileDetail> details = new HashSet<>();
}
Something like this does not work?
#Entity
public class Account {
#Id
#GeneratedValue
private long id;
private String name;
#ManyToOne(cascade={CascadeType.ALL})
#JoinColumn(name="manager_id")
private Account manager;
#OneToMany((fetch = FetchType.EAGER, mappedBy="manager")
private Set<Account> linkedAccounts = new HashSet<Account>();
}
I recently had this issue due to a poorly defined Jackson2HttpMessageConverter.
I had done something like the following.
#Bean
RestTemplate restTemplate(#Qualifier("halJacksonHttpMessageConverter")
TypeConstrainedMappingJackson2HttpMessageConverter halConverter) {
final RestTemplate template = new RestTemplateBuilder().build();
halConverter.setSupportedMediaTypes(List.of(/* some media types */));
final List<HttpMessageConverter<?>> converters = template.getMessageConverters();
converters.add(halConverter);
template.setMessageConverters(converters);
return template;
}
This caused a problem because the media types did not include all the defaults. Changing it to the following fixed the issue for me.
halConverter.setSupportedMediaTypes(
new ImmutableList.Builder<MediaType>()
.addAll(halConverter.getSupportedMediaTypes())
.add(/* my custom media type */)
.build()
);
This simple way worked for me. Just use JsonIgnoreProperties .
#JsonIgnoreProperties(value = {"linkedAccounts"})
#ManyToOne(cascade = { CascadeType.PERSIST})
#JoinColumn(name = "abc", referencedColumnName = "abc")
private ParentAccount parentAccount;
This way worked for me without removing #ToSring annotation:
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#AllArgsConstructor
#Table(name = "parent_accounts")
public class ParentAccount {
#JsonIgnoreProperties({"parentAccount"})
#OneToMany(mappedBy = "parentAccount",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<LinkedAccount> linkedAcounts;
// ...
}
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#AllArgsConstructor
#Table(name = "linked_accounts")
public class LinkedAccount {
#JsonIgnoreProperties("linkedAcounts")
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "parentAccount_id")
private ParentAccount parentAccount;
// ...
}
PS: In #JsonIgnoreProperties You can also ignore more than one field for preventing infinite loop

Spring data JPA using Entity Type expression

I would like to use the JPA 2.0 Type expression in a spring data JPA repository #Query but it give me a validation error Validation failed for query for method public abstract java.util.List my.org.FooRepository.findAllByRoomCode()!.
Here is my entities definition :
inherited :
#Entity(name = "Location")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class LocationEntity {
#Column(name = "CODE")
protected String code;
#OneToMany(mappedBy = "location")
protected List<LampEntity> lamps = new ArrayList<LampEntity>();
...
}
#Entity(name = "Floor")
#Table(name = "FLOOR", uniqueConstraints = {
#UniqueConstraint(name = "FLOOR_UK", columnNames = "CODE")
})
public class FloorEntity extends LocationEntity {
...
#OneToMany(mappedBy = "floor")
private List<RoomEntity> rooms = new ArrayList<RoomEntity>();
...
}
#Entity(name = "Room")
#Table(name = "ROOM", uniqueConstraints = {
#UniqueConstraint(name = "ROOM_UK", columnNames = { "CODE", "FLOOR_ID" })
})
public class RoomEntity extends LocationEntity {
...
#ManyToOne
#JoinColumn(name = "FLOOR_ID", nullable = false, referencedColumnName = "ID")
private FloorEntity floor;
...
}
requested entity :
#Entity(name = "Lamp")
#Table(name = "LAMP")
public class LampEntity {
#ManyToOne
#JoinColumn(name = "LOCATION_ID", referencedColumnName = "ID")
private LocationEntity location;
}
repository :
public interface LampRepository extends JpaRepository<LampEntity, Long> {
#Query("SELECT lamp FROM Lamp lamp WHERE lamp.location.code = :locationCode AND TYPE(lamp.location) = Room")
public List<LampEntity> findAllByRoomCode(#Param("code") String locationCode);
...
}
I'm using spring-data-JPA 1.4.1.RELEASE with Hibernate 4.2.7.Final as provider.
My questions are :
Is it possible to do that with Spring data JPA ?
In a old stackoverflow post i saw that Hibernate accepted it only with InheritanceType.SINGLE_TABLE but maybe now it's ok in 4.2.7 ?
If not, what are my alternative if I want to keep my InheritanceType.TABLE_PER_CLASS strategy ?
Thanks a lot
Updated with unique constraints and relation between Floor and Room
If you use Spring JPA repository, you can do this without #Query by renaming the method as:
public List<LampEntity> findByLocation_Code(#Param("code") String locationCode);
Just make sure all your setters and getters are in place.
OK, my mistake, I have misunderstood a couple of things and not read fully the stacktrace :
The error doesn't belong to Spring Data but to Hibernate. The real Caused by is org.hibernate.QueryException: could not resolve property: class of: xxx.yyy.LampEntity [SELECT lamp FROM xxx.yyy.LampEntity lamp WHERE lamp.location.code = :locationCode AND TYPE(lamp.location) = Room]
As the property class is clearly named here, I ran a couple of tests of validity (the requests below are totally functionnaly incorrect, it's just for the syntax) :
#Query("SELECT lamp FROM Lamp lamp WHERE lamp.location.code = :code AND TYPE(lamp) = Lamp") fails, same stacktrace
#Query("SELECT lamp FROM Lamp lamp WHERE lamp.location.code = :code AND lamp.location.class = Room") OK, works correctly, the expected results are returned (only the lamps which have a room's location)
I Created a new Repository public interface LocationRepository extends JpaRepository<LocationEntity, Long> and I tested :
#Query("SELECT location FROM Location location WHERE TYPE(location) = Room AND location.code = :code") OK, works correctly.
So I'll go with the lamp.location.class (Hibernate specific :() because it seems Hibernate (or JPA ?) doesn't allow to TYPE nor an attribute in an HQL request, neither an entity which is not inherited (pretty logic this one).
Please let me know if there is any complement or correction to this answer.

Categories

Resources