gpt4 book ai didi

mysql - Oracle ADF - 在创建父实体时创建子实体

转载 作者:行者123 更新时间:2023-11-29 06:32:05 25 4
gpt4 key购买 nike

我正在使用 MySQL 数据库构建 ADF Fusion Web 应用程序 (12c)。

为了在插入时获得自动递增的 PK 值,我使用了分配“AutoIncrementProperty”属性集(属性“AI”,值“true”)来自动递增 PK 字段的方法。所有实体都从覆盖 doDML() 的类扩展而来。一切正常,但仅供引用,这是我为此使用的代码:

protected void doDML(int i, TransactionEvent transactionEvent) {
super.doDML(i, transactionEvent);

if (i == DML_INSERT) {
populateAutoincrementAtt();
}
}

/*
* Determines if the Entity PK is marked as an autoincrement col
* and executes a MySQL function to retrieve the last insert id
*/
private void populateAutoincrementAtt() {
EntityDefImpl entdef = this.getEntityDef();
AttributeDef pk = null;
//look for primary key with Autoincrement property set
for (AttributeDef att : entdef.getAttributeDefs()) {
if (att.isPrimaryKey() && (att.getProperty("AI") != null )) {
pk = att;
break;
}
}
if (pk != null) {
try (PreparedStatement stmt =
this.getDBTransaction()
.createPreparedStatement("SELECT last_insert_id()", 1)) {
stmt.execute();
try (ResultSet rs = stmt.getResultSet()) {
if (rs.next()) {
setAttribute(pk.getName(), rs.getInt(1));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

好的,这不是问题。一切正常。

我有两个具有相应 MySQL 表的实体。我们称它们为“人”和“文件夹”。文件夹实体/表是递归的,因为它看起来像这样:

文件夹

  • id(自增 PK 整数)
  • parentId(父文件夹对象的外键)
  • 文件夹名称
  • ...

而且,Person 实体/表看起来像这样:

  • id(自增 PK 整数)
  • 用户名
  • rootFolderId(文件夹表的外键)
  • ...

因此,一个人有一个分配给他/她的根文件夹。 (该文件夹然后可能具有子文件夹的层次结构。)

我遇到的问题是,当我创建一个 Person 实体时,我想创建一个新的 Folder 实体,获取其 PK 值,然后将该 Integer 分配给新 Person 的 rootFolderId 属性。

应该很简单,不是吗?

在 PersonImpl 类(扩展 EntityImpl)中,我有以下方法:

private Integer createRootFolder() {
Integer newIdAssigned;

String entityName = "com.my.model.entity.Folder";
EntityDefImpl folderDef = EntityDefImpl.findDefObject(entityName);
EntityImpl newFolder = (EntityImpl) folderDef.createInstance2(getDBTransaction(), null);
newFolder.setAttribute("folderName", "ROOT");
try {
getDBTransaction().commit();
newIdAssigned = (Integer) newCollection.getAttribute("Id");
} catch (JboException ex) {
getDBTransaction().rollback();
newIdAssigned = null;
}

return newIdAssigned;
}

所以,这个方法很管用。实际上,它确实插入了一个 Folder 对象并返回其 PK 值。问题在于我何时/何地/如何调用此方法。

我可以从 PersonImpl 的 Create 方法中调用它,如下所示:

protected void create(AttributeList attributeList) {
Integer rootFolderId = null;

rootFolderId = createRootFolder();

super.create(attributeList);
this.setRootFolderId(rootFolderID);
}

但是,当然,这会在拥有的 Person 对象提交到数据库之前创建根 Folder 对象。因此,如果 Person 从未提交,我们就会无缘无故地创建一个孤立的 Folder 对象。

我试过从 PersonImpl 类中的 doDML() 调用它,如下所示:

protected void doDML(int operation, TransactionEvent e) {
Integer rootFolderId;

super.doDML(operation, e);

if (operation == DML_INSERT) {
rootFolderId = createRootFolder();
if (rootFolderId != null) {
this.setRootCollectionId(rootCollID);
getDBTransaction().commit();
}
}
}

但是……当然,当 createRootFolder() 调用 commit() 时,这会导致 doDML() 再次触发,我们遇到了一个递归问题。我玩过诸如设置标志以防止递归 doDML() 调用之类的问题,但它们都有问题。而且,我认为一定有一些我忽略的更简单的东西。

如果我能以某种方式在实体本身中以声明方式执行此操作,而无需代码,那将是理想的选择。但是,我认为编码解决方案会非常简单。我只是没有看到它。

有什么话吗?

最佳答案

好的,感谢@MihaiC,我已经解决了这个问题。

正如 MihaiC 所建议的,最好的解决方案是不使用 MySQL 的自动增量。但是,ADF 不支持 SQL92 风格的序列(出于其他原因我必须使用它)。我研究了几种模拟序列的方法,并确定了一种效果很好的混合方法。

借用这篇文章:http://www.sqlines.com/oracle-to-mysql/sequence ,我创建了一个简单的表来存储序列来模拟这种行为。

表:_sequences

  • 名称:PK VARCHAR(存储序列名称)
  • next: INT(存储命名序列中的下一个可用值)
  • inc: INT(存储序列的增量值)

我使用以下值创建了一条记录:

  • name = "Folder_Seq";
  • next = 100(因为我已经有一些低ID号的测试记录)
  • inc = 1(不知道你为什么要使用其他任何东西,但我可以)

然后,只需查找给定命名序列的“next”值,并为下一次调用递增该值即可。为此,我基于 _sequences 表创建了一个 Sequence 实体对象。

这是我在 PersonImpl 类中使用的代码:

protected void doDML(int operation, TransactionEvent e) {
if (operation == DML_INSERT) {
// We do this only when we are ready to commit.
Integer nextSeqVal = getNextFolderSequenceNumber();
createRootFolder(nextSeqVal);
setRootFolderId(nextSeqVal);
// Need to add logic to bail out if something went wrong in the above steps.
}
// Nothing actually gets committed until we reach this line
super.doDML(operation, e);
}

private Integer getNextFolderSequenceNumber() {
SequenceImpl seq = getSequenceByName("Folder_Seq");
Integer nextVal = seq.getNext();
if (nextVal != null) {
seq.setNext(nextVal + seq.getInc());
} else {
// handle this as an error
}
return nextVal;
}

// Looks up the Sequence entity by name
private SequenceImpl getSequenceByName(String seqName) {
EntityDefImpl seqDef = SequenceImpl.getDefinitionObject();
Key seqKey = SequenceImpl.createPrimaryKey(seqName);
return (SequenceImpl) seqDef.findByPrimaryKey(getDBTransaction(), seqKey);
}

// Creates a Folder object with the passed Integer as its PK
private void createRootFolder(Integer pkID) {
String entityName = "com.my.model.entity.Folder";
EntityDefImpl folderDef = EntityDefImpl.findDefObject(entityName);
// I chose to fully-qualify EntityImpl since I have overriden oracle.jbo.server.EntityImpl
oracle.jbo.server.EntityImpl newFolder = folderDef.createInstance2(getDBTransaction(), null);
newFolder.setAttribute("FolderName", "ROOT"); // Because I want all root folders to be named "ROOT"
newFolder.setAttribute("Id", pkID);
}

因此,在 Person 主实体的 doDML() 中,我在提交到数据库之前做了四件事:

  • 获取 Folder_Seq 序列的下一个可用序列号。
  • 增加相应序列实体中的“下一个”值。
  • 创建一个新的 Folder 实体并使用该下一个序列值设置其 PK。
  • 将此值设置为外键“RootFolderId”属性值。

然后,如果以上所有操作都成功,则在调用 super.doDML() 时将所有内容提交到数据库。

也就是说,我相信这个逻辑是合理的。

我有点担心,在多用户环境中,两个用户可能会获得相同的 next-in-sequence 值,从而在第二个人尝试提交时导致问题。如果 ADF 允许以下事件顺序,这将是可能的:

  1. 用户 A 获得“下一个”值……比方说……1001。
  2. 用户 B 得到相同的值,因为用户 A 没有提交他增加的值。
  3. 用户 A 提交所有更改。
  4. 用户 B 在尝试提交时看到错误,因为 PK 1001 现在存在于数据库中。

我不知道 ADF 是否允许一个用户开始 doDML() 而另一个用户正在执行该过程。不关心此应用程序,但如果不做进一步研究,我不会相信它会用于大容量多用户应用程序。

关于mysql - Oracle ADF - 在创建父实体时创建子实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27304809/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com