2

I am writing an application, based on spring-boot. In my code I have a manager class UManager in which there is a method save. Inside this method, two actions are taking part. The first one saves some data using another Manager and the second one saves the data given into the save method. Now when one of both actions may fail, both should be undone. I assume, that using the @Transactional annotation would provide what I need, as if one of both actions failed, the whole transaction would be rolled back and no changes would be committed to the database.

@Component
public class UManager {

    @Autowired
    AManager aManager;

    @Autowired
    URepository uRepository;

    ... some stuff happening here

    @Transactional
    public U save(UBuilder uBuilder) {
            aManager.save(something);
            uRepository.save(uBuilder.build());
    }
}

As soon as I add the @Transactional annotation to the save method, the application fails to startup. I get the following error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bController': Injection of autowired dependencies failed;

What am I doing wrong or am I missing out on something?

EDIT: This is the stacktrace:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: UManager BController.uManager; nested exception is java.lang.IllegalArgumentException: Can not set UManager field BController.uManager to com.sun.proxy.$Proxy102
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839) ~[spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) ~[spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) ~[spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:764) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.doRun(SpringApplication.java:357) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:305) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1124) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1113) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at RApplication.main(RApplication.java:44) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_72]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_72]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_72]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_72]
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:467) [spring-boot-maven-plugin-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_72]

EDIT 2: The RApplication:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@SpringBootApplication
@EnableScheduling
@EnableTransactionManagement
public class RApplication {

    public static void main(String[] args) {
        SpringApplication.run(RaumbuchungApplication.class, args); //this is the line mentioned in the stacktrace
    }
}

The UManager:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import de.mischok.hfmw.raumbuchung.data.URepository;
import de.mischok.hfmw.raumbuchung.types.U;
import de.mischok.hfmw.raumbuchung.types.UBuilder;

@Component
public class UManager {

    @Autowired
    URepository uRepository;

    @Transactional
    public U save(UBuilder uBuilder){

    }
}

The BController:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import UManager;

@Controller
@RequestMapping("my/Path")
public class BController {

    @Autowired
    UManager uManager;
}

These are the important parts of the code, as the other parts are not involved in the problem.

daniel
  • 55
  • 1
  • 7

3 Answers3

2

If you are not using xml spring configuration but you have an Application class (I guess RApplication from the stacktrace you posted) which does the configuration make sure to annotate with @EnableTransactionManagement

e.g.:

@Configuration  
@ComponentScan 
@EnableAutoConfiguration  
@EnableJpaRepositories
@EnableTransactionManagement
public class Application { 
     //rest code goes here
}

EDIT The error shows that a proxy object failed, you should autowire an interface of the class (UManager) you want to inject not the class itself.

e.g.

Change your UManager class to implement an interface (and of course declare any public methods of UManager to the interface too)

public class UManager implements IUManager

and in your bController class autowire the interface not the class

public class BController {
    @Autowired
    IUManager uManager;
}
pleft
  • 7,567
  • 2
  • 21
  • 45
  • Thank you for your quick reply. I added the `@EnableTransactionManagement` annotation you recommended to my `RApplication` class, but the only thing different than before is the line in which the error occurs in the `RApplication` (I put both into meld it changed from 44 to 46). Is there something else, you would suggest trying? – daniel Feb 23 '16 at 14:11
  • Can you update your question including the full code of `RApplication`, `UManager` and `BController` ? – pleft Feb 23 '16 at 14:22
  • @elefasGR he is using Spring boot and that already enables everything you try to enable (again) manually. So this will not solve the issue at hand. – M. Deinum Feb 24 '16 at 08:35
  • @M.Deinum: Please see my EDIT on the answer. The OP is trying to inject an implementation class rather than an interface in his `BController` class. If the class he is trying to inject contains `@Transactional` the proxy object will fail with the error message the OP gets. He should autowire an interface and not the class containing the `@Transactional` annotation. – pleft Feb 24 '16 at 08:46
  • From the question it isn't clear if he even uses interfaces as he only posted classes and with that it should work fine. So in the light of the question and the currently provided information that doesn't make sense, next to that you still don't need the `@Enable` annotations as spring boot provides them fore. – M. Deinum Feb 24 '16 at 10:58
  • Thank you for answering my question. After extracting the public methods from `UManager` to `IUManager` and autowiring it in the Controller, the Application is starting up again. – daniel Mar 11 '16 at 11:48
0

You'll have to create a Transaction Manager first like below:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

And then make it annotation-driven by specifying this:

<tx:annotation-driven transaction-manager = "txManager" />

Now you should be able to use @Transactional.

user2004685
  • 9,548
  • 5
  • 37
  • 54
  • Thank you for your fast reply. But how can I do this without having an `beans.xml`? I am using spring-boot and thus I don't have one. – daniel Feb 23 '16 at 13:58
0

The error described looks like it's from another bean attempting to Autowire UManager. I'm not an expert on Spring, but when you put Transactional you made it a DAO type of Component, and so you should declare the bean as a Repository bean instead of the generic Component. There is more on the difference between the various annotations at What's the difference between @Component, @Repository & @Service annotations in Spring?.

Community
  • 1
  • 1
K.Nicholas
  • 10,956
  • 4
  • 46
  • 66