Named query with input parameter - jpa

I am learning JPA with Hibernate, using maven as well. My problem is How can I use input parameters with UPDATE and SET clause in named query ?
#NamedQuery(name = "updateEmailAddress", query = "Update User u set u.email = :email where u.username = :username")
It gives me an error that a parameter can only be used in the WHERE or HAVING clause. I referred several article but still cannot find the proper solution.

In JPA 2.0 and below, parameters are not allowed in the set clause of a named query; only literals. This limitation is lifted if you are using JPA 2.1.
From what I can gather, you are not using JPA 2.1. Hence, I'll give you a couple of ways to sidestep this limitation.
Option 1:
Use the createQuery method and pass a dynamically generated string to the method.
String queryString = generateQueryString(email, username);
entityManager.createQuery(queryString).executeUpdate();
Option 2:
Update the associated entity and merge.
List<User> result = entityManager.createQuery('select u from user u where
u.username = :username').setParameter('username', username).getResultList();
for (User user : result) {
user.setEmail(email);
entityManager.merge(user);
}
Option 3:
Create the query using HQL not JPQL. I haven't tested this nor do I recommend it because you are going behind the entity manager's back.
Query q = sessionFactory.getCurrentSession().createNamedQuery('updateEmailAddress');
q.setParameter('email', email);
q.setParameter('username', username);
q.executeUpdate();

While in fact until JPA 2.1 this was not allowed, you can actually use it because the providers will let you provide parameters in that way (which turns out to be a good thing!).
It seems the JPA providers are not conforming to the spec regarding this validation, and I think is just because it didn't make any sense (you can see in 2.1 it is now permitted). "Why would me make it difficult do developers?"
I am also using EclipseLink 2.3.1 and it is working fine.
The recommended solution
Just disable Eclipse's JPQL query validation.
If the provider accepts it, you should be fine, otherwise you need to conform to the spec. Very simple. Code will be cleaner and it will conform to recent evaluations of the spec.
Just go to: Preferences > Java Persistence > JPA > Errors/Warnings > Queries and Generators > Invalid or incomplete JPQL queries: and Ignore it
Check this article for details:
Conclusion
Hibernate does not follow the specification on this point but one
might guess that the new version of the JPA-spec will allow this
behavior as indicated by the draft JSR. JBoss Tools is probably
validating the query against the JPQL-grammar which is based on the
specification and is therefore showing a validation error.
And this is the resolution:
End remark
After a discussion in out team we decided to keep the current
implementation despite the breach of specification. Changing the
behavior would mean string concatenation or string substitution to
build the query and the current approach is much cleaner. As we see no
indications of a shift in persistence provider or application server
at this stage we believe the gains of keeping the code are larger than
the risks at this point.

Can you try positional parameter and see if it works?
#NamedQuery(name = "updateEmailAddress", query = "UPDATE User u SET u.email = ?1 WHERE u.username = ?2")
//The parameter needs to be passed as
query.setParameter(1, "the_emailaddress");
query.setParameter(2, "the_username");

You must build a query named as follows:
Query query = getEntityManager().createNamedQuery("updateEmailAddress");
query.setParameter("email", "email#test.com");
query.setParameter("username", "emailuser");
int result = query.executeUpdate();
System.out.println("Rows affected: " + result);
Sources:
Hibernate 3.6 - DML-style operations
hibernate 4.2 - HQL for UPDATE and DELETE
Hibernate Query examples (HQL)
Hibernate Query Languages

Related

Entity Framework query based on string stored in configuration file

i would like to know if you have any idea how i can achieve this, considering a query stored as string in the configuration file.
I tried to use SqlQuery applied to the DBSet, but the problem is that SqlQuery requires me to select all properties of the required entities in my query. If i don't consider any column, it will complain because is not able to map the query to the entities.
I don't want to select all properties of the entities i want to query.
Thanks
If you are using EF then why not use Database.ExecuteSqlCommand()? It's in the System.Data.Entity namespace.
For example:
int result = db.Database.ExecuteSqlCommand("Non SELECT SQL etc...");
Well, I ended up implementing a mechanism using reflection that basically receives a group of fields to select, and constructs dynamic objects with those fields, so when applied the query with the joins between the entities, will only bring the fields I am looking for.
So, considering Entity1, Entity2, Entity3 with the following relationship
<b>Entity1</b>{
<br/> Entity1Name, <br/> List<*Entity2*> Entity2Items, <br/> etc..
<br/>}
and
<b>Entity2</b> { <br/> Entity2Name, <br/> List<*Entity3*> Entity3Items <br/>}
I can store e.g. the following query in the configuration file, and retrieve the information:
"Entity1.Entity1Name", <br/>
"Entity1.Entity2Items.Entity2Name", <br/>
"Entity1.Entity2Items.Entity3Items.Entity3Name"
Anyway, I was just trying to see if there would be any solution out-of-the-box that would require minimal code changes.
Thank you.

Purpose of Eclipselink 2.6.0 query - SELECT ID FROM TBL WHERE ID=?

Has anyone got an idea why JPA Provider - Eclipselink (using version 2.6.0) generates query like:
SELECT ID FROM OPERATION WHERE (ID = ?);
or
SELECT ID FROM SUBSCRIPTION WHERE (ID = ?);
Why it needs to get ID providing an ID...
Maybe 1st or 2nd level Cache synchronization?
Maybe my queries are inaccurate...
In my queries I never ask directly this to execute - I use JPQL and never ask for ID giving some ID.
I have quite complex model and queries so I see no point in providing full code (or only if U really insist but I dont think it will help a lot).
Using Oracle 12c DB.
We have encountered the same issue with our Java application using EclipseLink 2.5.2. Specifically, we saw it whenever we inserted a new entity that had a M:1 relationship with another entity. A simplified use case would be:
A a = new A(); // the new entity
B b = lookupB(); // get existing entity from database
a.setB(b); // set M:1 relationship
em.persist(a); // save
In this case, we would always see a query for B (i.e., SELECT ID FROM B WHERE ID = #). After some research, we traced it down to the existence checking that EclipseLink performs before merging records.
We found that annotating the entity class for B with #ExistenceChecking(ExistenceType.ASSUME_EXISTENCE) prevented EclipseLink from running the query.
For a further discussion of this, see this related post.

Writing custom n1ql queries in spring

I'm trying to query a spring-data couchbase repository using N1QL queries. I have two doubts:
I'm using #Query annotation to generate queries, my code looks like this:
#Query("#{#n1ql.selectEntity} WHERE $0 = $1 AND #{#n1ql.filter}")
public Page<GsJsonStore> matchJson(String term, String value, Pageable pageable);
//Query
Page<GsJsonStore> p = repo.matchJson("_object.details.status", "ready", pg);
This query doesn't return any results. However, when I run the same query (below) in cbq I get the desired result:
select * from default where _object.details.status = 'ready';
How can I view the query string generated by the Couchbase repository? I'm using spring-boot. Am I correct in using the #Query annotation for this use-case?
Also, how can I perform n1QL queries on CouchbaseOperations template? I know that there's a findByN1QL method, but I didn't find any good documentation about it. Could someone please explain how to use this?
The query looks ok. You did persist your GsJsonStore entities using the Spring Data Couchbase repository did you?
In order to log all the queries generated and executed by the framework (including inline queries like in your case), you can configure the logger like so in a logback.xml configuration:
<logger name="org.springframework.data.couchbase.repository.query" level="debug"/>
You'll see that the query that got executed and the query that you ran in cbq are not the same, since at least you didn't use a WHERE clause.
In CouchbaseOperations there are two methods relative to N1QL queries:
findByN1QL: this expects specific structure of the query, in order to make sure all data necessary to correct deserialization of a Spring Data annotated entity is selected (which is the purpose of the #n1ql.selectEntity and #n1ql.filter SpEL).
findByN1QLProjection is more free-form. If Jackson can deserialize the results of the provided query to the requested Class, then it will. As such, the SELECT clause is much less implicitly restricted in this method.
To use both, you must pass in a N1qlQuery object from the SDK. Such queries can be constructed using factory methods of the N1qlQuery class, for example:
//a version of the query that is constructed from positional parameters
N1qlQuery queryWithParameter = N1qlQuery.parameterized("SELECT name FROM `beer-sample` WHERE name LIKE $0", JsonArray.from("%dog%"));
//let Spring Data execute the query, projecting to the String class
List<String> beerNamesContainingDog = template.findByN1QLProjection(queryWithParameter, String.class);

JPA criteria builder - in human readable language

I understand the advantages of using a JPA criteria builder above the Java Persistence Query Language.
Is there an easy way to explain how to build up this kind of queries?
I need a more human readable explanation to build up my queries, this to have a kind of intuitive approach to query my database.
Example:
SQL:
SELECT id,status,created_at from transactions where status='1'
and currency='USD' and appId='123' order by id
Critera Builder with MetaModel:
Map<SingularAttribute<Transaction, ?>, Object> params = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Transaction> r = cq.from(Transaction.class);
Predicate p= cb.conjunction();
for (Map.Entry<SingularAttribute<Transaction, ?>, Object> param: params.entrySet())
p = cb.and(p, cb.equal(r.get(param.getKey()), param.getValue()));
cq.multiselect(r.get(Transaction_.id), r.get(Transaction_.status),
r.get(Transaction_.created_at))
.where(p)
.orderBy(cb.asc(r.get(Transaction_.id)));
List<Tuple> result = em.createQuery(cq).getResultList();
This example was based on another question:
Complex queries with JPA criteria builder
I don't think there's an more clean way to write that kind of query following the standards and not using an hand wrote JPQL query, anyway out of the standard the are many query builders like: query dsl or Torpedo query or Object Query that allow to write query in a more clean way if that can help :)
Criteria query have some advantages of JPQL, such as: type safety, write SQL querias based on Java Programming model and make it portable. The easiest way in which I understand this when I started to work with JPA is think in the main objects that you need to use and their features.
CriteriaBuilder: Any statement that can be done using SQL like functions, reserved works, operations, predicates are part of this class, so builder need to be used to create those and apply them to the criteriaQuery.
CriteriaQuery: Any statement to have as goal to create a formal SQL statement are here, think on this as the boilerplate of the query definition, it have from, select, where, group by and all the statements, so this is the query itself and must be use to determine what you are really looking for from the database.
Now think to do a query you always must to use a Root, this mean select what will be your main table in your query definitions, based on that and helping from CriteriaQuery and CriteriaBuilder objects you can redefine the search to be whatever you want.

Adding OrderBy clause to a named query

Is it possible to add an OrderBy clause in to JPA Named query at runtime?
Named queries are processed by the persistence provider when the EntityManagerFactory is created. You can't change/modify/append anything about a named query dynamically at runtime.
If you are using JPA 2.0 and you need a way to do high-performance dynamic queries at runtime, you should look into the Criteria API.
From Java EE 5 Documentation : "Is used to specify a named query in the Java Persistence query language, which is a static query expressed in metadata. Query names are scoped to the persistence unit".
As it says, it is static & you can't change the query at runtime.
Rather use custom query or if ordering element is fixed then you can use annotation;
Field:
#OrderBy(value="nickname")
List<Person> friends;
Method:
#OrderBy("nickname ASC")
public List<Person> getFriends() {...};
Even if you can't add order-by clause to your named query, you can provide a parametric orderBy. The following is a perfectly valid query:
SELECT u FROM User u ORDER BY :orderBy
And you are going to change ordering with something like:
query.setParameter("orderBy", "id");