2

I have followig service:

@Service
public class CompanyServiceImpl implements CompanyService {
    @PostConstruct
    public void init() {
        this.refreshStopJobs();
    }
    @Transactional(readOnly = true)
    @Override
    public void refreshStopJobs() {
        companyDao.getCompanysByStatus(CampaignStatus.START).forEach(this::refreshStopJob);
    }
}

and following dao:

@SuppressWarnings("unchecked")
@Override
public List<Campaign> getCompanysByStatus(CampaignStatus campaignStatus) {
    Criteria criteria = createCriteriaForGettingList(null, campaignStatus);
    return criteria.list();
}

If I run my application I see following log:

2015-11-08 17:54:04.601:WARN:oejw.WebAppContext:main: Failed startup of context o.e.j.m.p.JettyWebAppContext@48e4fba9{/,file:/D:/freelance/marcproject/src/main/webapp/,STARTING}{file:/D:/freelance/marcproject/src/main/webapp/}
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'companyServiceImpl': Invocation of init method failed; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
.....
Caused by: 
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
    at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
    at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
    at com.terminal.dao.impl.CompanyDaoImpl.createCriteriaForGettingList(CompanyDaoImpl.java:77)
    at com.terminal.dao.impl.CompanyDaoImpl.getCompanysByStatus(CompanyDaoImpl.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:201)
    at com.sun.proxy.$Proxy80.getCompanysByStatus(Unknown Source)
    at com.terminal.service.impl.CompanyServiceImpl.refreshStopJobs(CompanyServiceImpl.java:319)
    at com.terminal.service.impl.CompanyServiceImpl.init(CompanyServiceImpl.java:313)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)

If mark dao method getCompanysByStatus as @Transactional - application starts fine. But I am not understand why. Because I had already started a transaction in service method refreshStopJobs

1

1 Answer 1

4

This is because you are not invoking refreshStopJobs() through Spring proxy but directly through this. You can see this clearly by observing stack trace. In the first case you wont see transactional aspect around your method invocation.

If you move @Transactional to dao this will work but it is considered as a bad practice to have @Transactional in DAO layer.

Another solution is to move refreshStopJobs() method to another service or inject self reference to your service.

You may see invocation like yours works for some people. This is because they use AspectJ proxy instead of spring proxy based AOP. To get know how Spring AOP works read about "proxy pattern".

AspectJ uses bytecode manipulation during compile time so it just adds some code around real methods and during runtime it works as good as normal object invocation.

Example how to inject a proxy (works only when CompanyService is defined as singleton not prototype):

@Service
public class CompanyServiceImpl implements CompanyService, BeanNameAware {

private String name;

  private CompanyService proxy;

  @Autowired
  private ApplicationContext applicationContext;

  @Override
  public void setBeanName(String name) {
    this.name = name;
  }

@PostConstruct
  public void postConstruct() {
    proxy = (CompanyService)applicationContext.getBean(name);
    proxy.refreshStopJobs();
  }

@Transactional(readOnly = true)
    @Override
    public void refreshStopJobs() {
        companyDao.getCompanysByStatus(CampaignStatus.START).forEach(this::refreshStopJob);
    }

}

Getting proxy statically:

@Service
public class SpringContext implements ApplicationContextAware {
  private static ApplicationContext context;

  public void setApplicationContext(ApplicationContext context) throws BeansException {
    this.context = context;
  }

public static <T> T getProxy (Class<T> proxyClass){
    return (T) context.getBean(proxyClass);
  }
}

Please keep in mind this service has to be initialized before CompanyService.

10
  • I believe that it is very bad if my business bean depends on applicationContext. Commented Nov 8, 2015 at 18:55
  • It is just an example how to prove what is the cause of the problem. How do you implement this is up to you. Some people create BeanPostPorcessors, some create utility classes to find a proxy, you may find many of working solutions here on SO.
    – Marek Raki
    Commented Nov 8, 2015 at 21:15
  • can you show example of utility classes to find a proxy ? Commented Nov 8, 2015 at 21:45
  • Same way like here but without having reference to ApplicationContext inside of bean.
    – Marek Raki
    Commented Nov 8, 2015 at 22:21
  • You may use also try AopContext.currentProxy() but then you need to expose proxies in Spring which is also bad in some way: "Spring's AOP framework does not expose proxies by default, as there is a performance cost in doing so." docs.spring.io/spring/docs/current/javadoc-api/org/…
    – Marek Raki
    Commented Nov 8, 2015 at 22:29

Not the answer you're looking for? Browse other questions tagged or ask your own question.