ZetCode

Hibernate Derby

最后修改日期:2020 年 8 月 31 日

Hibernate Derby 教程展示了如何将 Hibernate ORM 与 Derby 数据库结合使用。

Hibernate

Hibernate 是一个用于 Java 语言的对象关系映射框架。它提供了一个将面向对象领域模型映射到关系数据库的框架。对象关系映射 (ORM) 是一种编程技术,用于在面向对象编程语言中不兼容的类型系统之间转换数据。

Hibernate 查询语言 (HQL) 是一种类似于 SQL 的面向对象查询语言。SQL 操作表和列,而 HQL 操作持久化对象及其属性。HQL 理解继承、多态和关联。HQL 查询最终由 Hibernate 翻译成 SQL 查询,这些 SQL 查询对数据库执行某些操作。

除了其原生 API 外,Hibernate 还包含 Java Persistence API (JPA) 的实现。

Apache Derby

Apache Derby 是一个完全用 Java 实现的开源关系数据库。Derby 的占用空间小,易于部署和安装。它同时支持嵌入式和客户端/服务器模式。

Hibernate 构件

hibernate.cfg.xml 文件定义了 Hibernate 的配置信息。它包含有关数据库连接、资源映射和其他连接属性的信息。

SessionFactory 是我们获取 Session 并执行数据库操作的工厂类。Session 是 Java 应用程序与 Hibernate 之间的主要运行时接口。Session 的主要功能是为映射的实体类的实例提供创建、读取和删除操作。

在 Derby 中创建数据库

我们从 https://downloads.apache.org/db/derby/ 网页下载 Derby。我们将文件解压到选定的目录。

~/bin/derby-10.15$ ls
bin  demo  derby_cars.sql  derby.log  docs  index.html  javadoc  KEYS  lib
LICENSE  NOTICE  RELEASE-NOTES.html  test  testdb

这是 Derby 安装目录的内容。

~/bin/derby-10.15$ java -jar lib/derbyrun.jar sysinfo
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
------------------ Java Information ------------------
Java Version:    13.0.4
Java Vendor:     Debian
Java home:       /usr/lib/jvm/java-13-openjdk-amd64
Java classpath:  lib/derbyrun.jar
OS name:         Linux
OS architecture: amd64
...

我们获取系统信息。

我们在 Derby 中创建一个新的 testdb 数据库。它将有一个简单的 cars 表。

cars_derby.sql
CREATE TABLE cars(id INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY
    (START WITH 1, INCREMENT BY 1), name VARCHAR(255), price INT);

INSERT INTO cars(name, price) VALUES('Audi', 52642);
INSERT INTO cars(name, price) VALUES('Mercedes', 57127);
INSERT INTO cars(name, price) VALUES('Skoda', 9000);
INSERT INTO cars(name, price) VALUES('Volvo', 29000);
INSERT INTO cars(name, price) VALUES('Bentley', 350000);
INSERT INTO cars(name, price) VALUES('Citroen', 21000);
INSERT INTO cars(name, price) VALUES('Hummer', 41400);
INSERT INTO cars(name, price) VALUES('Volkswagen', 21600);

这是创建 cars 表的 SQL;汽车对象的 ID 是自动递增的。

~/bin/derby-10.15$ java -jar lib/derbyrun.jar ij
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
ij version 10.15
ij> CONNECT 'jdbc:derby:testdb;user=user7;create=true';

我们运行 ij 工具并创建 testdb 数据库。

ij> RUN 'cars_derby.sql'

我们运行 RUN 命令,创建 cars 表。

~/bin/derby-10.15$ bin/startNetworkServer &

我们使用 startNetworkServer 命令启动数据库。

Derby 与原生 Hibernate API 和 XML 映射

在本节中,我们将创建一个 Java 控制台应用程序,该应用程序在 Derby 数据库上执行一些数据库任务。我们使用 Hibernate 原生 API 和 XML 映射。

pom.xml
src
├── main
│   ├── java
│   │   └── com
│   │       └── zetcode
│   │           ├── bean
│   │           │   └── Car.java
│   │           ├── hibernate
│   │           │   └── Car.hbm.xml
│   │           ├── main
│   │           │   └── HibernateDerbyEx.java
│   │           ├── service
│   │           │   └── CarService.java
│   │           └── util
│   │               └── HibernateUtils.java
│   └── resources
│       └── hibernate.cfg.xml
└── test
    └── java

这是项目结构。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zetcode</groupId>
    <artifactId>HibernateDerbyEx</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derbyclient</artifactId>
            <version>10.15.2.0</version>
        </dependency>


        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.10.Final</version>
        </dependency>

    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>

</project>

pom.xml 文件中,我们定义了两个依赖项:hibernate-core 库和 derbyclient 驱动程序。在 <build> 元素中,我们允许构建系统包含 XML 文件 — 我们将把 XML 映射文件放在 src/main/java 目录中。

src/main/java/resources/hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.username">app</property>
        <property name="hibernate.connection.password">app</property>
        <property name="hibernate.connection.url">jdbc:derby://:1527/testdb</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <property name="hibernate.dialect">org.hibernate.dialect.DerbyTenSevenDialect</property>
        <mapping resource="com/zetcode/hibernate/Car.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

在 Hibernate 配置文件中,我们提供了到 Derby 数据库的连接属性。我们启用了 Hibernate 的自动 Session 上下文管理,并指定了 Derby SQL 方言。映射使用 <mapping> 元素添加。我们有一个映射 — 将 Car 对象映射到 cars 表。

com/zetcode/bean/Car.java
package com.zetcode.bean;

import java.util.Objects;

public class Car {

    private Long Id;
    private String Name;
    private Integer Price;

    public Long getId() {
        return Id;
    }

    public void setId(Long Id) {
        this.Id = Id;
    }

    public String getName() {
        return Name;
    }

    public void setName(String Name) {
        this.Name = Name;
    }

    public Integer getPrice() {
        return Price;
    }

    public void setPrice(Integer Price) {
        this.Price = Price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Objects.equals(Id, car.Id) &&
                Objects.equals(Name, car.Name) &&
                Objects.equals(Price, car.Price);
    }

    @Override
    public int hashCode() {
        return Objects.hash(Id, Name, Price);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Car{");
        sb.append("Id=").append(Id);
        sb.append(", Name='").append(Name).append('\'');
        sb.append(", Price=").append(Price);
        sb.append('}');
        return sb.toString();
    }
}

这是一个 Car bean。它有三个属性以及相应的 getter 和 setter。


com/zetcode/hibernate/Car.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.zetcode.bean.Car" table="cars" catalog="app">
        <id name="Id" type="java.lang.Long">
            <column name="Id" />
            <generator class="identity" />
        </id>
        <property name="Name" type="string">
            <column name="Name" length="30"/>
        </property>
        <property name="Price" type="integer">
            <column name="Price" />
        </property>
    </class>
</hibernate-mapping>

Car.hbm.xml 文件中,我们提供了 Car 类和 CARS 表之间的映射。我们将类的属性映射到数据库表的列。映射在 <hibernate-mapping></hibernate-mapping> 元素之间指定。

<generator class="identity" />

generator 元素告知 Hibernate 用于生成主键的策略。identity 生成器类允许整数/ bigint 列按需自动递增。Derby 支持此生成器。

com/zetcode/util/HibernateUtils.java
package com.zetcode.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {

    private HibernateUtils() {}

    private static final SessionFactory sessionFactory;

    static {
        try {
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

   public static void shutdown() {

        getSessionFactory().close();
    }
}

HibernateUtils 是一个辅助类,负责处理启动并访问 SessionFactory 以获取 Session 对象。然后使用 Session 对象来访问数据库。

sessionFactory = new Configuration().configure().buildSessionFactory();

此行从 hibernate.cfg.xml 文件创建 SessionFactory

getSessionFactory().close();

此行关闭缓存和连接池。

com/zetcode/service/CarService.java
package com.zetcode.service;

import com.zetcode.bean.Car;
import com.zetcode.util.HibernateUtils;
import java.util.List;
import org.hibernate.Session;

public class CarService {

    private CarService() {};

    public static Car getCarById(Long id) {

        Car car;
        try (Session session = HibernateUtils.getSessionFactory().openSession()) {
            car = session.get(Car.class, id);
        }

        return car;
    }

    @SuppressWarnings("unchecked")
    public static List<Car> getCars() {

        List<Car> cars;
        try (Session session = HibernateUtils.getSessionFactory().openSession()) {
            cars = session.createQuery("from Car").list();
        }
        return cars;
    }

    public static void save(Car car) {

        try (Session session = HibernateUtils.getSessionFactory().openSession()) {
            session.beginTransaction();

            session.save(car);

            session.getTransaction().commit();
        }
    }
}

CarService 类中,我们有用于按 ID 获取汽车、获取所有汽车和保存新汽车的服务方法。

try (Session session = HibernateUtils.getSessionFactory().openSession()) {
    car = session.get(Car.class, id);
}

使用 HibernateUtils 获取和打开 Session 对象。Sessionget() 方法返回给定标识符的给定实体类的持久化实例,如果不存在这样的持久化实例,则返回 null

cars = session.createQuery("from Car").list();

createQuery 方法为给定的 HQL 查询字符串创建一个新的 Query 实例。from Car 查询返回 Car 类所有实例。

session.save(car);

save 方法持久化给定的实例。

com/zetcode/main/HibernateDerbyEx.java
package com.zetcode.main;

import com.zetcode.bean.Car;
import com.zetcode.service.CarService;
import com.zetcode.util.HibernateUtils;
import java.util.List;

public class HibernateDerbyEx {

    public static void main(String[] args) {

        Long id = 1L;

        Car car = CarService.getCarById(id);

        System.out.println(car);

        Car newCar = new Car();

        newCar.setName("Toyota");
        newCar.setPrice(34500);

        CarService.save(newCar);

        List<Car> cars = CarService.getCars();

        for (Car mycar : cars) {

            System.out.println(mycar);
        }

        HibernateUtils.shutdown();
    }
}

这是主应用程序类。我们按 ID 获取汽车,保存新汽车,并列出数据库表中的所有汽车。

Long id = 1L;

Car car = CarService.getCarById(id);

我们使用 CarServicegetCarById() 方法按 ID 检索汽车。

Car newCar = new Car();

newCar.setName("Toyota");
newCar.setPrice(34500);

CarService.save(newCar);

创建一个新汽车并将其保存到数据库中。

List<Car> cars = CarService.getCars();

for (Car mycar : cars) {

    System.out.println(mycar);
}

我们列出 cars 表中的所有汽车。

HibernateUtils.shutdown();

最后,我们关闭打开的资源。

Derby 与原生 Hibernate API 和注解映射

在本节中,我们将创建一个 Java 控制台应用程序,该应用程序在 Derby 数据库上执行一些数据库任务。我们使用 Hibernate 原生 API 和注解映射。

pom.xmlCarService.javaHibernateDerbyEx.javaHibernateUtils.java 文件不变。hibernate.cfg.xmlCar.java 文件会改变。

src/main/resources/hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.username">app</property>
        <property name="hibernate.connection.password">ap</property>
        <property name="hibernate.connection.url">jdbc:derby://:1527/testdb</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <property name="hibernate.dialect">org.hibernate.dialect.DerbyTenSevenDialect</property>
        <mapping class="com.zetcode.bean.Car"></mapping>
    </session-factory>
</hibernate-configuration>

在 hibernate.cfg.xml 文件中,<mapping> 元素已更改。

<mapping class="com.zetcode.bean.Car"></mapping>

我们使用 class 属性指向包含映射注解的 Java 实体。

com/zetcode/bean/Car.java
package com.zetcode.bean;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Table;
import javax.persistence.Id;
import java.util.Objects;

@Entity
@Table(name="cars")
public class Car {

    private Long Id;
    private String Name;
    private Integer Price;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return Id;
    }

    public void setId(Long Id) {
        this.Id = Id;
    }

    public String getName() {
        return Name;
    }

    public void setName(String Name) {
        this.Name = Name;
    }

    public Integer getPrice() {
        return Price;
    }

    public void setPrice(Integer Price) {
        this.Price = Price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Objects.equals(Id, car.Id) &&
                Objects.equals(Name, car.Name) &&
                Objects.equals(Price, car.Price);
    }

    @Override
    public int hashCode() {
        return Objects.hash(Id, Name, Price);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Car{");
        sb.append("Id=").append(Id);
        sb.append(", Name='").append(Name).append('\'');
        sb.append(", Price=").append(Price);
        sb.append('}');
        return sb.toString();
    }
}

Car.java 类中,我们使用注解定义映射。类属性的名称和表列的名称会自动配对。如果名称不同,我们将不得不使用 @Column 注解指定列名。

@Entity
@Table(name="cars")
public class Car implements Serializable {

该类用 @Entity 注解修饰;@Table 注解指定了被注解实体的基本表。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {

@Id 注解指定了实体的标识符,@GeneratedValue 提供了为主键值生成策略的规范。

在本教程中,我们介绍了 Hibernate ORM。我们使用了 Derby 数据库。