How to bulk delete from element collection in jpa - java

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.

Related

QueryDSL, specify projection for join

I'm trying to use QueryDSL to fetch a collection of entities (table Category) where each entity has a #OneToMany association with an other type of entity (table User), and a join is used to get it all in one go.
Problem is that the second entity (the users) contains CLOB fields which should not be included. I don't want to include them in my query. Given the following QueryDSL query :
JPAQuery<CategoryEntity> baseQuery = new JPAQuery<>(entityManager)
.select(QCategoryEntity.categoryEntity)
.from(QCategoryEntity.categoryEntity)
.leftJoin(QCategoryEntity.categoryEntity.users, QUserEntity.userEntity)
.where(somePredicate);
QueryDSL will generate something like
SELECT categoryen0_.id, (...), useren0_.id, (...)
FROM category categoryen0
LEFT OUTER JOIN user useren0 ON ...
WHERE ...
How can a specific projection be applied to this query such that the CLOB data is excluded?
Notes :
I'm trying to avoid native queries and changing the domain model.
I have not found a way to use Projections on the join
itself.
Using subqueries inside joins is not supported by JPQL hence
it's not supported by QueryDSL either.
Turns out this was not working well due to my use of fetch joins, which prevented me from using projections. Solving it was a matter of using a basic join instead and manually reconstructing the relationship.
Collection<Tuple> tuples = new JPAQuery<>(entityManager)
.select(QCategoryEntity.categoryEntity, Projections.constructor(UserEntity.class, <constructor arguments>)
.from(QCategoryEntity.categoryEntity)
.join(QCategoryEntity.categoryEntity.users, QUserEntity.userEntity)
.where(somePredicate);

Hibernate JPA Meta Model -- reference nested properties?

Suppose an entity model where an Employee has a Supervisor who has an id. Using hibernate-jpamodelgen to generate the meta model for the entities, how can I query a nested field?
For instance, "get all employees whose supervisor has id 4", using JpaSpecificationExecutor:
Page<Employee> getEmployeesBySupervisorId(int id) {
return findAll((root, query, criteriaBuilder) -> {
return criteriaBuilder.equal(root.get(Employee_.supervisor.id), id);
});
}
Note that Employee_ is the model meta class for Employee (and was generated by Hibernate).
This code will produce an error because the id symbol cannot be found on type SingularAttribute<Employee, Supervisor>. I get that, but it seems like these should somehow be chainable. I can't find great examples of how to do this cleanly.
In order to navigate to related entities, you must use From#join() join method, which works well with MetaModel:
CriteriaQuery<Employee> cq = criteriaBuilder.createQuery(Employee.class);
Root<Employee> from = cq.from(Employee.class);
Predicate p = criteriaBuilder.equal(from.join(Employee_.supervisor).get(Supervisor_.id), id);
See also
Oracle's Java EE Tutorial - Using the Criteria API and Metamodel API to Create Basic Typesafe Queries
Yes, I also stumbled upon this problem that the Metamodel classes are not offering deeper visibility to relationships > 1.
While accessing A.b is possible, A.b.c is not.
But there is another possibility besides Joins:
Just concatenate by using several getter(). For this you will need a root element (= CriteriaQuery & CriteriaBuilder).
return criteriaBuilder.equal(root.get(Employee_.supervisor).get(Supervisor_.id), id);
While this still ensures type safety, the whole path should be correct as it is not validated until runtime.
Also for sorting a resultset using the Metamodel there is a similar solution. Say you want to sort by the Supervisor's id:
Use JpaSort and JpaSort.Path
JpaSort.of(JpaSort.Direction.ASC, JpaSort.path(Employee_.supervisor).dot(Supervisor_.id));

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.

How to use 'Insert' in a nativeSQL query in Hibernate outside the mapped class ?

In Hibernate, you can use the 'SELECT' queries in native SQL like this :
Query query = session.createSQLQuery("SELECT ... FROM ...");
But I would want to use an 'INSERT' query.
So, I looked at the documentation, and it seems you must go directly to the mapped class and write the code inside it.
But I would want to use it as I do for a 'SELECT' query (outside the mapped class) since it looks much more pratical.
Indeed, why would the treatment be different between 'SELECT' and 'INSERT' for a hibernate native SQL query ?
An HQL INSERT cannot be used to directly insert arbitrary entities—it can only be used to insert entities constructed from information obtained from SELECT queries (unlike ordinary SQL, in which an INSERT command can be used to insert arbitrary data into a table, as well as insert values selected from other tables).
Here’s the syntax of the INSERT statement:
INSERT INTO path ( property [, ...]) select
The name of an entity is path. The property names are the names of properties of entities listed in the FROM path of the incorporated SELECT query. The select query is an HQL SELECT query (as described in the next section).
As this HQL statement can only use data provided by an HQL select, its application can be limited. An example of copying users to a purged table before actually purging them might look like this:
Query query=session.createQuery("insert into purged_accounts(id, code, status) "+
"select id, code, status from acount where status=:status");
query.setString("status", "purged");
int rowsCopied=query.executeUpdate();
Or you can use
session.persist(obj);
where obj is the object of the POJO class let say TestClass
TestClass obj=new TestClass('','',''); //paramatrized constructor for the values you want to insert in your database.

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.

Categories