Iam new to spring.How can I throw the exception from my Hibernate DAO calss to service and then to my controller and how to inform UI about the exception.Plese help me with exception throwing to indicate the user.
Service Class:
#Service
public class ServiceImpl implements Service {
private static Logger LOGGER = Logger.getLogger(AccessServiceImpl.class);
#Autowired
private DAO DAO;
#Override
#Transactional
public List<PrinterVO> getDetails() {
List<VO> printerList = null;
try {
LOGGER.info("Inside getAllPrinters()");
printerList = DAO.getAllPrinters();
} catch(Exception e) {
LOGGER.error("getAllPrinters() Error :", e);
}
return printerList;
}
DAO:
public class DAOImpl implements DAO {
#Autowired
#Qualifier(value="ressessionFactory")
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
#Override
public List<VO> getDetails() {
List<VO> printerList = null;
try {
LOGGER.info("Inside getAllPrinters()");
printerList = sessionFactory.getCurrentSession().createQuery("from PrinterVO").list();
} catch(HibernateException e) {
LOGGER.error("getAllPrinters() Error : ", e);
}
return printerList;
}
You're catching the exception, so it cannot propagate to your UI. You should only catch an exception if you have some action that you need to perform. Logging is not an action.
In your DAO, you should inject your Session (if you really want to depend on Hibernate) or a JPA EntityManager. If you are using dependency injection, you shouldn't be invoking factory methods.
I don't see anything in your code that would tell me how data gets to your UI, so I can't tell you how to pass an exception along to your UI either. Spring come with its own exception handling and translation facilities, so you shouldn't have to write many try/catch blocks, but rather rely on Spring's capabilities to present sensible error messages.
Related
I have a service with a method, which is not annotated with #Transactional:
#Service
#RequiredArgsConstructor
public class MainService {
private final ServiceA serviceA;
public void processData() {
List<EntityA> list = serviceA.getList();
list.forEach(item -> {
try {
serviceA.doSomeDbOperations(item);
} catch(Exception e) {
// some processing here
} finally {
// some processing and DB interactions here
}
})
}
}
The goal is to roll back the changes happened in try block (serviceA.doSomeDbOperations(item)) if an exception is thrown. so I annotated this method in ServiceA with #Transactional:
#Service
public class ServiceA {
// dependencies
#Transactional
public void doSomeDbOperations(EntityA item) {
// some logic here
repositoryA.save(item)
serviceB.deleteSomething(input)
}
}
serviceB.deleteSomething(input) could throw an exception:
#Service
public class ServiceB {
// dependencies
public void deleteSomething(EntityA item) {
// some logic here
if(condition) {
Throw new RuntimeException();
}
}
}
The problem is that when an Exception is thrown, the changes in try block are not rolled back and data is non-consistent. Any idea where the problem is?
I guess in ServiceB you are using a checked Exception.
In Spring the transactions are rolled back just on unchecked exceptions by default, so try throwing for example a RuntimeException in your ServiceB.deleteSomething() method.
In case you need a checked Exception there, you can do something like this in your ServiceA.doSomeDbOperations() method:
#Transactional(rollbackFor = Throwable.class)
Some references:
https://www.baeldung.com/java-checked-unchecked-exceptions
How to make Spring #Transactional roll back on all uncaught exceptions?
I have a particular class used to interface with a service that requires initialization. In the application lifecycle, the only place this makes sense is in the start of the application because the rest of the spring application cannot run without it. I had the idea to do this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
try {
MyRequiredService mrs = new MyRequiredService();
mrs.connect(); // This will throw if it fails
run(MyApplication.class, args);
} catch(MyException e) {
System.out.println("Failed to connect to MyRequiredService!");
}
}
}
This will launch the service and attempt to connect but I have one big problem. How do I pass this class around the application? I need it's functions in the service endpoints I am writing.
I didn't see anything obvious and searching "passing class instance in spring boot application" turns up a bunch of unrelated topics.
Is there a smart, clean way to do this in spring boot? I apologize for a contrived example. The names of the service are unique enough I didn't want to violate any agreements.
You can make Spring do this for you. First, you need to annotate your class with #Service, so Spring will pick it up when scanning for classes.
Then, define an init() method and annotate it with #PostConstruct. Spring will instantiate your MyRequiredService class and call init()
#Service
public class MyRequiredService {
#PostConstruct
public void init() {
connect();
}
public void connect() {
// ...
}
}
You could call connect() from the constructor, but I don't like to define objects that may throw exceptions out of the constructor.
And then, you can use MyRequiredService in some other class by injecting it via the #Autowired annotation:
#Component
public class MyOtherClass {
private final MyRequiredService service;
public MyOtherClass(final MyRequiredService service) {
this.service = service;
}
// Other methods here.
}
This has the same overall effect as what you're trying to do above. If MyRequiredService fails, the application will not start up.
Make it a bean. Then it will be in the ApplicationContext which then you can pass to your desired other classes through the constructor
#Configuration
public class ApplicationConfiguration
{
#Bean
public MyRequiredService myRequiredService()
{
MyRequiredService mrs = new MyRequiredService();
try {
mrs.connect(); // This will throw if it fails
return mrs;
} catch(MyException e) {
log.error("Failed to connect to MyRequiredService!");
throw new IllegalStateException("MyRequiredService failed connection. Stopping startup");
}
}
#Bean
public SomeOtherService someOtherService(MyRequiredService mrs) {
return new SomeOtherService(mrs);
}
}
IMHO Instead of catching the error and logging it. I would throw it and stop the application from starting, but to keep with your example I added the throw IllegalStateException after the log.
Doing it this way Spring will create your MyRequiredService bean in the ApplicationContext then you can see I added as a parameter needed by the bean below that. Spring will grab that bean out of the ApplicationContext and supply it to the bean. If Spring doesn't find the bean in the ApplicationContext it will throw an error and stop the application from startup.
a class implements BeanFactoryPostProcessor which is init before normal bean
#Configuration
public class MyRequiredService implements BeanFactoryPostProcessor,
PriorityOrdered, InitializingBean {
#Override
public int getOrder() {
return Integer.MIN_VALUE;
}
public void connect() {
// ...
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
#Override
public void afterPropertiesSet() throws Exception {
connect();
}
}
I want to create an exception log in the database when an #Async operation fails with an exception.
You can see the implementation for AsyncExecutorConfiguration and AsyncExceptionHandler classes below.
Inside AsyncExceptionHandler class, when I call a service that tries to access the database, I am getting: org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
#Configuration
#EnableAsync
public class AsyncExecutorConfiguration implements AsyncConfigurer {
#Autowired
private AsyncExceptionHandler asyncExceptionHandler;
private static final int CORE_POOL_SIZE = 3;
private static final int MAX_POOL_SIZE = 3;
private static final int QUEUE_CAPACITY = 24;
private static final String THREAD_NAME_PREFIX = "AsynchThread-";
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
#Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Autowired
private NotificationService notificationService;
#Override
#Transactional(rollbackFor = Exception.class, readOnly = false)
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
AsyncErrorLog log = new AsyncErrorLog(ex);
notificationService.saveLogAndNotify(log); // throws exception "Could not obtain transaction-synchronized Session for current thread"
}
}
#Service
public class MyServiceImpl implements MyService {
#Override
#Async
#Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void doSomething(Long id) {
// I can execute database operations here
}
...
#Async function itself already has a valid session. What should I do to have a valid session in AsyncExceptionHandler class too?
--
UPDATE
Here is the simplified implementations for NotificationServiceImpl and LogDaoImpl.class where we get the error.
#Service
public class NotificationServiceImpl implements NotificationService {
#Autowired
private LogDao logDao;
#Override
#Transactional(rollbackFor = Exception.class, readOnly = false)
public void saveLogAndNotify(Log log) {
return logDao.createLog(log);
}
#Repository
public class LogDaoImpl{
#Autowired
protected SessionFactory sessionFactory;
#Override
public void createLog(Log log) {
sessionFactory.getCurrentSession().saveOrUpdate(log);
}
Per the Hibernate exception; if you're not using Spring Data, you'll have to make sure the notification service explicitly invokes the database calls on the Hibernate session.
On another note, in my experience, the main use cases for the UncaughtExceptionHandler (in general) are used for:
A simple last-resort to handle RuntimeExceptions that may be unknown to the programmer that for some reason cannot (or are not) caught in code
A way to catch exceptions in code that the programmer has no control over (e.g. if you're invoking Async directly on some third party library, etc.)
The commonality between the two is that this Handler is used for something unexpected. In fact, Spring itself accounts for the "unexpectedness" in your own code and Spring Async already sets a default one for you that will log to the console (code here), letting you not have to worry about rogue exceptions killing threads and not knowing why. (Note: The message in the source code says it's catching an "unexpected" exception. Of course exceptions are unexpected, but these are one's that you really didn't know could happen. Spring Async will log it for you.)
That being the case, in your example, since you're doing Spring Database operations and should know exactly what's happening inside of #doSomething, I would just go with removing the AUEH a try-catch (and/or -finally) and handle the exception inside of #doSomething:
#Service
public class MyServiceImpl implements MyService {
// Self autowired class to take advantage of proxied methods in the same class
// See https://stackoverflow.com/questions/51922604/transactional-and-stream-in-spring/51923214#51923214
private MyService myService;
private NotificationService notificationService;
#Override
#Async
public void doSomething(Long id) {
// I can execute database operations here
try {
myService.doDatabaseOperations(...);
} catch(DatabaseAccessException e) {
AsyncErrorLog log = new AsyncErrorLog(ex);
notificationService.saveLogAndNotify(log);
}
// Other exceptions (from DB operations or the notifications service) can be
// handled with other "catches" or to let the SimpleAsyncExHandler log them for you.
// You can also use standard multithreading exception handling for this
}
#Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void doDatabaseOperations(...) {
...
}
}
You can use the applicationContext in your handler to lookup the notificationService. I had the same issue when I used #Autowired for the handler, which in turn injected my LogService. After looking at the logs I saw that the TransactionSynchronizationManager is clearing transaction synchronization after the rollback of the exception and nothing else except the no transaction for ...... error.
After using the applicationContext for looking up the logService bean and changing my handler, I saw the desired result in the logs.
begin
Initializing transaction synchronization
Getting transaction for [....AsyncService.doAsync]
Exception
rolling back
Clearing transaction synchronization
begin
Initializing transaction synchronization
Getting transaction for [.....LogService.save]
Change your config to include the interface ApplicationContextAware which will give you a convenience method to access the applicationContext. Set it as a instance variable.
See my configuration class below.
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer, ApplicationContextAware {
private static final int CORE_POOL_SIZE = 3;
private static final int MAX_POOL_SIZE = 3;
private static final int QUEUE_CAPACITY = 24;
private static final String THREAD_NAME_PREFIX = "AsynchThread-";
private ApplicationContext applicationContext;
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler(this.applicationContext);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
I have removed the #Component from the handler and use it as a POJO.
Every time getAsyncUncaughtExceptionHandler is called with an exception, a new handler instance is created with the applicationContext as a dependency.
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private final ApplicationContext applicationContext;
public AsyncExceptionHandler(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
Log log = new Log();
log.setEntry(ex.getMessage());
LogService logService = this.applicationContext.getBean(LogService.class);
logService.save(log);
}
}
The save method on logService requires a new transaction every time it is called.
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void save(Log log)
This will help you:
#Override
public void createLog(Log log) {
try {
session = sessionFactory.getCurrentSession();
} catch (HibernateException e) {
session = sessionFactory.openSession();
}
session.saveOrUpdate(log);
}
When writing transactional methods with #Async, it's not possible to catch the #Transactional exceptions. Like ObjectOptimisticLockingFailureException, because they are thrown outside of the method itself during eg transaction commit.
Example:
public class UpdateService {
#Autowired
private CrudRepository<MyEntity> dao;
//throws eg ObjectOptimisticLockingFailureException.class, cannot be caught
#Async
#Transactional
public void updateEntity {
MyEntity entity = dao.findOne(..);
entity.setField(..);
}
}
I know I can catch #Async exceptions in general as follows:
#Component
public class MyHandler extends AsyncConfigurerSupport {
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> {
//handle
};
}
}
But I'd prefer to handle the given exception in a different way ONLY if it occurs within the UpdateService.
Question: how can I catch it inside the UpdateService?
Is the only chance: creating an additional #Service that wraps the UpdateService and has a try-catch block? Or could I do better?
You could try self-injecting your bean which should work with Spring 4.3. While self-injecting is generally not a good idea, this may be one of the use-cases which are legitimate.
#Autowired
private UpdateService self;
#Transactional
public void updateEntity() {
MyEntity entity = dao.findOne(..);
entity.setField(..);
}
#Async
public void updateEntityAsync(){
try {
self.updateEntity();
} catch (Exception e) {
// handle exception
}
}
I have problem with understanding the EJBTransactionRolledbackException.
I have entity:
#Entity
public class MyEntity {
#Id
#GeneratedValue
private Long id;
#Size(max=5)
private String name;
//...
}
and repository which is SLSB because of ease of CMT:
#Stateless
public class ExampleRepository {
#PersistenceContext
private EntityManager em;
public void add(MyEntity me) {
em.persist(me);
}
}
now I have test Servlet, when I simulate ConstraintViolation (too long name).
#WebServlet("/example")
public class ExampleServlet extends HttpServlet {
#Inject
private ExampleRepository repo;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
MyEntity me = new MyEntity();
me.setName("TooLongName");
try {
repo.add(me);
} catch(EJBTransactionRolledbackException e) {
System.out.println("Exception caught");
}
}
}
I know that in such scenario EJB container will wrap the ConstraintViolationException so I catch the EJBTransactionRolledbackException instead. The problem is that in the console I can see my message from catch block ("Exception caught") but before that there are tons of exception logs produced (link).
I don't quite understand what happened - is this exception caught or not? How to prevent all these error messages in the console in such simple scenario?
Please look at this explanation: A clear explanation of system exception vs application exception
You have to understand that handling exceptions and handling transactions are two different things happening alongside. System exceptions unconditionally trigger transaction rollback. When you see a ConstraintViolationException, which is a system exception because it extends RuntimeException, it is not just wrapping and re-throwing. A bad thing have happened along the way - your transaction has been aborted.
So, answering the first question if the exception (ConstraintViolationException) was caught - yes, it was caught by the container. The transaction was aborted and a new exception was thrown to notify the application code about that.
You can suppress logging these messages, but then you would not know about data persistence failures.
I can suggest you two solutions:
Use bean managed transactions:
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class ExampleRepository {
#PersistenceContext(synchronization = SynchronizationType.UNSYNCHRONIZED))
private EntityManager em;
#Resource
private UserTransaction tx;
public void add(MyEntity me) {
try {
tx.begin();
em.joinTransaction();
em.persist(me);
tx.commit();
} catch (ValidationException ex) {
throw new AppValidationException(ex);
}
}
}
Delegate / facade pattern:
You leave your ExampleRepository as is:
#Stateless
public class ExampleRepository {
#PersistenceContext
private EntityManager em;
public void add(MyEntity me) {
em.persist(me);
}
}
Create new EJB without transaction (with same methods as initial):
#Stateless
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class ExampleRepositoryDelegate {
#EJB
private ExampleRepository repository;
public void add(MyEntity me) {
try {
repository.add(me);
} catch (ValidationException e) {
e.printStackTrace();
}
}
}
And in servlet you use new delegate bean:
#WebServlet("/example")
public class ExampleServlet extends HttpServlet {
#Inject
private ExampleRepositoryDelegate repoDelegate;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
MyEntity me = new MyEntity();
me.setName("TooLongName");
try {
repoDelegate.add(me);
} catch(Exception e) {
System.out.println("Exception caught");
}
}
}