In my grails application, I want to display all the current entries of the second-level cache from all regions.
My code is as following :
def getCacheStats() {
StatisticsImpl stats = sessionFactory.statistics
for (regionName in stats.secondLevelCacheRegionNames) {
log.debug stats.getSecondLevelCacheStatistics(regionName).entries
}
}
However everything works fine as long as the region name is not org.hibernate.cache.StandardQueryCache (region used for Query Cache). In that case, an exception is thrown :
java.lang.ClassCastException: org.hibernate.cache.QueryKey cannot be cast to org.hibernate.cache.CacheKey
Having googling around, I didn't find any clues about how to display the list of entries of the cached query result sets associated with regions StandardQueryCache and UpdateTimestampsCache.
Could you please help me find a solution for this?
It's fairly complicated but this should get you further. You can access the query cache via the SessionFactory, so assuming you have access to that (e.g. via 'def sessionFactory') then you can get to the underlying caches like this:
def cache = sessionFactory.queryCache
def realCache = cache.region.#underlyingCache.backingCache
def keys = realCache.keys
for (key in keys) {
def value = realCache.get(key).value
// do something with the value
}
Note that the values will be a List of Long values. I'm not sure what the first one signifies (it's a large value, e.g. 5219682970079232), but the remaining are the IDs of the cached domain class instances.
Related
Riddle me this Stackoverflow:
I have a query that I am sending to GAE. The query (When in String format) looks like this:
SELECT * FROM USER WHERE USER_ID = 5884677008
If I go to the GAE console and type it in via a manual GQL query, it returns the item just fine. If I browse via the GUI and scroll to it, I can see it just fine. But when I call it from the Java code, it returns nothing every time.
code:
I have already confirmed the query is correct as I printed it out as a String just so I can test it.
Anyone have any idea what is going on with this?
q = new Query(entityName); //entityName = "User", confirmed
q.setFilter(filter); //filter = "USER_ID = 5884677008", confirmed
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
PreparedQuery pq = datastore.prepare(q);
/*
This always is empty here. Calling either pq.countEntities()); or
pq.toString()); returns size 0 or a String of nothing.
*/
Thanks!
-Sil
Edit: I Do have an index built, but it did not seem to help with the problem.
From the docs, you don't necessarily need to do toString. Have you tried asIterable or asSingleEntity on pq? Something like:
PreparedQuery pq = datastore.prepare(q);
for (Entity result : pq.asIterable()) {
String test = (String) result.getProperty("prop1");
}
That's if you have multiple entries. In the event you only have one:
PreparedQuery pq = datastore.prepare(q);
Entity result = pq.asSingleEntity();
String test = (String) result.getProperty("prop1");
Basically, if you don't call asIterable or asSingleEntity, the query is JUST prepared and doesn't run
Took quite a bit of testing, but found the issue.
The problem revolved around the filter being set. If I removed the filter, it worked fine (but returned everything). Turns out, what was being passed as a filter was a String version of the user_id as opposed to the Long version of it. There was really no way to tell as the exact SQL query DID NOT read ( SELECT * FROM USER WHERE USER_ID = "5884677008" ) when I printed it, which would have been a dead giveaway.
I changed the passed filter parameter (which I had stored in a hashmap of (String, Object) btw) from a String to a Long and that solved the issue.
One thing to point out though, as #Patrice brought up (And as I excluded from my code while posting to save space), to actually iterate through the list of results, you do need to call a method against it (Either .asIterable() or .asSingleEntity() ).
You actually can check against the number of returned entities / results by calling pq.countEntities() and it will return the correct number even before you call a formatting method against the pq, but as #tx802 pointed out, it is deprecated, and despite the fact that it worked for me, someone in the future using this post as a reference may not have it work for them.
So in my database, I have 3 rows, two rows have defaultFlag as 0 and one is set to 1, now in my processing am updating defaultProperty of one object to 1 from 0 but am not saving this object yet.
Before saving I need to query database and find if any row has defaultFlag set or not, there would be only 1 default set.
So before doing update am running query to find if default is set and i get 2 values out, note here if i go and check in db then there is only 1 row with default set but query gives me two result because this.object default property has changed from 0 to 1 but note that this object is not yet saved in database.
I am really confused here as to why hibernate query is returning 2 when there is one row with default set in database and other object whose default property has changed but it is not saved.
Any thoughts would be helpful. I can provide query if need be.
Update
Following suggestions, I added session.clear() to before running the query.
session.clear();
String sql = "SELECT * FROM BANKACCOUNTS WHERE PARTYID = :partyId AND CURRENCYID = :currencySymbol AND ISDEFAULTBANKACCOUNT= :defaultbankAccount";
SQLQuery q = session.createSQLQuery(sql);
q.addEntity(BankAccount.class);
q.setParameter("partyId", partyId);
q.setParameter("currencySymbol", currencySymbol);
q.setParameter("defaultbankAccount", 1);
return q.uniqueResult();
and it returns 1 row in result as expected but now am getting
nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session exception
Either query which row has the "default flag" set before you start changing it, or query for a list of rows with default flag set & clear all except the one you're trying to set.
Very easy, stop mucking about with your "brittle" current approach which will break in the face of concurrency or if data is ever in an inconsistent state. Use a reliable approach instead, which will always set the data to a valid state.
protected void makeAccountDefault (BankAccount acc) {
// find & clear any existing 'Default Accounts', other than specified.
//
String sql = "SELECT * FROM BANKACCOUNTS WHERE PARTYID = :partyId AND CURRENCYID = :currencySymbol AND ISDEFAULTBANKACCOUNT= :defaultbankAccount";
SQLQuery q = session.createSQLQuery(sql);
q.addEntity(BankAccount.class);
q.setParameter("partyId", partyId);
q.setParameter("currencySymbol", currencySymbol);
q.setParameter("defaultbankAccount", 1);
//
List<BackAccount> existingDefaults = q.list();
for (BankAccount existing : existingDefaults) {
if (! existing.equals( acc))
existing.setDefaultBankAccount( false);
}
// set the specified Account as Default.
acc.setDefaultBankAccount( true);
// done.
}
This is how you write proper code, do it simple & reliable. Never make or depend on weak assumptions about the reliability of data or internal state, always read & process "beforehand state" before you do the operation, just implement your code clean & right and it will serve you well.
I think that your second query won't be executed at all because the entity is already in the first level cache.
As your transaction is not yet commited, you don't see the changes in the underlying database.
(this is only a guess)
That's only a guess because you're not giving many details, but I suppose that you perform your myObject.setMyDefaultProperty(1) while your session is open.
In this case, be careful that you don't need to actually perform a session.update(myObject) to save the change. It is the nominal case when database update is transparently done by hibernate.
So, in fact, I think that your change is saved... (but not commited, of course, thus not seen when you check in db)
To verify this, you should enable the hibernate.show_sql option. You will see if an Update statement is triggered (I advise to always enable this option in development phase anyway)
I am creating a REST API using play framework. I want to use lazy loading ( finder.ref(id) or Ebean.getReference(id) ) to see if an enity with a specific id exists in database. If it doesn't exist, I will return a 404.
If I try to delete using an id that doesn't exist, an OptimisticLockException is thrown. But that doesn't seem like a valid basis to see if an entity exists.
Is it possible to check if an entity exists by an id using lazy loading? I can always do finder.byId(id) and that can get me what I want. But I want to do this efficiently.
Thanks
You can just count items with specified id, while your id is unique, it will return 1 if item exists and 0 if it doesn't, so you can easily make a condition:
boolean itemExists
= (YourModel.find.where().eq("id", id).findRowCount() == 1) ? true : false;
Logger.info("Item " + ((itemExists) ? "exists" : "not found!"));
On the other hand if your intension is returning existing entity for an example in Json, you don't need to make separate checking, just check if it's not null...
YourModel entity = YourModel.find.byId(id);
if (entity == null) return notFound("No such record");
// .. rest of code for preparing API...
Edit
About costs: find.byId(id) tries to fetch whole entity, while find.ref(id) gets only reference. Unfortunately you can't determine if object exists by ref(id) as it's always not null, therefore IMHO counting elements by id is cheaper than selecting even single field to check if Db returns the entity.
Actually find.byId(id) is most expensive option as it loads whole entity, for well optimized APIs it's usually better to write dedicated methods using Ebean's select() and fetch(), like:
YourModel e = YourModel.find.select("id, name, age").where().eq("id", id).findUnique();
or
List<YourModel> le = YourModel.find.select("id, name, age").where().eq("id", id).findList();
I am trying to create a junit test. Scenario:
setUp: I'm adding two json documents to database
Test: I'm getting those documents using view
tearDown: I'm removing both objects
My view:
function (doc, meta) {
if (doc.type && doc.type == "UserConnection") {
emit([doc.providerId, doc.providerUserId], doc.userId);
}
}
This is how I add those documents to database and make sure that "add" is synchronous:
public boolean add(String key, Object element) {
String json = gson.toJson(element);
OperationFuture<Boolean> result = couchbaseClient.add(key, 0, json);
return result.get();
}
JSON Documents that I'm adding are:
{"userId":"1","providerId":"test_pId","providerUserId":"test_pUId","type":"UserConnection"}
{"userId":"2","providerId":"test_pId","providerUserId":"test_pUId","type":"UserConnection"}
This is how I call the view:
View view = couchbaseClient.getView(DESIGN_DOCUMENT_NAME, VIEW_NAME);
Query query = new Query();
query.setKey(ComplexKey.of("test_pId", "test_pUId"));
ViewResponse viewResponse = couchbaseClient.query(view, query);
Problem:
Test fails due to invalid number of elements fetched from view.
My observations:
Sometimes tests are passing
Number of elements that are fetched from view is not consistent(from 0 to 2)
When I've added those documents to database instead of calling setUp the test passed every time
Acording to this http://www.couchbase.com/docs/couchbase-sdk-java-1.1/create-update-docs.html documentation I'm adding those json documents synchronously by calling get() on returned Future object.
My question:
Is there something wrong with how I've approached to fetching data from view just after this data was inserted to DB? Is there any good practise for solving this problem? And can someone explain it to me please what I've did wrong?
Thanks,
Dariusz
In Couchbase 2.0 documents are required to be written to disk before they will show up in a view. There are three ways you can do an operation with the Java SDK. The first is asynchronous which means that you just send the data and at a later time check to make sure that the data was received correctly. If you do an asynchronous operation and then immediately call .get() as you did above then you have created a synchronous operation. When an operation returns success in these two cases above you are only guaranteed that the item has been written into memory. Your test passed sometimes only because you were lucky enough that both items were written to disk before did your query.
The third way to do an operation is with durability requirements and this is the one you want to do for your tests. Durability requirements allow you to say that you want an item to be written to disk or replicated before success is returned to the client. Take a look at the following function.
https://github.com/couchbase/couchbase-java-client/blob/1.1.0/src/main/java/com/couchbase/client/CouchbaseClient.java#L1293
You will want to use this function and set the PersistedTo parameter to MASTER.
Using the GeoTools WFS-T plugin, I have created a new row, and after a commit, I have a FeatureId whos .getId() returns an ugly string that looks something like this:
newmy_database:my_table.9223372036854775807
Aside from the fact that the word "new" at the beginning of "my_database" is a surprise, the number in no way reflects the primary key of the new row (which in this case is "23"). Fair enough, I thought this may be some internal numbering system. However, now I want a foreign key in another table to get the primary key of the new row in this one, and I'm not sure how to get the value from this FID. Some places suggest that you can use an FID in a query like this:
Filter filter = filterFactory.id(Collections.singleton(fid));
Query query = new Query(tableName, filter);
SimpleFeatureCollection features = simpleFeatureSource.getFeatures(query);
But this fails at parsing the FID, at the underscore of all places! That underscore was there when the row was created (I had to pass "my_database:my_table" as the table to add the row to).
I'm sure that either there is something wrong with the id, or I'm using it incorrectly somehow. Can anyone shed any light?
It appears as if a couple things are going wrong - and perhaps a bug report is needed.
The FeatureId with "new" at the beginning is a temporary id; that should be replaced with the real result once commit has been called.
There are a number of way to be aware of this:
1) You can listen for a BatchFeatureEvent; this offers the information on "temp id" -> "wfs id"
2) Internally this information is parsed from the Transaction Result returned from your WFS. The result is saved in the WFSTransactionState for you to access. This was before BatchFeatureEvent was invented.
Transaction transaction = new transaction("insert");
try {
SimpleFeatureStore featureStore =
(SimpleFeatureStore) wfs.getFeatureSource( typeName );
featureStore.setTransaction( transaction );
featureStore.addFeatures( DataUtilities.collection( feature ) );
transaction.commit();
// get the final feature id
WFSTransactionState wfsts = (WFSTransactionState) transaction.getState(wfs);
// In this example there is only one fid. Get it.
String result = wfsts.getFids( typeName )[0];
}
finally {
transaction.close();
}
I have updated the documentation with the above example:
http://docs.geotools.org/latest/userguide/library/data/wfs.html