arjun kumar
arjun kumar

Reputation: 193

Spring + Jboss7 @Transactional Not working

I am upgrading my JBoss server from 5 to 7 and am now incorporating Spring 4. I am having some trouble using Spring's @Transactional annotation. It does not appear to be working. I am also trying to use a java based configuration file instead of an xml file (I believe I can get away without using any xml, but correct me if I am wrong). The problem is that nothing is being saved in my db, leading me to believe that the @Transactional isn't working. Here is my config file:

@Configuration
@ComponentScan
@EnableTransactionManagement
public class SpringBeanConfiguration {

@Bean
public FirstTestBean firstTestBean() {
    return new FirstTestBean();
}

@Bean
public TestService testService() {
    return new TestServiceImpl();
}

@Bean 
public SomethingDAO somethingDAO(){
    return new SomethingDAOImpl();
}

@Bean 
public GeneralDAO generalDAO(){
    return new GeneralDAOImpl();
}

Here is a test class with the @Transactional method:

//@RequestScoped
@ManagedBean(name="firstTestBean")
@Component
public class FirstTestBean {

private EntityManager em;
private EntityManagerFactory emf;


@Transactional
public String transactionalTest() {
    //ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
    Something something = new Something();
    getEntityManager().persist(something);
    return "dkljs";
}


public EntityManager getEntityManager() {
    if (em == null) {
        emf = Persistence.createEntityManagerFactory("xxx");
        em = emf.createEntityManager();
    }
    return em;
}

I am also using Hibernate 4, which is compatible with Spring. I commented out the ApplicationContext because I have that running separately on the start up of JBoss. I was using that earlier to access a bean, but I have since simplified things in order to get the @Transactional working and thus do not need it here. The @ComponentScan does not need parameters because these classes are in the same package.

Any help would be greatly appreciated. Thanks!

Updates

I've made some of the changes suggested. Things appear to be moving in the right direction.

Here are my updated files:

@ManagedBean(name="firstTestBean")
public class FirstTestBean {

public String getTestString() {     
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
    TestService testService = context.getBean(TestService.class);       


    testService.transactionalTest();
    return "adfs";
}

public String test() {
    return "firstTestBean works";
}   
}

Note - some of these classes will be run outside of the Jboss application server as standalone applications, so for that reason, I am staying away from FacesContext when instantiating the TestService in FirstTestBean, as Spring beans work in standalone, but FacesContext beans do not.

@Component
@Transactional
public class TestServiceImpl implements TestService {

public GeneralDAO generalDAO;

//@Autowired
private EntityManager em;

//@Autowired
private EntityManagerFactory emf;


public TestServiceImpl(){}



public String transactionTest() {
    Something something = new Something();
    getEntityManager().persist(something);
    return "dkljs";
}

@autowired on the EntityManager and EntityManagerFactory did not work - I received an error saying No Qualified Bean of type EntityManager when it was annotated with the @autowired like suggested.

@Configuration
@ComponentScan
@EnableTransactionManagement
public class SpringBeanConfiguration implements     TransactionManagementConfigurer {



@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
    String hibernatePropsFilePath = "/[path]/hibernate.cfg.xml";
    File hibernatePropsFile = new File(hibernatePropsFilePath);

    org.hibernate.cfg.Configuration cfg = new org.hibernate.cfg.Configuration().configure(hibernatePropsFile);
    SessionFactory sessionFactory = cfg.buildSessionFactory();

    HibernateTransactionManager txManager = new HibernateTransactionManager(sessionFactory);
    txManager.setNestedTransactionAllowed(true);
    return txManager;
}

}

The error I am getting now is:Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.event.internalEventListenerProcessor': Initialization of bean failed ; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration': Injection of autowired dependencies failed; nested exception is org.hibernate.service.UnknownUnwrapTypeException: Cannot unwrap to requested type [javax.sql.DataSource]

I take it this means that the @Transactional is at least being recognized, but I'm having some issues getting this to work. Any further advice would be greatly appreciated.

More Updates

Found this article: http://www.baeldung.com/the-persistence-layer-with-spring-and-jpa#javaconfig

and followed it. My new config file:

@Configuration
@ComponentScan
@EnableTransactionManagement
public class SpringBeanConfiguration { //implements  TransactionManagementConfigurer {

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
      LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
      em.setDataSource(dataSource());
      em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });

      JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
      em.setJpaVendorAdapter(vendorAdapter);
   //   em.setJpaProperties(additionalProperties());

      return em;
   }

   @Bean
   public DataSource dataSource(){
      DriverManagerDataSource dataSource = new DriverManagerDataSource();
      dataSource.setDriverClassName("com.mysql.jdbc.Driver");
      dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
      dataSource.setUsername( "root" );
      dataSource.setPassword( "root" );
      return dataSource;
   }

   @Bean
   public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(emf);

      return transactionManager;
   }

   @Bean
   public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
      return new PersistenceExceptionTranslationPostProcessor();
   }

Good news is that I am no longer having deployment issues. The logging also suggests that my changes are having some effect on the server. Unfortunately, nothing is being saved, so it is still not quite working.

Upvotes: 1

Views: 1513

Answers (2)

arjun kumar
arjun kumar

Reputation: 193

Ok, so I finally got it (Check out my OP for all updates that led me to this point).

Here's my final config file:

@Configuration
@ComponentScan
@EnableTransactionManagement
public class SpringBeanConfiguration { 

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
      LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
      em.setPersistenceUnitName("myPersistenceContext");
      em.setDataSource(dataSource());
      em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });

      JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
      em.setJpaVendorAdapter(vendorAdapter);
   //   em.setJpaProperties(additionalProperties());

      return em;
   }

   @Bean
   public DataSource dataSource(){
      DriverManagerDataSource dataSource = new DriverManagerDataSource();
      dataSource.setDriverClassName("com.mysql.jdbc.Driver");
      dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
      dataSource.setUsername( "root" );
      dataSource.setPassword( "root" );
      return dataSource;
   }

   @Bean
   public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
      JtaTransactionManager transactionManager = new JtaTransactionManager();
     // transactionManager.setEntityManagerFactory(emf);

      return transactionManager;
   }

   @Bean
   public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
      return new PersistenceExceptionTranslationPostProcessor();
   }

So first, I added in

em.setPersistenceUnitName("myPersistenceContext");  

This gave me the error:

Spring IllegalStateException: A JTA EntityManager cannot use getTransaction()

From here, I did some research (Spring IllegalStateException: A JTA EntityManager cannot use getTransaction())

and changed

JpaTransactionManager transactionManager = new JpaTransactionManager();

to

JtaTransactionManager transactionManager = new JtaTransactionManager();

Also, my persistence.xml file is:

    <persistence-unit name="myPersistenceContext">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:jboss/datasources/myDS</jta-data-source>
    <class>xxx.xxx.xxx.Something</class>    
    <exclude-unlisted-classes>false</exclude-unlisted-classes>

    <properties>
        <property name="hibernate.cache.use_second_level_cache" value="true" />
        <property name="hibernate.id.new_generator_mappings" value="false"/>
        <property name="hibernate.classloading.use_current_tccl_as_parent" value="false"/>
        <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" /> 
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.archive.autodetection" value="class, hbm" />
    </properties>
</persistence-unit>

Thanks to Maciej Kowalski for your help - you pushed me in the right direction.

Upvotes: 0

Maciej Kowalski
Maciej Kowalski

Reputation: 26492

The first thing that draws my attention is the use of @Transactional and @Component on a ManagedBean.

JSF and Spring are definately made to work together but i never saw them used this way on many projects i was working on.

Im not sure if that is the cause of your problems but please consider changing that.

I would do like this:

a) Define some service layer where you would wrap your calls with transactions and inject the JPA classes:

@Component
@Transactional
class Service{

   @Autowired
   private EntityManager em;

   @Autowired
   private EntityManagerFactory emf;

   public String serviceMethod(..){ .. }

}

b) Inject that to Jsf's ManagedBean while removing the unnecessary annotations:

@ManagedBean(name="firstTestBean")
public class FirstTestBean {

  @ManagedProperty("#{service}")
  private Service service;

  public String transactionalTest() {
      return service.serviceMethod(); 
  }

}

Upvotes: 2

Related Questions