以下代码无法可靠地创建新记录。
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()
调用等到它在返回之前写出记录?
答案 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
}
}
当然,只有当交易的业务性质允许采用这种方法时,才建议采用这种方法。通常,必须将属于业务事务的所有业务活动范围限定为实际事务。