WildFly: No transaction for EntityManager with @Transactional, @Stateless

296 Views Asked by At

I am currently facing a weird issue with a plain Java EE application on WildFly 25 for which I don't find the root cause. I've been using similar configurations for a few customers and never had any issue with the code. I know for transactions to work, I need to inject everything involved properly, use the @Stateless annotation and work with the @Transactional annotation on methods which need it. But I never had the issue that I just don't get any transaction, and I am somewhat lost right now. The datasource was also configured with JTA set to true.

My repository:

@Stateless
public class DocumentImportLogRepository{
    
    @PersistenceContext(unitName = "AktenimportPU")
    EntityManager em;
    
    public <T> Object find(Class<T> entityClass, Object primaryKey)
    {
        return em.find(entityClass, primaryKey);
    }
    
    public void persist(Object object)
    {
        em.persist(object);
    }
    
    public void forcePersist(Object object)
    {
        em.persist(object);
        em.flush();
    }
    
    public void merge(Object object)
    {
        em.merge(object);
    }
    
    public void forceMerge(Object object)
    {
        em.merge(object);
        em.flush();
    }

    public void remove(Object object)
    {
        em.remove(object);
    }   

is called within the following service class:

@Stateless
public class DocumentImportService 

[...]
    @Inject
    DocumentImportLogRepository importLogRepo;

from several methods all originating from:

@Transactional
public void doImport()
{
    [...]
    readInputFolder(Config.DOCUMENT_IMPORT_FOLDER);
    prepareImport(importLogRepo.getByState(State.PARSED), getPersonalakten());
    performArchive(importLogRepo.getByState(State.PREPARED));
    performArchiveMove(importLogRepo.getByState(State.ARCHIVED));
    [...]
}

which is triggered by a controller:

@Named("StartController")
@ApplicationScoped
public class StartController implements Serializable {

    
    @Inject
    private transient DocumentImportService importService;
    
    [...]
    
    @Transactional
    @TransactionTimeout(value=120, unit = TimeUnit.MINUTES)
    public void performTask(Task task)
    {
        [...]

            switch(task)
            {
                case Personalaktenimport:
                    importService.doImport();
                    break;
            }

        [...]
            
    }

the actual method call failing:

@Transactional
public void readInputFolder(Path inputFolder) throws IOException
{
    [...] importLogRepo.forcePersist(entry); [...]
}

with the exception:

 javax.ejb.EJBTransactionRolledbackException: WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context

persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="AktenimportPU">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
      <jta-data-source>java:jboss/datasources/Aktenimport</jta-data-source>
      <properties>
         <property name="hibernate.hbm2ddl.auto" value="none"/>
         <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
         <property name="hibernate.jdbc.time_zone" value="Europe/Berlin"/>
         
      </properties>
   </persistence-unit>
      
</persistence>
1

There are 1 best solutions below

0
maio290 On

I finally found out what caused this error:

I wasn't really reading the exceptions prior since I thought that they are just commonly caught mistakes in the inputs, but yeah, I had several exceptions caught, however, javax.validation.ValidationException wasn't one of them. The mistake was that the entity had a field BARCODE whose type was an enumeration BARCODE_TYPE which contains a set of predefined values. My intention was to just let it run into an error but continue the import on unknown types, however, if this exception appears it seems to set the transaction into an error state from which the application cannot recover. Removing the @NotNull annotation on the enum field did get rid of the errors.