NativeQuery is better or HibernateOGM methods - java

I am using hibernate OGM to talk to my MongoDB instance. I had to get a list of all the products with category "abc". I am using the native query approach to achieve this as following:
String stringQuery = "db.Message.find({'CATEGORY':'" + category + "})";
Query query = entityManagerProvider.get().createNativeQuery(stringQuery, Product.class);
productList = query.getResultList();
I am not sure if it is the right approach to do this as I see a too much hard coding (look at the collection name). Can I use the .find() method to achieve the same thing?
We are using vertx server with gradle as building tool.

Do you mean the EntityManager.find()? You can use it if you filter using the primary key. It doesn't seem the case in your example.
What you can do is write a JP-QL query:
productList = entityManagerProvider.get().createQuery( "SELECT p FROM Product p WHERE p.category=:category", Product.class ).setParameter("category", category).getResultList();
I'm assuming that you have an entity Product with attribute category.

Related

How to use the same Java class with different SQL queries

I'm designing a Java report application that can receive any SQL query(on a already defined database) and then I display the result in a table grid in a Vue.js application.
I have some doubts on how to load a Java generic/hybrid class that can fit differents SQL queries.
I mean, is it possible to create a class in Java that can change/mutate during runtime so I can map different SQL queries in it?
I know that it is possible to use the java.sql.ResultSetMetaData to get the column name, table name, column class name, etc. (I don't know if it possible with hibernate)
But I don't know how to map the results in a unique class.
For example:
I have 3 differents queries and they could be a lot more:
1) SELECT ID, COUNTRY_NAME FROM COUNTRY;
2) SELECT CODE, NAME, PRICE FROM PRODUCT;
3) SELECT P.CODE, P.NAME, S.NAME
FROM PRODUCT P
JOIN SUPPLIER S
ON S.ID = P.SUPPLIER_ID
WHERE P.PRICE > 25;
I need to map this query results(one at a time obviously) in a generic class so then I pass it to the Vue app to display it in a grid.
Is it a way to do that?
Don't reinvent the wheel and use what is already available and what is well tested.
What you're looking for is called Spring JDBC, or, for an even higher level, Java Persistence API.
Using Spring JDBC to extract a set of records might look like
final List<YourClass> results =
jdbcTemplate.queryForList(
"Native SQL statement",
queryArguments,
YourClass.class
);
Using a JPA implementation (e.g. Hibernate, EclipseLink, ObjectDb), the operation might look like
final TypedQuery<YourClass> query = entityManager.createQuery("JPQL statement", YourClass.class);
final List<YourClass> results = query.getResultList();

How to load multiple Hibernate entities in order of the identifiers or Primary Keys

How do we load multiple entities using Hibernate in the order of the list of Pks that is provided to the Hibernate query?
In the code below, the order of the list output is in ascending order rather than the order in which the Pks is supplied in the argument
Criteria criteria = s.createCriteria(entityClass).add(Restrictions.in(idPropertyName, pks));
List list = criteria.list();
You get them, then sort them using a comparator that compares the index of each entity in the list.
For example:
Map<Long, Integer> indexById = new HashMap<>();
for (int i = 0; i < pks.size(); i++) {
indexById.put(pks.get(i), i);
}
List<MyEntity> entities = seachByIds(pks);
entities.sort(Comparator.comparing(entity -> indexById.get(entity.getId())));
As I explained in this article, there are several ways you can achieve this goal.
In your example, you were using the legacy Hibernate Criteria, but since it's been deprecated since Hibernate 4 and will probably be removed in Hibernate 6.
Therefore, it's better to use one of the following alternatives.
Note that, in your example, you have the entity identifier values defined in a pks variable of the List type, and I'm going to reuse that in the examples below as well.
JPQL
You can use a JPQL query like the following one:
List<Book> books = entityManager
.createQuery(
"select b " +
"from Book b " +
"where b.id in (:ids)", Book.class)
.setParameter("ids", pks)
.getResultList();
When using JPQL, The ids parameter will pass the entity identifiers in the same order they were defined in the pks variable.
Criteria API
If you want to build the query dynamically, then you can use a Criteria API query:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Book> query = builder.createQuery(Book.class);
ParameterExpression<List> ids = builder.parameter(List.class);
Root<Book> root = query
.from(Book.class);
query
.where(
root.get("id").in(
ids
)
);
List<Book> books = entityManager
.createQuery(query)
.setParameter(ids, pks)
.getResultList();
When using Criteria API, The ids parameter will pass the entity identifiers in the same order they were defined in the pks variable.
Hibernate-specific multiLoad
List<Book> books = entityManager
.unwrap(Session.class)
.byMultipleIds(Book.class)
.multiLoad(pks);
By default, the Hibernate multiLoad, The ids parameter will pass the entity identifiers in the same order they were defined in the pks variable. Only if you called enableOrderedReturn(false) explicitly, then the result set will not be ordered.
Now, the JPQL and Criteria API can benefit from the hibernate.query.in_clause_parameter_padding optimization as well, which allows you to increase the SQL statement caching mechanism.
For more details about loading multiple entities by their identifier, check out this article.

Dynamic Named Query in Entity class using JPQL example

I have a named query as below;
#NamedQuery(name = "MyEntityClass.findSomething", query = "SELECT item FROM MyTable mytbl")
Now I want to append dynamic sort clause to this query (based on UI parameters)
Can I get an example using JPQL for doing the same (like how to set a dynamic ORDER BY in the Entity class)
I have already tried using CriteriaQuery, but was looking for a JPQL implementation now.
NamedQueries are by definition NOT dynamic, it is not correct to change them programmatically.
So the way to go is to create a JPQL query (but not a named query) like this:
TypedQuery<MyEntity> query = em.createdQuery("SELECT item FROM MyEntity item ORDER BY "+sortingCol, MyEntity.class);
On the other hand, if you REALLY want to use the named query, you could do that the following way:
#NamedQuery(name = "MyEntityClass.findSomething", query = MyEntity.NAMED_QUERY)
#Entity
public class MyEntity {
public static final NAMED_QUERY= "SELECT item FROM MyTable mytbl";
//+your persistent fields/properties...
}
//and later in your code
TypedQuery<MyEntity> query = entityManager.createQuery(MyEntity.NAMED_QUERY + " ORDER BY " + sortingCol, MyEntity.class);
Complementing for JPA 2.1
As of JPA 2.1 it is possible to define named queries programmatically.
This can be achieved using entityManagerFactory.addNamedQuery(String name, Query).
Example:
Query q = this.em.createQuery("SELECT a FROM Book b JOIN b.authors a WHERE b.title LIKE :title GROUP BY a");
this.em.getEntityManagerFactory().addNamedQuery("selectAuthorOfBook", q);
// then use like any namedQuery
Reference here
This can be useful, for instance, if you have the orderby field defined as a application parameter. So, when the application starts up or on the first run of the query, you could define the NamedQuery with the defined OrderBy field.
On the other side, if your OrderBy can be changed anytime (or changes a lot), then you need dynamic queries instead of NamedQuery (static). It would not worth to (re)create a NamedQuery every time (by performance).
#NamedQuery
Persistence Provider converts the named queries from JPQL to SQL at deployment time.
Until now, there is no feature to create/update the query with #NamedQuery annotation at runtime.
On the other hand, you can use Reflection API, to change the annotation value at runtime. I think It is not solution, also it is not you wanted .
em.createQuery()
Persistence Provider converts the dynamic queries from JPQL to SQL every time it is invoked.
The main advantage of using dynamic queries is that the query can be created based on the user inputs.

How to bulk delete from element collection in jpa

I'm using jpa 2.0 and I have the following entity:
#Entity
public class Folder{
#ElementCollection
#CollectionTable(name="folder_files")
private Set<String> files;
// .....
}
Given a file name, I would like to delete all entries where files == theGivenFileName. In sql it would be something like this:
Delete from folder_files where files = XXX
Is there a way to perform this query using criteria-api?
If not, is there a way to perform this query using jpql?
UPDATE:
I think my question was not clear enough:
Since jpql uses entities (and not tables) I cannot just perform the sql written above plus since I'm using #ElementCollection I don't know how to address this variablr or even deal with it. I would like to delete all entries in that collection (in my case, the files set) which holds a given value, from all entities. Is that possible using jpql or (even better) criteria-api?
The Delete FROM clause requires an Entity, so there is no way to delete from an element collection from what I understand.
You can use a native SQL query, or you can map the element collection as a OneToMany to an Entity instead.
You can use the like query just the syntax is slightly changed.
query = em.createQuery("SELECT i FROM Item i WHERE UPPER(i.name) LIKE :keyword ");
query.setParameter("keyword", "%" + keyword.toUpperCase() + "%");
You can read more on following link,
https://forums.oracle.com/forums/thread.jspa?threadID=423742
Updated:
#Noam you can do it: Like in Criteria API
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.between("weight", minWeight, maxWeight) )
.list();
Kindly read more on it at following link:
http://ctpconsulting.github.com/query/1.0.0.Alpha3/criteria.html
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html
This cannot be done. Via JPQL it does not work because DELETE statement can only be applied to entities. Excerpt from JPA 2.0 specification:
Bulk update and delete operations apply to entities of a single entity
class (together with its subclasses,if any).
...
delete_statement ::= delete_clause [where_clause]
delete_clause ::= DELETE FROM entity_name [[AS] identification_variable]
Also it doesn't work via Criteria API. CriteriaQuery supports only selecting - not updates or removals.
You have to go for native SQL.

How To Configure Query Cacheing in EclipseLink

I have a collection of states, that I want to cache for the life of the application, preferably after it is called for the first time. I'm using EclipseLink as my persistence provider. In my EJB3 entity I have the following code:
#Cache
#NamedQueries({
#NamedQuery(
name = "State.findAll",
query = "SELECT s FROM State s",
hints = {
#QueryHint(name=QueryHints.CACHE_USAGE, value=CacheUsage.CheckCacheThenDatabase),
#QueryHint(name=QueryHints.READ_ONLY, value=HintValues.TRUE)
}
)
})
This doesn't seem to do anything though, if I monitor the SQL queries going to MySQL it still does a select each time my Session Bean uses this NamedQuery.
What is the correct way to configure this query so that it is only ever read once from the database, preferably across all sessions?
Edit: I am calling the query like this:
Query query = em.createNamedQuery("State.findAll");
List<State> states = query.getResultList();
The solutions posted here not worked for me. But i've made it work with:
#Cache
#NamedQueries({#NamedQuery(
name = "State.findAll",
query = "SELECT s FROM State s",
hints = {
#QueryHint(name = QueryHints.QUERY_RESULTS_CACHE, value = HintValues.TRUE)
}
)})
Just a guess here, but you might try
query.cacheQueryResults();
after you create it but before you getResultList.
-- MarkusQ
I got EclipseLink 1.0.1 cache to work by adding just the query hint:
Query query = em.createNamedQuery("Person.find");
query.setParameter("NAME", name);
query.setHint("eclipselink.cache-usage", "CheckCacheThenDatabase");
return (Person)query.getSingleResult();
I didn't change the entity at all, and haven't yet tried to configure cache using annotations.

Categories