I'm quite the rookie with JPA/JPQL, so please excuse me if this question is not crystal clear.
I am trying to find an efficient JQPL query in order to get all records of a complex object.
(ie. represented by multiple tables, with several one-to-many relationships - see simplified example below):
class ComplexObject {
private Set< SubOject1> so1 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject1>...
}
class SubObject1 {
private Set< SubOject2> so2 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject2>...
}
I am using the following JPQL query :
select distinct CO
from ComplexObject CO
left join fetch CO.so1 SO1
left join fetch SO1.so2
The query is run on a stateless session, in order to get a de facto snapshot of the current data in the DB, which is detached from the entity manager (hence the usage of left join fetch).
Unfortunately, I've encountered 2 problems :
Since the complex object contains multiple instances of so1, and each so1 instance contains multiple instances of so2, the underlying translation to SQL queries generates a specific select query per row of the product of all the table joins - a very wasteful solution. Is there a way to reduce the number of internal select queries? (This seems like the dreaded N+1 queries problem).
The JPQL query returns a ComplexObject instance per internal SQL query on the product of all the table joins - which means multiple references to the ComplexObject instances. Why does this happen on a 'select distinct' query?
The JPA framework I am using is hibernate, and the DB is HyperSQL.
The (1) issue turned out to be related to using the p6spy logging framework, which printed out all the results from a large DB table. The logging format led to an incorrect assumption that many queries where being executed.
While trying to fine tune performance, using native queries did not appear to have better performance then using JPQL queries.
Using a Native Query also resulted in Object typed results, which required post processing.
You can use View Objects to receive only the columns what you want:
StringBuilder sb = new StringBuilder();
sb.append(" SELECT new ").append(ObjectVO.class.getName()).append("(co.someInfo1, co.someInfo2, so1.someInfo )");
sb.append(" FROM ComplexObject co ");
sb.append(" JOIN co.subOject1s so1 ");
sb.append(" LEFT JOIN so1.so2 so2 ");
sb.append(" WHERE so1.id = :idSo1 AND so2 = :someThing");
Query q = em.createQuery(sb.toString());
q.setParameter("idSo1", idSo1);
q.setParameter("someThing", someThing);
List<ObjectVO> listResult = q.getResultList();
The ObjectVO class:
public class ObjectVO {
private String info1;
private Long info2;
private String info3;
public PedidoModel(String info1, Long info2, String info3){
this.info1 = info1;
this.info2 = info2;
this.info3 = info3;
}
}
Related
I'm trying to find the best way to map my data on ORM. I have a query which gets me data from MySQL database which look like
SELECT d.ID AS Id,
equipment.EQUIP_ID,
equipment.EQUIP_REFERENCE
FROM
tbl_devices d
INNER JOIN
tbl_equipment equipment ON equipment.EQUIP_ID = d.DEV_ID
What would be the most optimal way to get these data with Spring boot and Spring data??
Should I use #Query annotation and execute this or somehow create entities for Equipment and Devices tables and then use JPQL/HQL to join tables in a query, but then how should I map the result??
Thanks in advance.
You can use JdbcTemplate (import from org.springframework.jdbc.core.JdbcTemplate) to run the SQL statement above you wrote. After you can create a pojo to map result into it via BeanPropertyRowMapper. For example:
final String sql = "SELECT d.ID AS Id, equipment.EQUIP_ID, equipment.EQUIP_REFERENCE FROM tbl_devices d INNER JOIN tbl_equipment equipment ON equipment.EQUIP_ID = d.DEV_ID";
YourPojo result = jdbcTemplate.query(
sql,
new BeanPropertyRowMapper<>(YourPojo.class)
);
Pojo class maybe like following:
#Data
public class YourPojo {
private Long id;
private Long equipId;
private Your_type equip_reference_name;
}
A quick and dirty solution would be to use projections.
First, you create a projection interface:
public interface DeviceDetails {
Long getId();
Long getEquipId();
String getEquipReference();
}
You then modify the query to match column aliases with the projection properties:
SELECT d.ID AS id,
equipment.EQUIP_ID as equipId
equipment.EQUIP_REFERENCE As equipReference
...
Finally, you put the query method in a repository of your choice:
#Query(value =..., nativeQuery = true)
List<DeviceDetails> findDeviceDetails();
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.
I have the following 2 classes using JTA transaction type with openjpa & a derby embedded db. What I want is to get the parent Organisation & the requested WSpace in one query when I only have the WSpace id. I am quite new to JPA so am learning as I go and have been struggling with 'q2' query. I have been using queries 'q0' & 'q1' to debug and check the items do exist in the db. 'q0' returns 1 object as does 'q1', whereas 'q2' returns 0 objects
I have tried a variety of entity setups and different queries but nothing has worked yet.
Orignally the WSpace class did not have an Organisation field as it didn't seem necessary for persisting or selecting, but I added it (along with the mappedby parameter) incase it was needed for the query to work, but nothing has changed.
back to the original question how can I get this to work so it returns the parent object with the single child being requested
SELECT o FROM Organisation o JOIN FETCH o.spaces w WHERE w.id = :id
Here are my classes
#Entity
public class Organisation implements MyObjects
{
#Id
#NotNull
private Integer id;
private String name;
#OneToMany( mappedBy = "organisation",
cascade = { CascadeType.PERSIST, CascadeType.MERGE } )
private List<WSpace> spaces;
//getters/setter below
}
And
#Entity
public class WSpace implements MyObjects
{
#Id
#NotNull
private Integer id;
private String name;
#ManyToOne
private Organisation organisation;
#OneToMany
private List<Application> apps;
//getters/setter below
}
class DAO
{
...
public void foo( Integer id )
{
....
String q0 = "SELECT o FROM Organisation o WHERE o.id = 49068";
List<Organisation> res0 = em.createQuery( q0, Organisation.class ).getResultList();
String q1 = "SELECT w FROM WSpace w WHERE w.id = " + id;
List<WSpace> res1 = em.createQuery( q1, WSpace.class ).getResultList();
String q2 = "SELECT o FROM Organisation o "
+ "JOIN FETCH o.spaces w WHERE w.id = " + id;
List<Organisation> res2 = em.createQuery( q2, Organisation.class ).getResultList();
...
}
}
Have you tried to look in the logs for output of your q2 query?
I am learning JPA too and was dealing with Criteria and QL queries quite recently.
So after having pretty same problems with joins, I started checking logs and it was pretty clear, what the issues were, since logs showed up translated to SQL queries.
Another thing to look, how are you generating your Entities? I used Netbeans generating it for me. Also, many to many relations mignt have helper class generated too, I saw it in one of the projects.
Hope it helps..
The query you're looking for is probably this:
SELECT w FROM WSpace w LEFT JOIN FETCH w.organisation where w.id=:id
with query.setParameter("id", id); to bind the id parameter to the query. This effectively tells the persistence provider to fetch the WSpace.organisation relation while querying for WSpace entities in the same query. Using the LEFT [OUTER] keword (OUTER being optional) in front of the JOIN FETCH keywords tells your persistence provider to select WSpace objects even if there are no matching Organisation records for your WSpace.organisation relation.
I am kind of new to JPA and have read a bit about second level caching in JPA. And I think it should apply correctly to my scenario.
I have a table (say A) whose content will never change unless a new release is applied.
I need to query some data from the database : for that I have a JPQL working correctly and that uses a join between table A and an additional table (say B).
Since the contents of table A never change through the life of application, I could essentially mark this table A as Cacheable and reuse the content from Cache - rather than going to the database for that.
I have read about #NamedQuery that enables a one time JPQL to SQL translation for the life of the application. And it is not what I am looking for.
I want to know how should I go about using the Cacheable property for my purpose.
This is what I have done so far :
Marked tableA as cacheable -
#Entity
#Cacheable
#Table(name = "TableA")
public class Table{
#Id
#NotNull
#Column(updatable = false)
private String uuid;
#NotNull
#Size(min = 1)
private String description;
.
.
.
}
2. There is a DAO that does a find using the JPQL -
public Collection findAll(String description) {
final Cache cache = entityManager.getEntityManagerFactory().getCache();
if (cache.contains(TableA.class, "abc")) {
System.out.println("cached");
} else {
System.out.println("not cached");
}
final Query query = entityManager
.createQuery("Select distinct A from TableA A, IN(A.TableB) B where A.description = :description"); //$NON-NLS-1$
query.setParameter("description", description); //$NON-NLS-1$
return query.getResultList();
}
</code>
Can I take advantage of using Cacheable property in my scenario. If so, then could you suggest how?
Also, "not cached" gets printed always no matter what the value of string I use (from table) instead of "abc".
Appreciate your help. Thanks
I'm converting a legacy iBatis implementation to Hibernate, and for backwards compatibility purposes need to present counts of an object's collections rather than the collections themselves. The original query was:
select A.*, ( select count(*) from B where B.A_id = A.id ) as B_count from A;
and b_count would be presented in the response. I'd like to be able to do the same without lazy-loading A's collection of B's for each query result.
Any ideas or suggestions?
The best method seems to be using a Hibernate formula, mapped to the getter and setter of my BCount attribute in the class A. My code:
public class A {
// ...
private long bCount;
// ...
#Formula( "(select count(*) from B where B.A_id = id" )
public long getBCount() {
return this.bCount;
}
public void setBCount( long bCount ) {
this.bCount = bCount;
}
}
Great thing about this method is that the count is returned in the same fetch to hydrate the initial object, and does not result in 1+N queries for collection query results!
You can use a projection.
The syntax for the row count is below:
Criteria crit = session.createCriteria(B.class);
crit.setProjection(Projections.rowCount());
List results = crit.list();
Edit: After re-reading, I think this may not be what you're asking for....
Hibernate filters are used to apply additional restrictions to query results (e.g. think of them as part of "where" clause), so they won't do what you want. You have two options here:
A) You can eagerly get collection of Bs for your A:
from A a left join fetch a.Bs b
If you do so, keep in mind that for queries that would return multiple As you may get duplicates in the result list (e.g. if you have 2 As and each of them has 3 Bs you'll get 6 results back). Wrap them in a set to ensure uniqueness.
B) Assuming you have an appropriate constructor for A, you can do the following:
select new A(a.field1, a.field2, ... a.fieldN, count(*) as B_count)
from A a left join a.Bs b
group by a.field1, a.field2, ... a.fieldN