I am understanding JTA transaction and doing Junit. I have followed sample code from TomEE examples. I have done few changes for better understandin in service class.
I have 2 test cases. 1. With transaction 2. Without Transaction
First test case is working good. But second one is not. Because second one method starts Transaction Attribute with Never. But Service class' all methods have TA(Transaction Attribute) REQUIRED. Delete transaction is not committed, because of that my test case is failed.
Why delete transaction is not committed ? ( even though delete method has REQUIRED TA )
then How Add transaction is working ? ( getting movies from GetMovies method )
Entity @Entity public class Movie {
@Id
private long movieId;
private String title;
private String director;
private int year;
public Movie() {
}
public Movie(String director, String title, int year, long id) {
this.director = director;
this.title = title;
this.year = year;
this.movieId = id;
}
public String getDirector() {
return director;
}
public void setDirector(String director) {
this.director = director;
}
public String getTitle() {
return title;
}
public long getMovieId() {
return movieId;
}
public void setTitle(String title) {
this.title = title;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
}
Service Class
package com.demo.ex.service;
import com.demo.ex.entity.Movie;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import java.util.List;
@Stateless
public class MovieService {
@PersistenceContext(unitName = "movie-unit", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public void addMovie(Movie movie) {
System.out.println(" Add Movie "+movie.getTitle() +" "+movie.getMovieId());
em.persist(movie);
}
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public void deleteMovie(Movie movie) {
em.remove(movie);
System.out.println(" Delete Movie "+movie.getTitle()+" "+movie.getMovieId());
}
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public List<Movie> getMovies()throws Exception {
System.out.println(" Get Movie ");
return em.createQuery("select m from Movie as m").getResultList();
}
}
Test Class
package com.demo.ex;
import com.demo.ex.entity.Movie;
import com.demo.ex.service.MovieService;
import junit.framework.TestCase;
import javax.ejb.*;
import javax.ejb.embeddable.EJBContainer;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;
public class MovieTest extends TestCase {
@EJB
private MovieService movieService;
@EJB(beanName = "TestTransaction")
private Caller transactionCaller;
@EJB(beanName = "TestNoTransaction")
private Caller noTransactionCaller;
protected void setUp() throws Exception {
final Properties p = new Properties();
p.put("movieDatabase", "new://Resource?type=DataSource");
p.put("movieDatabase.JdbcDriver", "org.hsqldb.jdbcDriver");
p.put("movieDatabase.JdbcUrl", "jdbc:hsqldb:mem:moviedb");
EJBContainer.createEJBContainer(p).getContext().bind("inject", this);
}
@Override
protected void tearDown() throws Exception {
transactionCaller.call(new Callable<Object>() {
@Override
public Object call() throws Exception {
System.out.println(" Tear Down Action.................");
for (final Movie m : movieService.getMovies()) {
System.out.println(" Teardown delete :"+m);
movieService.deleteMovie(m);
}
System.out.println("After cleanup movie count="+movieService.getMovies().size());
return null;
}
});
}
private void doWork() throws Exception {
movieService.addMovie(new Movie("Quentin Tarantino", "Reservoir Dogs", 1992,1));
movieService.addMovie(new Movie("Joel Coen", "Fargo", 1996,2));
movieService.addMovie(new Movie("Joel Coen", "The Big Lebowski", 1998,3));
List<Movie> list = movieService.getMovies();
System.out.println(" Movie Serivce List :::"+list.size());
assertEquals("List.size()", 3, list.size());
for (Movie movie : list) {
movieService.deleteMovie(movie);
}
System.out.println(" ???????????????????? SIZE:"+movieService.getMovies().size());
assertEquals("Movies.getMovies()", 0, movieService.getMovies().size());
}
public void testWithTransaction() throws Exception {
transactionCaller.call(() -> {
doWork();
return null;
});
}
public void testWithoutTransaction() throws Exception {
try {
noTransactionCaller.call(() -> {
doWork();
return null;
});
} catch (EJBException e) {
e.printStackTrace();
}
}
public static interface Caller {
public <V> V call(Callable<V> callable) throws Exception;
}
@Stateless
@TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW)
public static class TestTransaction implements Caller {
@Override
public <V> V call(Callable<V> callable) throws Exception {
return callable.call();
}
}
@Stateless
@TransactionAttribute(value = TransactionAttributeType.NEVER)
public static class TestNoTransaction implements Caller {
@Override
public <V> V call(Callable<V> callable) throws Exception {
return callable.call();
}
}
}
Persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="movie-unit">
<jta-data-source>movieDatabase</jta-data-source>
<non-jta-data-source>movieDatabaseUnmanaged</non-jta-data-source>
<class>com.demo.ex.entity.Movie</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
<property name="openjpa.Log" value="SQL=TRACE" />
<property name="openjpa.ConnectionFactoryProperties"
value="printParameters=true"/>
</properties>
</persistence-unit>
</persistence>
Calling from a nontransactional context: You are doing remove with a detached instance. But you created the entity movie in another transaction. Therefore first you need to do a merge, to make it a managed entity.
managedMovie = em.merge(movie);
em.remove(managedMovie);
See Javadoc:
IllegalArgumentException - if the instance is not an entity or is a detached entity