EDIT: Thanks everybody for your answers, but the problem was with my data source configuration, which was actually in auto-commit mode. See my answer below for details.
Both the Javadoc of the EntityManager.flush() method and searching for it in Google seem to suggest that the flush
method only sends the pending statements to the database and does not commit the transaction. But a simple test web service I created (in Java 7, Oracle 11gR2, JBoss 7.1 and the Web service is packaged as a jar file) seem to indicate otherwise:
This is the table creation script:
CREATE TABLE test(
id INTEGER NOT NULL,
name VARCHAR2(20),
CONSTRAINT test_pk PRIMARY KEY ("ID")
);
CREATE SEQUENCE test_seq;
This is the corresponding entity:
@Entity @Table(name = "TEST")
public class Test implements Serializable {
private static final long serialVersionUID = 9192814682033048425L;
@Id @Column(name = "ID")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEST_SEQ")
@SequenceGenerator(name="TEST_SEQ",sequenceName="TEST_SEQ", allocationSize = 1)
private Integer id;
@Column(name = "NAME")
private String name;
// Getters and setters...
}
And the test web service:
@Stateless @WebService(serviceName = "TestService")
@TransactionManagement(TransactionManagementType.CONTAINER)
public class TestServiceBean implements TestService {
@PersistenceContext
private EntityManager entityManager;
@Override
public void createTest(String name) {
Test test = new Test();
test.setName(name);
entityManager.persist(test);
entityManager.flush();
throw new RuntimeException();
}
}
My understanding is that:
createTest
method is called, the application starts a new transactionpersist()
method generates an INSERT statement to be sent to the databaseflush()
method sends the INSERT statement to the database but does not commit the transaction!
But obviously my understanding is wrong: every time I run the web service method I get one new row in the table. Moreover, stepping into this method with a debugger reveals that the row is inserted when the flush()
method is called (I can "see" the row from another db session using SQL Developer).
Can someone please explain this behavior?
So, when you call EntityManager. flush() , queries for inserting/updating/deleting associated entities are executed in the database.
JPA also defines a COMMIT flush mode, which is described as follows: If FlushModeType. COMMIT is set, the effect of updates made to entities in the persistence context upon queries is unspecified. When executing a JPQL query, the persistence context is only flushed when the current running transaction is committed.
Commit a transaction by calling the commit() method on the Connection interface. This tells your database to perform all required consistency checks and persist the changes permanently. Rollback all operations performed during the transaction by calling the rollback() method on the Connection interface.
To save data in database permanently JPA needs to insert data using insert/update/delete statements and then commit this data. Committing a transaction is always necessary. Flush() method executes only insert/update/delete statements without commiting the data, so the transaction and data can be rolled back.
It seems that there is nothing wrong with flush()
after all. The problem was that I didn't set up the data source correctly in JBoss. The lesson here is that if you want to use container managed transactions in EBJs then you need to:
Additionally, and in order to clear any confusion, the transaction management in my code is correct. Throwing a RuntimeException
does rollback the Exception. Why is that? Well, from the Java EE 6 tutorial we have:
if a system exception is thrown, the container will automatically roll back the transaction.
But what is a system exception? The tutorial does not seem to touch upon the subject any further, so lets search the EJB spec. In page 382 we have:
A system exception is an exception that is a java.rmi.RemoteException (or one of its sub- classes) or a RuntimeException that is not an application exception.
OK, so maybe the RuntimeException is an application exception then? No it is not, because in page 380 we have this:
Application exceptions that are checked exceptions may be defined as such by being listed in the throws clauses of the methods of the bean’s business interface, no-interface view, home interface, component interface, and web service endpoint. An application exception that is an unchecked exception is defined as an application exception by annotating it with the ApplicationException metadata annotation, or denoting it in the deployment descriptor with the application-exception element.
So, because I didn't do any of the things listed above, the exception I throw in my code is indeed a system exception and indeed rolls back the transaction if you have set up your data source to use JTA.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With