Many to Many JPA join query - java

#Entity
#Table(name = "customer", schema = "customer")
public class Customer {
#Id
#GeneratedValue
#Column(columnDefinition = "uuid", updatable = false, name = "customer_id")
private UUID customerId;
private String name;
#ManyToMany(mappedBy = "bookMembers")
Set<Reservation> reservations
}
#Entity
#Table(name = "reservation", schema = "reservation")
#NoArgsConstructor
public class Reservation {
#Id
#GeneratedValue
#Column(columnDefinition = "uuid", updatable = false, name = "reservation_id")
private UUID reservationId;
#JoinColumn(name = "reservation_id")
#Column(name = "reserved_by_customer_id")
private UUID reservedByCustomerId;
#JoinColumn(name = "menu_id")
private UUID menuId;
#ManyToMany
#JoinTable(
name = "reservation_customer",
schema = "customer",
joinColumns = {#JoinColumn(name = "reservation_id")},
inverseJoinColumns = {#JoinColumn(name = "customer_id")}
)
private Set<Customer> reservationMembers = new HashSet<>();
}
Dao Entity
#Data
#AllArgsConstructor
public class MenuCount {
private UUID menuId;
private Long count;
}
Repository
public interface ReservationRepository extends JpaRepository<Reservation, UUID> {
#Query("select new com.de.dao.MenuCount(r.menuId, count(c.customerId)) from customer c join c.reservations r group by r.menuId")
List<MenuCount> getCustomerCountGroupByMenuId();
}
I am trying to get the total number of customers for each menu Id. But when running the code getting the below error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'Repository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List com.de.repository.Repository.getCustomerCountGroupByMenuId()!
Native -
select r.menuId, count(c.customerId) from Customer c join reservation_customer cr on c.customerId = cr.customerId join Reservation r on r.reservationId = cr.reservationId groupBy r.menuId;
Can I acheive the native query functionality using #Query() in JPA without creating an additional entity module

Related

Spring Boot sort by nested Entity

I am trying to sort my API result by a nested entity field.
My Entities look like that:
#Entity
#Table(name = "book_item")
public class BookItem {
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#ManyToOne
#NotNull
private Book book;
...
}
#Entity
#Table(name = "book")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Book {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "title", nullable = false)
private String title;
private String subtitle;
#ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinTable(name = "rel_book_author", joinColumns = #JoinColumn(name = "book_id"), inverseJoinColumns = #JoinColumn(name = "author_id"))
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#JsonIgnoreProperties(value = { "books" }, allowSetters = true)
private Set<Author> authors = new HashSet<>();
#Column(name = "isbn", nullable = false)
private String isbn;
#Column(name = "pages", nullable = false)
private Integer pages;
...
}
Now I want to query all BookItems sorted by the Book titles.
My repository lloks like that:
public interface BookItemRepository extends JpaRepository<BookItem, UUID> {
#Query(
"select distinct b from BookItem b left join b.book.authors authors where (upper(authors.name) like upper(concat('%', ?2, '%')) or authors is NULL ) and upper(b.book.title) like upper(concat('%', ?1, '%'))"
)
Page<BookItem> findAllByTitleAndAuthor(String title, String name, Pageable pageable);
}
I've implemented an Angular component which does the following request:
GET http://localhost:9000/api/book/items?size=5&sort=title,asc
I get a 500 error code:
org.hibernate.QueryException: could not resolve property: title of: org.pickwicksoft.libraary.domain.BookItem [select distinct b from org.pickwicksoft.libraary.domain.BookItem b left join b.book.authors authors where (upper(authors.name) like upper(concat('%', ?2, '%')) or authors is NULL ) and upper(b.book.title) like upper(concat('%', ?1, '%')) order by b.title asc, b.id asc]; nested exception is java.lang.IllegalArgumentException
I also tried the following and it also does not work:
GET http://localhost:9000/api/book/items?size=5&sort=book_title,asc
How can I solve this problem?

Invalid Identifier While Joining Tables in JPA

I am trying to join 3 tables in JPA. When i am trying to associate CRL_IC_IMPORT_TRANS table i am getting error as Invalid Identifier as shown below
from
crl_ic investorco0_
left outer join
crl_ic_import_trans icimporttr1_
on investorco0_.icimport_trans_event_id=icimporttr1_.event_id
left outer join
crl_ic_order investorco2_
on investorco0_.current_order_id=investorco2_.id
"INVESTORCO0_"."ICIMPORT_TRANS_EVENT_ID": invalid identifier
Edit
from
crl_ic investorco0_
left outer join
crl_ic_order investorco1_
on investorco0_.current_order_id=investorco1_.id
Error:
Provided id of the wrong type for class ICImportTrans. Expected: class java.lang.Long, got class java.lang.String
Why its going to primary key in ICImportTrans class even after i mapped to a non PK ?
Below are my 3 tables and its keys. What is the mistake i am doing .
#Table(name = "CRL_IC")
public class ICLoanOrder {
#Id
#Column(name="EVENT_ID")
String id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "EVENT_ID",referencedColumnName = "eventId" )
ICImportTrans iCImportTrans;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
ICOrder currentOrder;
}
#Table(name = "CRL_IC_ORDER")
public class ICOrder implements Serializable {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Type(type = "uuid-char")
UUID id;
#ManyToOne(fetch = FetchType.LAZY)
#JsonIgnore
#JoinColumn(name="EVENT_ID")
ICLoanOrder iCLoanOrder;
}
#Table(name = "CRL_IC_IMPORT_TRANS")
public class ICImportTrans implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.SEQUENCE, generator="SEQ1")
#SequenceGenerator(name="SEQ1",sequenceName = "SEQ_IC_IMPORT",allocationSize = 1)
#Column(name="PROCESS_ID")
private Long processId;
private String eventId;
}
The default name of the column should be iCImportTrans_id.
If you want to change it to icimport_trans_event_id you need to specify it in the #JoinColumn
#Table(name = "CRL_IC")
public class ICLoanOrder {
...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "icimport_trans_event_id", referencedColumnName = "eventId")
ICImportTrans iCImportTrans;
...
}
Provided id of the wrong type for class ICImportTrans. Expected: class java.lang.Long, got class java.lang.String
What kind of mapping are you trying to achieve?
In ICLoanOrder you mapped the id column and the association column to the same name EVENT_ID. I guess you want to use ICImportTrans.eventId as id of ICLoanOrder.
This willwork if you don't care about having an id field:
#Entity(name="LoanOrder")
#Table(name = "CRL_IC")
public static class ICLoanOrder implements Serializable{
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "EVENT_ID", referencedColumnName = "eventId")
ICImportTrans iCImportTrans;
}
Normally, you would use #MapsId, but it seems to ignore the referencedColumn attribute and gives the same error.

Hibernate annotations: Many-to-many with shared composite key attribute

I'm trying to map up an existing database schema using Hibernate+JPA annotations.
One of my entities are mapped like this:
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
private int department;
#Id
private int userId;
...
And another entity, Group:
#Entity
#Table(name = "groups")
public class Group implements Serializable {
#Id
private int department;
#Id
private int groupId;
...
Group and User should have a many-to-many relationship between them, but the issue is that the join table ("user_group") only has columns "DEPARTMENT, USERID, GROUPID" - i.e. the DEPARTMENT column needs to be used in both joinColumns and inverseJoinColumns:
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(
name = "user_groups",
joinColumns = { #JoinColumn(name = "department"), #JoinColumn(name = "groupid") },
inverseJoinColumns = {#JoinColumn(name = "department"), #JoinColumn(name = "userid") }
)
private List<User> groupUsers = new ArrayList<>();
which gives a mapping error - "Repeated column in mapping for entity".
However, it looks like this was/is possible using XML, because this exact example exists in the old Hibernate documentation. But I cannot find any evidence that this ever worked using annotations? I tried with #JoinFormula instead of #JoinColumn, but that does not compile. Is it possible?
Okay, I'm pretty sure it's not possible.
I found a promising workaround:
Create an #Embeddable for the "user_group" table:
#Embeddable
public class UserGroupMembership implements Serializable {
#ManyToOne
#JoinColumnsOrFormulas(
value = {
#JoinColumnOrFormula(column = #JoinColumn(referencedColumnName = "userid", name = "userid")),
#JoinColumnOrFormula(formula = #JoinFormula(referencedColumnName = "department", value = "department"))
})
private User user;
public UserGroupMembership(User user) {
this.user = user;
}
public UserGroupMembership() {
}
public User getUser() {
return user;
}
}
The trick is that #ManyToOne allows you to use #JoinColumnsOrFormulas, so one of the join conditions can be a formula, which I doesn't seem to work for #ManyToMany (the #JoinColumnsOrFormulas annotation is ignored as it expects the join columns to be part of the #JoinTable annotation).
The UserGroupMemberships are then mapped as a ElementCollection:
#ElementCollection
#CollectionTable(name = "user_group", joinColumns = {
#JoinColumn(name = "department", referencedColumnName = "department"),
#JoinColumn(name = "groupid", referencedColumnName = "groupid")
})
#OrderColumn(name = "seq", nullable = false)
private List<UserGroupMemberships> groupUsers = new ArrayList<>();
This only works right now for a unidirectional many-to-many relationship.

Invalid custom update query defined in Spring Data JPA CrudRepository

I have a custom JPQL query in a Spring CrudRepository that's not working.
This is my entity class, PK class and CrudRepository interface for the entity:
#Entity(name = "TBL_PRINCIPAL_CREDENTIAL")
public class PrincipalCredential {
#EmbeddedId
private PrincipalCredentialPK principalCredentialPK;
// ...getter & setter for principalCredentialPK
}
#Embeddable
public class PrincipalCredentialPK implements Serializable {
#Column(name = "PRINCIPAL_TYPE_ID", nullable = false)
private String principalTypeID;
#Column(name = "PRINCIPAL_ID", nullable = false)
private String principalID;
#Column(name = "CREDENTIAL_ID", nullable = false)
private Integer credentialID;
#Column(name = "CREDENTIAL_TYPE_ID", nullable = false)
private String credentialTypeID;
// ...getters & setters for all fields...
}
#Transactional
public interface PrincipalCredentialRepository extends CrudRepository<PrincipalCredential, PrincipalCredentialPK> {
#Modifying
#Query("update PrincipalCredential pc set pc.principalCredentialPK.principalID =:newPrincipalID " +
"where pc.principalCredentialPK.principalID =:oldPrincipalID and pc.principalCredentialPK.principalTypeID =:principalType")
void updatePrincipalID(#Param("oldPrincipalID") String oldPrincipalID, #Param("newPrincipalID") String newPrincipalID,
#Param("principalType") String principalType);
}
When I start my project using SpringBoot the repository bean cannot be instantiated and I get the following exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'principalCredentialRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract void com.consorsbank.services.banking.caas.repositories.PrincipalCredentialRepository.updatePrincipalID(java.lang.String,java.lang.String,java.lang.String)!
Caused by: java.lang.IllegalArgumentException: Validation failed for query for method public abstract void com.consorsbank.services.banking.caas.repositories.PrincipalCredentialRepository.updatePrincipalID(java.lang.String,java.lang.String,java.lang.String)!
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: PrincipalCredential is not mapped [update PrincipalCredential pc set pc.principalCredentialPK.principalID =:newPrincipalID where pc.principalCredentialPK.principalID =:oldPrincipalID and pc.principalCredentialPK.principalTypeID =:principalType]
Also for another repository this query is working, the difference is that the PK of the other entity is simpler and both ids are provided there...
#Entity
#Table(name = "TBL_PRINCIPALS")
public class Principal implements Serializable {
#EmbeddedId
private PrincipalPK principalPK;
#OneToOne
#JoinColumn(name = "PRINCIPAL_TYPE_ID", insertable = false, updatable = false)
private PrincipalType principalType;
#Column(name = "USER_ID")
private Integer userID;
#Column(name = "VALID_UNTIL")
private Date validUntil;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "ZAAS_TBL_PRINCIPAL_CREDENTIAL",
joinColumns = {#JoinColumn(name = "PRINCIPAL_ID"), #JoinColumn(name = "PRINCIPAL_TYPE_ID")},
inverseJoinColumns = {#JoinColumn(name = "CREDENTIAL_ID"), #JoinColumn(name="CREDENTIAL_TYPE_ID")})
public Set<Credential> credentials;
// ...getters and setters...
}
#Embeddable
public class PrincipalPK implements Serializable {
#Column(name = "PRINCIPAL_TYPE_ID", nullable = false)
private String principalTypeID;
#Column(name = "PRINCIPAL_ID", nullable = false)
private String principalID;
// ...getters and setters
}
#Transactional
public interface PrincipalsRepository extends CrudRepository<Principal, PrincipalPK> {
#Modifying
#Query("update Principal p set p.principalPK.principalID =:newPrincipalID " +
"where p.principalPK.principalID =:oldPrincipalID and p.principalPK.principalTypeID =:principalType")
void updatePrincipalID(#Param("oldPrincipalID") String oldPrincipalID, #Param("newPrincipalID") String newPrincipalID,
#Param("principalType") String principalType);
}
So the above query is working...
Could someone please point out what I'm missing for the query defined in the PrincipalCredentialRepository?
The entity definition seems to be wrong. Use the following annotations
#Entity
#Table(name = "TBL_PRINCIPAL_CREDENTIAL")
public class PrincipalCredential {
//...

How to select Entity in one-to-many relationship?

I have two entities with one-to-many relationship (owner-one , book-many).
#Entity
#Table(name = "owners")
#NamedEntityGraph(name = "Owner.books",
attributeNodes = #NamedAttributeNode("books"))
public class Owner implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "owner_id", nullable = false, unique = true)
private Long id;
#Column(name = "owner_name", nullable = false)
private String name;
#OneToMany(fetch = FetchType.LAZY,mappedBy = "owner")
private Set<Book> books= new HashSet<>(0);
}
#Entity
#Table(name = "books")
#NamedEntityGraph(name = "Book.owner",
attributeNodes = #NamedAttributeNode("owner"))
public class Book implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "book_id", unique = true, nullable = false)
private Long id;
#Column(name = "book_name", nullable = false, unique = true)
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "owner_id")
private Owner owner;
}
I've created method which finds List of Books by Owner. It was simply.
public interface BookRepository extends JpaRepository<Book, Long> {
#Query("select b from Book b where b.owner=:b")
#EntityGraph(value = "Book.owner", type = EntityGraph.EntityGraphType.LOAD)
List<Book> findListByOwner(#Param("w") Owner w);
}
But I have to make method in my JpaRepository which finds 'Owner' by 'Book' but I don't know how to make JPQL Query. I don't know ho to work with Set in my JPQL statement.
public interface OwnerRepository extends JpaRepository<Owner, Long> {
#Query("select w from Owner w join fetch w.books where ???? dont know")
#EntityGraph(value = "Owner.books", type = EntityGraph.EntityGraphType.LOAD)
Owner findOneByBook(#Param("b") Book);
}
UPDATE
I can not use getBook() and getOwner because when I read List of Books I have only Id of Owner. I can add the alias to "w.books" as something like "b" but I don't what to do next to that alias. Anyway I use new #query ("select o from Book b join b.owner o where b = :b"), but hibernate throws exception.
Caused by: org.hibernate.QueryException: could not resolve property: books of: com.company.entities.Task [select o from Book b join b.owner o where b = :b]
at org.hibernate.QueryException.generateQueryException(QueryException.java:137)
at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:120)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:234)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:158)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:131)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:98)
at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:568)
at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:495)
... 65 more
Caused by: org.hibernate.QueryException: could not resolve property: books of: com.company.entities.Book
at org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:83)
at org.hibernate.persister.entity.AbstractPropertyMapping.toType(AbstractPropertyMapping.java:77)
at org.hibernate.persister.entity.AbstractEntityPersister.toType(AbstractEntityPersister.java:1978)
at org.hibernate.hql.internal.ast.tree.FromElementType.getPropertyType(FromElementType.java:367)
at org.hibernate.hql.internal.ast.tree.FromElement.getPropertyType(FromElement.java:500)
at org.hibernate.engine.query.spi.EntityGraphQueryHint.getFromElements(EntityGraphQueryHint.java:95)
at org.hibernate.engine.query.spi.EntityGraphQueryHint.toFromElements(EntityGraphQueryHint.java:68)
at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:685)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:665)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:301)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:249)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:278)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:206)
... 70 more
Any ideas? I need help

Categories

Resources