Is it good practice to put all Java JDBC Select statements in a try-catch block ? Currently I write most of my code without it. However, I do try-catch for insert/update/delete.
Note: Currently using Sprint Boot.
String sqlQuery = "Select productId, productName, productStartDate from dbo.product where productId = 5"
public getProductData() {
....
List<Product> productList = namedJdbcTemplate.query(sqlQuery, new ProductMapper());
Since this question is tagged with spring-boot and you are using JdbcTemplate, I'm giving you a Spring-specific answer.
One of the points of Spring is to avoid boilerplate from developers. If you find yourself adding things repetitively, like putting try-catch blocks around code executing DML, that's cause for suspecting you're not doing something right. Adding your own try-catches in code using Spring isn't always wrong, but it usually is.
In the Spring reference doc https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#jdbc there is a table showing what is the developer's responsibility and what is Spring's responsibility. Processing exceptions, handling the transactions, and closing jdbc resources are all shown as being Spring's responsibility.
SpringJdbc takes care of a lot of things for you. It handles closing JDBC resources and returning connections to their pool, and converts exceptions from SQLException to a hierarchy of unchecked DataAccessExceptions. In Spring unchecked exceptions thrown from a method wrapped in a transactional proxy cause the transaction to get rolled back. If you do your own try-catch logic you can prevent rollback from occurring when it needs to, if you catch the exception and the proxy never sees it. Adding try-catch logic can cause problems if you don't understand what Spring is doing.
Exceptions do need to be caught somewhere. In a Spring web application, you can set up an exception handler that catches anything thrown from the controller layer, so that you can log it. That way the action in progress gets broken off cleanly, the current transaction rolls back, and the exception gets handled in a consistent way. If you have other entry points, such as reading messages from a queue, those will need their own exception handler.
Exceptions are thrown in order to escape the current context, which isn't able to deal with the problem, and relocate control to somewhere safe. For most exceptions coming from JDBC, they aren't anything you can fix, you just want to let it be thrown, let the current transaction rollback, then let the central exception handler catch and log it.
First of all, if you're working with raw JDBC API, you should always use PreparedStatement.
Yes, you'll just have to wrap the code with try-catch block at some point, though it's a good practice to catch exceptions just right away or at the point where it's logically suits. In case of SQL queries, you actually should wrap all of them into some Service class that will give you an access to modify your database objects without running through JDBC API every time. For example:
public class UserService {
private static final String CREATE_USER_SQL = "...";
private final Connection jdbcConnection;
public #Nullable User createUser(final String name) {
try (final PreparedStatement stmt = jdbcConnection.prepareStatement(CREATE_USER_SQL)) {
jdbcConnection.setAutoCommit(false);
stmt.setString(1, name);
stmt.executeQuery();
jdbcConnection.commit();
return new User(name);
} catch (final SQLException createException) {
System.out.printf("User CREATE failed: %s\n", createException.getMessage());
try {
jdbcConnection.rollback();
} catch (final SQLException rollbackException) {
System.out.printf("Rollback failed: %s\n", rollbackException.getMessage());
}
return null;
}
}
}
This solves two problems right away:
You won't need to put boilerplate JDBC code everywhere;
It will log any JDBC errors right away, so you won't need to go through a complex debugging process.
Brief explanation:
First of all any resource involving I/O access (database access is I/O access) must always be closed or it will cause a memory leak.
Secondly, it is better to rely on try-with-resources to close any resource as having to call the .close() method manually is always exposed to the risk of not being effectively executed at runtime due to a potential Exception/RuntimeException/Error getting thrown beforehand; even closing the resource in a finally method is not preferable as such block executes at a different phase compared to the try-with-resources - auto closure of try-with-resources happens at the end of the try block, while finally executes at the end of all try/catch block - , in addition to the basic problem that it is not a secure solution as a throw might happen even inside the finally block, preventing it from completing correctly.
This said, you always need to close:
Statement/PreparedStatement/CallableStatement
any ResultSet
the whole Connection when you don't need DB access anymore
Try-catch for DB Layer code is important if you're querying with JDBC.
Think about, what if the connection broke? Or what if Database crashed ? Or some other unfortunate scenario comes up.
For these things, I will recommend you to always keep the DB layer code within try-catch.
It's also recommended for you to have some fallback mechanism in-case of the above events.
You should always handle it with try cactch.
Why: For example you started a connection to db then some exception happened, if you don't rollback your transaction it stay on db and performance will be decreased, and memory leak will be happen.
Imagine, if your connection limit is 100 and 100 exception throwed after transaction started and you didn't rollback it your system will be locked because of you can't create any new connection to your database.
But if you want an alternative for "try catch finally" you can use like this:
EmUtil.consEm(em -> {
System.out.println(em.createNativeQuery("select * from temp").getResultList().size());
});
Source code:
public final class EmUtil {
interface EmCons {
public void cons(EntityManager em);
}
public static void consEm(EmCons t) {
EntityManager em = null;
try {
em = getEmf().createEntityManager();
t.cons(em);
} finally {
if (em != null && em.getTransaction().isActive())
em.getTransaction().rollback();
if (em != null && em.isOpen())
em.close();
}
}
private static EntityManagerFactory getEmf() {
//TODO
}
}
Spring translates those exceptions as DataAccessException (for more detail link). It will be good to catch those exceptions and you can rollback with #Transactional.
I want to throw a custom error class when a user searches my repo with an invalid ID. This should be very straight forward, but I cannot seem to catch any errors thrown by JpaRepository. I have made several attempts to solve this, but the following is my most straight forward attempt:
try {
Object obj = repository.getOne(id)
}
catch (EntityNotFoundException e) {
throw CustomException("message");
}
When running this in a debugger, repository throws the exact exception I am expecting javax.persistence.EntityNotFoundException, but the code simply skips over my catch statement and the function returns with an error.
I tried using repository.findById(id) with similar results. I also tried catching Exception and Throwable. Any ideas? I will add more information to my post if it ends up my problem is not immediately obvious.
getOne() is just a wrapper for EntityManager.getReference(). That method will not throw any exception.
It returns an uninitialized proxy, assuming that the entity indeed exists. It doesn't get the entity state from the database, and thus doesn't even know if it exists. It assumes it does.
You'll only get an exception later, if you try to access the state of the entity.
Use findById()/findOne(), check if you get a non-empty/non-null result (because these methods don't throw any exception if the entity doesn't exist, they return empty or null), and throw your exception if that's the case.
You can try this:
try {
Object obj = repository.findById(id).orElseThrow(EntityNotFoundException::new);
}
catch (EntityNotFoundException e) {
throw CustomException("message");
}
You need to annotate your repository class with #Repository annotation to perform wrapping of exceptions to specific persistence exceptions.
What regarding question - you don't need to use getOne() with potential exception, but could use some other methods without throwing errors in case if entity not present, like findById() or another one that return Optional<> object
you can catch DataAccessException instead of EntityNotFoundException
try {
Object obj = repository.getOne(id)
}
catch (DataAccessException e) {
throw CustomException("message");
}
In my service, have handled DataIntegrityViolationException when calling myCrudRepository.saveAndFlush to handle concurrent persist (insertion) requests. It works and I can catch the exception. After this, I prefer to make sure if the exception is exactly because entity already exists, not due to any other possible unknown issues. So, I call myCrudRepository.exists(entity.getId()) , but DataIntegrityViolationException is thrown again.
Here is my simple code:
private void save(final Employee entity) throws MyAppException {
try{
this.myCrudRepository.saveAndFlush(entity);
}catch (org.springframework.dao.DataIntegrityViolationException e){
// check if the error is really because the entity already exists
// entity.getId() is already generated before any save. it's like National ID
boolean exists = this.myCrudRepository.exists(entity.getId()); // DataIntegrityViolationException is thrown here again!
if (exists)
throw new MyAppException(HttpStatus.CONFLICT, "Entity already exists.");
else
throw e;
}
}
But if I use findOne instead of exists, it works fine. It's somehow strange, but of course it has a technical reason that I'm not good enough to make a guess.
Any idea? Thanks in advance.
The problem is when you are using Transactional method and after the method returns the transaction will auto commit and hibernate will wrap up the exceptions into another one. Actually the exception is occurring when transaction commits and at that time you are out of that method already. To catch the exception from inside the method you can simply use em.flush() after this.myCrudRepository.saveAndFlush(entity);.
I have sceneario in which I have to check if an onject instance exist in entity manager. It works fine when the instance eexists but throws null pointer exception when it doesn't. When it doesn't I have to do another thing. so how can catch this state? I already tried try catch, but it doesn't work.
Sorry when I was asking this question I was on the way and trying to type in a different device that I am used to. My code is:
AJPAController aJPAController;
AClass aClass = aJPAController.find((Integer.parseInt(request.getParameter("id")));
try{
if(aJpaController.contains(aClass)){
response.sendRedirect("gosomewhere.com");
}
}
catch (java.lang.NullPointerException R){
response.sendRedirect("gosomewhereelse.com");
}
I might have a logical mistake in trying first find and then contains method, but I couldn't find a better solution.
Thanks in advance.
You can check if an entity is managed (within the persistence context) with help of the contains method of your EntityManager
I have another question regarding checked exceptions. I have attempted to answer the question below.. Below my attempt is the original question and code. Could you let me know if I'm right or how I can change my attempt to make it correct. Kindest regards
public boolean checkMembership(MemberId memberId)
{
// Firstly the method is tried to see if it works.
try {
public boolean checkMembership(MemberId memberId)
}
// If it does not work, then the exception is called
catch (InvalidMemberIdException ex){}
}
The checkMembership method is part of the Membership class. Its purpose
is to validate the memberId it is passed as its parameter and then try to find it
in a list of members. It returns true if the memberId is found and false if not.
public boolean checkMembership(MemberId memberId)
{
if (!validate(memberId)) {
// An exception must be thrown.
…
}
// Further details of the check membership method are omitted.
…
}
If the memberId parameter to checkMembership is not valid then an
InvalidMemberIdException must be thrown. Rewrite the part of the
checkMembership method shown above to show how this would be done.
Remember, this is a checked exception. You must include detailed javadoc
comments for the method that conform to good stylistic conventions.
just add a
throw new InvalidMemberIdException("the id was invalid");
and update the javadocs.
edit -- i noticed them method as written is calling itself recursively (within the try catch block). You probably dont want to do this. Also, in the catch block you don't want to do nothing ('swallowing the exception' is usually bad). Put a log in there or something, or a comment that you are intentionally not doing anything.