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
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.
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;
}
}
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 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()?
Assume a Hibernate 4.2 JPA 2.0 entity class EntityA, it contains a #ManyToOne field List<EntityB> bs. Up to now I strongly believed that I must not replace bs by an new List, instead I have to use the list methods clear, add and remove.
To day I wanted to show a college that it will cause problems when I replace the List with an new one, but nothing strange happed, it worked, the database and the entity was updated correctly. Now I am confused: Is it with the hibernate 4.x JPA 2 allowed to replace a collection of an persisted entity?
The two enities, with a OneToMany relation ship, maintained by the one-site.
#Entity
public class EntityA {
#Id long id;
#OneToMany public List<EntityB>bs;
}
#Entity
public class EntityB {
#Id long id;
hashCode and equals based on id
}
The test, that did not find a problem
#Test
#Transactional
public testReplaceSet {
//given: a persistent entity a that contains a persistent entity b1
EntityA a = this.entityManager.persist(new EntityA());
EntityB b1 = this.entityManager.persist(new EntityB());
a.bs = new ArrayList();
a.bs = b1;
this.entityManager.flush();
//when: assining a NEW List with an new persistent Entity b2
EntityB b2 = this.entityManager.persist(new EntityB());
a.bs = new ArrayList();
a.bs = b2;
long aId = a.id;
this.entityManager.flush();
this.entityManager.clear();
//then: the collection is correct stored
EntityA AReloaded = this.entityManager.find(EntityA.class, aId);
//here I expected the failure, but there was no!
assertEquals(b2, AReloaded.bs.get(0));
}
Problems may happen when replacing contents of a list must trigger some operation in the DB.
For example, one known pitfall regarding this scenario is related to lists with orphanRemoval = true/CascadeType.DELETE_ORPHAN. In this case orphan removal won't be triggered when you replace a list.
Perhaps you may face other kinds of similar problems in different situations as well.