I faced a problem when i want to retrieve records with OneToMany relation in the entity, what i want is to fetch specific records for the entity and all the relations inside it for a Collection? how can i do it using JPA Query?
...
public class JsPost implements Serializable, Comparable<JsPost> {
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "postId")
private Collection<JsComment> jsCommentCollection;
...
}
and in JsPostDAO for fetch(...) method
public List<JsPost> fetch(int min, int max) {
Query query = em.createQuery("SELECT p FROM JsPost p order by p.postDateCreated DESC");
query.setFirstResult(min);
query.setMaxResults(max);
return query.getResultList();
}
After i call fetch method all JsComments for JsPost retrieved? how can i set max results for the Collection<JsComment>? Any help?
After i call fetch method all JsComments for JsPost retrieved?
No. The association is lazy by default, so no JsComment instance is loaded by the database by this query. It's only when calling a method on somePost.getJsCommentCollection() that the comments of the post will be retrieved.
how can i set max results for the Collection?
The question is irrelevant, since the comments are not fetched.
Side note: your naming is awful. Why not name these entities Post and Comment. And naming the collection comments, instead of jsCommentCollection? Isn't it more redable to have post.getComments() than jsPost.getJsCommentCollection()?
Related
I have two tables in my database:
1. Warehouse
2. WarehouseItem
Relation between them are like listed below:
#Entity
#Table(name = "warehouse")
public class WarehouseModel {
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy="warehouse")
private List<WarehouseItemModel> _items;
and
#Entity
#Table(name = "warehouseItem")
public class WarehouseItemModel {
#ManyToOne
public WarehouseModel warehouse;
and now I want to SELECT all the objects of the entity WarehouseModel:
public List getObjects(Class pClass)
{
startTime();
connect();
Session session = getSession();
Transaction lTransaction = session.beginTransaction();
List lRet = session.createCriteria(pClass).list();
lTransaction.commit();
endTime("getObjects: " + lRet.size() + " objects");
Collections.reverse(lRet);
return lRet;
}
In my database I have:
1x object in the table: Warehouse (WarehouseModel.java)
5x objects in the table: WarehouseItem (WarehouseItemModel.java)
When I want to retrive all the Warehouses including related WarehouseItems:
databaseConnector.eDocumentConnector.getObjects(WarehouseModel.class)
the result is:
- 5x the same object of WarehouseModel
It seems that there is dependancy that I always get as much entities of the same WarehouseModel as there is WarehouseItemModels inside field WarehouseModel._items
How to fix it and why it happens? (I have more relations like this one in my project and if it happends here, maybe it happends also in the other places)
Project details:
- Java 1.8
- Hibernate 5.0.7
- database: PostgreSQL 9.5.2
It is solved by using DISTINCT in hibernate query:
session.createCriteria(pClass).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
More importantly, you should understand how a 1-to-many relationship works in SQL.
The raw select statement is going to be something like
SELECT
.
.
<what columns you want>
.
FROM
warehouse w
JOIN warehouseItem wi ON (w.id = wi.warehouse_id)
WHERE
<whatever you need to filter on>
When you join, you're always going to get the number of rows that are related to the primary table's ID. Since there are 5 rows in the child table, 5 rows are returned, where the warehouse columns are duplicated each time but the warehouseItem columns are unique to each row.
Say I want to get all rows of the MyEntity that have an id lower than 10. This entity contains a list of Another entity. I would like this list to be fetched only by a subset of the listAnother. This subset containing only Another where the user contained in it is a specific one.
Basically in SQL it would be like this :
SELECT * FROM myentity m
LEFT JOIN another a
ON m.idTable=a.my_entity
AND a.user = "test1"
WHERE m.idTable < 10;
I didn't manage however to translate this query to jpql.
My entities being like this :
#Entity
public class MyEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idTable;
#OneToMany(mappedBy = "myEntity")
private List<Another> listAnother;
}
#Entity
public class Another implements Serializable {
#Id
private int idAnother;
// bi-directional many-to-one association to Thethread
#ManyToOne(fetch = FetchType.LAZY)
private MyEntity myEntity;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
}
#Entity
public class User implements Serializable {
#Id
private String username;
}
In jpa I could do this :
SELECT m FROM MyEntity where m.idTable < 10;
And then for each entity I get from this list call this:
SELECT a FROM Another Where a.user.username=:'test' AND a.myEntity=:entity;
However I would like to do it all at once in one query. Can I do this with criteria ? I didn't take the time to learn it but if it's possible then I will.
JPQL and Critaria API are equal in terms of what you can express with them. What is possible with JPQL is possible with Criteria and vice versa.
With JPQL, you can simply combine your 2 queries into one in this way:
SELECT a FROM Another a Where a.user.username=:test AND a.myEntity.idTable < 10
You can use dot notation (.) to join multiple entities in the query, provided that the relationship is X-to-one. If you have X-to-many relationship, you need to use JPQL JOIN, which is not very complicated. Example with (LEFT) JOIN:
SELECT m FROM MyEntity m LEFT JOIN m.listAnother a Where a.user.username=:test AND m.idTable < 10
The result is of course not equal - in first case you will get list of Another entities and you can get MyEntity by a.myEntity, in the second case you will get list of MyEntity, which all have at least one Another entity with given user
In hibernate you can use Filters and FilterJoinTable. Here you can read how to do that. Similar problem was solved here.
You need to extend the logic which you applied to check the username (a.user.username=:'test') which was for many-to-one relation between anything and user by taking it one level up to myEntity and then using it for one-to-many relation as well -
SELECT m FROM MyEntity where m.idTable < 10 and (m.listAnother.user.username=:'test')
The join condition "m.listAnother.myEntity=:entity" wouldn't be needed now as in our query we have started from the specific myEntity and then moved down to listAnother.user.username .
I don't have the table definitions to try this query myself, exact SQL may require some tweaks - but logically it should work like the way I showed above, i.e. just the way you joined Another with User, the same way you can join MyEntity with Another by just traversing down the child listAnother.
I am trying to find out if the following is possibe, using javax persistance JPA: join the same table twice, using aliases, so that two different values can be returned in the select statement. For the sample SQL below, the two values would be boss.name and employee.name, both stored in the same table in the same column.
SELECT staff.id, boss.name, employee.name
FROM staff, person as 'boss', person as 'employee',
WHERE
staff.id = boss.staff_id
AND staff.id = employee.staff_id
AND boss.code = 'boss'
AND employee.code = 'employee'
Also, I'd like to do this, if possible, with straight JDK, not Hibernate. Thanks!
I think you just need 2 relationships to Person entity from Staff, something like:
#Entity
public class Staff {
...
#OneToOne
#JoinColumn(name = "boss_id")
Person boss;
#OneToOne
#JoinColumn(name = "employee_id")
Person employee;
...
}
I'm using a #SqlResultSetMapping for an #Entity that is purely read-only (and has no backing table in the database). I'm loading tens of thousands of these into memory, so I need to detach the entities from the EntityManager to avoid Hibernate's dirty entity checking when I do work later.
Is there a way to annotate the Entity, or the SqlResultSetMapping, so that the entities are never added to the EntityManager?
Non-persisted entity:
#SqlResultSetMapping(name = "fooMapping", entities = #EntityResult(entityClass = Foo.class))
#Entity
public class Foo {
#Id
public Long row_id;
public String name;
}
Native query:
String sql = "SELECT id AS row_id, friendlyName AS name FROM SomeTable";
Query q = JPA.em().createNativeQuery(sql, "fooMapping");
List<Foo> fooList = q.getResultList();
Current solution:
for (Foo f : fooList) {
JPA.em().detach(f); // 100x improvement for subsequent DB work
}
// subsequent database work
One way to avoid dirty checking is to indicate to hibernate that the query is fetching read-only entities. You can do this in JPA by using query hints
q.setHint("org.hibernate.readOnly",true);
From hibernate doc:
org.hibernate.readOnly : Entities retrieved by this query will be loaded in a read-only mode where Hibernate will never dirty-check them or make changes persistent ( eg. new Boolean(true) ), default to false
We've got an entity model like this (non-related fields omitted for brevity):
#Entity
public class Invoice {
//The other portion of the sum that is invoiced from another payer.
#OneToOne(mappedBy = "parentInvoice", cascade = CascadeType.ALL)
private Invoice otherPayersInvoice;
//The patient's invoice, which is the parent of this other payer's invoice.
#OneToOne(cascade = CascadeType.ALL)
private Invoice parentInvoice;
#ManyToOne
private Payer payer;
}
So, basically we will split invoice's into parts and store the relations within the entities.
The following query works and returns all invoices by payerId:
SELECT i FROM Invoice i WHERE i.payer.id = :payerId
However, when i extend the query to also list all "child" invoices that the payer may have (Invoices invoiced from another payer), I get no results at all and no error or warning anywhere whatsoever.
SELECT i FROM Invoice i WHERE i.payer.id = :payerId OR
(i.parentInvoice IS NOT NULL AND i.parentInvoice.payer.id = :payerId)
What could possibly be the issue here?
i.parentInvoice.payer.id
This is doing an INNER join from Invoice to its parent to its payer, which will filter all rows without a parent or payer.
You need to use an outer-join instead.
SELECT i FROM Invoice i join i.parentInvoice parent join p.payer parentPayer join i.payer payer WHERE payer.id = :payerId OR (parentPayer.id = :payerId)