Java EntityManager.merge()不创建表记录
以下代码无法可靠地创建新记录Java EntityManager.merge()不创建表记录,java,hibernate,jpa,Java,Hibernate,Jpa,以下代码无法可靠地创建新记录 TransferRecord transfer = new TransferRecord(); transfer.setTransferId(metaData.getTransferId()); transfer.setUserName(metaData.getUserId().getUserName()); transfer.setCancelled(false); em.merge(transfer); em.flush(); 以下是转账记录的代码: /***
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()
调用等待,直到它写出记录后再返回?问题的附带注释说明-
“调用bean是无状态的,执行时间长达数秒。在bean的操作结束之前,数据不会显示给其他数据库用户。”
所描述的行为与刷新与EntityManager关联的持久性上下文无关。它与事务相关联的事务隔离级别有很大关系。与无状态会话bean(SLSB)关联的事务实际上只在从bean方法返回时才将数据提交到数据库,因为每个SLSB方法可能与默认事务属性REQUIRED
(使用现有事务或启动新事务)相关联;由此产生的行为是通过SLSB方法中的回滚或通过该方法返回时的提交来终止事务
此行为会影响其他客户端和事务执行的读取,因为结果取决于当前相关事务的隔离级别,而当前相关事务的隔离级别又取决于在数据库连接池上指定的隔离级别。对于大多数数据库和关联的连接池,事务隔离级别恰好是读取提交的
,因此其他事务只能读取相关事务提交的数据(即在SLSB方法返回时)。在大多数情况下,这是理想的行为,因为您不希望读取未提交(并且可能稍后回滚)的数据,这可能导致脏读
如果确实要执行脏读,则必须将JDBC连接池配置为允许脏读,或者换句话说,将池的事务隔离级别设置为readuncommitted
。配置更改会因容器而异,并且还将取决于数据库对readuncommitted
隔离级别的支持;例如,Oracle不允许您将隔离级别设置为读取未提交的
,并可能将其设置为它支持的下一个更高的隔离级别(读取已提交的
)
如果您希望避免事务隔离级别的混乱,请考虑在多个事务之间拆分方法调用。您可以通过在单独的SLSB中创建一个新的业务方法来实现这一点,该方法要求在每次调用时创建一个新事务(
requires\u 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
}
}
当然,只有当交易的业务性质允许采用这种方法时,才建议采用这种方法。通常,必须将属于业务交易一部分的所有业务活动范围界定为实际交易。您的一些陈述相当模糊。例如,“因此它需要数据立即显示在表中。”从事务的角度来看,这没有什么意义
em.flush()
将导致持久性上下文被刷新到数据库中;不同事务中这些更改的可见性取决于事务隔离级别。因此,陈述你想要的行为是值得的。另外,“有没有办法强迫em.flush()调用在返回之前等待它写出记录?”也没有意义,因为这是一个阻塞调用。您使用的是什么类型的事务,以及检查“TransferRecord是否存在”的代码在哪里运行?代码在另一个bean调用的bean中运行。调用bean是无状态的,执行时间长达数秒。在bean的操作结束之前,数据不会显示给其他数据库用户。