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?
Related
I have two table named recipe and tags, joined with many to many relationship
This is recipe table:
#Data
#NoArgsConstructor
#Entity()
#Table(name = "recipes",
uniqueConstraints = {
#UniqueConstraint(name = "recipe_title_unique", columnNames = "title")
})
public class RecipeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false)
private Long id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
joinColumns = #JoinColumn(name = "recipe_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "tag_id", referencedColumnName = "id")
)
private Set<TagEntity> tags;
And this is tags table:
#Data
#NoArgsConstructor
#Entity()
#Table(name = "tags", uniqueConstraints = {
#UniqueConstraint(name = "tag_unique", columnNames = "name")
})
public class TagEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false)
private int id;
#Column(name = "name", nullable = false)
private String name;
ERD Diagram for many to many relationship
My question is, how I access recipes_tags table with JPQL?
For example I want to know how many recipe that use tag with ID 1.
#Query(value = "SELECT " +
"COUNT t.tag_id " +
"FROM recipes_tags t +
"WHERE t.tag_id = :tagId")
Int getCount(#Param("tagId") int tagId);
I rewrite my SpringMVC app with using Hibernate.I try to make query for selecting lectures for group of students by id of group.with using SQL query(before I started rewrite it with using Hibernate)this query was:
"SELECT * FROM lectures WHERE id IN (SELECT lecture_id FROM lectures_groups WHERE group_id =?) ORDER BY date_of_lecture"
I have Lecture and Group etities:
#Entity
#Table(name = "lectures")
public class Lecture {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "lectures_groups", joinColumns = #JoinColumn(name = "lecture_id"), inverseJoinColumns = #JoinColumn(name = "group_id"))
private List<Group> groups = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "teacher_id")
private Teacher teacher;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "subject_id")
private Subject subject;
#Column(name = "date_of_lecture")
private LocalDateTime date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "audience")
private Audience audience;
public Lecture() {
}
//getters setters
}
and:
#Entity
#Table(name = "groups")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "group_name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cathedra_id", referencedColumnName = "id")
private Cathedra cathedra;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "lectures_groups", joinColumns = #JoinColumn(name = "group_id"), inverseJoinColumns = #JoinColumn(name = "lecture_id"))
private List<Lecture> lectures = new ArrayList<>();
public Group() {
}
//getters setters
}
I tried somthing like:
List<Lecture> lectures = session.createQuery("select l from lectures l join l.groups g where g.id=:groupId")
.setParameter("groupId", group.getId())
.list();
but I get Exception: org.hibernate.hql.internal.ast.QuerySyntaxException: lectures is not mapped
So how can i do it?
In hql query you need to provide the name of the entity in the query instead of the table name. So in your case, you should replace lectures with Lecture in the query.
List<Lecture> lectures = session.createQuery("select l from Lecture l join l.groups g where g.id=:groupId")
.setParameter("groupId", group.getId())
.list();
I am facing the n+1 query issue when I use #JoinColumnsOrFormulas or #JoinColumns with #NamedEntityGraph(includeAllAttributes = true)
I tried to also left join the entity in Specification and it also cause n+1 query issue.
Entity:
#Data
#Entity
#Table(name = "book")
#NamedEntityGraph(name = "all", includeAllAttributes = true)
public class BookEntity implements JpaEntity {
#ManyToOne(targetEntity = CustomerEntity.class)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column = #JoinColumn(name = "customer", referencedColumnName = "number", insertable = false, updatable = false)),
#JoinColumnOrFormula(column = #JoinColumn(name = "sub_customer", referencedColumnName = "sub_number", insertable = false, updatable = false)),
#JoinColumnOrFormula(formula = #JoinFormula(value = "'2019'", referencedColumnName = "year"))
})
private CustomerEntity customerEntity;
}
#Entity
#Data
#Table(name = "customer")
public class CustomerEntity implements JpaEntity {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "number", columnDefinition = "INTEGER")
private Integer number;
#Column(name = "sub_number", columnDefinition = "INTEGER")
private Integer subNumber;
#Column(name = "year")
private String year;
}
Repository:
#Override
#EntityGraph(value = "all")
Page<BookEntity> findAll(Specification<BookEntity> spec, Pageable pageable);
I see that the query is correct. I see the join for the first query, so it looks like I have the full entity and no need to make n+1 query. But after correct query I see additional selects for the CustomerEntity.
Can someone help me to avoid n+1 issue with this case?
I have two table in relation many to many
public class Repertoire {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, unique = true)
private Integer id;
private String name;
private Integer dayWeek;
#ManyToMany(cascade = CascadeType.REMOVE)
#JoinTable(
name = "repertoire_seance",
joinColumns = { #JoinColumn(name = "repertoire_id")},
inverseJoinColumns = {#JoinColumn(name = "seance_id")}
)
List<Seance> seances = new ArrayList<>();
}
and
public class Seance {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, unique = true)
private Integer id;
private java.time.LocalTime displayTime;
#ManyToOne
private Film film;
#Column(length=127)
private String kind;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Hall hall;
#OneToMany(mappedBy = "reservationSeance")
#JsonIgnore
private List<Reservation> reservations = new ArrayList<>();
}
Hibernate create linked tabel repertoire_seance is posible get seances by seancesId and repertoire.dayWeek using spring data (JpaRepository) something like that findBySeanceIdAndRepertoireDayWeek()?
You could achieve this by writing an HQL.
It would look something like this:
select s from Repertoire r inner join r.seances s where r.dayWeek ="Your Value" and s.id = "Your Id Value"
I have a class Comment:
#Entity
#Table(name = Constants.COMMENTS_TABLE)
#Audited
public class Comment {
#Column(name = "comment", nullable = false)
private String comment;
#ElementCollection(targetClass = CommentTopic.class)
#Enumerated(EnumType.STRING)
#Fetch(value = FetchMode.JOIN)
#CollectionTable(name = Constants.COMMENTS_TOPIC_JOIN_TABLE, joinColumns = #JoinColumn(name = "comment_id"))
#Column(name = "topic")
private Set<CommentTopic> commentTopics;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "comment_id", nullable = false)
private Long commentId;
}
Persisting the comment class works but the following criteria query:
Criteria criteria = session.createCriteria(Comment.class)
.add(Restrictions.eq("commentTopics", topic));
List<Comment> entries = criteria.list();
throws org.hibernate.exception.DataException: No value specified for parameter 1.
This is the query built:
select this_.comment_id as comment1_0_0_, this_.comment as comment0_0_, commenttop2_.comment_id as comment1_0_2_, commenttop2_.topic as topic2_ from comments this_ left outer join comments_topic commenttop2_ on this_.comment_id=commenttop2_.comment_id where this_.comment_id=?
Am I using incorrect annotations?
Is the criteria query not being constructed properly?
I placed the CommentTopic enum in a CommentTopicWrapper class.
I updated the annotation for the commentTopicsSet to:
#OneToMany(targetEntity = CommentTopicWrapper.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = Constants.COMMENTS_TOPIC_JOIN_TABLE, joinColumns = #JoinColumn(name = "comment_id"), inverseJoinColumns = #JoinColumn(name = "topic"))
private Set<CommentTopicWrapper> commentTopics;