JPA Cacheable specific example on how to use it - java

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

Related

Getting empty result from the #Query in Hibernate

I am using Hibernate to execute my query, in admin panel i am getting correct result but while using in Hibernate it is not giving any result.
Dao layer -
#Query("select new com.eventila.pms.entity.ReferenceLead(projectId,count(lm)) from LeadMaster lm where lm.vendorId= ?1 and lm.source = 'share' group by lm.projectId")
List<ReferenceLead> getReferenceByUser(String userId);
Pojo -
#lombok.Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ReferenceLead {
String projectId;
Long referenceLead;
Long count;
protected ReferenceLead(){}
public ReferenceLead(String projectId,Long count) {
this.projectId=projectId;
this.count=count;
}
}
After executing this i am getting a empty list.
Please help me out.
In your select query return the fields without calling new constructor:
#Query("select projectId, count(lm) from LeadMaster lm where lm.vendorId = ?1 and lm.source = 'share' group by lm.projectId")
List<ReferenceLead> getReferenceByUser(String userId);
Hibernate will instantiate the object using these fields. Also, add #Entity annotation to your ReferenceLead class.
'source' is the keyword in SQL.
It is a keyword used in MERGE. i.e. WHEN NOT MATCHED BY SOURCE.
The word MATCHED also exhibits the same behaviour in that it gets highlighted grey in the editor.
Neither of these are reserved keywords though so if used as an identifier they do not need to be delimited (unless you find the syntax highlighting distracting).

Filter collection by multiple attributes of its elements - QueryDSL

I am working on a dynamic filter component based on QueryDSL with the use of SpringData for query execution. Thus I create Predicate instances from the received data ad pass it to QueryDslPredicateExecutor. For dynamic access to entity attributes I use generic PathBuilder typed to the entity class.
Consider the following (simplified) code:
class Offer {
List<LanguageToName> names;
}
class LanguageToName {
String name;
String language;
}
When I try to query Offer entites, that have in their collection name element with attribute 'abc', I simply create the predicate as follows:
pathBuilder.getCollection("names", LanguageToName.class).any().getString("name")
.like("%" + fieldData.getFieldValue() + "%");
However, I was unable to come up with a solution to filter the collection by multiple attributes of the containing objects with the use of PathBuilder. When I append the code above with .and() and access the collection again via the pathBuilder variable, I naturally get the result equivalent to appending sql query with AND EXISTS..., which is not the desired result. I also tried to use getCollection().contains(), but I was unable to create the Expression<LanguageToName> that would describe such case.
Is there a way to create a Predicate that would filter entities by multiple attributes of the elements from a collection, that is a field of the queried entity?
I had similar issue and finally solved this with subquery (however, it seems to me that it works only for 1 level of nestedness).
My initial predicate was (it was making 2 independent sub-queries):
Predicate predicate = codeTable.customer.id.eq(customerId)
.and(codeTable.qualifierResults.any().customerQualifier.type.eq("TARGET_TYPE"))
.and(codeTable.qualifierResults.any().customerQualifier.referenceType.code.eq("TARGET_CODE"));
But the correct predicate that I ended up with was:
BooleanExpression customerQualifierCondition = JPAExpressions
.selectFrom(codeTableQualifierResult)
.where(codeTableQualifierResult.in(codeTable.qualifierResults),
codeTableQualifierResult.customerQualifier.type.eq("TARGET_TYPE"),
codeTableQualifierResult.customerQualifier.referenceType.code.eq("TARGET_CODE"))
.exists();
Predicate predicate = codeTable.customer.id.eq(customerId).and(customerQualifierCondition);
The idea is to write 1 separate sub-query where you apply all necessary conditions at once (instead of applying them for your collection independently).
I ran across the same problem in my project.
My workaround is to build the exists subquery manually.
Assuming that your both classes are mapped as Entities:
#Entity
#Table(name = "Offer")
public class Offer {
#Id
String id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "offer")
List<LanguageToName> names;
}
#Entity
#Table(schema = "dcsdba", name = "Language_To_Name")
public class LanguageToName {
#Id
String id;
#ManyToOne(fetch= FetchType.LAZY)
#JoinColumn(name="Offer_id")
private Offer offer;
String name;
String language;
}
A simple query with any():
BooleanExpression namesFilter = QOffer.offer.names.any().name.eq("Esperanto");
maps to
select
offer0_.id as id1_7_
from
offer offer0_
where
exists (
select
1
from
dcsdba.language_to_name names1_
where
offer0_.id=names1_.offer_id
and names1_.name=?
)
A subquery:
BooleanExpression namesFilter = JPAExpressions.selectOne()
.from(languageToName)
.where(languageToName.offer.eq(QOffer.offer)
.and(languageToName.name.eq("Esperanto")))
.exists();
Maps to:
select
offer0_.id as id1_7_
from
offer offer0_
where
exists (
select
1
from
dcsdba.language_to_name languageto1_
where
languageto1_.offer_id=offer0_.id
and languageto1_.name=?
)
which matches perfectly previous SQL.
You can add additional conditions like:
BooleanExpression namesFilter = JPAExpressions.selectOne()
.from(languageToName)
.where(languageToName.offer.eq(QOffer.offer)
.and(languageToName.name.eq("Esperanto"))
.and(languageToName.language.like("E%")))
.exists();

Hibernate find an existing object in db

Simple question here :
If i've got an object with initialized and uninitialized values in it. Is there an easy way to find in my db all the Entities that fit this one with hibernate ? (without listing and checking every variable of the object)
Example :
I got this class :
public class User {
private int id;
private String name;
private String email;
private boolean activ;
}
I would like to be able to do that :
User user1 = new User();
user.setActive() = true;
User user2 = new User();
user.setActive(true);
user.setName("petter")
listUser1 = findAllUser(user1);
listUser2 = findAllUser(user2);
Here listUser1 will contain all the active users and listUser2 will contain all the active user that are named petter.
Thx guys !
Edit/Solution
So my here is my code (i used a class wich is similar at the one of my example).
It work just fine but the problem is that according to Eclipse : "The method createCriteria(Class) from the type SharedSessionContract is deprecated"...
public static List<Personne> findAllPersonne(Personne personne) {
List<Personne> listPersonne;
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("testhibernate0");
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
Example personneExample = Example.create(personne);
Criteria criteria = session.createCriteria(Personne.class).add(personneExample);
listPersonne = criteria.list();
entityManager.close();
return listPersonne;
}
So .. How could i do that in a better way? I've looked into CriteriaQuery but i can't find how to use it with an example.
Yes it exists : the key word for google is "query by exemple" or "qbe".
https://dzone.com/articles/hibernate-query-example-qbe
In general, if an entity instance is already in your Persistence context, you can find it by primary key with EntityManager.find. Otherwise, you can pick up a result from your database by way of JPQL or native querying.
For your particular use case, it sounds like a querying solution would be the best fit; use one of the linked query creation methods from your entity, then use the Query.getResultList() method to pick up a list of objects that match the query criteria.
QueryByExample is also a good and valid solution, as Mr_Thorynque indicates, but as the article he linked mentions, that functionality is specific to certain JPA providers (Hibernate among them) and not JPA provider agnostic.

Efficient JPQL query for retrieving complex entities

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;
}
}

Can I auto-detach SqlResultSetMapping Entity from EntityManager?

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

Categories