我正在尝试以只读方式创建包含对象的域对象,但能够修改它指向的对象。
因此,在下面的示例中,我希望Hibernate能够处理填充Account和Category对象,但我不想更新这些对象。但是,我可能希望更改哪个帐户或我希望费用指定的类别。
换句话说,我可能希望更新费用表上的account_id或category_id,但我绝不想更新帐户或类别表,包括我对费用对象中包含的帐户或类别对象所做的更改。
@Entity
@Table(name = "expense")
public class Expense {
@Id
@GeneratedValue
private long id;
private String name;
private BigDecimal amount;
@OneToOne
@JoinColumn(name = "id", referencedColumnName = "category_id")
//From table called category
private Category category;
@OneToOne
@JoinColumn(name = "id", referencedColumnName = "account_id")
//From table called account
private Account account;
我是否完全滥用Hibernate或者它不是正确的工具?我应该用@SQLUpdate和@SQLInsert来定义自己的sql吗?
应用程序的一般流程是标准的Web应用程序。
由于
答案 0 :(得分:1)
这与hibernate的意图有点不同。对持久对象的更改应该是持久的。如果您不希望他们的状态被保存,请不要更改它们!解释并因此更好地了解自己为什么需要这种行为可能会帮助您找到解决方案(或帮助我们帮助您。)
您拥有的一个选项是在返回Expense对象之前分离/逐出您不希望在数据访问层内更改的映射对象。然后,只要没有启用级联,就不会保存对它们所做的更改。 IMO这会给其他程序员带来“令人惊讶”的行为。
另一种选择是以无会话的方式使用hibernate。每次调用都是自己的原子数据库操作,返回后所有对象都会被分离。然后,只有您明确调用saveOrUpdate的实体才会保存到数据库中。您可以在Hibernate参考文档中学习如何配置Hibernate以这种方式工作,尽管如此,如果您不想模拟某些遗留系统的行为,那么我不知道您为什么要这样做。
最后一点要理解,使用Session,通过调用expense.getCategory()获得的类别通常与通过调用session.get(Category.class,123)获得的实际对象相同。因此,如果在同一个会话中可以同时访问它的两种方式,那么如果您尝试手动管理它,将很容易失去对其实体状态的跟踪。
编辑:
阿公顷!现在它更有意义。
如果您正在使用纯粹的CRUDY Web表单屏幕,那么您无需担心。只要您没有任何级联,当您合并分离的Expense
对象时,Category
和Account
上的更改将不会在数据库中结束。你可以将所有其他字段归零,只要id仍然存在,你就可以了。 (假设你没有像级联验证这样的其他东西会让人感到厌烦。)
有两种基本模式可以更好地处理这个问题。
一种是将Expense
对象放在用户的网络会话上。这样你就拥有了整个东西,你的web数据绑定框架只需要将表单实际更改的字段绑定回它。原始的Category
和Account
仍然存在(或者它们的代理),并且活页夹不需要使用它们。下行当然是添加服务器端状态,以及需要清理它。
二是注册实际进入数据库并在Web绑定期间获取映射实体的数据绑定器。因此,实际出现在表单中的所有内容都是映射字段的ID,并且绑定器将获取并将完整对象放在那里。下行可能不需要往返数据库,但积极的L2缓存可以解决这个问题(假设类别几乎从不改变,而且账户很少改变。)
如果您目前通过在DAO中创建和处理会话,以基本无会话的方式使用hibernate,您可能还需要查看OpenSessionInView / OpenEntityManagerInView模式。