I have a very simple code comprising of Service -> RequestProcessor -> DAO having 2-3 classes (interface, abstract, concrete) in each layer.
Service layer:-
public interface Service {
public void saveOrUpdate(Object entity, String operationName);
}
}
public abstract class AbstractService implements Service{
public abstract ReqProcessor getRP();
#Override
public void saveOrUpdate(Object entity, String operationName) {
ReqProcessor hiberTestRP = getRP();
hiberTestRP.saveOrUpdate(entity, operationName);
}
}
#Component
public class ServiceImpl extends AbstractService {
#Autowired
public ReqProcessor hibertestRPImpl;
#Override
public HiberTestRP getRP() {
return hibertestRPImpl;
}
}
ReqProcessor layer:-
public interface ReqProcessor {
public void saveOrUpdate(Object entity, String operationName);
public void saveObject();
}
}
public abstract class AbstractReqProcessor implements ReqProcessor {
#Override
public void saveOrUpdate(Object entity, String operationName) {
saveObject();
}
}
#Component
public class ReqProcessorImpl extends AbstractReqProcessor {
#Autowired
public CustomHibernateDao customWSDaoImpl;
#Override
#Transactional(value="transactionManagerWS", propagation=Propagation.REQUIRED)
public void saveObject() {
// object created //
customWSDaoImpl.saveOrUpdate(object); // exception is thrown at this line
}
}
DAO layer:-
public interface CustomHibernateDao {
public void saveOrUpdate(Object entity, String operationName);
}
#Repository
#Transactional(value="transactionManagerWS", propagation=Propagation.MANDATORY)
public class CustomWSDaoImpl implements CustomHibernateDao {
#Autowired
public SessionFactory sessionFactoryWS;
protected Session getCurrentSession() {
return sessionFactoryWS.getCurrentSession();
}
#Override
public void saveOrUpdate(Object entity, String operationName) {
Session session = getCurrentSession();
session.saveOrUpdate(entity);
}
}
I get the following exception at the commented line :
Exception in thread "main" org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:359)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:447)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy37.saveOrUpdate(Unknown Source)
The code works absolutely fine when the abstract classes are removed, with only interfaces and their implementing classes remaining. But with the above setup, the transaction is not being propagated from ReqProcessor layer to the DAO layer. Please help. (Dont mind the 'public' accessors everywhere, it's just for testing)
I have also searched on SO and other forums but couldnt find a solution.
As #m-deinum has mentioned, Spring uses proxies to add "transactional" functionality, and this feature does not work when you call method annotated with #Transactional from another method of the class.
You have two ways to fix the problem:
In AbstractReqProcessor autowire ApplicationContext and then use it to get a bean of CustomHibernateDao type. On this retrieved object you can call saveObject - then the transactional magic happens.
The more preferred way is to annotate method saveOrUpdate of class AbstractService with #Transactional annotation too - then it will work again.
But I think you know the cause of the problem now and you can find another - more suitable for you - way.
When i call a service layer from controller,data are being saved in database.
My controller is:
#RestController
#RequestMapping("/api")
public class ApiController {
#Autowired(required = true)
PersonService personService; //This is service layer class
#RequestMapping("/add")
public void add(){
person.setLastName("Rahim");
person.setFirstName("Uddin");
person.setAddress("Dhaka");
person.setCity("Dhaka");
personService.add(person);
}
}
Service layer is:
#Service
#Transactional
public class PersonServiceImpl implements PersonService {
#Autowired
PersonDao personDao;
#Override
public void addPerson(Person person) {
personDao.addPerson(person);
}
}
Till now everything is ok.
But when i call the service layer through another class, null pointer exception is being shown.
At that time:
My controller is:
#RestController
#RequestMapping("/api")
public class ApiController {
#Autowired(required = true)
PersonService personService; //This is service layer class
#RequestMapping("/add")
public void add(){
MiddleClass m=new MiddleClass();
m.create();
}
}
My MiddleClass is:
public class MiddleClass {
#Autowired
PersonService personService;
public void create(){
Person person=new Person();
person.setLastName("Rahim");
person.setFirstName("Uddin");
person.setAddress("Dhaka");
person.setCity("Dhaka");
personService.addPerson(person);//this time same service layer
//is showing null pointer exception here
}
}
WHY?????????? is it for lacking of any annotation in MiddleClass?
You're creating the MiddleClass instance yourself, using new. So Spring has no way to know that it has to inject the service into the MiddleClass instance. It can only inject beans into other beans it creates itself.
So, MiddleClass must be a Spring bean, and it must be autowired into the controller, just like the service is.
I have a question regarding Propagation.NEVER usage with #Transactional in the service layer. I use this on a method of service layer which calls DAO layer method which calls session.save() method of Hibernate.
As NEVER means no transaction should be present, it is executing the service layer method without any error because as there is no session. But this also means save() method which calls in service layer doesn't have a transaction, right? And if it doesn't have a transaction, it should throw an error, right? Can you please correct me why the record is getting stored even without transaction.
Service layer code is as follows:
#Service("userSer")
#Transactional
public class UserServiceImpl implements UserService{
#Autowired
private UserDao userDao;
#Transactional(propagation=Propagation.NEVER)
public void saveUser(User user) {
userDao.create(user);
}
}
DAO layer code is as follows:
#Repository("user")
public class HibernateUserDao implements UserDao{
#Autowired
private SessionFactory sessionFactory;
public Session currentSession() {
return sessionFactory.getCurrentSession();
}
public User create(User user) {
currentSession().save(user);
return user;
}
}
And I am testing from a main method as follows:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-hibernate-idol.xml");
UserService service = (UserService)context.getBean("userSer");
User u1 = new User();
u1.setPassword("bsk5");
service.saveUser(u1);
System.out.println("Record created finally if this line is printed");
}
Can you please help me out in understanding this.
When i try to integrate Spring-Dependency-Injection in Play-framework with Java 8. In controller the dependencies are not injected. I am using spring stereo-type annotations. Get
Follwowing is my code:
Configuration:
public class GlobalConfiguration extends GlobalSettings{
private AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
#Override
public void onStart(Application app) {
super.onStart(app);
// AnnotationConfigApplicationContext can only be refreshed once, but we do it here even though this method
// can be called multiple times. The reason for doing during startup is so that the Play configuration is
// entirely available to this application context.
applicationContext.scan("com.harmeetsingh13.controllers", "com.harmeetsingh13.service.impl", "com.harmeetsingh13.dao.impl");
applicationContext.refresh();
// This will construct the beans and call any construction lifecycle methods e.g. #PostConstruct
applicationContext.start();
}
#Override
public void onStop(Application app) {
// This will call any destruction lifecycle methods and then release the beans e.g. #PreDestroy
applicationContext.close();
super.onStop(app);
}
#Override
public <A> A getControllerInstance(Class<A> clazz) throws Exception {
return applicationContext.getBean(clazz);
}
}
Controller:
#Component
public class UserController extends Controller{
#Autowired
private UserService userService;
public Result findUserById(Integer userId) {
Optional<User> user = userService.findUserById(userId);
if(user.isPresent()){
}
return null;
}
}
Service:
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserDao userDao;
#Override
public Optional<User> findUserById(int id) {
List<User> users = userDao.getAllUsers();
return users.stream().filter(user -> user.id == id).findFirst();
}
}
This is the link where i found sample application
This is really my stupid mistake. In play-framework we always need to put the custom global configuration file in project app folder at root and play-framework always find to search Global file name at app folder root and load into the memory. In my case, my GlobalConfiguration file are not loaded in the memory and default configuration are used by play-framework. For Global-Settings click on this link for more information
I want to read text data fixtures (CSV files) at the start on my application and put it in my database.
For that, I have created a PopulationService with an initialization method (#PostConstruct annotation).
I also want them to be executed in a single transaction, and hence I added #Transactional on the same method.
However, the #Transactional seems to be ignored :
The transaction is started / stopped at my low level DAO methods.
Do I need to manage the transaction manually then ?
Quote from legacy (closed) Spring forum:
In the #PostConstruct (as with the afterPropertiesSet from the InitializingBean interface) there is no way to ensure that all the post processing is already done, so (indeed) there can be no Transactions. The only way to ensure that that is working is by using a TransactionTemplate.
So if you would like something in your #PostConstruct to be executed within transaction you have to do something like this:
#Service("something")
public class Something {
#Autowired
#Qualifier("transactionManager")
protected PlatformTransactionManager txManager;
#PostConstruct
private void init(){
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//PUT YOUR CALL TO SERVICE HERE
}
});
}
}
I think #PostConstruct only ensures the preprocessing/injection of your current class is finished. It does not mean that the initialization of the whole application context is finished.
However you can use the spring event system to receive an event when the initialization of the application context is finished:
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
// do startup code ..
}
}
See the documentation section Standard and Custom Events for more details.
As an update, from Spring 4.2 the #EventListener annotation allows a cleaner implementation:
#Service
public class InitService {
#Autowired
MyDAO myDAO;
#EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event) {
event.getApplicationContext().getBean(InitService.class).initialize();
}
#Transactional
public void initialize() {
// use the DAO
}
}
Inject self and call through it the #Transactional method
public class AccountService {
#Autowired
private AccountService self;
#Transactional
public void resetAllAccounts(){
//...
}
#PostConstruct
private void init(){
self.resetAllAccounts();
}
}
For older Spring versions which do not support self-injection, inject BeanFactory and get self as beanFactory.getBean(AccountService.class)
EDIT
It looks like that since this solution has been posted 1.5 years ago developers are still under impression that if a method,
annotated with #Transactional, is called from a #PostContruct-annotated method invoked upon the Bean initialization, it won't be actually executed inside of Spring Transaction, and awkward (obsolete?) solutions get discussed and accepted instead of this very simple and straightforward one and the latter even gets downvoted.
The Doubting Thomases :) are welcome to check out an example Spring Boot application at GitHub which implements the described above solution.
What actually causes, IMHO, the confusion: the call to #Transactional method should be done through a proxied version of a Bean where such method is defined.
When a #Transactional method is called from another Bean, that another Bean usually injects this one and invokes its proxied (e.g. through #Autowired) version of it, and everything is fine.
When a #Transactional method is called from the same Bean directly, through usual Java call, the Spring AOP/Proxy machinery is not involved and the method is not executed inside of Transaction.
When, as in the suggested solution, a #Transactional method is called from the same Bean through self-injected proxy (self field), the situation is basically equivalent to a case 1.
#Platon Serbin's answer didn't work for me. So I kept searching and found the following answer that saved my life. :D
The answer is here No Session Hibernate in #PostConstruct, which I took the liberty to transcribe:
#Service("myService")
#Transactional(readOnly = true)
public class MyServiceImpl implements MyService {
#Autowired
private MyDao myDao;
private CacheList cacheList;
#Autowired
public void MyServiceImpl(PlatformTransactionManager transactionManager) {
this.cacheList = (CacheList) new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
#Override
public Object doInTransaction(TransactionStatus transactionStatus) {
CacheList cacheList = new CacheList();
cacheList.reloadCache(MyServiceImpl.this.myDao.getAllFromServer());
return cacheList;
}
});
}
The transaction part of spring might not be initialized completely at #PostConstruct.
Use a listener to the ContextRefreshedEvent event to ensure, that transactions are available:
#Component
public class YourService
implements ApplicationListener<ContextRefreshedEvent> // <= ensure correct timing!
{
private final YourRepo repo;
public YourService (YourRepo repo) {this.repo = repo;}
#Transactional // <= ensure transaction!
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repo.doSomethingWithinTransaction();
}
}
Using transactionOperations.execute() in #PostConstruct or in #NoTransaction method both works
#Service
public class ConfigurationService implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationService.class);
private ConfigDAO dao;
private TransactionOperations transactionOperations;
#Autowired
public void setTransactionOperations(TransactionOperations transactionOperations) {
this.transactionOperations = transactionOperations;
}
#Autowired
public void setConfigurationDAO(ConfigDAO dao) {
this.dao = dao;
}
#PostConstruct
public void postConstruct() {
try { transactionOperations.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(final TransactionStatus status) {
ResultSet<Config> configs = dao.queryAll();
}
});
}
catch (Exception ex)
{
LOG.trace(ex.getMessage(), ex);
}
}
#NoTransaction
public void saveConfiguration(final Configuration configuration, final boolean applicationSpecific) {
String name = configuration.getName();
Configuration original = transactionOperations.execute((TransactionCallback<Configuration>) status ->
getConfiguration(configuration.getName(), applicationSpecific, null));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
}
}