jpa和hibernate

    技术2024-03-28  14

    问题定义

    本技巧从对问题的简单描述开始:定义一个复合数据库密钥。 这是一个键,该键组合了许多列以唯一地定义数据库表的行。 有时,组合键称为自然键或业务键。 有时会使用复合密钥,因为密钥的选择在某种程度上与最终用户业务领域相关。 要定义组合键,您只需从域中获取一些属性,然后将它们组合起来即可提供所需的行唯一性。 复合键的缺点在于,它们在设计和编码上有些困难。 同样,他们倾向于将您的数据库和ORM设计与原始域联系在一起。 后者可能不是大问题。

    实体代码

    清单1展示了一个名为BillingAddress的Java类。 此类为个人或组织的帐单地址建模。 帐单本身与另一个名为PurchaseOrder Java类有关。 这里没有什么太令人惊讶的-下了采购订单,接着进行了计费过程。

    清单1. BillingAddress类
    import javax.persistence.*; import java.io.Serializable; @Embeddable public class BillingAddress implements Serializable { private String street; private String city; BillingAddress() {} public BillingAddress(String street, String city) { this.street = street; this.city = city; } public String getStreet() { return street; } private void setStreet(String street) { this.street = street; } public String getCity() { return city; } private void setCity(String city) { this.city = city; } }

    需要注意的重要一点是该类实现了Java Serializable接口。 还要注意带有注释@Embeddable的行。 此注释是复合键拼图的第一部分。 具有@Embeddable批注的Java类本身可以成为另一个类的子组件。 这听起来比实际要复杂得多。 为了说明这一点,清单2显示了PurchaseOrder类,该类使用清单1中的BillingAddress类。

    清单2. PurchaseOrder类
    import javax.persistence.*; @Entity @Table(name = "PURCHASE_ORDERS") @IdClass(BillingAddress.class) public class PurchaseOrder { PurchaseOrder() {} PurchaseOrder(BillingAddress billingAddress) { street = billingAddress.getStreet(); city = billingAddress.getCity(); } @Id @AttributeOverrides({ @AttributeOverride(name = "street", column = @Column(name="STREET")), @AttributeOverride(name = "city", column = @Column(name="CITY")) }) private String street; private String city; private String itemName; public String getItemName() { return itemName; } public void setItemName(String itemName) { this.itemName = itemName; } }

    我总是很难理解注释,清单2也不例外,因此我将其分解为可管理的块。 第一个注释是@Entity ,它指示该类是数据库实体(也就是说,它将构成ORM解决方案的一部分)。 通常,当您看到@Entity批注时,可以期望看到相应的数据库表。 后者由下一个注释表示,即@Table 。 我发现当您以这种方式分解流程时,它变得更容易理解。

    清单2中的下一个是@IdClass批注,它定义了组合键类的引用。 您可能已经注意到,该注释引用清单1中的BillingAddress类。跳过清单2中的构造函数,请注意@Id注释。 这是使用嵌套@AttributeOverrides批注定义复合键的@AttributeOverrides 。 这些注释用于定义组合键列:分别为“ STREET”和“ CITY”。

    在清单2中的注解之后,您可以发现清单1中两行重复的代码吗? 当然,重复的代码是街道和城市的两个私有数据成员。 要创建复合密钥,必须进行此复制。

    数据库架构

    到目前为止,这一切都是相当技术性的。 现在,通过生成数据库模式以更具体的方式表示这一点。 清单3展示了这种超简单的ORM数据库设计中的模式。

    清单3.数据库模式
    drop table PURCHASE_ORDERS if exists; create table PURCHASE_ORDERS ( street varchar(255) not null, city varchar(255) not null, itemName varchar(255), primary key (street, city) );

    您会看到主键确实是由街道和城市领域组成的组合。 在实际的数据库中,例如使用图形用户界面(GUI)工具的数据库,这看起来像什么? 在回答之前,我写了一些简单的客户端代码将一个或两个实体持久保存到数据库中。

    清单4给出了一些代码的摘录,以实例化先前定义的类的对象。

    清单4.一些ORM客户端代码
    // Start EntityManagerFactory EntityManagerFactory emf = Persistence.createEntityManagerFactory("helloworld"); // First unit of work EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); PurchaseOrder purchaseOrder = new PurchaseOrder(new BillingAddress("Broad Street", "Boston")); purchaseOrder.setItemName("My new computer"); em.persist(purchaseOrder); tx.commit(); em.close();

    清单4中的代码说明了从PurchaseOrder对象实例化和设置到数据库中该对象持久化的整个过程。 这确实说明了ORM的魔力。 看看会发生什么。

    首先,创建EntityManagerFactory的实例,然后依次使用它创建名为em的EntityManager实例。 然后使用后者将PurchaseOrder对象实例写入数据库。 到数据库的实际持久性是作为事务的一部分发生的。

    事务是一组原子动作,它们要么全部成功发生,要么在发生错误的情况下被回滚。 从清单4中可以看到, EntityManager对象用于创建一个名为tx的EntityTransaction实例。 后者是将事务中的工作单元包装起来的对象。

    请注意对persist()和commit()的调用。 重要的是要记住,除非同时发生这两个调用,否则不会对数据库进行任何更改。 这是Java Persistence API(JPA)的简单模式。

    为了完成ORM浏览, 图1显示了运行清单4中的代码后数据库的外观。该代码已使用称为HSQLDB的内存数据库进行了测试,并且该产品包含一个简单的GUI工具。 HSQLDB数据库的状态如图1所示。您可以看到我在PURCHASE_ORDERS表上运行了一个SQL查询。 该表是根据模式创建的,该模式本身是根据先前清单中的Java代码创建的。

    图1.填充的数据库

    在图1中,您可以看到清单4中的这一行代码具有的效果: purchaseOrder.setItemName("My new computer") 。 设置器代码的调用用String数据“ My new computer”填充了数据库行中的关联列。 在工作流程方面,您可以将整个程序运行视为为新计算机创建采购订单,然后进行开票流程。 工作流中的所有步骤都隐式存储在数据库中。

    关于组合键的结论意见

    清单1和2中定义的组合键使您可以将许多列捆绑在一起。 然后,列的组合提供了所需的唯一性,因此您可以在数据库表中具有任意数量的行。 您已经了解了它是如何完成的。 现在,您需要简要了解为什么人们可能会采用这种古怪的方法进行数据库设计。

    使用复合键的最常见原因可能是向后兼容。 换句话说,它发生在需要将新数据库代码集成到旧版环境中的情况下。 我认为,如今以这种方式有意设计数据库是非常不寻常的,因此很可能只需要在已存在很长时间的实践中创建复合键即可。

    在这种情况下,我的经验是反对这种方法毫无意义。 如果复合键是标准键,那么这不太可能很快改变。 在许多情况下,可能已有大量以这种方式构造的现有数据。 因此,如果将复合键更改为数字键,则必须迁移旧数据。 同样,可能存在映射到组合键数据的业务流程。 所有这些因素的组合可能使复合键设计成为必要。


    翻译自: https://www.ibm.com/developerworks/java/library/os-hibernatejpa/index.html

    Processed: 0.025, SQL: 8