JPA criteriabuilder "IN" predicate on foreign key error: Object comparisons can only use the equal() or notEqual() operators - jpa

I want to select a list of file references from a table by looking at which users have the rights to retrieve that file. To do this I have 3 tables, a file table, an access control table, and a users table.
I am using JPA and Criteriabuilder (because there are more tables involved and I need dynamicle create the query, I am leaving out the other tables and predicates from this question for the sake of readability).
The following code works
CriteriaBuilder queryBuilder = em.getCriteriaBuilder();
CriteriaQuery<File> queryDefinition = queryBuilder.createQuery(File.class);
Root<File> FileRoot = queryDefinition.from(File.class);
List<Predicate> predicateList = new ArrayList<Predicate>();
Predicate userPredicate = FileRoot .join("FileAccesControlCollection").join("userId").get("usersId").in(loggedInUser.getUsersId());
predicateList.add(userPredicate );
queryDefinition.where(predicateArray).distinct(true);
Query q = em.createQuery(queryDefinition);
List<Files> results = (List<Files>) q.getResultList();
For the userpredicate I want to leave out the last join to the users table because the ID that I want to filter on is already present in the FileAccesControlCollection table, and a join is a computational expensive database operation.
What I tried is to do this:
Predicate userPredicate = FileRoot .join("FileAccesControlCollection").get("usersId").in(loggedInUser.getUsersId());
But I guess because the userId value in the FileAccesControlCollection entity class is a foreignkey reference to the Users class I get the following error:
Exception Description: Object comparisons can only use the equal() or notEqual() operators. Other comparisons must be done through query keys or direct attribute level comparisons.
Is there a way, using the loggedInUser entity or its Id, to filter the files by just joining the File class to the FileAccesControlCollection class and filtering on the userId foreign key? I am kind of new to JPA and using google lead me to a lot of pages but not a clear answer for something which seems to me should be possible.

So "userId" is mapped as a OneToOne? Then you could do,
get("userId").get("id").in(...)
You could also add a QueryKey in EclipseLink using a DescriptorCustomizer for the foreign key field and then use it in the query,
get("userFk").in(...)

try this:
Predicate userPredicate = FileRoot.join(FileAccesControlCollection.class).join(Users.class).get("{id field name in class Users}").in(loggedInUser.getUsersId());
good luck.

Related

OrderBy Expression When Using An Association Table

I have two tables: CommentCategories and Comments. It is a many-to-many relationship. There is a CommentCategory_Comment association table. But that table contains a orderOfCommentInCategory field, so EF represents the association by creating a seperate entity rather than navigation properties.
I am trying to get the comments under a certain category and orer them by the orderOfCommentInCategory field. How should be the OrderBy expression that needs to be added to the query below?
List<Comment> comments = category.CommentCategory_Comment
.Select(ccc=>ccc.Comment)
.OrderBy(???)
.ToList();
As #GertArnold recommends the only you need to do is order first before select:
var comments = category.CommentCategory_Comment
.OrderBy(c=>c.orderOfCommentInCategory )
.Select(ccc=>ccc.Comment)
.ToList();
Using query syntax:
var query= from cc in category.CommentCategory_Comment
orderby cc.orderOfCommentInCategory
select cc.Comment;
var comments=query.ToList();

TypedQuery<x> returns vector of Object[] instead of list of x-type object

I have a method:
public List<Timetable> getTimetableTableForRegion(String id) {
List<Timetable> timetables;
TypedQuery<Timetable> query = em_read.createQuery("SELECT ..stuff.. where R.id = :id", Timetable.class).setParameter("id", Long.parseLong(id));
timetables = query.getResultList();
return timetables;
}
which returns this:
so, what am I missing in order to return a list of Timetable's?
ok, so, ..stuff.. part of my JPQL contained an inner join to other table. Even through in SELECT there were selected fields just from one table, which was used as type - Timetable, Eclipslink was unable to determine if this fields are part of that entity and instead of returning list of defined entity returned list of Object[].
So in conclusion: Use #OneToMany/#ManyToOne mappings (or flat table design) and query just for ONE table in your JPQL to be able to typize returned entities.
Not sure it might be something is looking for, but I had similar problem and converted Vector to ArrayList like this:
final ArrayList<YourClazz> results = new ArrayList<YourClazz>();;
for ( YourClazzkey : (Vector<YourClazz>) query.getResultList() )
{
results.add(key);
}
i have faced the same problem. and my entity has no one to one or one to many relationship. then also jpql was giving me queryresult as vector of objects. i changed my solution to query to criteria builder. and that worked for me.
code snippet is as below:
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Timetable> criteria = builder.createQuery(Timetable.class);
Root<Enumeration> root = criteria.from(Timetable.class);
criteria.where(builder.equal(root.get("id"), id));
List<Timetable> topics = this.entityManager.createQuery(criteria) .getResultList();
return topics;

JPQL Query Bulk UPDATE SET on an ElementCollection field

I have the following JPA Entity I want to update:
#Entity(name = "EmployeeImpl")
#Table(name = "EmployeeImpl")
public class EmployeeImpl {
#Id
#Column(name = "employeeId")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ElementCollection
private List<String> phonenumber;
}
I thought I use a namedQuery like so
#NamedQuery(name = "updateEmployee",
query = "Update EmployeeImpl e SET e.phonenumber :number WHERE e.id = :id")
But that doesn't work: Exception Description: Error compiling the query [updateEmployee: Update EmployeeImpl e SET e.phonenumber = :number WHERE e.id = :id], line 1, column 28: invalid access of attribute [phonenumber] in SET clause target [e], only state fields and single valued association fields may be updated in a SET clause.
Question is, how do I update an #ElementCollection? If it's possible i'd like to do it with a jpql query.
No, that is not possible in JPQL. As kostja says: the message says it clear and also according to the JPA specification, Chapter "4.10 Bulk Update and Delete Operations" you may update only state fields and single values object fields.
The syntax of these operations is as follows:
update_statement ::= update_clause [where_clause]
update_clause ::= UPDATE entity_name [[AS] identification_variable]
SET update_item {, update_item}*
update_item ::= [identification_variable.]{state_field | single_valued_object_field} = new_value
new_value ::=
scalar_expression |
simple_entity_expression |
NULL
WHAT TO DO?
Probably the most clean way to do that is simply to fetch the entities and to add/replace the phone number/s, although you can always do that also with Native Queries, i.e SQL queries as kostja says.
The reason for the failure is stated in the error message. You cannot use bulk updates on non-singular attributes of your entity, since they are stored in a different table.
How do you do it instead? You update the collection table.
The default table name for collection tables is <parent_entity>_<field_name>. So the table you are interested in should be named EmployeeImpl_phonenumber. The id column for the EmployeeImpl (the foreign key) should be named EmployeeImpl_id per default.
EDIT What I posted initially was not valid in JPQL. You might want to use a native query instead. It is simple, so it should be portable:
The native query could then look like this:
UPDATE EmplyeeImpl_phonenumber
SET phonenumber = ?
WHERE employeeimpl_id = ?

NamedQuery returning object instead of column

Hi I have a namedquery defined as below but when I execute it it retunrns me the whole object rather than just the fields that I have requested. Is there something that I am missing when I only want to return just a column of that object. Thanks in advance
#NamedQueries ({
#NamedQuery(
name="findSubmissionForSubmissionRowUniqueBankId",
query="SELECT o.submission FROM SubmissionRow o WHERE o.uniqueBankId = :uniqueBankId",
hints={#QueryHint(name=QueryHints.CACHE_USAGE, value=CacheUsage.CheckCacheThenDatabase),
#QueryHint(name=QueryHints.QUERY_RESULTS_CACHE_SIZE, value="1000"),
#QueryHint(name=QueryHints.QUERY_RESULTS_CACHE_EXPIRY, value="18000")
})
})
The sql that it excecutes for this query is
EJBQueryImpl(ReadObjectQuery(name="findSubmissionForSubmissionRowUniqueBankId" referenceClass=SubmissionRow sql="SELECT ID, ARCHIVE_BANK_ID, EXTERNAL_SOURCE_DETAILS,UNIQUE_BANK_ID, SUBMISSION_ID FROM FE_TEST.SUBMISSION_ROW WHERE (UNIQUE_BANK_ID = ?)"))
I have defined the join as folllows
#ManyToOne
#JoinColumn(name = "SUBMISSION_ID", referencedColumnName = "ID")
private Submission submission;
Your hints do not make sense,
#QueryHint(name=QueryHints.CACHE_USAGE, value=CacheUsage.CheckCacheThenDatabase),
#QueryHint(name=QueryHints.QUERY_RESULTS_CACHE_SIZE, value="1000"),
#QueryHint(name=QueryHints.QUERY_RESULTS_CACHE_EXPIRY, value="18000")
You seem to think you are using query caching, but are not. CACHE_USAGE does not enable query caching, but in-memory querying (searches the entire cache for the object).
To enable the query cache use, QueryHints.QUERY_RESULTS_CACHE = true.
Remove CACHE_USAGE. CACHE_USAGE in-memory querying is only supported with the whole objects, it does not support selecting parts. If you want to use in-memory querying, just query the whole object, and then access the part you want.

Porting query from JPQL to mongo using spring data mongo Criteria

I've ported some of my Entity from JPA to document and now porting some of my queries.
here is the JPA query:
em.createQuery("select distinct c from CustomerImpl c left join fetch c.addresses ca where (:name is null or c.firstName LIKE :name or c.lastName LIKE :name) and (:ref is null or c.externalReference LIKE :ref) and (:city is null or ca.city LIKE :city) order by c.firstName").setParameter("name", name).setParameter("ref", customerRef).setParameter("city", city).getResultList();
below is my attempt :
Criteria orNameCriteria = new Criteria().orOperator(Criteria.where("firstName").is(null), Criteria.where("firstName").is(name), Criteria.where("lastName").is(name));
Criteria orCustomerRefCriteria = new Criteria().orOperator(Criteria.where("externalReference").is(null), Criteria.where("externalReference").regex(customerRef,"i"));
Criteria orAddress = new Criteria().orOperator(Criteria.where("addresses.city").is(null), Criteria.where("addresses.city").regex(city, "i"));
Query nameq = new Query(new Criteria().andOperator(orNameCriteria,orCustomerRefCriteria,orAddress));
this query return zero size arraylist. I've then changed the orNameCriteria to use is clause and making sure the data contained in name variable has / as suffix and prefix. That didn't work as well.
but queries from mongoVue and RockMongo clients :
{ firstName: /SAM/}
returns data.
Question 1: How do you write LIKE CLAUSE with spring-data-mongo Criteria?
Question 2 : is that the right way to use or and and clause with criteria
Thanks for reading
Criteria.where("field").regex(pattern) should work
Since I don't have the ability add comments...
If you do a static import on Criteria, it will make your where clauses look a lot better.
Criteria orAddress = new Criteria().orOperator(where("addresses.city").is(null), where("addresses.city").regex(city, "i"));