I am using Hazelcast as caching Solution for my application.
My application has few inserts and updates to the database and these needs to be synced to Cache also.
I want to use MapStore functionality so that when I do IMap.put(), Hazelcast takes care of persisting the Object in underlying Db and also update its cache.
In the overridden store implementation, I want to call my DAO in following way to persist the Data.
public void store(Long key, Product value)
{
log.info("Storing Data for Employee {} in Database using DataStore ", value);
Long employeeId = employeeDao.create(value);
value.setId(employeeId );
}
There are few issues listed below:-
1) In put call, I want to use "key" as the "employeeId", but this is generated only after insertion happens for this record in the Db. So how do I put into the Cache when I don't have the Id.? I want Hazelcast to use the "id" generated as part of store method call (or any other way) as the key to my Object.
Imap.put(key,new Employee("name_of_Employee","age_of_employee"))
2) The MapStore implementation's store method returns a void so I cannot return the Id generated for this Object to the Client. How can I achieve this?
I tried using MapEntryListeners on the Map but the entry added callback does not return new Object. I also added PostProcessingMapStore interface to my MapStore but could not get the new Value back to client.
Please advice
You have 2 options:
1) Generate the employeeId outside of the database. You can use the IdGenerator from Hazelcast to do this.
2) If you must let the database generate the id, then you need to put the Employee in the cache manually AFTER it has been stored in the database.
Related
I am working on a spring application.
We have a specific requirement where when we get a specific event, we want to look it up in the DB. If we find the record in the DB, then we delete it from DB, create another event using the details and trigger it.
Now my concern is:
I do not want to use two different calls, one to find the record and another to
delete the record.
I am looking for a way where we can delete the record using a custom
query and simultaneously fetch the deleted record.
This saves two differnet calls to DB, one for fetch and another for delete.
What I found on the internet so far:
We can use the custom query for deletion using the annotation called #Modifying. But this does not allow us to return the object as a whole. You can only return void or int from the methods that are annotated using #Modifying.
We have removeBy or deleteBy named queries provided by spring. but this also returns int only and not the complete record object that is being deleted.
I am specifically looking for something like:
#Transactional
FulfilmentAcknowledgement deleteByEntityIdAndItemIdAndFulfilmentIdAndType(#Param(value = "entityId") String entityId, #Param(value = "itemId") String itemId,
#Param(value = "fulfilmentId") Long fulfilmentId, #Param(value = "type") String type);
Is it possible to get the deleted record from DB and make the above call work?
I could not find a way to retrieve the actual object being deleted either by custom #Query or by named queries. The only method that returns the object being deleted is deleteById or removeById, but for that, we need the primary key of the record that is being deleted. It is not always possible to have that primary key with us.
So far, the best way that I found to do this was:
Fetch the record from DB using the custom query.
Delete the record from DB by calling deleteById. Although, you can now delete it using any method since we would not be requiring the object being returned by deleteById. I still chose deleteById because my DB is indexed on the primary key and it is faster to delete it using that.
We can use reactor or executor service to run the processes asynchronously and parallelly.
Hi I am using Spring Boot Cache in my application. I am able to fetch data from db and cached that data.
#Cacheable("employee")
public Optional<List<Employee>> employeeData(){
log.info("Fetched employee details from DB and cached in memory!!");
return employeeRepository.findActiveEmployee();
}
I want to delete or update or add new record in cached object.
How can I use #cachePut to update existing record or insert record or delete existing record based on some condition.
You can keep your original method as is to call it whenever you want to fetch data from cache. Note that the #Cacheable annotation does not execute the method's body if the cache with name "employee" is not empty, instead it returns the results from cache.
#Cacheable("employee")
public Optional<List<Employee>> employeeData(){
}
Then proceed in creating a new method annotated with #CachePut. Having in mind that #CachePut annotation will both execute the method as well as cache the results each and every time:
#CachePut(value="employee", condition="#name=='Tom'")
public Optional<List<Employee>> employeeDataCacheByName(String name){
log.info("Fetched employee details from DB and cached in memory depending on condition!!");
return employeeRepository.findActiveEmployee();
}
The above method will run the query every time and put into employee cache the results if the name argument is "Tom" (condition logic is up to you, this is just an example). This way cache is always updated with the results from the database (as long as the condition is truly evaluated).
For deleting (I don't think #CachePut can be used) maybe you can combine the #CacheEvict annotation, you can see an example in this Answer: https://stackoverflow.com/a/62488344/3635454
I have a Spring 4 and Hibernate 5 back-end RESTful web-service. This works great and is all unit tested. The front-end is a SmartGWT 5.0p application which uses DataSources, not RestDataSources to communicate with the back-end.
The front-end SmartGWT 5.0p uses a listgrid to edit data, and then the ListGrid is attched to a datasource. Only the edited data in the ListGrid is sent back, not the entire row. If I could, I'd like to be able send backthe entire listgrid row with edited data, and the unedited data. If I could get an answer to that, that would be great.
Or, the alternate is we let SmartGWT only send back part of the data which is edited. This comes to the back-end as JSON and is changed into an Object/Entity. The controller/end-point is not in a session yet, but then we call a method in the service layer which is transactional.
So, then question becomes we have a detached object in a session in the method in the service layer. We have a detached object with a database primary key ... but it also has 1 or 2 fields of updated data, and now we want to merge that data back to the database. We can't call an update with this entity because with the partial data, some of the fields are being set to null. In reality, we want to pull back the item from the databae, update the edited fields, and then write the data back to the database.
I could do this all manually ... but do I have to? I expect there is a more graceful way to handle this.
Thanks!
This is only a partial answer:
I can make SmartGWT combine old values and new values with a link I found here:
SMARTGWT DataSource (GWT-RPC-DATASource) LISTGRID
And the code is as follows:
private ListGridRecord getEditedRecord(DSRequest request)
{
// Retrieving values before edit
JavaScriptObject oldValues = request
.getAttributeAsJavaScriptObject("oldValues");
// Creating new record for combining old values with changes
ListGridRecord newRecord = new ListGridRecord();
// Copying properties from old record
JSOHelper.apply(oldValues, newRecord.getJsObj());
// Retrieving changed values
JavaScriptObject data = request.getData();
// Apply changes
JSOHelper.apply(data, newRecord.getJsObj());
return newRecord;
}
This JSON string contains the new fields updated, and the old fields. When this json string is sent back to the RESTful back end, the Jackson Mapper creates an entity and puts all the fields in, this is essentially a detached entity. It's an object outside of the session, but the id resides in the database.
Because I have a complete entity, I can all an update, and that works.
Problem solved.
BUT, I'd still like to find out an elegant solution for taking a detached entity, get the original record from the database, and then merge these two objects, and then finally update the record.
In the Service layer in Spring which has a transaction, and creates the session,
a manual process, which I don't want to do might look something like this:
1) get the id from the updatedEntity
2) get that attachedEntity from the database using that id
3) compare the fields
if( updatedEntity.updateField1 != null )
{ attachedEntity.setField1(updatedEntity.updateField1) }
If I had to do step 3 for multiple fields,
that's not very elegant.
4) Update attachedEntity to the database, because it now has updated fields.
So, again, an elegant solution to fix this might be helpful. Thanks!
I have in my application list of customers and users. I would like get the list of them only on start. Then use data that is stored locally, not from DB.
Can You advice me some methods with examples?
I think about HttpSession object? But I am not sure is it ok?
Cause this data should be available only for logged user, that access it on start.
List of customers will be available on each page off application!
Take a look at Spring cache http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html
You can annotate your repository methods:
#Cacheable(value="customer", key="#prsonalNum")
public Customer findCustomer(String prsonalNum) {
...
}
The application will enter the method body only the first time. After that the value will be taken from the cache.
You can also evict the cache when you update some customer for example.
#CacheEvict(value="customer", allEntries=true)
public void addCustomer(Customer cust)
For synchronizing insert and update operations with the cache use #CachePut annotation and for synchronizing delete operations with the cache use #CacheEvict annotation.
Use the same cache name (value paramter) and same key value
And you should enable caching with #EnableCaching annotation on one of your configuration classes.
You can still use the HttpSession object, however, only put it in the session once, the user has logged in... and you can remove it from the session on page close...
If you want to reuse the data multiple times and do not want to query each time. i would suggested to create a simple Cache suing HashMap or HashTable.
You can easily save the list of customers across every id in such data structure.
and in spring you can easily create a singleton bean which will hold this hashmap and accessible across the application.
I am new with Spring, my application, developed with Spring Roo has a Cron that every day download some files and update a database.
The update is done, after downloading and parsing the files, using merge(),
an Entity class Dataset has a list called resources, after the download I do:
dataset.setResources(resources);
dataset.merge();
and dataset.merge() does the following:
#Transactional
public Dataset Dataset.merge() {
if (this.entityManager == null) this.entityManager = entityManager();
Dataset merged = this.entityManager.merge(this);
this.entityManager.flush();
return merged;
}
I expect that doing dataset.setResources(resources); I would overwrite the filed resources, and so even the database entry would be overwritten.
But I get double entries in the database: every resource appear twice, with different IDs (incremental).
How can I succed in let my application doing updates and not insert? A naive solution would be delete manually the old resource and then call merge(); is this the way or is there some more smart solution?
This situation occurs when you use Hibernate as persistence engine and your entities have version field.
Normally the ID field is what we need for merging a detached object with its persistent state in the database, but Hibernate takes the version field in account and if you don't set it (it is null) Hibernate discards the value of ID field and creates a new object with new ID.
To know if you are affected by this strange feature of Hibernate, set a value in the version field, if an Exception is thrown you got it. In that case the best way to solve it is the data to parse contain the right value of version. Another ways are to disable version checking (see Hibernate ref guide to know about it) or load persistent state before merging.