GGTS 3.4 Grails 2.3.3 - When generating controllers this version includes a number of #Transactional lines I haven't seen before, and I don't fully understand what they are doing.
At the top of the controller there is the line:
#Transactional(readOnly = true)
Then just before certain dB changing actions: 'save', 'update' and 'delete' there is the line:
#Transactional
I presume that this switches the readOnly to false for each dB changing action. Does it open a new transaction that can be committed or rolled back as well? Is there simple way to force a rollback?
The 'create' action does not have #Transactional line before it despite it carrying out a 'new' db command to create a new instance of the specific domain class. What happens to this newly created but unsaved instance if the save transaction is not completed or if it is rolled back? By not completed I am thinking of introducing a 'cancel' button in the 'create' view to enable users to pull out of the creation if they choose to - also a user could simply navigate out of the create view without invoking the save.
-mike
The standard #Transactional without any properties set uses the platform defaults. These depend upon your transaction manager and your data source. It does however, create a transaction that can be comitted or rolled back.
Conroller methods without any annotation do not particpate in any transactions (provided the entire class isn't annotated as well).
In the case of create there is no need for a transaction because you aren't interacting with the database/transaction manager. Simply creating a new instance of a domain class e.g. new MyDomainClass() doesn't interact with the database at all, which is what you are seeing in the create method.
So in short, you don't need to worry about that instance if your users navigate away from the page or click cancel.
You can you use "withTransaction" method of domains to manage your Transaction manually as follow:
Account.withTransaction { status ->
try{
write your code or business logic here
}catch(Exception e){
status.setRollbackOnly()
}
}
if exception generate then this Transaction will be rollback
Related
Info
I'm using ef core together with .net core 3.1 (code-first approach) and this is my first time using this framework.
Scenario
I want to commit some changes that I made to my entities to the database (simple add/remove/update operations). I'm using SaveChanges or SaveChangesAsync method (I do not manually operate on any transactions). Let's say something went wrong and I get an exception.
Question
From what I can understand no changes were made to the database, but what about the context? Let's say that I want to perform some other operation within try..catch block on those entities (e.g. try to save at least part of the changes made).
Do changes persist in the context after the exception was thrown on SaveChanges method?
When does the context refresh (per transaction, per request from user)?
Yes, your changes are saved and can be handled by catching DbUpdateException or DbUpdateConcurrencyException as an example.
For example:
try
{
// Commit your operation
}
catch (DbUpdateException ex) when (ex.InnerException is SqlException sqlEx)
{
if (ex.Entries.Any())
{
// entity framework knows about failed entities
foreach (var e in ex.Entries)
{
// Make necessary actions
}
}
}
Entity Framework has different states of entities handling. Adding an entity to a Context does not make any changes in your database. You will be able to see current changes in context.ChangeTracker.
All changes will be released after transaction(SaveChanges or SaveChangeAsync).
I've an Attachment model that stores file metadata in MySQL and actual file on file system. I've implemented deletion with Callback Methods:
public function beforeDelete($cascade = true) {
$this->data = $this->findById($this->id);
return parent::beforeDelete($cascade);
}
public function afterDelete() {
$file = new File($this->data['Attachment']['path']);
$file->delete();
}
Is there a way to determine if there's an open transaction and only perform the file system deletion if such transaction gets committed? (Transaction is of course handled in the controller, which may not even be AttachmentsCrontroller but some other.)
That's already a little tricky in CakePHP 3.x, where there are actual events after things have been committed, and where options objects are passed throughout the whole saving/delete process that can store information about the transaction, and even there you'd have to somehow invoke such a process yourself if you'd manually wrap a save/delete operation in a transaction.
You could for example try to implement executing things transactionally in a behavior, your models could then store references to the files to delete in that behavior on beforeDelete, and the behavior could dispatch events on the involved models after things have been committed, something like afterCommit, which your models could listen to and then delete the files.
I want to listen change in my legacy system whenever there is any change in SF object (add/update/delete). So I have created outbound message and workflow. But in workflow I don't see any way to fire if object is deleted.
Is there anyway I can trigger outbound message on record delete? I know have heard that it can be done by trigger. But I don't want to write apex code for this.
To the best of my knowledge it cannot be done, the workflow actions are decoupled from the workflow rule (you can even reuse them) so they probably do not receive the transaction scope and when they execute the record is already gone and any reference inside action would point to a non-existing data. Thus the only way I know how to do it is via trigger.
Here is a workaround. However this will only be able to capture deletion made via std. Salesforce UI.
1.Create a custom checkbox field "Is Deleted"
2.Override the Del link with a custom VF page, that first updates the record status to "Is Deleted", and deletes the record.
3.Write workflow rule using the "Is Deleted" field.
Perhaps a compromise architecture would be to write an extremely small and simple after delete trigger that simply copies the deleted records in question to some new custom object. That new custom object fires your workflow rule and thus sends the outbound message you're looking for. The only issue with this would be to periodically clean up your custom object data that would grow in size as you deleted records from your other object. In other words, your "scratch" object would just need periodic cleaning - which could be done on a nightly schedule with batch Apex.
Here's a delete trigger that would do the trick using Opportunity as an example:
trigger AfterDelete on Opportunity (after delete)
{
List<CustObj__c> co = new List<CustObj__c>();
for(Opportunity o : Trigger.old)
{
CustObj__c c = new CustObj__c();
c.Name = o.Name;
c.Amount__c = o.Amount;
c.CloseDate__c = o.CloseDate;
c.Description__c = o.Description;
// etc.
co.add(c);
}
insert co;
}
It's not ideal but at least this would save you from having to code your own trigger-based outbound messages. These can only be done using the #Future annotation, btw, since callouts directly from triggers are forbidden. Hope that helps.
write a single email send in the trigger delete event. You have it in less than 1 hour.
I have a simple TClientDataSet component that I use to populate some data-aware components. However, if I insert data into my database using this dataset, I can't seem to find a proper way to sync it back into my TClientDataSet component.
How do I achieve this?
The TClientDataSet component has a Refresh method that does exactly that. Took some time for me to find this out in the docs, though. :)
From the docs:
From DB.pas
procedure Refresh;
Re-fetches data from the database to update a dataset's view of data.
Call Refresh to ensure that an application has the latest data from a database. For example, when an application turns off filtering for a dataset, it should immediately call Refresh to display all records in the dataset, not just those that used to meet the filter condition.
Note: The Refresh method does not work for all TDataSet descendants. In particular, TQuery components do not support the Refresh method if the query is not "live". To refresh a static TQuery, close and reopen the dataset.
TDataSet generates a BeforeRefresh event before refreshing the records and an AfterRefresh event afterwards.
Note: Most datasets try to maintain the current record position when you call refresh. However, this is not always possible. For example, the current record may have been deleted from the server by another user. Unidirectional datasets have no mechanism for locating the current record after a refresh, and always move back to the first record.
Warning: Unidirectional datasets refresh the data by closing and reopening the cursor. This can have unintended side effects if, for example, you have code in the BeforeClose, AfterClose, BeforeOpen, or AfterOpen event handlers.
Closing and opening the CDS didn't work for me. I'm using Delphi XE2, Update 3, with ADO tables connecting to an Access 2007 database. The only way I could refresh the data in my CDS was this:
procedure TForm1.PeopleRefreshButtonClick(Sender: TObject);
// this actually re-loads the data from the table!
begin
DM1.PeopleTable.Close;
DM1.PeopleTable.Open;
DM1.PeopleCDS.Refresh;
end;
A refresh button on the form closes, then opens the table. Then I refresh the ClientDataSet.
Basically, you must close the TClientDataset and then open it, loading the data from the database the same way you did originally. If the TClientDataset is connected to a TDataSetProvider, which is connected to a TDataset/TQuery descendant, all you have to do is close TClientDataset then open it.
I have created a class that inherits for DomainService and have a Silverlight app that uses System.ServiceModel.DomainServices.Client to get a DomainContext. I have also created POCO DataContracts that are used in the DomainServices's Query, Update, Insert, and Delete methods. I also have a ViewModel that executes all the LoadOperations. And now I'm at the part of my app where I want to add new Entities to the generated EntitySets but am unsure about what's going to happen when one user creates new and sets the Key value; all while another user creates a similar entity with the same Key value.
I have seen in the documentation that an ObjectContext is used, but in my situation I was not able to use the EntityFramework model generator. So I had to create my datacontracts by hand.
So I guess my question is, is there any way I can force other silverlight apps to update on database change?
When you make a Save operation to your DomainContext, depending on the load behavior, it will automatically refresh.
TicketContext.Load(TicketContext.GetTicketByIdQuery(ticketId),
LoadBehavior.RefreshCurrent,
x =>
{
Ticket = x.Entities.First();
Ticket.Load();
((IChangeTracking) TicketContext.EntityContainer).AcceptChanges();
}, null);
Here I've set the LoadBehavior to RefreshCurrent. When you make a save, RIA will send the entity back across the wire to the client and merge the changes with the entity already cached on your client side context. I don't know if that quite answers your question or not however.