سؤال

It seem like EntityManager is NOT flushing out the changes to the database. What makes this problem harder is that there is no exception thrown. I am using declarative transactions to define my transaction boundaries with the following flow:

SignUpController --> @Transactional: Tx.Start --> AccountServiceImpl.createAccount --> AccountDAOImpl.createAccount --> Tx.Commit --> SignUpController

From my investigation it seems like:

1.Spring is properly defining the Transaction Boundaries using @Transactional. Hence Transaction are being created

2.Also please Note the I am using the SharedEntityManagerBean, which means I CANNOT explicitly define my transaction boundaries e.g:

em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();

The above code results in the following exception: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead

Below is the relevant code and configuration. Any assistance will be greatly appreciated. Thanks.

MainConfig.java:

@Configuration
@ComponentScan(basePackages="com",excludeFilters=                {@Filter(Configuration.class)})
@EnableTransactionManagement
public class MainConfig  {

@Bean
public DataSource dataSource() {
DataSource ds = (DataSource)getFromInitialContext("java:jboss/datasources/PrimaryDB");
return ds;
}

@Bean
public PlatformTransactionManager transactionManager() {
 return new DataSourceTransactionManager(dataSource());
}

@Bean
public EntityManagerFactory entityManagerFactory() {
   EntityManagerFactory emf = (EntityManagerFactory) getFromInitialContext("java:jboss/entityManagerFactory");
return emf;
}

private Object getFromInitialContext(String jndiValue) {
    Context ctx = null;
    Object object = null;
try {
  ctx = new InitialContext();
  object = ctx.lookup(jndiValue);
} catch (NamingException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}
return object;
}
}

persistance.xml:

<persistence-unit name="PrimaryDB" transaction-type="JTA">
    <jta-data-source>java:jboss/datasources/PrimaryDB</jta-data-source>
    <class>com.domain.entities.Account</class>
    <properties>
        <!-- Bind entity manager factory to JNDI at java:jboss/myEntityManagerFactory -->
        <property name="jboss.entity.manager.factory.jndi.name"
            value="java:jboss/entityManagerFactory" />

        <!-- Properties for Hibernate -->
        <property name="hibernate.hbm2ddl.auto" value="create-drop" />
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.use_sql_comments" value="true" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    </properties>
</persistence-unit>

AccountServiceImpl:

@Service
public class AccountServiceImpl implements AccountService {

  @Inject
  AccountDAO accountDAOImpl;

  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public Account createAccount(SignupForm form, BindingResult formBinding) {
               TransactionDebugUtil.transactionRequired("AccountServiceImpl.createAccount");
   Account account =
        new Account(form.getUsername(), form.getPassword(),form.getFirstName(), form.getLastName());
    try {
      accountDAOImpl.createAccount(account);
    } catch (Exception ex) {
      formBinding.rejectValue("username", "user.duplicateUsername",
          "already in use: " + ex.getMessage());
      return null;
    }
    return account;
  }
}

AccountDAOImpl.java:

@Repository
public class AccountDAOImpl extends GenericJpaDAO<Account> implements    AccountDAO {

  @Override
  @PersistenceContext
  public void setEntityManager(EntityManager entityManager) {
   this.entityManager = entityManager;
   entityManager.setFlushMode(FlushModeType.COMMIT);
  }

 @Override
 public void createAccount(Account account) throws     UsernameAlreadyInUseException {
    entityManager.persist(account);
 }
هل كانت مفيدة؟

المحلول

Hi the confusion was caused due to using annotations for configuring the transaction manager rather then XML.

Typical XML Configuration for SpringTransaction:

1.

<tx:annotation-driven /> 

^It scans all beans in the application context and creates AOP interceptor for those which are annotated.This is done via the SpringTransactionAnnotationParser, which is used by TransactionInterceptor.

@EnableTransactionManagement

^This is the equivalent configuration bean annotation to allow transactional support

2 . JTA datasource is defined in the persistence.xml. JBoss 7 automatically creates a JTA Transaction Manager and binds it to the following JNDI Location: java:/TransactionManager by default. This Transaction Manager is automatically discovered in spring with the following xml configuration:

<tx:jta-transaction-manager />

The equivalent annotation configuration for this is:

@Bean
public PlatformTransactionManager transactionManager() {
   JtaTransactionManager txManager = new JtaTransactionManager();
   return txManager;
}

This resolved my issue. Thanks @geoand for your help.

نصائح أخرى

this kind of behaviour usually happens when we dont have @transactional on the persistent method.

so add @Transactional on top of method like below

@Override
@Transactional
 public void createAccount(Account account) throws     UsernameAlreadyInUseException {
    entityManager.persist(account);
 }

You are not handling transaction management correctly. Replace your transaction manager bean with:

@Bean
public PlatformTransactionManager transactionManager() {
   final JpaTransactionManager txManager = new JpaTransactionManager();
   txManager.setEntityManagerFactory(entityManagerFactory());
   return txManager;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top