如何利用Hibernate开发一个一对一的映射应用

80 阅读8分钟

利用Hibernate开发一个一对一的映射应用程序

本教程适用于那些对使用hibernate的工作知之甚少或一无所知、需要减少某个项目中编写的SQL查询的Java开发人员。对于那些想了解对象关系映射是如何实现的Hibernate爱好者来说,它也很有用。

简介

再加上它提供的所有不同的功能,以缓解开发过程。Hibernate是由Gavin King和Cirrus Technologies的同事于2001年开始的,作为使用EJB2风格的实体豆的替代方案。最初的目标是提供比EJB2更好的持久化能力。

据报道,有173家公司在其技术栈中使用Hibernate,包括Platform、Trendyol Group和WealthSiple。

在本教程中,读者将学习如何创建一个一对一的关系应用程序,其中客户只能有一个订阅。

目录

  • 设置开发环境。
  • 创建数据库并配置hibernate配置文件。
  • 创建实体。
  • 创建并执行主应用程序。

先决条件

使用面向对象编程的基本Java知识,如何实现实体之间的组合SQL命令,MySQLshell/bash和Intellij,但可以自由使用任何首选环境。

第1步:设置开发环境

成功运行应用程序需要Java开发工具包,在您的计算机上安装Java 11。

安装MySQL服务器。

成功安装MySQL并设置数据库凭证后,使用以下命令登录MySQL。

mysql -u username -p

提示将询问您的密码,输入后,访问权将被授予,显示以下信息。

MySQL login

Maven是一种用于管理Java项目的构建工具。Maven基于项目对象模型(POM),这是一个包含配置细节和项目信息的XML文件。

依赖关系可以从maven中央仓库获得,并添加到POM文件中,maven将负责源代码的生成、代码编译和编译后的JAR文件打包。

这将发生在本地资源库中,在创建新的maven项目时可以访问这些资源。所需的依赖项将由maven自动下载,并在POM文件中加入后添加到项目的classpath中。

Eclipse通过原型提供了一种更简单的方法来创建maven项目。原型用于创建新的maven项目,其中包含特定maven项目的模板文件。你可以把它看成是Java或Web项目的启动文件的集合。

常见的原型有。

  • Maven-archetype-quickstart - 这是一个原型,用于生成适合独立项目的maven项目样本。
  • maven-archetype-webapp - 这是一种原型,用于生成Java网络应用程序的maven示例项目。

在Eclipse中,点击文件、新建、其他、maven,然后点击maven项目。按下一步,选择maven-archetype-quickstart,在打开的窗口中填写组ID和工件ID,分别代表包和项目名称。

点击 "完成",生成一个新的maven项目,其中包含独立应用中使用的所有文件。

在IntelliJ中点击文件、新建、项目,在打开的窗口中选择maven,然后点击下一步,给项目取名。这将创建一个新的maven项目,其结构如下。

maven structure

在名为pom.xml的文件中,在属性结束标签后添加以下依赖项。

 <dependencies>
        <!-- Hibernate's core ORM functionality -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.3.6.Final</version>
        </dependency>

        <!-- JDBC driver for MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.12</version>
        </dependency>

        <!-- Support for Java 9/10/11 -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.sun.activation</groupId>
            <artifactId>javax.activation</artifactId>
            <version>1.2.0</version>
        </dependency>
    </dependencies>

第2步:创建数据库和配置hibernate配置文件

使用以下命令为该应用程序创建一个新的数据库。

create database database_name;

在IntelliJ中,在资源下创建一个新文件,命名为hibernate.cfg.xml或任何喜欢的名称,并添加以下属性。

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- JDBC Database connection settings -->
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/database_name?useSSL=false&amp;serverTimezone=UTC</property>
        <property name="connection.username">username</property>
        <property name="connection.password">password</property>

        <!-- JDBC connection pool settings ... using built-in test pool -->
        <property name="connection.pool_size">10</property>

        <!-- Select our SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQL8Dialect</property>

        <!-- Echo the SQL to stdout -->
        <property name="show_sql">true</property>
        
        <!-- Set the current session context -->
        <property name="current_session_context_class">thread</property>

       <property name="hibernate.hbm2ddl.auto">update</property>
 
    </session-factory>

</hibernate-configuration>
  • 第一部分包含数据库的连接细节,包括数据库URL、用户名和密码。
  • 连接池可以最大限度地减少应用程序和数据库之间打开的连接数。
  • Dialect指定了hibernate中使用的数据库类型,这样hibernate就会生成适当类型的SQL语句。
  • show_sql将把所有SQL语句写到控制台。
  • current_session_context_class 设置实现当前会话上下文的类。
  • 更新类型的hibernate.hbm2dl.auto将自动在数据库中创建表,如果表在第一次执行时不存在,那么它将在实体被创建后更新记录,维护数据。

第3步:创建实体

创建一个名为customer的类,其细节如下。

  • id
  • 姓氏
  • 电子邮件
  • 构造函数,除了id之外,所有的字段都将被生成。
  • toString方法来查看对象实例的数据。
import javax.persistence.*;

@Entity
@Table(name = "customer")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column(name = "email")
    private String email;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "sub_id")
    private Subscription subscription;


    public Customer(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    public Customer() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Subscription getSubscription() {
        return subscription;
    }

    public void setSubscription(Subscription subscription) {
        this.subscription = subscription;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}
  • @Entity - 将该类标记为Hibernate要创建的数据库表。
  • @Table(name="customer") - 告诉hibernate表的名称将被称为customer。
  • @Id - 当前实体的主键。
  • @GeneratedValue - 主键的自动递增策略。
  • @Column - 表示将由Hibernate在数据库中创建的字段的名称。
  • @OneToOne(cascade = CascadeType.ALL) - 这表明一个客户有一个订阅,级联类型为all,告诉hibernate对客户进行的CRUD操作也应该级联到订阅。例如,如果一个客户被删除,他们的订阅也应该被删除。
  • @JoinColumn(name = "sub_id") - 告诉hibernate在客户表中寻找列sub_id,并使用该信息为客户找到合适的订阅。

在创建完客户类后,创建一个订阅类,其细节如下。

  • id
  • 名称
  • 价格
  • 描述
  • 构造函数中没有id
  • toString方法
import javax.persistence.*;

@Entity
@Table(name = "subscription")
public class Subscription {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "name")
    private String name;

    @Column(name = "price")
    private Double price;

    @Column(name = "description")
    private String descrition;


    @OneToOne(cascade = {
            CascadeType.MERGE,
            CascadeType.PERSIST,
            CascadeType.REFRESH,
            CascadeType.DETACH
    },mappedBy = "subscription")
    private Customer customer;

    public Subscription() {

    }

    public Subscription(String name, Double price, String descrition) {
        this.name = name;
        this.price = price;
        this.descrition = descrition;
    }


    public String getName() {
        return name;
    }

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

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public String getDescrition() {
        return descrition;
    }

    public void setDescrition(String descrition) {
        this.descrition = descrition;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    @Override
    public String toString() {
        return "Package{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", descrition='" + descrition + '\'' +
                '}';
    }
}

CascadeType.REMOVE被省略了,以避免一旦订阅被删除就删除客户,以展示级联的作用。

mappedBy = "subscription" - 添加的目的是将关系标记为双向的,这意味着当一个订阅被加载时,客户可以被识别,反之亦然。

第4步:创建和执行主应用程序

在这个阶段,将涵盖以下内容。

  • 创建一个新的客户。
  • 通过ID检索客户,并使用setter方法为客户分配一个新的订阅。
  • 通过id检索客户并删除,以看到应用于订阅的级联删除。
  • 使用setter方法检索和更新一个客户。

创建一个新的客户

import com.javadev.hibernate.demo.entity.Customer;
import com.javadev.hibernate.demo.entity.Subscription;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class CreateCustomer {
    public static void main(String[] args) {
        //create session factory
        SessionFactory factory = new Configuration()
                .configure("hibernate.cfg.xml")
                .addAnnotatedClass(Customer.class)
                .addAnnotatedClass(Subscription.class)
                .buildSessionFactory();
        //create session
        Session session = factory.getCurrentSession();
        try {
            //create the object
            Customer tempCustomer = new Customer("john","doe","john@javadev.com");

            //start a transaction
            session.beginTransaction();
            //save the object
            System.out.println("Saving the customer");
            session.persist(tempCustomer);

            //commit the transaction
            session.getTransaction().commit();
            System.out.println("Done!!");
        }finally {
            //clean up code
            session.close();
            factory.close();
        }

    }
}

当主程序被执行时,将创建一个表customer和subscription,并在customer表中插入一条新行,使用下面的命令可以看到创建的表和插入的行。

use database_name;

show tables;

select * from customer;

新的客户条目将由查询返回,注意sub_id为空。

create customer

通过id检索客户,并使用setter方法为客户分配一个新的订阅。

import com.javadev.hibernate.demo.entity.Customer;
import com.javadev.hibernate.demo.entity.Subscription;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class CreateCustomerSubscription {
    public static void main(String[] args) {
        //create session factory
        SessionFactory factory = new Configuration()
                .configure("hibernate.cfg.xml")
                .addAnnotatedClass(Customer.class)
                .addAnnotatedClass(Subscription.class)
                .buildSessionFactory();
        //create session
        Session session = factory.getCurrentSession();
        try {
            int theId =1;

            Subscription subscription = new Subscription("Beginner",1400.00,"This subscription is for beginners");
            //start a transaction
            session.beginTransaction();

            //read a customer
            Customer customer = session.get(Customer.class,theId);

            customer.setSubscription(subscription);
            //save the object
            System.out.println("Saving the customer");
            session.persist(customer);

            //commit the transaction
            session.getTransaction().commit();
            System.out.println("Done!!");
        }finally {
            //clean up code
            session.close();
            factory.close();
        }

    }
}

一条新的记录将被插入到订阅表中,客户sub_id列将被更新为外键id,指的是客户被设置到的订阅。这是因为CascadeType.ALL被设置在客户类中。

customer subscription

通过ID检索客户并删除,以看到应用于订阅的级联删除。

import com.javadev.hibernate.demo.entity.Customer;
import com.javadev.hibernate.demo.entity.Subscription;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class DeleteSubscription {
    public static void main(String[] args) {
        //create session factory
        SessionFactory factory = new Configuration()
                .configure("hibernate.cfg.xml")
                .addAnnotatedClass(Customer.class)
                .addAnnotatedClass(Subscription.class)
                .buildSessionFactory();
        //create session
        Session session = factory.getCurrentSession();
        try {

            int theId =1;
            session.beginTransaction();

            //read a subscription
            Subscription subscription = session.get(Subscription.class,theId);

            subscription.getCustomer().setSubscription(null); //brakes the bidirectional link of customer and subscription

            session.delete(subscription);

            //commit the transaction
            session.getTransaction().commit();
            System.out.println("Done!!");
        }finally {
            //clean up code
            session.close();
            factory.close();
        }

    }
}

请注意,因为在customer和subscription之间有一个双向的链接,所以要删除一个subscription,链接必须被破坏。

由于CascadeType.REMOVE在订阅类中被遗漏,删除一个订阅将保持客户。

deleting subscription

订阅表将返回一个空集,而客户表的列sub_id将被更新为null。

使用setter方法检索和更新一个客户

import com.javadev.hibernate.demo.entity.Customer;
import com.javadev.hibernate.demo.entity.Subscription;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class UpdateCustomer {
    public static void main(String[] args) {
        //create session factory
        SessionFactory factory = new Configuration()
                .configure("hibernate.cfg.xml")
                .addAnnotatedClass(Customer.class)
                .addAnnotatedClass(Subscription.class)
                .buildSessionFactory();
        //create session
        Session session = factory.getCurrentSession();
        try {

            //start a transaction
            session.beginTransaction();

            int customerId =1;

            //read a customer
            Customer tempCustomer = session.get(Customer.class,customerId);

            //update customer last name
            tempCustomer.setLastName("bilal");

            //save the object
            System.out.println("Saving the customer");

            session.persist(tempCustomer);

            //commit the transaction
            session.getTransaction().commit();
            System.out.println("Done!!");
        }finally {
            //clean up code
            session.close();
            factory.close();
        }

    }
}

updating customer

总结

通过覆盖和实现这些操作,读者应该更有信心接近一对一的Hibernate任务并按要求交付。

至此,读者已经学会了如何通过应用一对一的映射策略,使用hibernate创建、读取、更新和删除数据。