Spring Boot 扩展:使用 Hibernate & JPA 持久化 Java 对象

刺骨的言语ヽ痛彻心扉 2024-03-26 14:28 162阅读 0赞

Hibernate 是业界名气非常大的一款 ORM(Object/Relational Mapping)解决方案。 ORM 是指将 Java 中的域模型映射到关系数据库的数据模型。 Hibernate 能够很好的处理从 Java 类到数据库表的映射,以及从 Java 数据类型到 SQL 数据类型的映射。 今天,我将介绍如何使用 Hibernate API 和 JPA 进行数据持久化。

在开始之前,我们先定义一个域类型 Event,用来表示需要持久化的对象。

  1. public class Event {
  2. private Long id;
  3. private Date time;
  4. private String title;
  5. /** 省略属性的 getter/setter 以及无参构造器 */
  6. }

01-使用 Hibernate 原生 API

在 Hibernate 原生 API 中,提供数据持久化能力或接口的是org.hibernate.Session。 Hibernate 提供了一个工厂类org.hibernate.SessionFactory,用来创建 Session 对象,

在 Hibernate 的早期版本中,需要使用 hibernate.cfg.xml 搭配 hbm.xml 文件来配置对象持久化。 随着语言的发展,hbm.xml 逐渐被@Entity注解等替代。 接下来,我们将逐一学习这两种方式,注意体会两者的不同。

01.1-原生 API 和 hbm.xml

首先,我们先介绍下用到的 hibernate.cfg.xml,它是用来配置 SessionFactory 行为的。

  1. <hibernate-configuration>
  2. <session-factory>
  3. <!-- 省略其他的 property -->
  4. <property name="connection.driver_class">org.h2.Driver</property>
  5. <mapping resource="hbm/Event.hbm.xml"/>
  6. </session-factory>
  7. </hibernate-configuration>

<property/>是用来设置诸如使用得数据库类型、数据库驱动类、数据库链接、用户、密码等属性的。 <mapping/>主要是用来引入需要用到的 hbm.xml 文件,其中定义了 Java 类与数据库表的映射关系。 例如:

  1. <hibernate-mapping package="self.samson.example.jpa">
  2. <class name="self.samson.example.jpa.entity.Event" table="EVENTS">
  3. <id name="id" column="EVENT_ID">
  4. <generator class="increment"/>
  5. </id>
  6. <property name="time" type="timestamp" column="EVENT_DATE"/>
  7. <property name="title"/>
  8. </class>
  9. </hibernate-mapping>

<class/>标签指定了要映射的类、其映射的表及它们之间具体的映射关系(<id/><property/>)。

配置好上述文件,即可对使用 Hibernate 原生 API 持久化 Java 对象进行测试了。

  1. final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
  2. .configure()
  3. .build();
  4. try (SessionFactory sessionFactory = new MetadataSources(registry)
  5. .buildMetadata()
  6. .buildSessionFactory()){
  7. final Session session = sessionFactory.openSession();
  8. } catch (Exception e) {
  9. StandardServiceRegistryBuilder.destroy(registry);
  10. e.printStackTrace();
  11. }

通过 SessionFactory 创建的 Session 对象提供了持久化数据相关操作的主要接口,例如 persist / find 等。 例如,我们向通过 Session 持久化两个对象,则可以这样实现:

  1. session.beginTransaction();
  2. session.save(new Event(new Date(), "Our very first event!"));
  3. session.save(new Event(new Date(), "A follow up event!"));
  4. session.getTransaction().commit();
  5. 复制代码

01.2-原生 API 和@Entity注解

除了像 hbm.xml 文件这种表述映射关系的方式外,Hibernate 还支持使用@Entity/@Id/@Column等注解方式实现。 接下来我们将看一下如何实现。

首先,在 Java 类上增加对应的注解,以下注解等价于上节中的 Event.hbm.xml 中描述的映射关系。

  1. public class Event {
  2. @Id
  3. @GeneratedValue(generator = "increment")
  4. @GenericGenerator(name = "increment", strategy = "increment")
  5. private Long id;
  6. @Temporal(TemporalType.TIMESTAMP)
  7. @Column(name = "EVENT_DATE")
  8. private Date time;
  9. private String title;
  10. /** 省略属性的 getter/setter 以及无参构造器 */
  11. }

@Id与 hbm.xml 中的<Id/>作用是一样的。 @GeneratedValue(generator = "increment")@GenericGenerator(name = "increment", strategy = "increment")等价于<generator/>

然后,需要修改下 hibernate.cfg.xml 中的<mapping/>内容,将 resource 替换为 class,如下:

  1. <mapping class="self.samson.example.jpa.entity.Event"/>
  2. 复制代码

之前使用的创建 SessionFactory 和 Session 的代码不用改变。 运行程序后,发现数据被持久化到了数据库中。

02-使用 Hibernate JPA API

JPA(Java Persistence API)是一个规范,通过提供 ORM 功能,使开发者能够利用 Java Domain Model 控制关系数据库。 JPA 仅是一个规范,目前业界有几种不同的实现,例如 Hibernate、EclipseLink、TopLink、Open JPA 等。 Spring Boot2 默认使用 Hibernate 作为底层实现。

JPA 中定义的启动流程与 Hibernate 原生的方式不太一样。 JPA 使用 META-INF/persistence.xml 作为配置文件。 而且 JPA 中定义了持久化单元和 EntityManger 的概念,与 Hibernate 原生也不太一样。

不过,从使用方式上来看,org.hibernate.Session 和 javax.persistence.EntityManager 都表示处理持久化数据的上下文,称为”持久化上下文”。 持久化数据具有一个与持久化上下文和底层数据库都有关系的状态,称为实体状态,共有以下几种取值:

  • new/transient,指实体刚被实例化,尚未与持久化上下文关联。它在数据库中没有持久化表示,也没有分配标识符(identifier)值。
  • managed/persistent,指实体具有关联的标识符,且与持久化上下文关联。
  • removed,指计划被数据库删除的 managed/persistent 状态的实体。
  • detached,指实体具有关联的标识符,但却不再与持久化上下文关联。造成实体处于这种状态通常有两种常见原因:其一,持久化上下文被关闭;其二,实体从上下文中被擦除。

02.1-使用 persistent.xml

前节中的例子,如果要使用 JPA 方式,需要做如下的修改。 首先,先定义一个 persistent.xml 文件,其中定义了持久化单元。

  1. <persistence xmlns="http://java.sun.com/xml/ns/persistence"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
  4. version="2.0">
  5. <persistence-unit name="self.samson.example.jpa">
  6. <description>jpa entity-manager example</description>
  7. <class>self.samson.example.jpa.entity.Eventself.samson.example.jpa.entity.Event</class>
  8. <properties>
  9. <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
  10. <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1"/>
  11. <property name="javax.persistence.jdbc.user" value="sa"/>
  12. <property name="javax.persistence.jdbc.password" value=""/>
  13. <property name="hibernate.show_sql" value="true"/>
  14. <property name="hibernate.hbm2ddl.auto" value="create"/>
  15. </properties>
  16. </persistence-unit>
  17. </persistence>

可以看出,持久化单元中的<class/>与 hibernate.cfg.xml 中的<mapping class=xxx/>是等价的。

然后,需要创建 EntityManager。类似地,JPA 中也通过 EntityManagerFactory 的方式生成 EntityManager。

  1. final EntityManagerFactory sessionFactory = Persistence.createEntityManagerFactory("self.samson.example.jpa");
  2. final EntityManager entityManager = sessionFactory.createEntityManager();

得到 EntityManager 对象后,使用方式与 Hibernate 中的 Session 类似:

  1. entityManager.getTransaction().begin();
  2. entityManager.persist(new Event(new Date(), "Our very first event!"));
  3. entityManager.persist(new Event(new Date(), "A follow up event!"));
  4. entityManager.getTransaction().commit();

02.2-使用程序化方式

根据上一小节的介绍,需要用到 persistent.xml 作为启动 JPA 的配置文件。 除此之外,也可完全采用 Java 方式启动 JPA。

采用程序化方式启动 JPA 需要借助 PersistenceUnitInfo 接口,它定义了一系列的方法能够获得创建 EntityMangerFactory 必要的信息。 例如,以下 Java 类提供的信息与上小节的 persistent.xml 信息等价:

  1. public class HibernatePersistenceUnitInfo implements PersistenceUnitInfo {
  2. /**
  3. * 等价于 <persistence-unit name="self.samson.example.jpa">
  4. * @return
  5. */
  6. @Override
  7. public String getPersistenceUnitName() {
  8. return "self.samson.example.jpa";
  9. }
  10. /**
  11. * 等价于 <class>self.samson.example.jpa.entity.Event</class>
  12. * @return
  13. */
  14. @Override
  15. public List<String> getManagedClassNames() {
  16. return Arrays.asList(Event.class.getName());
  17. }
  18. /**
  19. * 等价于 <properties/>
  20. * @return
  21. */
  22. @Override
  23. public Properties getProperties() {
  24. Properties properties = new Properties();
  25. properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
  26. properties.put("hibernate.id.new_generator_mappings", false);
  27. properties.put("hibernate.show_sql", true);
  28. properties.put("hibernate.hbm2ddl.auto", "create");
  29. return properties;
  30. }
  31. /**
  32. * 等价于 <properties/> 中定义的数据库连接信息
  33. * 这里,只不过是直接返回一个 DataSource 实例
  34. * @return
  35. */
  36. @Override
  37. public DataSource getNonJtaDataSource() {
  38. return new EmbeddedDatabaseBuilder()
  39. .setType(EmbeddedDatabaseType.H2)
  40. .build();
  41. }
  42. }

创建 EntityManagerFactory 时与上节中的方式也略有不同。

  1. EntityManagerFactory sessionFactory = new EntityManagerFactoryBuilderImpl(
  2. new PersistenceUnitInfoDescriptor(new HibernatePersistenceUnitInfo()), new HashMap<String, Objects>(32)).build();

创建完毕后,获得 EntityManager 的方式和后续 EntityManger 的使用方式上与之前的并无差别。

03-总结

我今天介绍了使用 Hibernate API 和 JPA 进行 Java 实体类对象持久化方法。希望今天的介绍能对你有所帮助。

发表评论

表情:
评论列表 (有 0 条评论,162人围观)

还没有评论,来说两句吧...

相关阅读