Spring JdbcTemplate how to log parameters on exceptions? - java

Using Spring's JdbcTemplate, I've been trying to figure out a clean way to log exceptions in the DAO layer, but can't seem to figure it out. I want to log the SQL statement that was used and the parameters.
For example, where addStoreSql is a parameterized statement
public int addStore(Store store) {
return jdbcTemplate.update(addStoreSql, store.getId(), store.getName());
}
I'm doing something like..
public int addStore(Store store) {
try{
return jdbcTemplate.update(addStoreSql, store.getId(), store.getName());
} catch (DataAccessException ex) {
logger.error("exception on deleting store - " + store.toString(), ex);
throw ex;
}
}
My question, is there a way to write this any cleaner across many dao methods? Possibly at the logger level or some Spring library? Or is this the cleanest way (Or is the above code even bad)?
I have multiple methods that do basically the same thing, take in a object, pass the fields to a query and return the result.

The difficulty of doing this with Spring is that the JDBC objects that you would want to get this information from are not Spring-managed objects, they're created by the driver. So Spring AOP won't apply (without using AspectJ).
Spring can supply the query and parameters separately for you, if you log the category "org.springframework.jdbc.core.JdbcTemplate" at DEBUG level and "org.springframework.jdbc.core.StatementCreatorUtils" at TRACE level.
There are existing libraries log4jdbc and p6spy that implement a wrapper around the JDBC driver, in order to generate a SQL statement with the parameters inserted in place. See this question. Using either of these should be a matter of adding the jar to the project, changing your jdbc url to point to the wrapper, and tweaking the logging to get the level of information you want.
The existing logging code is not good because it is repetitious cut-n-paste code, and it will result in exceptions being logged multiple times. The logs will be harder to read and will roll more frequently.

Definitely don't use this pattern:
logger.error("exception on deleting store - " + store.toString(), ex);
throw ex;
because if often leads to duplicates log entries. There should be some global trap for exceptions and its responsibility is to log the error.
EDIT
By global trap for exceptions, I mean that every application should have some mechanism for catching most (ideally all) exceptions from Java code and log them. Imagine that you don't catch and miss log for some important error. You are than blind when trying to figure out what happened in production.
So let's pretend that we have such exception logging mechanism in place. Your pattern would log SQL error and throw exception that would be catched by global exception trap and logged again. You don't want that to happen, so don't log it in your code, save one line of code and don't create duplicate log entry.

Related

JUnit test for a method that contains SQL queries

I have an old Java project (no frameworks/build tools used) that has a class full of SQL methods and corresponding Bean-classes. The SQL methods mostly use SELECT, INSERT and UPDATE queries like this:
public static void sqlUpdateAge(Connection dbConnection, int age, int id) {
PreparedStatement s = null;
ResultSet r = null;
String sql = "UPDATE person SET age = ? WHERE id = ?";
try {
s = dbConnection.prepareStatement(sql);
s.setInt(1, age);
s.setInt(2, id);
s.addBatch();
s.executeBatch();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (r != null)
r.close();
if (s != null)
s.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
What is the best practice in unit testing when it comes to SQL queries?
The easiest way I can think of, would be to use my development database; just call the sqlUpdateAge() in the test class, query the database for a result set and assertTrue that the set age is in the result set. However, this would fill up the development database with unnecessary data, and I would like to avoid that.
Is the solution to create a so-called in-memory database or somehow rollback the changes I made?
If I need an in-memory database:
Where and how would I create it? Straight to the test class, or perhaps to a config file?
How do I pass it to the updateAge() method?
I would suggest to see if you can start with autmoating the build. That is either by introducing a build tool such as maven or gradle - or if not possible - scipting the build. In any case, your goal should be to get to a point where it's easy for you to trigger a buil together with tests whenever code changes.
If you are not able to produce a consistent build on every change with the guarantee that all unit tests have been run, then there's really no value in writing unit tests in the first place. That is because otherwise, your tests are going to fail eventually due to code modifications and you wouldn't notice unless all your tests are automatically run.
Once you have that, you might have some hints to how you would like to run unit or integration tests.
As you can't benefit from testing support that many application frameworks provide, you're basically left on your own for how to configure database testing setup. In that case, I don't think that an inmemory database is really the best opion, because:
It's a different database technology than what you are normally using, and as the code indicates you are not using an ORM that will take care of different SQL dialects for you. As that's the case, you might find yourself in a position, where you are unable to accurately test your code because of SQL dialect differeces.
You will need to do all the setup of the inmemory DB yourself - which is of course possible, but still it's a piece of code that you need to maintain and that can also fail.
The two alternatives I can think of are:
Use Docker to start your actual database technology for every time you run the tests. (that's also something you have to script for yourself, but it will most likely be a very simple and short command you need to execute)
have a test database running on your test environment that you use. Every time before you run the tests, esure the database is reset to the original state. (easiest way to do this is to drop the existing schema and restore to the original schema). In this case, you will need to ensure that you don't run multiple builds in parallell against the same test database.
These suggestions apply only if you have experience on the shell and/or have support from someone in ops. If not, setting up H2 might be easier and more straight forward.
Things would have been easy with a Spring Boot project. In your case, you have many strategies:
Configure a H2 database. You can initialize your database with the creation of a schema and insertion of data in a setUp method with the #BeforeEach annotation.
You can use a dedicated framework like DbUnit.
You will have to initialize your dbConnection also in a setUp method in your unit test.

Java Micrometer #Counted - exception="none" and result="success" for #ExceptionHandler?

Quick question regarding Java Micrometer with #Counted and #ExceptionHandler please.
I have a very straightforward #ExceptionHandler:
#ExceptionHandler
#Counted(value = "MY_EXCEPTION", description = "SOME_DESCRIPTION")
public Mono<ResponseEntity<String>> myCoolExceptionHandler(final RuntimeException runtimeException) {
System.err.println("an exception!" + runtimeException);
return Mono.just("bad");
}
I think this combination is quite interesting, as it gives visibility on exception happening. We can build dashboard, alerts, etc, quite cool.
Unfortunately, when I looked at the metric generated, it was something like:
# HELP MY_EXCEPTION_total SOME_DESCRIPTION
# TYPE MY_EXCEPTION_total counter
MY_EXCEPTION_total{class="package.MyController",exception="none",method="myCoolExceptionHandler",result="success",} 3.0
I am quite puzzled on exception="none" and result="success"
May I ask how those values got into the metric in the first place?
Also, how to change them into something more meaningful, such as the exception class for instance?
Thank you!
The annotated method itself does not throw an exception and always completes normally. Therefore, the intercepting code installed by the annotation will never record an exception (exception=none) and the outcome will always be good (result=success). Only exceptions thrown from within the annotated method will be recorded as an error outcome.
You can always manually record metrics by injecting the MetricRegistry and then registering a metric with the appropriate name and tags.

Delete All Records using Liferay Service Builder

I can delete specific record using Liferay Service Builder but what to do when I want to delete All the Records from that table.
I am new to Liferay So any Help would be appreciated...!!!
As your entity name is Location, add following method in your LocationLocalServiceImpl.java and build service:
public void deleteAllLocations(){
try{
LocationUtil.removeAll();
}catch(Exception ex){
// Log exception here.
}
}
On successful build, deleteAllLocations will be copied to LocationLocalServiceUtil.java from where you can use it in your action class as:
LocationLocalServiceUtil.deleteAllLocations();
The question already has an answer that the asker is satisfied with but I thought I'd add another just the same. Since you're writing custom method in your service implementation (in your case LocationLocalServiceImpl):
You have direct access to the persistence bean so there is no need to use the LocationUtil.
The accepted answer suggests catching any Exception and logging it. I disagree with this because it will fail silently and depending on the application logic, could cause problems later on. For example, if your removeAll is called within a transaction whose success depends on the correct removal of all entities and the accepted approach fails, the transaction won't be rolled back since you don't throw a SystemException.
With this in mind, consider the following (within your implementation, as above):
public void deleteAllLocations() throws SystemException {
locationPersistence.removeAll();
}
Then, wherever you're calling it from (for example in a controller), you have control over what happens in the case of a failure
try {
LocationLocalServiceUtil.removeAllLocations();
} catch (SystemException e) {
// here whatever you've removed has been rolled back
// instead of just logging it, warn the user that an error occurred
SessionErrors.add(portletRequest, "your-error-key");
log.error("An error occurred while removing all locations", e);
}
Having said that, your LocationUtil class is available outside of the service so you can call it from a controller. If your goal is only to remove all Location entities without doing anything else within the context of that transaction, you can just use the LocationUtil in your controller. This would save you from having to rebuild the service layer.

Throw own custom exception If MySql error occurs using MyBatis with annotations

Heres my problem. I am using mybatis using annotations. For doing that I have created my own mybatis Interface and now I want to throw my own custom exception if anything goes wrong executing the query. Here is in code form what I exactly mean.
Consider the following interface I use to encapsulate some SQL queries:
public interface MyMapper {
#Select("SELECT id, title, description, creationDate, modificationDate, owner_id AS \"owner.id\" "
+ " FROM article WHERE article_id = #{id}")
Article getArticle(int id);//If this fails I want my own custom Exception to be thrown
}
As commented if I have a problem with my query or somehow the execution of getArticle(int id) fails I want it to throw my own custom exception not an IBatisException.If that is not possible by can someone suggest the tree hierarchy for Exceptions Since knowing the tree hierarchy would also help me know what all exceptions I can catch. Unfortunately I can't even catch a SqlException if something goes wrong. Only IBatisException and Exception.
Firstly I'd wrap every call to MyMapper in DAO instance and keep out exception hierarchy in specific place(ExceptionHandler):
public class MyDAO {
MyMapper mapper;
public Article getArticle(int id) {
try {
return mapper.getArticle(id);
} catch (PersistenceException e) {//IbatisException is deprecated
throw ExceptionHandler.handle(e);
}
}
}
try/catch clause probably would be duplicated in every method, so using Java proxy or AspectJ to refactor exception handling functionality might be an option.
It might be possible to use load time weaving and weave around mapper itself instead of DAO. But I'd stil prefer DAO to leave myself room to extend functionality.
Regarding exception hierarchy MyBatis already has exception hierarchy of it's own. And in case your are using Spring, it has it's own data access exception hierarchy, so you might try out MyBatis-Spring integration. But as documentation states, base DataAccessException is
Root of the hierarchy of data access exceptions discussed in Expert
One-On-One J2EE Design and Development
So you might try to implement something similar based on the aforementioned book.

Fail silently on constraint violation

I have entity Foo, which maps to sql table with some unique constraints. Thus saving Foo may fail. I am using FooDao to save Foo:
#Repository
public class FooDao
{
#Autowired
private SessionFactory sessionFactory;
#Transactional
#Override
public void add(Foo item) {
sessionFactory.save(item);
}
}
when I call method FooDao#add(Foo) it may fail for two reasons: either because of unique constraint violation (in this case, I know how to handle the problem) or because of some other problem (in this I probably should propagate the exception). How do I distinguish between those two situations?
I could add method find(Foo item) to FooDao and check, whether something like item, which I was trying to add is database. But this would require additional select from database and I am a bit worried about this.
Thats actually SQLState.
do something like this
Catch(HibernateException he){
SQLException sqe = he.getSQLEception();
String sqlState = sqe.getSQLState();
if(sqlState.equals("23000"){
// Handle your exception
}
}
Java doc:
SQLState - an XOPEN or SQL:2003 code identifying the exception
One link I found for ISO sqlStates,
link to reference
But look for exact reference and value..
One obvious (but maybe nasty) solution is that you catch javax.persistence.PersistenceException and parse the error message for "violant" or "constraint".
From my point of view you should do the select/find upfront. Remember that you are using an ORM! Hibernate has caches involved so neither the select/find nor the key contraint error might be the result of an actual db query but the result of an calculation of Hibernate based on your already in cache loaded data.
Sebastian
Catch org.hibernate.JDBCException. This has getErrorCode(). For unique constraint voilation its ORA-00001.
-Maddy
If there is a database exception it gets caught in a HibernateException, which is a checked exception. Spring wraps this in a DataAccessException, which is unchecked. This exception will run up to your Controller and out to the Servlet and end up in a stack trace in the browser and log file. You can catch this exception and print it out so you can see what is happening. This will at least get you to the point where you know what is actually breaking.
Bad keys is probably one issue. But bad values is probably another. Some non-null fields are null, or something isn't long/short enough etc. Fixing this probably involves validation. You can use the Hibernate Validator. You give your fields some nifty annotations and then you get validation errors in java before you even get to the database - errors happen faster.

Categories