如何用 JPA 覆盖 OneToMany 映射?
How do you overwrite a OneToMany mapping with JPA?
我在 JPA 中有一个一对多映射,如下所示:
在我的区块链中 class 我在 ArrayList(这是“链”属性)上有 @OneToMany 注释,我有一个块 class.
我有一种方法可以在广播新链或在线上广播新块时用另一个区块链实例的链替换(例如,由异步侦听器触发的 pubsub)。如果发现有效且长度足够,该方法将替换链。
链在数据库中反映为区块链和区块之间的连接 table。当一个新的链条出现时,它几乎是相同的。换句话说,如果只是创世块,将至少与主键发生 1 次冲突。除了尖端的一个或几个块之外,其他所有块更有可能发生碰撞,因此我希望能够毫无意外地处理这个问题。理想情况下,JPA 会想出如何在没有我的情况下使用以下代码来完成它,但事实并非如此。
@Override
public boolean replaceChain(String name, ArrayList<Block> new_chain) throws NoSuchAlgorithmException, ChainTooShortException, GenesisBlockInvalidException, BlocksInChainInvalidException {
this.connect();
em.getTransaction().begin();
Query query = em.createQuery("select b from Blockchain b where b.instance_name = :name");
query.setParameter("name", name);
Blockchain blockchain = (Blockchain) query.getSingleResult();
blockchain.replace_chain(new_chain);
em.getTransaction().commit();
this.disconnect();
return true;
}
从那里我尝试了很多我能想到的排列和技巧。我尝试手动删除块实体中重复的每个块,但随后连接 table 出现问题,堆栈溢出表示显然 JPA 未设置为手动管理。这不是面向对象的方式。我对此很好,但我的问题是什么是 OOP 方式。只是为了覆盖一对多的关系。新传入的 OneToMany 应该覆盖所有内容,取代其他所有内容,仅此而已,但它会尝试复制。我阅读了其他 SO 帖子,但我对它们的理解不够好,或者它们没有帮助。
我 运行 通过 DAO 服务,通过 pubnub 侦听器回调连接工作。我有两台服务器,实际上是代码库 运行- 这个主要的“网络节点”正在处理数据库(端口 8080),还有一个位于 9080 上的“内存中”节点,它仅从创世块开始,如果它收到一个 200 GET 请求到 8080 将克隆它并替换那个链。替换链工作 - 只是不写入数据库。我将网络上的第二个节点称为 PEER 实例。它具有挖掘块的能力,当它这样做时,它会广播到触发主节点的 pubsub。那是我的设置。除了其中的 JPA 部分外,一切似乎都运行良好。我正在使用 Eclipselink 和 Tomcat.
根据我的理解,当您开始使用 entitymanager 进行交易时,它基本上会观察您所做的事情并做笔记并记录结果并发挥其魔力,有点像抄写员或观察员。你设置它观看,然后你做你的事情,然后你告诉它提交,它仍然有处理的限制和约束,否则会抛出异常,但这是我的理解,也是我最初走的路。
这是我上面这段代码的错误日志,而不是尝试手动删除给定链的块。我可以这样做,但我无法加入 table,而且我知道这不是理想的方式
Error Code: 1062
Call: INSERT INTO block (TIMESTAMP, DATA, DIFFICULTY, HASH, LASTHASH, NONCE) VALUES (?, ?, ?, ?, ?, ?)
bind => [1617166967254, [B@5ebbe21e, 9, 002057d17e0de9c5f97f6a0f3e7534c0599036ae307ece2ee3f645025c153f80, 007e833b320c58bcf29096e22ced52a5c90c915e23830eeae0a7093290af4080, 246]
Query: InsertObjectQuery(privblock.gerald.ryan.entity.Block@d6f817c0)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:157)
at privblock.gerald.ryan.dao.BlockchainDao.replaceChain(BlockchainDao.java:97)
at privblock.gerald.ryan.service.BlockchainService.replaceChainService(BlockchainService.java:38)
at pubsub.PubNubSubCallback.message(PubNubSubCallback.java:132)
at com.pubnub.api.managers.ListenerManager.announce(ListenerManager.java:61)
at com.pubnub.api.workers.SubscribeMessageWorker.processIncomingPayload(SubscribeMessageWorker.java:228)
at com.pubnub.api.workers.SubscribeMessageWorker.takeMessage(SubscribeMessageWorker.java:83)
at com.pubnub.api.workers.SubscribeMessageWorker.run(SubscribeMessageWorker.java:74)
at java.base/java.lang.Thread.run(Thread.java:832)
感谢任何帮助或见解!
我明白了。它似乎可以使用一个额外的注释 属性 orphanRemoval=true
如下
@OneToMany(targetEntity = Block.class, cascade = CascadeType.PERSIST, orphanRemoval=true)
@JoinTable(name = "BlocksByChain")
List<Block> chain; // The chain itself
我知道它必须是简单的并且一些已经存在的特性。这不是框架默认值
编辑:不完全是。不完美。我仍然需要在 DAO 中使用代码将其清除,这很笨重而且不是最优的。我还得到有关死锁或其他内容的控制台输出。我之前没有注意到这一点,因为我的应用程序按预期工作,但我知道必须有更好的方法。
此代码也必须存在才能工作:
@Override
public boolean replaceChain(String name, ArrayList<Block> new_chain) throws NoSuchAlgorithmException,
ChainTooShortException, GenesisBlockInvalidException, BlocksInChainInvalidException {
this.connect();
em.getTransaction().begin();
Query query = em.createQuery("select b from Blockchain b where b.instance_name = :name");
query.setParameter("name", name);
Blockchain blockchain = (Blockchain) query.getSingleResult();
// blockchain.replace_chain(new_chain);
// em.merge(blockchain);
System.out.println("GOING TO REPLACE CHAIN AS SERVICE");
// THIS LONG BLOCK IS BECAUSE I COULDN'T FIND A MORE NATURAL WAY. I KEEP GETTING
// ERRORS.
// I JUST WANT TO OVERWRITE THE CHAIN OR DO A SMART MERGE
// INSTEAD IT TRIES TO APPEND. I HAVE TO WRITE AN EMPTY SET TO DB AND COMMIT IT
// AND THEN REPOPULATE IT. ALTERNATELY I COULD MAYBE DO A NATIVE QUERY AND
// TRUNCATE
// REGARDLESS IT DOESN'T SEEM TO SMARTLY MERGE THE TWO CHAINS
// -- IT SHOULD BE EASY WHEN THE NEW CHAIN IS AN EXTENSION, VS A FORK
// -- HANDLING THE "FORK" POTENTIAL OF BLOCKCHAIN ADDS TO THE COMPLEXITY IN
// WHICH CASE EASIEST TO TRUNCATE AND START FRESH
// Try Flush
if (blockchain.willReplace(new_chain)) {
blockchain.setChain(null);
em.getTransaction().commit();
em.getTransaction().begin();
// em.flush();
Query query2 = em.createQuery("select b from Blockchain b where b.instance_name = :name");
query.setParameter("name", name);
Blockchain blockchain2 = (Blockchain) query.getSingleResult();
blockchain2.setChain(new_chain);
em.getTransaction().commit();
this.disconnect();
return true;
}
em.getTransaction().commit();
this.disconnect();
return true;
}
我在 JPA 中有一个一对多映射,如下所示:
在我的区块链中 class 我在 ArrayList(这是“链”属性)上有 @OneToMany 注释,我有一个块 class.
我有一种方法可以在广播新链或在线上广播新块时用另一个区块链实例的链替换(例如,由异步侦听器触发的 pubsub)。如果发现有效且长度足够,该方法将替换链。
链在数据库中反映为区块链和区块之间的连接 table。当一个新的链条出现时,它几乎是相同的。换句话说,如果只是创世块,将至少与主键发生 1 次冲突。除了尖端的一个或几个块之外,其他所有块更有可能发生碰撞,因此我希望能够毫无意外地处理这个问题。理想情况下,JPA 会想出如何在没有我的情况下使用以下代码来完成它,但事实并非如此。
@Override
public boolean replaceChain(String name, ArrayList<Block> new_chain) throws NoSuchAlgorithmException, ChainTooShortException, GenesisBlockInvalidException, BlocksInChainInvalidException {
this.connect();
em.getTransaction().begin();
Query query = em.createQuery("select b from Blockchain b where b.instance_name = :name");
query.setParameter("name", name);
Blockchain blockchain = (Blockchain) query.getSingleResult();
blockchain.replace_chain(new_chain);
em.getTransaction().commit();
this.disconnect();
return true;
}
从那里我尝试了很多我能想到的排列和技巧。我尝试手动删除块实体中重复的每个块,但随后连接 table 出现问题,堆栈溢出表示显然 JPA 未设置为手动管理。这不是面向对象的方式。我对此很好,但我的问题是什么是 OOP 方式。只是为了覆盖一对多的关系。新传入的 OneToMany 应该覆盖所有内容,取代其他所有内容,仅此而已,但它会尝试复制。我阅读了其他 SO 帖子,但我对它们的理解不够好,或者它们没有帮助。
我 运行 通过 DAO 服务,通过 pubnub 侦听器回调连接工作。我有两台服务器,实际上是代码库 运行- 这个主要的“网络节点”正在处理数据库(端口 8080),还有一个位于 9080 上的“内存中”节点,它仅从创世块开始,如果它收到一个 200 GET 请求到 8080 将克隆它并替换那个链。替换链工作 - 只是不写入数据库。我将网络上的第二个节点称为 PEER 实例。它具有挖掘块的能力,当它这样做时,它会广播到触发主节点的 pubsub。那是我的设置。除了其中的 JPA 部分外,一切似乎都运行良好。我正在使用 Eclipselink 和 Tomcat.
根据我的理解,当您开始使用 entitymanager 进行交易时,它基本上会观察您所做的事情并做笔记并记录结果并发挥其魔力,有点像抄写员或观察员。你设置它观看,然后你做你的事情,然后你告诉它提交,它仍然有处理的限制和约束,否则会抛出异常,但这是我的理解,也是我最初走的路。
这是我上面这段代码的错误日志,而不是尝试手动删除给定链的块。我可以这样做,但我无法加入 table,而且我知道这不是理想的方式
Error Code: 1062
Call: INSERT INTO block (TIMESTAMP, DATA, DIFFICULTY, HASH, LASTHASH, NONCE) VALUES (?, ?, ?, ?, ?, ?)
bind => [1617166967254, [B@5ebbe21e, 9, 002057d17e0de9c5f97f6a0f3e7534c0599036ae307ece2ee3f645025c153f80, 007e833b320c58bcf29096e22ced52a5c90c915e23830eeae0a7093290af4080, 246]
Query: InsertObjectQuery(privblock.gerald.ryan.entity.Block@d6f817c0)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:157)
at privblock.gerald.ryan.dao.BlockchainDao.replaceChain(BlockchainDao.java:97)
at privblock.gerald.ryan.service.BlockchainService.replaceChainService(BlockchainService.java:38)
at pubsub.PubNubSubCallback.message(PubNubSubCallback.java:132)
at com.pubnub.api.managers.ListenerManager.announce(ListenerManager.java:61)
at com.pubnub.api.workers.SubscribeMessageWorker.processIncomingPayload(SubscribeMessageWorker.java:228)
at com.pubnub.api.workers.SubscribeMessageWorker.takeMessage(SubscribeMessageWorker.java:83)
at com.pubnub.api.workers.SubscribeMessageWorker.run(SubscribeMessageWorker.java:74)
at java.base/java.lang.Thread.run(Thread.java:832)
感谢任何帮助或见解!
我明白了。它似乎可以使用一个额外的注释 属性 orphanRemoval=true
如下
@OneToMany(targetEntity = Block.class, cascade = CascadeType.PERSIST, orphanRemoval=true)
@JoinTable(name = "BlocksByChain")
List<Block> chain; // The chain itself
我知道它必须是简单的并且一些已经存在的特性。这不是框架默认值
编辑:不完全是。不完美。我仍然需要在 DAO 中使用代码将其清除,这很笨重而且不是最优的。我还得到有关死锁或其他内容的控制台输出。我之前没有注意到这一点,因为我的应用程序按预期工作,但我知道必须有更好的方法。
此代码也必须存在才能工作:
@Override
public boolean replaceChain(String name, ArrayList<Block> new_chain) throws NoSuchAlgorithmException,
ChainTooShortException, GenesisBlockInvalidException, BlocksInChainInvalidException {
this.connect();
em.getTransaction().begin();
Query query = em.createQuery("select b from Blockchain b where b.instance_name = :name");
query.setParameter("name", name);
Blockchain blockchain = (Blockchain) query.getSingleResult();
// blockchain.replace_chain(new_chain);
// em.merge(blockchain);
System.out.println("GOING TO REPLACE CHAIN AS SERVICE");
// THIS LONG BLOCK IS BECAUSE I COULDN'T FIND A MORE NATURAL WAY. I KEEP GETTING
// ERRORS.
// I JUST WANT TO OVERWRITE THE CHAIN OR DO A SMART MERGE
// INSTEAD IT TRIES TO APPEND. I HAVE TO WRITE AN EMPTY SET TO DB AND COMMIT IT
// AND THEN REPOPULATE IT. ALTERNATELY I COULD MAYBE DO A NATIVE QUERY AND
// TRUNCATE
// REGARDLESS IT DOESN'T SEEM TO SMARTLY MERGE THE TWO CHAINS
// -- IT SHOULD BE EASY WHEN THE NEW CHAIN IS AN EXTENSION, VS A FORK
// -- HANDLING THE "FORK" POTENTIAL OF BLOCKCHAIN ADDS TO THE COMPLEXITY IN
// WHICH CASE EASIEST TO TRUNCATE AND START FRESH
// Try Flush
if (blockchain.willReplace(new_chain)) {
blockchain.setChain(null);
em.getTransaction().commit();
em.getTransaction().begin();
// em.flush();
Query query2 = em.createQuery("select b from Blockchain b where b.instance_name = :name");
query.setParameter("name", name);
Blockchain blockchain2 = (Blockchain) query.getSingleResult();
blockchain2.setChain(new_chain);
em.getTransaction().commit();
this.disconnect();
return true;
}
em.getTransaction().commit();
this.disconnect();
return true;
}