Regarding the SubmitChanges order (Insert, Update, Delete), is there a way to change that order? I need to have the Deletes executed first, any updates, then any new inserts. I have a datagrid where the user can do all add, changes and updates and submit. Since each grid row must have a unique item chosen in it (via a dropdown), it's possible for the the user to delete the row, then try to utilize the deleted dropdown item in a new row, then try to update all changes and have the update fail (since the item the user wants to delete actually still exists in the database because the submit is doing the inserts first). Is there a setting where I can control the automatic update order or do I have to manually do the updates myself?
I have not tried this, but you could consider the following. First, get the ChangeSet using DataContext.ChangeSet. Then, run through the ChangeSet.Deletes calling Table<T>.DeleteOnSubmit on a new instance of your DataContext. Rinse repeat for the ChangeSet.Updates and the ChangeSet.Inserts.
Good luck.
I don't believe it's possible to do this. You would have to process the changes in the order you want, and call SubmitChanges() after each insert, update or delete. If you want the whole thing within the scope of a transaction, use the TransactionScope object.
Related
is there a way to rollback a specific change. I have a button that creates a row in two different tables. I want the changes in one of the tables to be rolled back before the committing so that only the second table is committed. How would this be done? Running normal rollback rollbacks both table iterators.
Ive been trying different methods and nothing is working, Please help before I go insane.
find out the corresponding 'VO' row which you want to rollback .
#1. row.setNewRowState(Row.STATUS_INITIALIZED) ;
or
#2. row.revertRow() ;
or
#3. row.remove() ;
The closest thing to rolling back a row would probably be calling refresh on the view row with the appropriate parameters, something like this:
//Obtain app module
DCBindingContainer dcb = (DCBindingContainer) BindingContext.getCurrent().getBindingsEntry();
ApplicationModuleImpl am = (ApplicationModuleImpl) dcb.getDataControl().getDataProvider();
//Get your view
ViewObjectImpl vo = am.getMyView1;
ViewRowImpl row = vo.getCurrentRow(); //alternately use vo.findByKey to lookup a row or simply iterate through every row if dataset is sufficiently small
//rollback the row
row.refresh(Row.REFRESH_REMOVE_NEW_ROWS | Row.REFRESH_UNDO_CHANGES | Row.REFRESH_CONTAINEES);//review modes for ideal combination for use case
Note that rather than a refresh, a row.remove() would probably be sufficient for what it sounds like you are trying to do. In any case, you will need to keep track of the rows you do not want to commit.
While this solution would work, it does not sound ideal for your use case. If you never commit data in the other table, there is no reason to link it to a database table. I would probably do something like one of the following instead:
If the fields are the same in both tables (or similar) and it will be an all-at-once action, create a transient attribute on the ADF view object to denote whether or not the row is approved. Use view criteria on different instances of the view object (add to your application module twice) to display/process the rows you want. Remove not approved rows prior to committing.
If the fields needed are too different or you want to be able to handle one row at a time, make you history view object a programmatic view object with transient attributes, rather than basing it on the table/Entity. When a row is approved remove it from your history view and add it to your approved view.
Is there a way to disable updating data in a form i.e. when we navigate through a block and we modify a field ; a new line is inserted whithout updating the original tuple ?
Thank you in avance.
The simplest way - from my point of view - is to modify data block's Update allowed property to "No". It will force users to insert new rows.
Moreover, Forms' toolbar allows them to duplicate record so that they wouldn't have to type everything from scratch - duplicate previous row and update whatever needed.
If you thought of some other option, I'm afraid you'll have to write some code to enforce it (your own trigger(s)).
i am using Spring/Hibernate/ZK. In one tab i get object from DB for editing by user, but second user can open the same tab and the same object for editing . I want to informed second user whit message like "This object is аlready open" and hide buttons for save.Тhus second user can see current data from DB to this object but can`t edint him.Is there a way to check session for this object or another way to do that.
The other answers mostly look at the database, but if all users use the same zk application to access the database, you could keep track of opened objects in the Composer or ViewModel (depending whether you use MVC or MVVM; I'll just call it controller).
Your controller would need a static list of objects that are currently modified. If a user requests to open an object that is not in the list, everything is fine and your controller enables the fields and save button. Otherwise, those are disabled and/or you display a message.
The tricky part is clearing objects from that list. If a user presses the save button, you just remove the object from the list. But what if the user doesn't and just closes the tab or their session just times out? In this case you need a callback, or a mechanism that regularly checks whether the screen is still open.
You could achieve this by adding a zk timer to the tab that pings every now and then and updates the timestamp in your static list (so make it a map). If a new user tries to edit the object, check how old the last timestamp is. If it is old enough (i.e. the previous user saved it or abandoned the screen), allow them to edit it.
Still, you have to think about what to do if a user just keeps the screen open. How long are they allowed to keep the lock on the object? This is an issue in Microsoft Office as well. If multiple users try to open an Excel file from a network location, the first one gets to lock and the others cannot save until that user saves.
You may have additional field which indicates that column is being edited. When first user starts work, the field would be updated. The second user would query object with 'on hold' status and your code would handle this.
Other way - use Hibernate #Version field in your entity. It holds object version which is incremented after every update operation. If second user would save object after first one already saved, it would throw OptimisticLockException which you could handle in your code. More about optimistic and pesimistic locking: Chapter 5. Locking. Related discussions: Hibernate Automatic Versioning and When to use #Version and #Audited in Hibernate?
The best solution is to use Optimistic Concurrency Control with Versioning and when Hibernate throws Concurrency Update issue due to same row is being updated in two transaction then use one of below strategy
First Wins Strategy
Last Wins Strategy
Merge Conflicting Update Strategy
First Wins Strategy is not good solution as it leads to lost update and user will get frustrated that all his work is lost.
By Last Wins Strategy one of user will get error message that you are working on Stale data and start your transaction again . By this way also user can get frustrated due to fact that now again he need to restart operation from beginning but his changes will not lost.
Instead go with Merge conflicting Update Strategy, when Hibernate throws Stale object exception reload screen with new data and user will see updated result and allow him to proceed with latest data. In this user changes will not loss and user will not get error message , just his screen reloads with fresh data and he can decide whether to proceed or not .
You can take example any e-commerce site and you will get one of result of either Last Wins Strategy or Merge Conflicting Update Strategy. Two user can start to by one item but one of user will get message in last screen that item is not stock.
I know you can soft delete in Doctrine (i.e. do not delete a record but rather add a "deleted" value). There's an extension for that.
Now I wonder if there's a way to "soft update" a record. I mean not actually update the record but rather create a new record and make the old one invalid. In the same extension as soft-delete, there's a function loggable, but this one logs to a different table.
I could create a controller that, instead of updating, soft-deletes
(and thus invalidates) the old record, and then creates a new one
with the new values. But I'm unsure if this is a good practice.
Maybe I should create this action on the object itself? But I'm
unsure how to do this.
Edit
I've looked into Versionable and EntityAudit (as suggested by Tomas), but it seems these bundles do way too much. I merely want to check if a given field is different from the old one, and if not: soft-delete the old one (I'm using softDeleteable so a simple remove() will do); then create a new one with the changed values.
So ideally it would lurk in the shadows until an update is performed. Then read from the mapping configuration which fields it needs to watch, and if these fields are indeed different from what's persisted, the program should execute the remove() and persist() commands.
This extension might suit your use case:
simplethings/EntityAudit
It records any changes you want to track.
So it should be pretty easy to modify it to meed your needs.
By convention our DB only alows the use of stored procedures for INSERT, UPDATE and DELETE. For some tables / types there is no DELETE stored procedure, because it is not allowed to delete rows. (You can only update the status of such a type to "deleted"). e.g. a customer may be marked as deleted but is never really removed from the DB.
How do I prevent the use of Delete() for certain types in the Data Access Layer = in the DMBL?
The "Default Methods" for Insert and Update are mapped to the corresponding stored procedure. But for Delete it says "use runtime". I would like to set it to "not allowed".
Is there a way to achieve this on the DB model layer?
Many thanks
Implement a partial class for each such entity and implement the OnValidate partial method. It takes a ChangeAction as a parameter. When the ChangeAction is ChangeAction.Delete, throw an exception that indicates that the operation is disallowed (IllegalOperationException, maybe).
I had the same constraint while developing one of my application. You can always condifure the delete action to use a certain stored procedure instead of the framework generating a sql command for the same. In my case when I say delete we just wanted to mark a particular row as deleted but not physically deleting it. So our update stored procedure was reused in the delete command to simply mark the isDeleted col value as true. Besides that you may want to build some kind of wrapper around the classes built by DBML and suppress the delete methods. Right now I do not see any speciall setting to have the framework only generate create and update methods. Partial classes may be one more alternative.
How about to configure rights for the user you're using to connect to DB? You can set Deny for Delete operation for this particular user, so it wouldn't be possible to use DELETE statement on DB level.
The deletion API is not generated, by which I mean an option on a generated table cannot remove the ability to mark an item as deleted. DeleteOnSubmit is part of the Table<TEntity> class.
If it is always an error to delete an entity, OnValidate should throw an InvalidOperationException as tvanfosson suggests.
I tried mapping Linq to Sql deletes to stored procedures which simply set the field to true. It gets weird because the DataContext removes instances after "deleting", but they are legal domain entities and should still be in the DataContext after submitting.