Spring Boot: Handle Raised Exception from Database Trigger - java

I have a requirement to prevent the update action for ceratin columns in the table and display the message. I am using the liquibase to manage the database schema. To achieve this I used the Trigger Functions and Triggers which works fine.
Function
CREATE OR REPLACE FUNCTION FN_BU_CATEGORY() RETURNS trigger
LANGUAGE plpgsql AS
$$BEGIN
IF NEW.created_by <> OLD.created_by THEN
RAISE EXCEPTION 'Not allowed to update the value of created_by';
END IF;
RETURN NEW;
END;$$;
Trigger:
CREATE TRIGGER TR_BU_CATEGORY
BEFORE UPDATE ON category FOR EACH ROW
EXECUTE PROCEDURE FN_BU_CATEGORY();
I am managing the exception handling using #ControllerAdvice and #ExceptionHandler with PSQLException.class, GenericJDBCException.class, JpaSystemException.class and I was able to handle the exception. To verify the functionality, when I make an API hit to update the values of restricted columns, trigger raised the exception and I can see the following in the console.
handleTriggerException
#ExceptionHandler({PSQLException.class, GenericJDBCException.class, JpaSystemException.class})
public ResponseEntity<Problem> handleTriggerException(Exception ex, NativeWebRequest request) {
Problem problem = Problem.builder()
.withStatus(Status.BAD_REQUEST)
.withDetail("Test Message " + ex.getMessage())
.build();
return create(ex, problem, request);
}
Console:
Hibernate: update category set created_by = 'abc' where id = 1234
19:37:49.126 [XNIO-1 task-9] WARN o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 0, SQLState: P0001
19:37:49.127 [XNIO-1 task-9] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - ERROR: Not allowed to update
the value of created_by
Where: PL/pgSQL function noupdate() line 3 at RAISE
.......
org.springframework.orm.jpa.JpaSystemException: could not execute statement; nested exception is
org.hibernate.exception.GenericJDBCException: could not execute statement
Caused by: org.postgresql.util.PSQLException: ERROR: Not allowed to update the value of created_by
Where: PL/pgSQL function noupdate() line 3 at RAISE
Question is
Currently, ex.getMessage() returns org.hibernate.exception.GenericJDBCException: could not execute statement.
How can I fetch the message described in the trigger (i.e PSQLException: Not allowed to update
the value of created_by)
If I remove the JpaSystemException, handleTriggerException does not work anymore, why?
Environment:
Framework: Spring Boot
ORM: Hibernate
Database: Postgres 11
UPDATE:
I have tried to get a message with the following methods but unfortunately, they all return the same message.
System.out.println("1: " +ex.getCause());
System.out.println("2: " +ex.getMessage());
System.out.println("3: " +ex.getLocalizedMessage());
System.out.println("4: " +ex.fillInStackTrace());
System.out.println("5: " +ex.getStackTrace());
1: org.hibernate.exception.GenericJDBCException: could not execute statement
2: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement
3: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement
4: org.springframework.orm.jpa.JpaSystemException: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement
5: [Ljava.lang.StackTraceElement;#6f3072be

Using ExceptionUtils with getRootCause(throwable) gives the root cause message.
#ExceptionHandler({PSQLException.class, GenericJDBCException.class, JpaSystemException.class})
public ResponseEntity<Problem> handleTriggerException(Exception ex, NativeWebRequest request) {
Problem problem = Problem.builder()
.withStatus(Status.BAD_REQUEST)
.withDetail(ExceptionUtils.getRootCause(ex).getMessage())
.build();
return create(ex, problem, request);
}
How to get root cause message?

Related

SQL Server query throws Deadlock Exception on rs.next

We run a SELECT statement using preparedStatement.executeQuery on a SQL Server database. The query fails with a deadlock exception which is expected. The problem here is, the exception is not thrown on the executeQuery but thrown while we are doing rs.next() ie., while reading the result set. Is there any reason for this behaviour? What can we do to make it throw the exception on executeQuery?
Driver jar - sqljdbc4 - 4.0.2206.100
Java version - 1.8
Query Executed: SELECT Col1 from Table1 where Col2 = ?
Error trace:
com.microsoft.sqlserver.jdbc.SQLServerException: Transaction (Process ID 66) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:217)
at com.microsoft.sqlserver.jdbc.SQLServerResultSet$FetchBuffer.nextRow(SQLServerResultSet.java:6357)
at com.microsoft.sqlserver.jdbc.SQLServerResultSet.fetchBufferNext(SQLServerResultSet.java:1798)
at com.microsoft.sqlserver.jdbc.SQLServerResultSet.next(SQLServerResultSet.java:1049)

org.springframework.dao.CannotAcquireLockException for select sql query

I am getting org.springframework.dao.CannotAcquireLockException for select statment. Not sure why spring is trying to put lock for select query.
I am using NamedParameterJdbcTemplate and performing queryforlist. Its all select statements so we are not using and transactions. This database is shared so it might possible there are other application who are writing or performed lock but my app is just read.
This is MS SQL Server.
I am getting exception after 3 secs so looks like spring is trying to get lock and waited for 3 sec and then throws exception.
Below is full exception.
org.springframework.dao.CannotAcquireLockException: PreparedStatementCallback; SQL [select id ticketDBId from fpscdb002_ws_004.incident where ticket_number = ?]; Lock request time out period exceeded.; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: Lock request time out period exceeded.
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:259)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:660)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:695)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:722)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:772)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:211)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForMap(NamedParameterJdbcTemplate.java:238)
at com.abc.servicedesk.fp.dao.FPBaseDAO.getTicketDbId(FPBaseDAO.java:44)

Spring sql-error-codes.xml does not show correct exception on timeout

The following error occurs when an exception occurs for myJDBCTemplate.queryForList() , before which a setQueryTimeout(1) is set. I have a database which has 1.2 million rows, and looking for the timeout exception to be printed or occur in the case when the statement is executed. So, basically, the timeout occurs but the exception does not mention that.
I am using springFramework-version => 4.1.3.RELEASE in pom.xml
INFO: org.springframework.beans.factory.xml.XMLBeanDefinitionReader - Loading XML bean definition for class path resource [org/springframework/jdbc/support/sql-error-code.xml]
org.springframework.jdbc.UncategorizedSQLException: StatementCallback; uncategorized SQLException for SQL [select * from myTable where userCategory='1']; SQL state [70100]; error code [1317]; Query execution was interrupted; nested exception is java.sql.SQLException: Query execution was interrupted
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:416)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:471)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:481)
……..
caused by java.sql.SQLExcepion: Query execution was interrupted.
From the answer found at Query execution was interrupted, error #1317 states, the interruption occurs because of timeout, which I think is the possible cause.
Also, the exception states it is caused by java.sql.SQLException, but there are no exact details, why it occurred? So, I am not sure is it because of timeout or something else.
The error is clear in your stack trace:-
error code [1317]; Query execution was interrupted
, which means your query is being interrupted by an execution time limit. This error occurs when your query takes an unexpectedly long time to execute.
The error can be solved by fetching the data in batches by executing the query repeatedly for a certain data range.

Mybatis: java.sql.SQLRecoverableException: Closed Statement

when I use Mybatis execute query sql for oracle database, it sometimes throw the exception:
org.springframework.dao.RecoverableDataAccessException:
### Error querying database. Cause: java.sql.SQLRecoverableException: Closed Statement
### The error may exist in com/mapper/TestMapper.xml
### The error may involve com.mapper.TestMapper.getList
### The error occurred while handling results
### SQL: SELECT * FROM person_view
the xml config for the query:
<select id="getList" resultType="com.domain.TestBean" parameterType="map">
select * from person_view where rownum < #{limit}
</select>
when the query time > 60s, it will throw the exception, however I can't find any query time limit in my mybatis configuration and connection pool configuration. Any ideas, thanks in advance.

Catched ActivitiException still gets thrown and breaks application

I'm having trouble understanding what is happening and why my loop doesn't continue. I'm creating a dashboard for all open activiti tasks. Now the trouble i am having is when someone closes a task while the dashboard is being created.
The code is like this:
List<Task> approvalTasks = taskQueryApproval.list();
for (Task task : approvalTasks) {
try {
ActivitiApplicationRequest activitiRequest = (ActivitiApplicationRequest) taskService
.getVariable(task.getId(), ACTIVITIREQUEST);
if (!dashboardValues.containsKey(activitiRequest.getGlobalRequestId())) {
GlobalRequest globalRequest = globalRequestDao
.findMinimalGlobalRequestForDashboardBySyscode(activitiRequest.getGlobalRequestId());
if (globalRequest != null) {
DashboardValueObject vo = new DashboardValueObject(globalRequest);
vo.setHasApproval(true);
dashboardValues.put(activitiRequest.getGlobalRequestId(), vo);
}
}
} catch (ActivitiException ex) {
LOGGER.debug("Approval already done, skipping activititask");
}
}
The trouble I'm having is I know when the task doesn't exist Activiti is going to throw an exception, that's why I placed a try-catch inside the for loop.
What I'm expecting is that when the error is catched it just continues with the rest of the list. While debugging I even saw the catch being done. But the loop still breaks and the application stops. With these exceptions :
SEVERE: Error while closing command context org.activiti.engine.ActivitiException: task 203039 doesn't exist
at org.activiti.engine.impl.cmd.GetTaskVariableCmd.execute(GetTaskVariableCmd.java:55)
at org.activiti.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:24)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:42)
9-nov-2012 14:45:42 org.activiti.engine.impl.interceptor.CommandContext close
SEVERE: Error while closing command context
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: com.atomikos.jdbc.AtomikosSQLException: The transaction has timed out - try increasing the timeout if needed
### The error may exist in org/activiti/db/mapping/entity/Task.xml
### The error may involve org.activiti.engine.impl.persistence.entity.TaskEntity.selectTask
### The error occurred while executing a query
### SQL: select * from ACT_RU_TASK where ID_ = ?
### Cause: com.atomikos.jdbc.AtomikosSQLException: The transaction has timed out - try increasing the timeout if needed
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:8)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:81)
What am I doing wrong?

Categories