Using Pageable to query a collection - spring-data

I have two entities. A NewsCategory and a NewsItem which have a one-to-many relationship.
NewsCategory
#Entity
public class NewsCategory extends AbstractEntity<Long> {
private String name;
#OneToMany(cascade = CascadeType.ALL)
private List<NewsItem> items = new ArrayList<>();
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public List<NewsItem> getItems() {
return items;
}
}
NewsItem
#Entity
public class NewsItem extends AbstractEntity<Long> {
private String title;
private LocalDate startDate;
private LocalDate endDate;
private String resource;
#Column(columnDefinition = "text")
private String content;
// getters and setters...
}
Repository interface
I would like to have the items collection to be pageable but I'm having some difficulties with defining the repository interface for it.
This interface does not work like expected.
public interface NewsCategoryRepository extends JpaRepository<NewsCategory, Long> {
#Query("SELECT e.items FROM #{#entityName} e WHERE e = ?1")
public List<NewsItem> findItems(NewsCategory category, Pageable pageable);
}
When executing findItems() the following exception is thrown.
Caused by: org.hibernate.QueryException: illegal attempt to dereference collection [newscatego0_.id.items] with element property reference [startDate] [SELECT e.items FROM NewsCategory e WHERE e = ?1 order by e.items.startDate asc]
How can I modify the above interface so it will return a portion of the items property using Spring Data and Pageable?

Related

Find an entity which use a class Id

To find an object from entity with primary key we use em.find(Person.class, <Id>).
I'm using JPA EclipseLink and I have a Person entity which has a composite primary key(#classId),
the Person entity:
#Entity
#IdClass(PersonId.class)
public class Person {
#Id
private int id;
#Id
private String name;
public String getName() {
return name;
}
// getters & setters
}
and the PersonID:
public class PersonId implements Serializable {
private static final long idVersionUID = 343L;
private int id;
private String name;
// must have a default construcot
public PersonId() {
}
public PersonId(int id, String name) {
this.id = id;
this.name = name;
}
//getters & setters
//hachCode & equals
}
How to use em.find to get a Person object?
I found the solution :
PersonId personeId = new PersonId(33, "Jhon");
Person persistedPerson = em.find(Person.class, personeId);
System.out.println(persistedPerson.getID() + " - " + persistedPerson.getName());

fetch one to many side with jpql

so I have done two entities with one to many relationship,
I have one category whohas many visitors,
and this is my code:
this is the Category entity :
#Entity
public class Category implements Serializable {
private Integer id;
private String name;
private List<Visitor> visitors = new ArrayList<Visitor>();
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(cascade=CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "category", orphanRemoval = true)
public List<Visitor> getVisitors() {
return visitors;
}
public void setVisitors(List<Visitor> visitors) {
this.visitors = visitors;
}
}
and here is the Visitor Entity :
#Entity
public class Visitor extends User {
private String passport;
private String citizenship;
private String gender;
private Company company;
private Category category;
public String getPassport() {
return passport;
}
public void setPassport(String passport) {
this.passport = passport;
}
public String getCitizenship() {
return citizenship;
}
public void setCitizenship(String citizenship) {
this.citizenship = citizenship;
}
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
and here is the service method who list all the visitors and works fine :
public List<Visitor> findAllVisitors() {
return em.createQuery(
"SELECT v from Visitor v left join fetch v.category",
Visitor.class).getResultList();
}
with this method I can list all the visitors each with his category object associated,
now the problem is in the other side of the relationship ,
here is the method who list the categories each with their visitors list :
public List<Category> findAllCategories() {
return em.createQuery("select c from Category c",
Category.class).getResultList();
}
I want to get the list of all the categories but when I call this method in a REST call , I get this result :
I want just to get a simple list of categories (id and name).
what is wrong in my code please help me i am confused.
UPDATE:
this is how I get JSON from persistence context with RESTful method :
#Inject
private CategoryServiceLocal categoryServiceLocal;
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Category> dofindAllCategories() {
return categoryServiceLocal.findAllCategories();
}
You have a lazy association from Category to visitors. To load all visitors you need to use left join fetch too.
select c from Category c left join fetch c.visitors
Please, use additional annotations to control how to JSON generated
Infinite Recursion with Jackson JSON and Hibernate JPA issue

JPA Mapping embedded fields with createNativeQuery

I have an entity with has a field which represents composite primary key annotated with embeddeid and another field which is annotated with embedded annotation.
Both of these fields are not directly mapped with the the columns returned by the query passed to createNativeQuery method.
The getResultList returns me the list of entities, but the two fields which I mentioned are null in all the entities.
public interface Key{
public int hashCode()
}
#Embeddable
public class CompositePK impements Key{
private int empid;
private Date startdate;
private Date enddate;
}
#Embeddable
public class PartitionKey implements Key{
private String empname;
}
#Entity
public class Employee {
#EmbeddedId
private CompositePK id;
#Embedded
private PartitionKey name;
#Column(name="empid")
private int empid;
#Column(name="empname")
private String empname;
#Column(name="startdate")
private Date startdate;
#Column(name="enddate")
private Date enddate;
}
public class Loader{
private static EntityManager em;
public static void main(String [] args){
//code to instantiate em goes here
//...
//....
Query query = em.createNativeQuery("select empid,empname,startdate,enddate from employees", Employee.class );
List entities = query.getResultList();
//print the list
System.out.println(entities);
}
}
The outcome of this is that the entities are populated but their fieldsid and name which are emdedded fields are null. Can anyone please suggest how to populate these two fields?
Thanks

JPA with hibernate implementation is generating wrong named query

I configured JPA with spring. I am using spring 4.
I have an entity
#Entity
#NamedQueries({
#NamedQuery(name="PartnerCourseMapping.findByPartnerCourseIdAndHandlerName", query="select pm from PartnerCourseMapping pm where pm.partnerCourseId=:partnerCourseId and pm.handlerName=:handlerName")
})
#Table(name="PARTNER_COURSE_MAPPING")
public class PartnerCourseMapping implements Serializable {
private static final long serialVersionUID = 1L;
#Id
protected Long id;
#Column(name="COURSE_ID")
protected Long courseId;
#Column(name="PARTNER_COURSE_ID")
protected String partnerCourseId;
#Column(name="PARTNER_ID")
protected Integer partnerId;
#Column(name="PRODUCT_TYPE")
protected String productType;
#Column(name="HANDLER_NAME")
protected String handlerName;
//getters and setters
}
I have another entity which i defined like below
#Entity
#NamedNativeQueries({
#NamedNativeQuery(
name="ExternalCourse.findExternalCourseMappingByLearningSessionGuid",
query="SELECT PCM.*, LE.id AS LearnerEnrollmentId, LE.LEARNER_ID AS LearnerId "
+ "FROM LEARNINGSESSION LS "
+ "INNER JOIN LEARNERENROLLMENT LE ON LE.ID = LS.ENROLLMENT_ID "
+ "INNER JOIN PARTNER_COURSE_MAPPING PCM ON PCM.COURSE_ID = LE.COURSE_ID "
+ "WHERE LS.LEARNINGSESSIONGUID = :learningSessionGuid",
resultSetMapping="externalCourseMapping"
)
})
#SqlResultSetMappings({
#SqlResultSetMapping(
name="externalCourseMapping",
classes = {
#ConstructorResult(targetClass = ExternalCourse.class,
columns={
#ColumnResult(name = "ID", type=Long.class ),
// remaing ColumnResult
}
)
}
)
})
public class ExternalCourse extends PartnerCourseMapping /*implements Serializable*/ {
private Long learnerEnrollmentId;
private Long learnerId;
//default constructor
public ExternalCourse(Long id, Long courseId, String partnerCourseId, Integer partnerId, String productType,
String handlerName, Long learnerEnrollmentId, Long learnerId) {
this.id = id;
// remaing values
}
//getters and setters for learnerEnrollmentId and learnerId
}
Now I query PartnerCourseMapping.findByPartnerCourseIdAndHandlerName
TypedQuery<PartnerCourseMapping> query = entityManager.createNamedQuery("PartnerCourseMapping.findByPartnerCourseIdAndHandlerName", PartnerCourseMapping.class);
query.setParameter("partnerCourseId", paernerCourseId);
query.setParameter("handlerName", handlerName);
return getResult(query);
protected T getResult(TypedQuery<T> query) {
List<T> list = query.getResultList();
return CollectionUtils.isEmpty(list) ? null : list.get(0);
}
Hibernate is generating exception that
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
...
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid column name 'learnerEnrollmentId'.
When I debug then I saw hibernate is generating query like below
select partnercou0_.id as id2_13_,
partnercou0_.COURSE_ID as COURSE_I3_13_,
partnercou0_.HANDLER_NAME as HANDLER_4_13_,
partnercou0_.PARTNER_COURSE_ID as PARTNER_5_13_,
partnercou0_.PARTNER_ID as PARTNER_6_13_,
partnercou0_.PRODUCT_TYPE as PRODUCT_7_13_,
partnercou0_.learnerEnrollmentId as learnerE8_13_,
partnercou0_.learnerId as learnerI9_13_,
partnercou0_.DTYPE as DTYPE1_13_
from PARTNER_COURSE_MAPPING partnercou0_ where partnercou0_.PARTNER_COURSE_ID=? and partnercou0_.HANDLER_NAME=?
I want to ask that why hibernate is including learnerEnrollmentId and learnerId column? I am passing the query name and query. If I refactor my code like below then I get the correct result
#Entity
#NamedNativeQueries({
#NamedNativeQuery(
name="ExternalCourse.findExternalCourseMappingByLearningSessionGuid",
...
resultSetMapping="externalCourseMapping"
)
})
#SqlResultSetMappings({
#SqlResultSetMapping(
name="externalCourseMapping",
classes = {
..
}
)
})
public class ExternalCourse implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private Long courseId;
private String partnerCourseId;
private Integer partnerId;
private String productType;
private String handlerName;
private Long learnerEnrollmentId;
private Long learnerId;
//default constructor
//constructor with all parameters
//getters and setters
}
Why I am getting exception when I am extending class? I am passing the query name. Why ?
Thanks

not quite understanding lazy-loading

I have a situation where lazy-loading is not occurring when I want it to in a one-to-many.
Obviously, I am new to this. Why won't the SongComposers get fetched when I call s.getSongComposers()?
Here is my DAO class:
public class SongDAOImpl implements SongDAO {
#PersistenceContext
private EntityManager em;
#Override
#SuppressWarnings(value = { "unchecked" })
public List<Song> getAllSongsOnAlbum(int albumID) {
Query q = em.createQuery("SELECT s FROM Song s WHERE s.album.albumID = :albumid ORDER BY s.songName");
q.setParameter("albumid", albumID);
List<Song> list = (List<Song>) q.getResultList();
for (Song s: list) {
s.getSongComposers();
}
return list;
}
}
Here is my Song entity:
#Entity
#Table(name="songs")
public class Song {
#Id
#GeneratedValue
#Column(name="SongID")
private int songID;
#Column(name="SongName")
private String songName;
#Column(name="Length")
private int length;
#ManyToOne
#JoinColumn(name="AlbumID", referencedColumnName="AlbumID")
private Album album;
#OneToMany(mappedBy="song")
private List<SongComposer> songComposers;
.... all getters/setters ......
}
Here is my SongComposer entity:
#Entity
#Table(name="song_composer")
public class SongComposer {
#Id
#GeneratedValue
#Column(name="SongComposerID")
private int songComposerID;
#ManyToOne
#JoinColumn(name="SongID", referencedColumnName="SongID")
private Song song;
#ManyToOne
#JoinColumn(name="ComposerID", referencedColumnName="ComposerID")
private Composer composer;
..... getters/setters .......
}
You have to do it explicitly. Try this query:
SELECT DISTINCT s FROM Song s JOIN FETCH s.songComposers WHERE s.album.albumID = :albumid ORDER BY s.songName"