Currently using eclipselink JPA provider for accessing backend entities. I'm using namedqueries for accessing data and using the below options on query caching.
#NamedQueries({
#NamedQuery(name = Supplier.FIND_ALL, query = "select o from Supplier o",hints={
#QueryHint(name=QueryHints.READ_ONLY, value=HintValues.TRUE),
#QueryHint(name = QueryHints.QUERY_RESULTS_CACHE, value = HintValues.TRUE),
#QueryHint(name = QueryHints.CACHE_STATMENT, value = HintValues.TRUE),
#QueryHint(name = QueryHints.CACHE_STORE_MODE, value = "REFRESH"),
#QueryHint(name = QueryHints.CACHE_RETRIEVE_MODE,value=CacheUsage.CheckCacheThenDatabase),
}),
})
Also i'm using the below Cache options on the entity as well.
#Cache(refreshOnlyIfNewer=true,
coordinationType=CacheCoordinationType.SEND_OBJECT_CHANGES,alwaysRefresh=true)
The query seems to be taking sometime for the first time, but is pretty fast for further retrievals ( b'cozs of QueryHints.QUERY_RESULTS_CACHE, value = HintValues.TRUE ). But it seems any changes to the database subsequently, are not getting reflected in the output. It seems the cache is not getting refreshed and updated changes to the database are not reflected in the output.
Require help on the same.
Thanks,
Krishna
You settings don't make a lot of sense.
QueryHints.QUERY_RESULTS_CACHE is the correct way to enable the query cache. But you should not set the others,
QueryHints.CACHE_STATMENT - this is JDBC statement caching, very odd to be setting this on a query, normally this is configured for all statements in the DatabaseSource, or persistence unit config if using EclipseLink connection pooling.
QueryHints.CACHE_STORE_MODE - I'm not sure this makes sense, you cannot refresh and cache the query.
QueryHints.CACHE_RETRIEVE_MODE,value=CacheUsage.CheckCacheThenDatabase, this makes no sense, CacheUsage is not for this property, and CheckCacheThenDatabase makes no sense for a query that is using a query cache.
EclipseLink has two types of caches, the "object cache" (by Id) and the "query cache" (by query name and parameters). When you enable a query cache, the query results will be cached until they expire. By default there is no expiry, but you can configure it, or manually clear the query cache. The query cache will not be updated with database changes.
See,
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Caching/Query_Cache
Related
Two (JSF + JPA + EclipseLink + MySQL) applications share the same database. One application runs a scheduled task where the other one creates tasks for schedules. The tasks created by the first application is collected by queries in the second one without any issue. The second application updates fields in the task, but the changes done by the second application is not refreshed when queried by JPQL.
I have added QueryHints.CACHE_USAGE as CacheUsage.DoNotCheckCache, still, the latest updates are not reflected in the query results.
The code is given below.
How can I get the latest updates done to the database from a JPQL query?
public List<T> findByJpql(String jpql, Map<String, Object> parameters, boolean withoutCache) {
TypedQuery<T> qry = getEntityManager().createQuery(jpql, entityClass);
Set s = parameters.entrySet();
Iterator it = s.iterator();
while (it.hasNext()) {
Map.Entry m = (Map.Entry) it.next();
String pPara = (String) m.getKey();
if (m.getValue() instanceof Date) {
Date pVal = (Date) m.getValue();
qry.setParameter(pPara, pVal, TemporalType.DATE);
} else {
Object pVal = (Object) m.getValue();
qry.setParameter(pPara, pVal);
}
}
if(withoutCache){
qry.setHint(QueryHints.CACHE_USAGE, CacheUsage.DoNotCheckCache);
}
return qry.getResultList();
}
The CacheUsage settings affect what EclipseLink can query using what is in memory, but not what happens after it goes to the database for results.
It seems you don't want to out right avoid the cache, but refresh it I assume so the latest changes can be visible. This is a very common situation when multiple apps and levels of caching are involved, so there are many different solutions you might want to look into such as manual invalidation or even if both apps are JPA based, cache coordination (so one app can send an invalidation even to the other). Or you can control this on specific queries with the "eclipselink.refresh" query hint, which will force the query to reload the data within the cached object with what is returned from the database. Please take care with it, as if used in a local EntityManager, any modified entities that would be returned by the query will also be refreshed and changes lost
References for caching:
https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Caching
https://www.eclipse.org/eclipselink/documentation/2.6/concepts/cache010.htm
Make the Entity not to depend on cache by adding the following lines.
#Cache(
type=CacheType.NONE, // Cache nothing
expiry=0,
alwaysRefresh=true
)
My use case is:
I have a graph of objects from many entities in Entity Framework Core 2 (EFC 2). In EFC 2, the SaveChanges Operation is very slow. The reason appears to be the limit in number of parameters that Sql Server can receieve per query. As Sql Server receive 2100 parameters per query, saving hundreds of thousands of registers must cause many roundtrips, with much latency implied. See issue 2484 for more information. My current solution is to generate a SqlCommand with a query with Table Valued Parameters (TVP). The plan is to use only one query with a TVP for each table and operation (insert, update and delete), using only one roundtrip for save all the changes. EFC cannot do that actually.
In theory, i'm almost finish this, but i have a problem. To use TVP, i must get the SqlDbType for each column from my Table Type. The Table Types are generated using the metadata in the IModel from EFC. But, i cant get the SqlDbType. Simplified, I tried with:
var typeMapper = new SqlServerTypeMapper(new RelationalTypeMapperDependencies());
var entityType = context.Model.GetEntityTypes().First();
var prop = entityType.GetProperties().First();
var mapping = typeMapper.GetMapping(prop);
var dbType = mapping.DbType;
Having dbType, the plan is get the SqlDbType from dbType using a Dictionary. The problem is dbType is getting null.
I'm searched in the api, and i can get the way to extract a SqlType from IModel. Is this possible?
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
None of the many questions on this topic seem to match my situation. I have a large data model. In certain cases, only a few of the fields need be displayed on the UI, so for those I replaced the LINQ to Entity query that pulls in everything with an Entity SQL query retrieving only the columns needed, using a Type constructor so that I got an entity returned and not a DbDataRecord, like this:
SELECT VALUE MyModelNameSpace.INCIDENT(incident.FieldA, incident.FieldB, ...) FROM ... AS ...
This works and displays the fields in the UI. And if I make a change, the change makes it back to the entity model when I tab out of the UI element. But when I do a SaveChanges, the changes do not get persisted to the database. No errors show up in the Log. Now if I very carefully replace the above query with an Entity Sql query that retrieves the entire entity, like this:
SELECT VALUE incident FROM MyDB.INCIDENTs AS incident...
Changes do get persisted in the database! So as a test, I created another query like the first that named every column in the entity, which should be the exact equivalent of the second Entity SQL query. Yet it did not persist changes to the database either.
I've tried setting the MergeOption on the returned result to PreserveChanges, to start tracking, like this:
incidents.MergeOption = MergeOption.PreserveChanges;
But that has no effect. But really, if retrieving the entire entity with Entity Sql persists changes, what logical purpose would there be for behaving differently when a subset of the fields are retrieved? I'm wondering if this is a bug?
Gert was correct, the problem was that the entity was not attached. Dank U wel, Gert! Ik was ervan verbluft!
I just wanted to add a little detail to show the full solution. Basically, the ObjectContext has an Attach method, so you'd think that would be it. However, when your Entity SQL select statement names columns, and you create the object using a Type as I did, the EntityKey is not created, and ObjectContext.Attach fails. After trying and failing to insert the EntityKey I created myself, I stumbled across ObjectSet.Attach, added in Entity Framework 4. Instead of failing, it creates the EntityKey if it is missing. Nice touch.
The code was (this can probably be done in fewer steps, but I know this works):
var QueryString = "SELECT VALUE RunTimeUIDesigner.INCIDENT (incident.INCIDENT_NBR,incident.LOCATION,etc"
ObjectQuery<INCIDENT> incidents = orbcadDB.CreateQuery<INCIDENT>(QueryString);
incidents.MergeOption = MergeOption.PreserveChanges;
List<INCIDENT> retrievedIncidents = incidents.ToList<INCIDENT>();
orbcadDB.INCIDENTs.Attach(retrievedIncidents[0]);
iNCIDENTsViewSource.Source = retrievedIncidents;
I am getting very strange problem. My problem is that this
FIRST am selecting entity from the database using EJB 3.0 and jboss 5.1.0.GA
Subscriber s = (Subscriber)manager.createQuery("SELECT s FROM Subscriber s " +
"WHERE s.subscriber_id = ?1").setParameter(1,123).getSingleResult();
Then I am doing update to the entity with query like this
int a = manager.createQuery(" UPDATE Subscriber s SET s.balance = s.balance + "+10+"WHERE s.subscriber_id = ?1").setParameter(1,123).executeUpdate();
THEN againg I am selecting the entity like this
s = (Subscriber)manager.createQuery("SELECT s FROM Subscriber s " +
"WHERE s.subscriber_id = ?1").setParameter(1,123).getSingleResult();
BUT I am not getting the updated value of "balance" field BUT as soon as I comment the first SELECT statement I am getting the updated value. But I need the first SELECT statement in any case because I want to use it.
Can any one tell me why it is happening and what is its solution?
I would bet this is because of the cache problem.
I will assume that your manager is an EntityManager instance.
You're executing the first query, so the PersistenceContext is fetching the Subscriber entity from the database and puts it into the cache. Then, you're executing batch UPDATE query which directly hits the database omitting the cache structures, so it doesn't affect the PersistenceContext.
At the end, you execute once again the SELECT query. Doing so, the PersistenceContext checks if it have Subscribe entity cached somewhere. It does, so it doesn't hit the database but returns the value stored in its cache.
I don't quite get why you're executing batch UPDATE query instead of just updating your object state and letting the JPA to commit the changes when appropriate.
So instead of:
int a = manager.createQuery("UPDATE Subscriber s SET s.balance = s.balance +
"+10+"WHERE s.subscriber_id = ?1")
.setParameter(1,123).executeUpdate();
you could just do:
// 's' is the Subscribe entity previously fetched from the database
s.setBalance(s.getBalance() + 10);
Although if you still really need to use bach UPDATE then you could try doing
manager.refresh(s);
after the batch UPDATE query. This will let the JPA access the database instead of its cached version.
If you comment the first SELECT statement, the example works because the PersistenceContext didn't cache your entity. It fetches it from the database for the first time just after the batch UPDATE query.