Currently i am using spring declarative transaction manager in my application. During DB operations if any constraint violated i want to check the error code against the database. i mean i want to run one select query after the exception happened. So i am catching the DataIntegrityViolationException inside my Catch block and then i am trying to execute one more error code query. But that query is not get executed . I am assuming since i am using the transaction manager if any exception happened the next query is not getting executed. Is that right?. i want to execute that error code query before i am returning the results to the client. Any way to do this?
#Override
#Transactional
public LineOfBusinessResponse create(
CreateLineOfBusiness createLineOfBusiness)
throws GenericUpcException {
logger.info("Start of createLineOfBusinessEntity()");
LineOfBusinessEntity lineOfBusinessEntity =
setLineOfBusinessEntityProperties(createLineOfBusiness);
try {
lineOfBusinessDao.create(lineOfBusinessEntity);
return setUpcLineOfBusinessResponseProperties(lineOfBusinessEntity);
}
// Some db constraints is failed
catch (DataIntegrityViolationException dav) {
String errorMessage =
errorCodesBd.findErrorCodeByErrorMessage(dav.getMessage());
throw new GenericUpcException(errorMessage);
}
// General Exceptions handling
catch (Exception exc) {
logger.debug("<<<<Coming inside General >>>>");
System.out.print("<<<<Coming inside General >>>>");
throw new GenericUpcException(exc.getMessage());
}
}
public String findErrorCodeByErrorMessage(String errorMessage)throws GenericUpcException {
try{
int first=errorMessage.indexOf("[",errorMessage.indexOf("constraint"));
int last=errorMessage.indexOf("]",first);
String errorCode=errorMessage.substring(first+1, last);
//return errorCodesDao.find(errorCode);
return errorCode;
}
catch(Exception e)
{
throw new GenericUpcException(e.getMessage());
}
}
Please help me.
I don't think problem you're describing has anything to do with Transaction management. If DataIntegrityViolationException happens within your try() block you code within catch() should execute. Perhaps exception different from DataIntegrityViolationException happens or your findErrorCodeByErrorMessage() throwing another exception. In general, Transaction logic would be applied only once you return from your method call, until then you could do whatever you like using normal Java language constructs. I suggest you put breakpoint in your error error handler or some debug statements to see what's actually happening.
Related
I have a service that handles the insertion of a new record into a MongoDB collection:
public Mono<ProductDto> insertProduct(Mono<ProductDto> in) {
//TODO Must handle Duplicate key inserts --> Throw a ProductAlreadyExistsException
Mono<ProductDto> productDtoMono ;
try{
productDtoMono= in.map(ProductDto::toEntity)
.flatMap(productRepository::insert)
.map(ProductDto::new)
;
}
catch (DuplicateKeyException ex) {
throw new ProductAlreadyExistsException();
}
return productDtoMono;
}
When the ID given is already in use, the application throws a org.springframework.dao.DuplicateKeyException.
I am aware the above code with the try/catch block is incorrect, it is mostly there to demonstrate what I want to do. I am very new to Webflux, and reactive programming... I'd like to find out the correct way to handle this, but I have not been able to find much in the way of decent sample code for exception handling in the service layers for this, it is almost always in the router or request handler layer.
Hoping someone might be able to guide me on this.
The exception would be caught, and the application would throw the new, custom ProductAlreadyExistsException created for this purpose.
I have also tried to do this within the flatMap insert, but at this point I am kind of throwing poop at the wall to see if I can stumble into how it should be done:
public Mono<ProductDto> insertProduct(Mono<ProductDto> in) {
//TODO Must handle Duplicate key inserts --> Throw a ProductAlreadyExistsException
Mono<ProductDto> productDtoMono ;
productDtoMono= in.map(ProductDto::toEntity)
.flatMap(p -> {
try{
return productRepository.insert(p);
}
catch (DuplicateKeyException ex) {
return Mono.error(new ProductAlreadyExistsException());
}
})
.map(ProductDto::new)
;
return productDtoMono;
}
Since DuplicateKeyException is an unchecked exception and not a checked exception (which are quite annoying to use in Reactive code), you can use the onErrorMap()-method here:
public Mono<ProductDto> insertProduct(Mono<ProductDto> in) {
return in.map(ProductDto::toEntity)
.flatMap(productRepository::insert)
.onErrorMap(DuplicateKeyException.class, e -> new ProductAlreadyExistsException())
.map(ProductDto::new);
}
The intermediate productDtoMono variable here is redundant.
If however you need to work with checked exceptions, your last snippet of code is typically how you would do it.
Your first snippet of code does not do what you think it does, the catch-block will never run because Project Reactor catches it before your code does and transforms it into an error signal for downstream operators.
I'm using Spring JDBC and I've got this test method to connect to PostgreSQL DB (It's a example code, this isn't the original code):
public void toConnect() {
final String sql = "SELECT 1";
template.execute(sql);
}
If I catch the DataAccessException, and I call method getCause() the program trigger the PLSQException anyway (though finally printing the message that I set in the catch).
public void toConnect() throws CGConnException {
final String sql = "SELECT 1";
try {
template.execute(sql);
} catch (DataAccessException e) {
throw new CGConnException("CG : Error DataBase connection. - ", e.getCause());
}
}
The problem is than I can't handle the exception and I want than only print the Message of the catch, without the exception details.
If it's run with an error on config parameters or the PostgreSQL database is down, a PSQLException is generated and that is great but I need to control this exception. However JdbcTemplate.execute(sql) method throws a DataAccessException, therefore, doesn't possibly catch the PSQLException on try/catch block (or I don't know how to do). So, when run the program always appears the stack trace in the console:
2021-05-02 03:10:14.052 ERROR 73837 --- [ main] .PostgresClientTestConnectionApplication : Error : CG : Error DataBase connection.
In this case, I need to print only a log as "Connection Error" or something similar as the example above.
Thank you in advance.
Actually there is no mistake. The exception is handled and the program finally correctly when is catch it.
try {
template.execute(sql);
logger.error("Connection Successful . . .");
} catch (Exception e) {
logger.error("Error connection" + e.getMessage());
throw new CGConnException("CG : Error DataBase connection. - ", e);
}
In the run console it's print the associate stack trace because Spring internal classes to do it, but don't mean than we are doing something wrong or than no handle the exception.
I know we can have access to the new/modified/deleted entities in OpenAccessContext by the following commands.
dbContext.GetChanges().GetInserts<object>()
dbContext.GetChanges().GetUpdates<object>()
dbContext.GetChanges().GetDeletes<object>()
If an execption occures for any reasons while perfoming SaveChanges(), I need to log those entities but they get cleared in case of exceptions.
Is there anyway to get the entities out of OpenAccessContext in case on exceptions?
You could override the SaveChanges method like this:
public override void SaveChanges()
{
ContextChanges cruds = this.GetChanges;
IList<object> inserts = cruds.GetInserts<object>();
IList<object> updates = cruds.GetUpdates<object>();
IList<object> deletes = cruds.GetDeletes<object>();
try {
base.SaveChanges(ConcurrencyConflictsProcessingMode.AggregateAll);
} catch (Exception ex) {
// Retry or in your case log...
this.Refresh(RefreshMode.PreserveChanges, updates);
this.Delete(deletes);
this.Add(inserts);
Thread.Sleep(1000);
base.SaveChanges();
} finally {
this.FlushChanges(true);
}
}
When the savechanges failed, the changes are still accessible. You could also log the exception while you are at it.
I have a CRM plugin registered on Create (synchronous, post-operation) of a custom entity that performs some actions, and I want the Create operation to succeed in spite of errors in the plugin. For performance reasons, I also want the plugin to fire immediately when a record is created, so making the plugin asynchronous is undesirable. I've implemented this by doing something like the following:
public class FooPlugin : IPlugin
{
public FooPlugin(string unsecureInfo, string secureInfo) { }
public void Execute(IServiceProvider serviceProvider)
{
try
{
// Boilerplate
var context = (IPluginExecutionContext) serviceProvider.GetService(typeof (IPluginExecutionContext));
var serviceFactory = (IOrganizationServiceFactory) serviceProvider.GetService(typeof (IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
// Additional validation omitted
var targetEntity = (Entity) context.InputParameters["Target"];
UpdateFrobber(service, (EntityReference)targetEntity["new_frobberid"]);
CreateFollowUpFlibber(service, targetEntity);
CloseTheEntity(service, targetEntity);
}
catch (Exception ex)
{
// Send an email but do not re-throw the exception
// because we don't want a failure to roll-back the transaction.
try
{
SendEmailForException(ex, context);
}
catch { }
}
}
}
However, when an error occurs (e.g. in UpdateFrobber(...)), the service client receives this exception:
System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]:
There is no active transaction. This error is usually caused by custom plug-ins
that ignore errors from service calls and continue processing.
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ref ProxyRpc rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(ref MessageData msgData, Int32 type)
at Microsoft.Xrm.Sdk.IOrganizationService.Create(Entity entity)
at Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy.CreateCore(Entity entity)
at Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy.Create(Entity entity)
at Microsoft.Xrm.Client.Services.OrganizationService.<>c__DisplayClassd.<Create>b__c(IOrganizationService s)
at Microsoft.Xrm.Client.Services.OrganizationService.InnerOrganizationService.UsingService(Func`2 action)
at Microsoft.Xrm.Client.Services.OrganizationService.Create(Entity entity)
at MyClientCode() in MyClientCode.cs: line 100
My guess is that this happens because UpdateFrobber(...) uses the IOrganizationService instance derived from the plugin, so any CRM service calls that it makes participate in the same transaction as the plugin, and if those "child" operations fail, it causes the entire transaction to rollback. Is this correct? Is there a "safe" way to ignore an error from a "child" operation in a synchronous plugin? Perhaps a way of instantiating an IOrganizationService instance that doesn't re-use the plugin's context?
In case it's relevant, we're running CRM 2013, on-premises.
You cannot ignore unhandled exceptions from child plugins when your plugin is participating in a database transaction.
However, when your plugin is operating On Premise in partial trusted mode, you can actually create a OrganizationServiceProxy instance of your own and use that to access CRM. Be sure you reference the server your plugin is executing on to avoid "double hop" problems.
If really needed, I would create an ExecuteMultipleRequest with ContinueOnError = true, for your email you could just check the ExecuteMultipleResponse...
But it looks a bit overkill.
You can catch exceptions if running in async mode. Be sure to verify your mode when catching the exception.
Sample Code:
try
{
ExecuteTransactionResponse response =
(ExecuteTransactionResponse)service.Execute(exMultReq);
}
catch (Exception ex)
{
errored = true;
if (context.Mode == 0) //0 sync, 1 Async.
throw new InvalidPluginExecutionException(
$"Execute Multiple Transaction
Failed.\n{ex.Message}\n{innermessage}", ex);
}
if(errored == true)
{
//Do more stuff to handle it, such as Log the failure.
}
It is not possible to do so for a synchronous plugin.
A more detailed summary, explaining the execution mode and use case can be found on my blog: https://helpfulbit.com/handling-exceptions-in-plugins/
Cheers.
I am writing a RESTful web service using spring 3 and I noticed that when I implemented my DAO's (I am using spring-jdbc for database access), the exceptions that get thrown are pretty generic, so i am not able to identify if the exception occurred because my database is down or my query failed.
sample code:
try {
Q q = jdbcTemplate.queryForObject(MY_QUERY, new Object[]{id}, new MyMapper());
return q;
} catch (DataAccessException e) {
// What is this exception ? database down ? query failed ?
}
Unless I know what exception is this during runtime, I can't send back reasonable error message to service client.
Any help is appreciated. Thanks
You shouldn't be trying to catch every single possible exception; code so that you don't run into the possibility of multiple user or programmer error type exceptions. Generally there are three type of exceptions that occur in every DAO regardless of what you do. (1) your database is down... imo if this is the issue you've got bigger problems. (2) user authentication error which is easy enough to catch and deal with (however you should probably be handling that situation on your RESTful front-end. (3) improper data. If you have bad data, just send the attempted data back and the reason for the exception.
try {
Q q = jdbcTemplate.queryForObject(MY_QUERY, new Object[]{id}, new MyMapper());
return q;
} catch (DataAccessException e) {
throw new DaoException("Could not retrieve q with ID: " + qID, e);
}
The method queryForObject only throws DataAccessException or IncorrectResultSizeDataAccessException. You shouldn't catch exception due to database connection failure at this level. However, in your DAO class, you normally have code to establish the dataSource which can be injected by Spring's IoC container. That part of code will throw exception if the database connection fails. You should catch DB failure exception there.