EntityManager.merge()不创建表记录

时间:2011-07-07 05:36:58

标签: java hibernate jpa

以下代码无法可靠地创建新记录。

TransferRecord transfer = new TransferRecord();
transfer.setTransferId(metaData.getTransferId());
transfer.setUserName(metaData.getUserId().getUserName());
transfer.setCancelled(false);
em.merge(transfer);
em.flush();

以下是转移记录的代码:

/***
 * Contains a record of an ongoing transfer.
 * @author anchapma
 *
 */
@Entity
@Table(name = "TransferRecord")
public class TransferRecord implements Serializable
{
    /**
     * Generated static unique ID.
     */
    private static final long serialVersionUID = -8131586518447878482L;

    /***
     * The id of the transfer.
     */
    private String transferId;

    /***
     * The name of the user who ownes the transfer.
     */
    private String username;

    /***
     * Has this transfer been cancelled?
     */
    private boolean cancelled;

    /***
     * Constructor.
     */
    public TransferRecord()
    {
        this.transferId = "";
        this.username = "";
        this.cancelled = false;
    }

    /***
     * Gets the transfer ID.
     * 
     * @return The transfer ID.
     */
    @Id
    @Column(name = "TransferID", unique = true, nullable = false, length = 50)
    public String getTransferId()
    {
        return this.transferId;
    }

    /***
     * Sets the transfer ID.
     * @param transferId The new transfer ID.
     */
    public void setTransferId(String transferId)
    {
        this.transferId = transferId;
    }

    /***
     * Gets the username.
     * 
     * @return The username.
     */
    @Column(name = "UserName", nullable = false, length = 50)
    public String getUserName()
    {
        return this.username;
    }

    /***
     * Sets the username.
     * @param username The new username.
     */
    public void setUserName(String username)
    {
        this.username = username;
    }

    /***
     * Gets whether or not the transfer has been cancelled.
     * 
     * @return True if the transfer has been cancelled.
     */
    @Column(name = "Cancelled", nullable = false, length = 50)
    public boolean getCancelled()
    {
        return this.cancelled;
    }

    /***
     * Sets whether or not the transfer has been cancelled.
     * @param cancelled True if the transfer has been cancelled.
     */
    public void setCancelled(boolean cancelled)
    {
        this.cancelled = cancelled;
    }
}

我认为发生的事情是延迟后记录被添加到数据库中。我的代码使用TransferRecord记录的存在或不存在作为标志。因此,它需要立即在表格中显示数据。

我的假设可能是正确的吗?如果是这样,有没有办法强制em.flush()调用等到它在返回之前写出记录?

1 个答案:

答案 0 :(得分:1)

随后对该问题的评论指出 -

  

“调用bean是无状态的并且执行很多秒。在bean的操作结束之前,数据不会显示给其他数据库用户。”

此描述的行为与刷新与EntityManager关联的持久性上下文无关。它与事务关联的事务隔离级别有很大关系。与无状态会话bean(SLSB)相关联的事务实际上只在从bean方法返回时将数据提交到数据库,因为每个SLSB方法可能与REQUIRED的默认事务属性相关联(使用现有事务)或者开始一个新的);产生的行为是通过SLSB方法中的回滚或通过方法返回时提交来终止事务。

此行为会影响其他客户端和事务执行的读取,因为生成的结果取决于当前相关事务的隔离级别,而后者又取决于数据库连接池上指定的隔离级别。对于大多数数据库和关联的连接池,事务隔离级别恰好是READ COMMITTED,因此其他事务只能读取相关事务提交的数据(即,在SLSB方法返回时)。在大多数情况下,这是理想的行为,因为您不希望读取未提交的数据(以后可能会回滚),从而导致脏读的可能性。

如果确实打算执行脏读,则必须配置JDBC连接池以允许脏读,或者换句话说,将池的事务隔离级别设置为READ UNCOMMITTED。配置更改因容器而异,并且还受数据库对READ UNCOMMITTED隔离级别的支持;例如,Oracle不允许您将隔离级别设置为READ UNCOMMITTED,并且可以将其设置为其支持的下一个更高的隔离级别(READ COMMITTED)。

如果您想避免使用事务隔离级别,请考虑在多个事务中拆分方法调用。您可以通过在单独的SLSB中创建新的业务方法来实现此目的,该方法需要在每次调用(REQUIRES_NEW)上创建新事务。伪代码示例如下所述:

@Stateless
@Local(X.class)
public class SLSBX implements X {

    @EJB Y y;

    @TransactionAttribute(TransactionAttributeType.REQUIRED) // start a new transaction to do work
    public void sampleMethod() {
         // suspends the existing transaction on invoking Y.anotherSampleMethod()
         Y.anotherSampleMethod();
         // continue doing work in the current transaction. Work done in anotherSampleMethod() would have either committed or rolled back.
    }
}

@Stateless
@Local(Y.class)
public class SLSBY implements Y {
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) // Suspends the existing transaction and creates a new transaction that terminates on method return
    public void anotherSampleMethod() {
        // do some stuff
    }
}

当然,只有当交易的业务性质允许采用这种方法时,才建议采用这种方法。通常,必须将属于业务事务的所有业务活动范围限定为实际事务。