Hibernate:直接在bean中设置Id或调用load()或get()方法之间的区别?
以下是加载的示例: –
Stock stock = (Stock)session.load(Stock.class, new Integer(2)); StockTransaction stockTransactions = new StockTransaction(); //set stockTransactions detail stockTransactions.setStock(stock); session.save(stockTransactions);
有什么区别,如果我直接设置id如下: –
Stock stock =new Stock(); stock.setId(2); StockTransaction stockTransactions = new StockTransaction(); //set stockTransactions detail stockTransactions.setStock(stock); session.save(stockTransactions);
因为我已经知道了股票表的Id。 你打电话给负载还是搞定?
有什么不同 …
您的第一个代码示例从数据库中获取对象,因此加载的对象将处于持久化状态。 您的第二个示例将尝试使用全新的 Stock
存储StockTransaction
。 这可能会导致主键错误(如果库存ID唯一)或重复条目。 您应该根据您的要求选择使用哪种方式。 如果您需要StockTransaction
与现有Stock
(我认为这是你的情况,因为你写了你知道ID) – 你应该首先从数据库加载它。
你打电话给负载还是搞定?
如果数据库中没有此类对象(具有此类ID), Session.load()将返回具有空字段的代理。
如果没有具有此类id的对象, Session.get()将返回null。
使用哪一个取决于您和您的任务。 我个人更喜欢get()
。
对象create将处于持久状态。
Stock stock = (Stock)session.load(Stock.class, new Integer(2));
对象创建将处于暂时状态。
Stock stock =new Stock(); stock.setId(2);
瞬态:
与Session无关的持久化类的新实例在数据库中没有表示,Hibernate没有标识符值被认为是瞬态的:
持久国家:
持久化实例在数据库中具有表示,标识符值并与会话相关联。 您可以通过将瞬态实例与会话关联来使其持久化:
参考
http://www.dineshonjava.com/p/transient-persistent-and-detached.html#.U4LKlHakrlc
虽然如果您只计划保存子实体(StockTransaction)并不是两种方法都会产生相同的结果,但这并不常见。 您将保持子项持久化,并且在刷新之后,父项将附加到当前会话。
我在GitHub上创建了一个测试:
final Long parentId = cleanAndSaveParent(); transactionTemplate.execute(new TransactionCallback() { @Override public Void doInTransaction(TransactionStatus transactionStatus) { SetParent idOnlyParent = new SetParent(); idOnlyParent.setId(parentId); SetChild newChild = new SetChild(); newChild.setName("new"); newChild.setParent(idOnlyParent); entityManager.persist(newChild); entityManager.flush(); SetChild otherChild = new SetChild(); otherChild.setName("Other"); idOnlyParent.addChild(otherChild); entityManager.flush(); assertEquals(1, idOnlyParent.getChildren().size()); return null; } }); transactionTemplate.execute(new TransactionCallback () { @Override public Void doInTransaction(TransactionStatus transactionStatus) { SetParent parent = entityManager.find(SetParent.class, parentId); assertEquals(3, parent.getChildren().size()); return null; } });
调用flush()时,newChild对象将附加到当前Session。 但是父母不会被附加,因此父母方面的改变根本不会同步。
如果从DB中获取父级,则更改为父级将被“脏检查”并在刷新时同步。
即使有可能这样做,也不是真的可取。 它可能会让您产生错误的印象,即您仍在使用附加的父实体,而您只获得一个临时父实体。
为了正确地将对象状态与数据库同步,对象需要由hibernate 管理 ( aka
与持久化上下文相关联)。 在刷新时,它是持久化上下文的内容,它决定将什么内容刷新到数据库然后提交。 除此之外,始终在持久实体之间创建关系。 持久性本质可以是显式的(通过调用任何适当的API方法 – 在本机hibernate或它们的JPA对应物中保存,更新,合并等)或隐式(由于级联属性或由于通过适当的API调用加载,如load,得到等)。 实体持久化的另一个原因是后写事务性质,您可以继续修改对象的状态,并在事务提交时或显式刷新时将增量传播到数据库。 在对象图中,需要传播到数据库的内容取决于管理或持久的对象(这两个术语大多数时间都可互换使用)。 有一个分离对象的概念(对象的数据库标识符已设置,但无法保证代表数据库中的实际状态。实际上,任何数据都可能在事务外部过时)。 对于这些,您也可以选择重新连接/合并到持久化上下文(在本机hibernate中)或合并(在JPA中)。
此外,在创建双向关系时 – 需要考虑另一个概念 – 反向。 反面不负责创建关系,因此仅从反面设置关系不会导致关系通过外键保留在数据库中。
让我用这种方式解释一下:你的问题是合理的,并且有一个明确的答案 – 使用load()
。 为什么? 一般来说,我们有三种方法可以通过ID获得一个实例。
1)一个,极端的,事实上不恰当的,仅提到复杂性,将使用查询,例如对ID的限制。 此查询必须命中DB …
2)然后,我们有get()
,这是如何通过ID获取项目的最常见方式。 它始终命中DB,因为它的合同说( get ):
使用给定标识符返回给定实体类的持久实例,如果没有此类持久实例,则返回null
因此,为了确保该对象存在与否,必须击中DB。
3)最后,出于某种原因,还有第三个合同 load()
。 它永远不会命中DB来检查是否有这样的项目(提供ID) : load() :
假定实例存在,返回给定实体类的持久实例和给定标识符。
合同在这里期望。 另一方面,这个约定可以帮助我们解决此查询中描述的情况
如果我们确实有参考ID,并且我们知道它确实存在,我们shouuld使用load()
。 它将创建一个代理实例 – 使用提供的ID,并且在该根/持有者实体的INSERT或UPDATE期间,代理ID将用于创建正确的SQL语句。
总结: get()
和load()
是我们的原因所在。 它们旨在支持不同的场景。 对于根实体的CRUD操作,我们应该通过get()
接收它,以确保上层要求现有ID。 load()
的用例是当我们有ID时,只想坚持……
也可以看看:
- session.get()和session.load()之间有所不同
- NHibernate – 获取,加载和通过id查询之间的区别