Java EE JTA 从不事务

Posted

技术标签:

【中文标题】Java EE JTA 从不事务【英文标题】:Java EE JTA Never Transaction 【发布时间】:2018-10-30 20:49:01 【问题描述】:

我正在了解 JTA 事务并正在做 Junit。我遵循了来自 TomEE 示例的示例代码。为了更好地理解服务类,我做了一些更改。

我有 2 个测试用例。 1. 有交易 2. 没有交易

第一个测试用例运行良好。但第二个不是。因为第二种方法以从不启动事务属性。但是服务类的所有方法都需要 TA(事务属性)。删除事务未提交,因为我的测试用例失败。

为什么删除事务没有提交? (即使 delete 方法有 REQUIRED TA )

那么添加事务是如何工作的? (从 GetMovies 方法获取电影)

实体 @实体 公共类电影

    @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;
    


服务类

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();
    

测试类

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>

【问题讨论】:

【参考方案1】:

从非事务性上下文调用:您正在对分离的实例进行 remove。但是您在另一个事务中创建了实体 movie。因此,首先您需要进行合并,使其成为托管实体。

managedMovie = em.merge(movie);

em.remove(managedMovie);

参见 Javadoc:

IllegalArgumentException - 如果实例不是实体或分离实体

【讨论】:

以上是关于Java EE JTA 从不事务的主要内容,如果未能解决你的问题,请参考以下文章

什么是JTA?

转载-Java事务与JTA

java事务相关

Spring的事务机制

JTA 和本地事务有啥区别?

java事务学习笔记--深度剖析JTA原理与实现