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 是 Java 应用程序和 Hibernate 之间的主要运行时接口。 会话的主要功能是为已映射的实体类的实例提供创建、读取和删除操作。

在 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 表。

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 'derby_cars.sql'

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

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

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

使用原生 Hibernate API 和 XML 映射的 Derby

在本节中,我们创建一个 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

这是项目结构。

<?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 目录中。

<?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 的自动会话上下文管理并指定 Derby SQL 方言。映射使用 <mapping> 元素添加。我们有一个映射——将 Car 对象映射到 cars 表。

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 方法。


<?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 生成器类允许按需自动递增整数/大整数列。 Derby 支持此生成器。

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 以获取会话对象。然后使用会话对象访问数据库。

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

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

getSessionFactory().close();

此行关闭缓存和连接池。

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 用于获取和打开会话对象。 Sessionget 方法返回具有给定标识符的给定实体类的持久实例,如果不存在这样的持久实例,则返回 null

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

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

session.save(car);

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

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();

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

使用原生 Hibernate API 和注解映射的 Derby

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

pom.xmlCarService.javaHibernateDerbyEx.javaHibernateUtils.java 文件没有变化。 hibernate.cfg.xmlCar.java 发生了变化。

<?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 实体,其中包含映射注解。

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 注解指定带注解实体的 primary 表。

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

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

在本教程中,我们介绍了 Hibernate ORM。我们使用了 Derby 数据库。 ZetCode 提供了以下相关教程:EclipseLink 教程MySQL Java 教程JDBC 模板教程Apache Derby 教程