Search code examples
mysqlhibernatejakarta-eetransactionswildfly-8

@transactional, wildfly, mysql(innodb) ejb not rolling back


I know this question has been asked many times before but i couldn't find any answer to my problem.

I have a Webapp that uses wildfly 8 as container. The problem is that if I throw an exception within my ejb the transaction is not rolled back.

I know if I annotate my ejb with @stateless and @localbean it is per default transactional.

I have tried the following things:

  • checked if the db does roll back a transaction if i write sql code in the workbench

    begin;
    INSERT INTO `bookservicewebapp`.`author` (`firstname`, `lastname`) VALUES ('firstname1', 'lastname1');
    SELECT * FROM bookservicewebapp.author;
    INSERT INTO `bookservicewebapp`.`author` (`firstname`, `lastname`) VALUES ('firstname2', 'lastname2');
    rollback; //worked!
    
  • turn off autocommit in mysql //in my.ini

  • check that innodb is used
  • annotate the method with @Transactional(rollbackOn = Exception.class) myself
  • throw an exception that extends runtime exception and is annotated with @ApplicationException(rollback=true)
  • exceptions are not catched at all
  • I have tried to provoke a timeout with @TransactionTimeout(unit=TimeUnit.SECONDS,value=5) and wait 6 seconds within the method.

The code under discussion is:

    public List<Long> saveBooks(List<Book> books){
        List<Long> savedBooksIds = new ArrayList<>(books.size());
        for (Book book : books) {
            // are these authors in the db?
            List<Author> fetchedAuthors = new ArrayList<Author>();
            if (book.getAuthors() == null || book.getAuthors().size() == 0) {
                throw new RuntimeException("no authors");
            }

            for (Author a : book.getAuthors()) {
                //select all authors with given attributes
                List<Author> buf = getAuthorsByStuff(a.getFirstname(),
                        a.getLastname(), a.getBirthday());
                if (buf.size() != 1) {
                    throw new RollbackException("author " + a.getFirstname()
                            + " does not exist");
                } else {
                    fetchedAuthors.add(buf.get(0));
                }
            }
            book.setAuthors(fetchedAuthors);
            // is the publisher in the db?
            if (book.getPublisher() != null) {
                Publisher pub = book.getPublisher();
                //select all publishers with given attributes
                List<Publisher> buf = getPublishersByStuff(pub.getName(),
                        pub.getPostcode(), pub.getCountrycode());
                if (buf.size() != 1) {
                    throw new RollbackException("publisher " + pub.getName()
                            + " does not exist");
                } else {
                    book.setPublisher(buf.get(0));
                }
            }
            //em.persist(book);
            savedBooksIds.add(em.merge(book).getId());
        }
        return savedBooksIds;
    }

The selection code:

    public List<Author> getAuthorsByStuff(String firstname, String lastname,
            Date date) {
        return em.createNamedQuery("Author.getAuthorsByStuff", Author.class)
                .setParameter("firstname", firstname)
                .setParameter("lastname", lastname)
                .setParameter("birthday", date).getResultList();
    }

Solution

  • The error was that wildfly was misconfigured. JTA had to be enabled and a isolationlevel has to be configured.

         <datasource jta="true" jndi-name="java:jboss/datasources/BookServiceWebAppDS" pool-name="BookServiceWebAppDS" enabled="true" use-ccm="false">
             <connection-url>jdbc:mysql://localhost:3306/bookservicewebapp</connection-url>
             <driver-class>com.mysql.jdbc.Driver</driver-class>
             <driver>mysql</driver>
             <transaction-isolation>TRANSACTION_REPEATABLE_READ</transaction-isolation>
             <security>
                 <user-name>username</user-name>
                 <password>password</password>
             </security>
             <validation>
                 <validate-on-match>false</validate-on-match>
                 <background-validation>false</background-validation>
             </validation>
             <statement>
                <share-prepared-statements>false</share-prepared-statements>
             </statement>
         </datasource>