JPA CriteriaQuery multiselect from several entities - jpa

Right now, I am using the method multiselect of CriteriaQuery to put some values from entity Termine in entity Task like this:
CriteriaBuilder builder = getEm().getCriteriaBuilder();
CriteriaQuery<Task> taskCriteria = builder.createQuery(Task.class);
Root<Termin> terminRoot = taskCriteria.from(Termin.class);
taskCriteria.multiselect(terminRoot.get("text"), terminRoot.get("empfaenger"), terminRoot.get("datVon"));
taskCriteria.where(builder.equal(terminRoot.get("empfaenger"), "000"));
List<Task> task = getEm().createQuery(taskCriteria).getResultList();
return task;
This is working fine, but now I am willing to gather the values text, empfaenger and datVon not only from the entity Termine but also from the entity Aufgabe, so that I will have a list of Tasks, that contains every Termin and Aufgabe which are having the same empfaenger.
Is it possible? If yes, how?
Thanks a lot in advance for your help!

I would derive both classes from task.
#Entity(name="Task")
#Inheritance(strategy = InheritanceType.JOINED)
#NamedQuery(name="Task.findAll", query="SELECT t FROM Task t")
public class Task {
#Id
Long id;
String text;
String empfaenger;
}
#Entity
public class Termin extends Task{
...
}
#Entity
public class Aufgabe extends Task{
...
}
And select them with a named query
List<Task> resultList = entityManager.createNamedQuery("Task.findAll",Task.class).getResultList();
or a criteria query with Task as Root.

This is the way I did to collect data from multiple entities (custom Select).
For example, multiple entities:
Root<InflowEntity> rootInflow = criteriaQuery.from(InflowEntity.class);
Root<OutflowEntity> rootOutflow = criteriaQuery.from(OutflowEntity.class);
You select the attributes you need from the above 2:
criteriaQuery.multiselect(rootInflow.get("inflowID"), rootInflow.get("name"),
rootOutflow.get("count"), rootOutflow.get("dateRange"));
Add the predicates (constraints) you need, for example:
Predicate[] predicates = new Predicate[2];
predicates[0] = criteriaBuilder.equal(rootInflow.get("uuid"), loginContext.getUuid());
predicates[1] = criteriaBuilder.equal(rootOutflow.get("uuid"), loginContext.getUuid());
Process the results:
criteriaQuery.where(predicates);
List<ResultsBean> results = session.createQuery(criteriaQuery).getResultList();
This Java bean (this is not the Hibernate entity), ResultsBean, stores the results. That is, it needs to have a constructor to accommodate the input the way the multiselect is arranged.

Related

Spring Data JPA: Work with Pageable but with a specific set of fields of the entity

I am working with Spring Data 2.0.6.RELEASE.
I am working about pagination for performance and presentation purposes.
Here about performance I am talking about that if we have a lot of records is better show them through pages
I have the following and works fine:
interface PersonaDataJpaCrudRepository extends PagingAndSortingRepository<Persona, String> {
}
The #Controller works fine with:
#GetMapping(produces=MediaType.TEXT_HTML_VALUE)
public String findAll(Pageable pageable, Model model){
Through Thymeleaf I am able to apply pagination. Therefore until here the goal has been accomplished.
Note: The Persona class is annotated with JPA (#Entity, Id, etc)
Now I am concerned about the following: even when pagination works in Spring Data about the amount the records, what about of the content of each record?.
I mean: let's assume that Persona class contains 20 fields (consider any entity you want for your app), thus for a view based in html where a report only uses 4 fields (id, firstname, lastname, date), thus we have 16 unnecessary fields for each entity in memory
I have tried the following:
interface PersonaDataJpaCrudRepository extends PagingAndSortingRepository<Persona, String> {
#Query("SELECT p.id, id.nombre, id.apellido, id.fecha FROM Persona p")
#Override
Page<Persona> findAll(Pageable pageable);
}
If I do a simple print in the #Controller it fails about the following:
java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to com.manuel.jordan.domain.Persona
If I avoid that the view fails with:
Caused by:
org.springframework.expression.spel.SpelEvaluationException:
EL1008E:
Property or field 'id' cannot be found on object of type
'java.lang.Object[]' - maybe not public or not valid?
I have read many posts in SO such as:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
I understand the answer and I am agree about the Object[] return type because I am working with specific set of fields.
Is mandatory work with the complete set of fields for each entity? Should I simply accept the cost of memory about the 16 fields in this case that never are used? It for each record retrieved?
Is there a solution to work around with a specific set of fields or Object[] with the current API of Spring Data?
Have a look at Spring data Projections. For example, interface-based projections may be used to expose certain attributes through specific getter methods.
Interface:
interface PersonaSubset {
long getId();
String getNombre();
String getApellido();
String getFecha();
}
Repository method:
Page<PersonaSubset> findAll(Pageable pageable);
If you only want to read a specific set of columns you don't need to fetch the whole entity. Create a class containing requested columns - for example:
public class PersonBasicData {
private String firstName;
private String lastName;
public PersonBasicData(String firstName, String lastName) {
this.firstName = fistName;
this.lastName = lastName;
}
// getters and setters if needed
}
Then you can specify query using #Query annotation on repository method using constructor expression like this:
#Query("SELECT NEW some.package.PersonBasicData(p.firstName, p.lastName) FROM Person AS p")
You could also use Criteria API to get it done programatically:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<PersonBasicData> query = cb.createQuery(PersonBasicData.class);
Root<Person> person = query.from(Person.class);
query.multiselect(person.get("firstName"), person.get("lastName"));
List<PersonBasicData> results = entityManager.createQuery(query).getResultList();
Be aware that instance of PersonBasicData being created just for read purposes - you won't be able to make changes to it and persist those back in your database as the class is not marked as entity and thus your JPA provider will not work with it.

How to query #ElementCollection HashMap

I have an entity with different fields:
#Entity
public class TestEntity {
private int id;
private String name;
private String description;
#ElementCollection
private Map<String, String> parameter = new HashMap<>();
}
The resulting tables are the following:
TestEntity(id, name, description)
TestEntity_parameter(TestEntity_id, parameter, parameter_KEY)
Now I want to create a named query for this TestEntity that checks if there exists a parameter_KEY of value "test" and with a parameter :parameter.
I tried something like this:
select te from TestEntity te join TestEntity_parameter tep where tep.parameter_KEY = test AND tep.parameter = :parameter
But when I try to deploy, I get an error.
I'm relatively new to hibernate and java ee. Maybe my approach is wrong but I did not find anything how to access the fields of a map with a named query since it creates a new table for that map. So i thought that I need to join those tables.
Hope you guys can help me :)
Thanks a lot :)
Greetings
Simon
You could use the below query.
SELECT te FROM TestEntity te INNER JOIN te.parameter p WHERE KEY(p) = :YOUR_KEY
AND VALUE(p) = :YOUR_VALUE

Spring Data JPA JPQL queries on parent interface

Say I have a #MappedSuperClass like this:
#MappedSuperclass
public abstract class Rating
{
#Id
private Long id;
#Column(name="USER_ID")
private Long userId;
private int rating;
...
With a concrete child entity like this
#Entity
#Table(name="ACTIVITY_RATING")
public class ActivityRating extends Rating
{
private Long activitySpecificData;
...
Then there is a Spring Data JPA repository like this:
#NoRepositoryBean
public interface RatingRepository<R extends Rating> extends JpaRepository<R, ID>
{
public List<R> findByUserId(Long userId);
...
and this:
public interface ActivityRatingRepository extends RatingRepository<ActivityRating>
{
}
This all works great and I can call findByUserId() on any of specific rating repositories that extend RatingRepository. I am now wanting to write some JPQL in the RatingRepository that all the child interfaces can inherit. I just don't know what (or if it's even possible) to put after the FROM in the query. For example:
#Query("SELECT NEW com.foo.RatingCountVo(e.rating, COUNT(e.rating)) FROM ??????? e GROUP BY e.rating")
public List<RatingCountVo> getRatingCounts();
I can add this method to each of the individual repositories that extend RatingRepository but everything would be exactly the same except for the specific entity name. If I want to change the query, I'd then have to go to all the child repositories and update them individually. I really want the query to live in the parent class and not be duplicated. Is there any way to accomplish this?
I'm currently using spring-data-jpa 1.7.2 and eclipselink 2.5.2. I'm not necessarily opposed to switching to newer versions if necessary.
Will it work if you will split query into 3 parts: start, entity and end of query? Than, if it'll work, in each interface you define constant like
String ENTITY = "ActivityRating";
And then you can use it like
#Query(RatingRepository.QUERY_START + ENTITY + RatingRepository.QUERY_END)
List<RatingCountVo> getRatingCounts();
BTW, there is no need to define public modifier in interface.
UPDATE: here is described another way:
#Query("SELECT NEW com.foo.RatingCountVo(e.rating, COUNT(e.rating)) FROM #{#entityName} e GROUP BY e.rating

How to filter child entities collections with predicate?

I have an entity service on which I need to filter a collection of child entity, based on a list of id's. My service have a public method which receive the id of the parent entity and a list of id's of some of his children entities.
By default, I know that JPA will fetch all related entities and this his the actual behavior. But we need to work on the performance of the service. So instead of getting all related entities and filter them with many loop (filter on id's and also on other properties like date property), I want to get only entities concerned by my request.
My Parent entity
#Entity
#Table(name = "MyParent")
public class MyParentEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "SEQ_MyParent")
#SequenceGenerator(allocationSize = 1, name = "SEQ_MyParent",
sequenceName = "SEQ_MyParent")
#Column(name = "ID_PARENT")
private Long id;
#OneToMany(mappedBy = "myParent", cascade = CascadeType.ALL,
fetch = FetchType.EAGER, orphanRemoval = true)
private final List<MyChildEntity> myChild = new ArrayList<MyChildEntity>();
}
My Child Entity
#Entity
#Table(name = "MyChild")
public class MyChildEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "SEQ_MyChild")
#SequenceGenerator(allocationSize = 1, name = "SEQ_MyChild",
sequenceName = "SEQ_MyChild")
#Column(name = "ID_CHILD")
private Long id;
#ManyToOne
#JoinColumn(name = "ID_PARENT")
private MyParentEntity myParent;
}
I'm using Spring-data CrudRepository to get data from my DB and I also extends JpaSpecificationExecutor to use Predicate.
public interface MyParentRepository extends CrudRepository<MyParentEntity, Long>,
JpaSpecificationExecutor<MyParentEntity> {
}
This let me use CrudRepository findOne() method but with a Specification object instead of the regular Long parameter.
Also, I combine multiples Specification's object with the following call:
this.myParentRepository.findOne(Specifications
.where(firstSpecification(parentId))
.and(secondSpecification(childrenIdsList)));
I created a simple junit test with one Parent linked to two children entities. In my request, I'm able to get the parent entity with the provided Id. But even if I provide the child id, I always get both children entities in the list inside the parent.
In my method which return a new Specification object, in which the toPredicate method is override, I'm unable to create a Predicate that will filter my children collection and only get those one I'm interested. I know that the Hibernate Criteria has the possibility to add "Restrictions" but this is not available in the CriteriaBuilder that is provided with the toPredicate method.
public static Specification<MyParentEntite> firstSpecification(final Long id) {
return new Specification<MyParentEntite>() {
#Override
public Predicate toPredicate(Root<MyParentEntite> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate predicate = cb.equal(root.get(MyParentEntity_.id), id);
return cb.and(predicate);
}
};
}
public static Specification<MyParentEntite> secondSpecification(final List<Long> ids) {
return new Specification<MyParentEntite>() {
#Override
public Predicate toPredicate(Root<MyParentEntite> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
Root<MyChildEntity> child = query.from(MyChildEntity.class);
Expression<Long> exp = child.get(MyChildEntity_.id);
Predicate p = exp.in(ids);
return cb.and(p);
}
};
}
In the secondSpecification() method, I also tried to use ListJoin instead of Root directly in the Entity. I searched in other questions here but it seems that this concern is solved with the Hibernate Criteria restrictions or with a LeftJoin, which I tried in my ListJoin in specifing JoinType.LEFT parameter.
Here are links to already tested solutions whitout success :
JPA CriteriaBuilder - How to use "IN" comparison operator
JPA2 Criteria-API: select... in (select from where)
I want to mention that I'm relatively new with Criteria API and Predicate. Maybe I'm missing something that is simple but that is obvious to experienced JPA developpers!
Thanks a lot for your help!
Finally, I found a way to resolved my issue. Requesting only partial collections of sub-entities is something that we found dangerous in terms of data integrity. If a remote service calls to request my parent entity with a partial collection of children's entities within a get, this parent entity object may be return for a modify operation which will result in many "delete" calls on the removed instances of children entities. The persistence API will consider these missing children as relations that were removed, which is something we don't want.
I created a dummy transfert object which contains the partial collections of children's entities requested so this dummy transfert object can't not be use in a future modify operation call. The full version of the parent entity will be used for the "modify" purpose.
Is your JPA provider hibernate?Have you considered the filters in hibernate which can filter the child entities instead of removing them.But the filter usage is somehow very difficult to understand!

JPA not updating ManyToMany relationship in returning result

Here are my entities:
#Entity
public class Actor {
private List<Film> films;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="actor_id"),
inverseJoinColumns = #JoinColumn(name="film_id"))
public List<Film> getFilms(){
return films;
}
//... more in here
Moving on:
#Entity
public class Film {
private List actors;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="film_id"),
inverseJoinColumns = #JoinColumn(name="actor_id"))
public List<Actor> getActors(){
return actors;
}
//... more in here
And the join table:
#javax.persistence.IdClass(com.tugay.sakkillaa.model.FilmActorPK.class)
#javax.persistence.Table(name = "film_actor", schema = "", catalog = "sakila")
#Entity
public class FilmActor {
private short actorId;
private short filmId;
private Timestamp lastUpdate;
So my problem is:
When I remove a Film from an Actor and merge that Actor, and check the database, I see that everything is fine. Say the actor id is 5 and the film id is 3, I see that these id 's are removed from film_actor table..
The problem is, in my JSF project, altough my beans are request scoped and they are supposed to be fetching the new information, for the Film part, they do not. They still bring me Actor with id = 3 for Film with id = 5. Here is a sample code:
#RequestScoped
#Named
public class FilmTableBackingBean {
#Inject
FilmDao filmDao;
List<Film> allFilms;
public List<Film> getAllFilms(){
if(allFilms == null || allFilms.isEmpty()){
allFilms = filmDao.getAll();
}
return allFilms;
}
}
So as you can see this is a request scoped bean. And everytime I access this bean, allFilms is initially is null. So new data is fetched from the database. However, this fetched data does not match with the data in the database. It still brings the Actor.
So I am guessing this is something like a cache issue.
Any help?
Edit: Only after I restart the Server, the fetched information by JPA is correct.
Edit: This does not help either:
#Entity
public class Film {
private short filmId;
#ManyToMany(mappedBy = "films", fetch = FetchType.EAGER)
public List<Actor> getActors(){
return actors;
}
The mapping is wrong.
The join table is mapped twice: once as the join table of the many-to-many association, and once as an entity. It's one or the other, but not both.
And the many-to-many is wrong as well. One side MUST be the inverse side and use the mappedBy attribute (and thus not define a join table, which is already defined at the other, owning side of the association). See example 7.24, and its preceeding text, in the Hibernate documentation (which also applies to other JPA implementations)
Side note: why use a short for an ID? A Long would be a wiser choice.
JB Nizet is correct, but you also need to maintain both sides of relationships as there is caching in JPA. The EntityManager itself caches managed entities, so make sure your JSF project is closing and re obtaining EntityManagers, clearing them if they are long lived or refreshing entities that might be stale. Providers like EclipseLink also have a second level cache http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching