为什么我要将事务上下文保留在EJB之外?

时间:2018-02-15 09:44:14

标签: java jpa java-ee jax-rs

我在拥有WildFly 10的POC中有两层。 使用JaxRS的Web服务调用EJB维护@PersistenceContext

它可以处理一系列加密货币的钱包。

每一行都知道它的钱包有@ManyToOne以便快速使用数据库,但业务逻辑说钱包应该知道它的行。

所以我有一个循环依赖:钱包知道它的Line,每一行都知道它的钱包。

@Stateless
public class WalletBusiness {

    // EntityManager is given by Wildfly. It's a managed object
    @PersistenceContext
    EntityManager em;

    public JpaWallet findWallet(int id) {
        // transaction is opened in your back
        JpaWallet w = em.find(JpaWallet.class, id);

        String jpql = "SELECT l FROM JpaLine l JOIN l.wallet w WHERE w.id = :id";

        List<JpaLine> lines = em.createQuery(jpql, JpaLine.class)
                .setParameter("id", id)
                .getResultList();

        w.setLines(lines);

        return w;
    }// and now closed. <=== Argh: is it ???
}

JaxRS Webservice获取电子钱包,然后削减循环依赖。

@GET
@Path("{id}")
public Wallet getWallet(@PathParam("id") int walletId){

    JpaWallet wallet = walletBusiness.findWallet(walletId);
    // I thought that now, I'm out of the transactionnal context

    // Creating a kind of DTO: Data Transfer Object
    // Cutting circular reference
    wallet.getLines().stream()
        .forEach(jpaLine -> jpaLine.setWallet(null));

    return wallet;

}

它按预期显示在网络上,但在剪切依赖关系时,我删除了数据库上的Lines!当我认为在退出EJB方法时关闭了持久化上下文时,客户端就毁了。

我在哪里错过了什么?

以下是两个实体:

@Entity
public class JpaWallet implements Wallet {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    int id;

    String name;

    @Transient // Don't want to save in database. It is a Business attribute, not a database item
    List<JpaLine> lines = new ArrayList<>();

    @Override
    public int getId() {
        return this.id;
    }



    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public List<JpaLine> getLines() {
        return lines;
    }

    public void setLines(List<JpaLine> lines) {
        this.lines = lines;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return this.name;
    }
}


@Entity
public class JpaLine implements Line{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    int id;

    String symbol;

    double quantity;

    @ManyToOne
    JpaWallet wallet;


    @Override
    @XmlAttribute(name = "coin")
    public String getSymbol() {
        return this.symbol;
    }

    @Override
    public double getQuantity() {
        return this.quantity;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setSymbol(String symbol) {
        this.symbol = symbol;
    }

    public void setQuantity(double quantity) {
        this.quantity = quantity;
    }

    public JpaWallet getWallet() {
        return wallet;
    }

    public void setWallet(JpaWallet wallet) {
        this.wallet = wallet;
    }

    @Override
    public String toString() {
        return this.symbol+ ": "+this.quantity;
    }
}

1 个答案:

答案 0 :(得分:0)

这是Java EE 7的一个新功能:

  

JTA为EJB提供事务支持,现在在Java EE 7中为CDI提供管理   用@Transactional注释的bean

Webservice层使用CDI,并且在Webservice中扩展了Transactional上下文。我不喜欢这样:JSF可以生成大量的SQL查询。

我的解决方案是在网络服务中添加注释:

@GET
@Path("{id}")
// CDI is now able to continue the transaction opened by walletBusiness
// Shut it down there
@Transactional(Transactional.TxType.NEVER)
public Wallet getWallet(@PathParam("id") int walletId){

    JpaWallet wallet = walletBusiness.findWallet(walletId);

    // No more propagation. 
    wallet.getLines().stream().forEach(jpaLine -> jpaLine.setWallet(null));

    return wallet;
}