SSH框架-初识Hibernate

Author Avatar
Orange 4月 08, 2018
  • 在其它设备中阅读本文章

概述

早期的应用程序均直接调用数据库软件提供的方法手册读取数据,当需要对数据库进行更换、升级时,必须重写与数据库的通讯方法,这对开发人员是个巨大的挑战。因此孕育出JDBC解决方案。
JDBC(Java DataBase Connectivity)方案是Java连接数据库的标准规范。通过增加一层固定的标准类与接口,使得开发人员无需关注数据库操作方法,只需调用JDBC提供的各种API即可。而数据库生产商只需实现JDBC驱动层即可。如图所示:
JDBC简图 JDBC API
JDBC执行流程图如下:
JDBC执行流程图
现在只需导入相应数据库JDBC驱动包,根据流程图,即可写出如下代码:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
  1. public class JDBCUtil {
    public static Connection getConnection() throws Exception {
    //根据Java反射机制动态加载驱动程序
    Class.forName("com.dataBase.jdbc.Driver");
    return DriverManager.getConnection(
    "jdbc:dataBase://localhost:port/example",
    "username",
    "password");
    }
    }
    public class Product {
    private int id;
    private String name;
    private float price;

    public Product(int id, String name, float price) {
    this.id = id;
    this.name = name;
    this.price = price;
    }
    }
    public class ProductDao {
    public Product getProduct(int id) throws Exception {
    Connection conn = null;
    Statement st = null;
    ResultSet rs = null;
    try{
    conn = JDBCUtil.getConnection();
    st = conn.createStatement();
    rs = st.executeQuery("select id,name,price from t_product where id='" + id + "'");
    if (rs.next()) {
    int id = rs.getInt("id");
    String name = rs.getString("name");
    float price = rs.getFloat("price");
    Product product = new Product(id, name, price);
    return product;
    }
    } finally {
    try{
    rs.close();
    st.close();
    conn.close();
    } catch(SQLException e) {
    e.printStackTrace();
    }
    }
    }
    }

现在只用JDBCUtil类即可,而无需关心数据库调用方式。不过每次执行SQL语句均需重新创建Connection对象与数据库连接,且代码中依旧夹杂着驱动程序包名、JDBC URL地址、用户名、密码,不便修改。于是引入JNDI技术,通过创建数据库连接池缓存Connection对象,同时将配置字段写入XML配置文件,无需改动JDBC连接代码,以不变应万变。以Tomcat为例,操作如下:

  1. 配置Tomcat,在web.xml配置文件中写入如下内容:

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
    1. <web-app>
      ...
      <resource-ref>
      <res-ref-name>dataBaseDataSource</res-ref-name>
      <res-ref-type>javax.sql.DataSource</res-ref-type>
      <res-auth>Container</res-auth>
      <res-sharing-scope>Shareable</res-sharing-scope>
      </resource-ref>
      </web-app>
  2. 在MATA-INF文件夹中创建文件 context.xml 并写入以下内容:

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
    1. <?xml version="1.0" encoding="UTF-8"?>
      <Context antiJARLocking="true" path="/webProject">
      <!--auth="Container"表示资源由容器管理-->
      <Resource
      name="dataBaseDataSource"
      auth="Container"
      type="javax.sql.DataSource"
      driverClassName="com.dataBase.jdbc.Driver"
      url="jdbc:dataBase://localhost:port/example"
      username="username"
      password="password"
      />
      </Context>
  3. 创建JNDI工具类用于连接数据库

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
    1. public class JNDIUtil {
      public static Connection getConnection() throws Exception {
      Connection connOracle = null;
      try {
      Context initContext = new InitialContext();
      DataSource ds = (DataSource)initContext.lookup("java:comp/env/dataBaseDataSource");
      return ds.getConnection();
      }
      }
      }

JNDI执行流程图如下:
JNDI执行流程图

  1. 创建Context对象读取配置文件
  2. 通过DataSource数据资源对象应用配置创建数据库连接池
  3. 创建Connection数据库连接对象,连接数据库
  4. 创建Statement数据库操作对象,用于数据存取操作
  5. 调用Statement会话对象的数据库操作方法返回ResultSet结果集对象。

然而在有关数据库的操作代码中,我们依然需要使用 模型(POJO, 即JavaBean) + 数据库操作层(Dao) 模式,在Dao层(如上文ProductDao类)编写大量冗余代码以及不同SQL语句,为让生活更加美好,对JDBC轻量封装的Hibernate横空出世,其采用的ROM框架技术大受追捧。

ROM技术

由于Java是面向对象编程(OOP)语言与关系型数据库思想格格不入,故产生ORM(Object Relationship Mapping)即对象关系映射技术,使开发人员更加专注于面向对象思考。

Hibernate简介

Hibernate为一实现JPA标准(即 ROM+实体对象持久化 标准)的开源对象关系映射框架,支持分布式数据库。其对JDBC进行了轻量级对象封装,使得由直接操纵数据库变成使用依据映射数据表(XML配置文件)或注解生成的Java类,且生成的实体对象持续自动将数据变动保存至数据库(即持久化技术),从而以对象编程思维操纵数据库。同时,Hibernate摆脱不同数据库SQL语句差异,转而接收符合描述对象的HQL语句操作。

Hibernate原理

Hibernate原理如图所示:
Hibernate原理

  • 持久化对象:依据映射数据表(HBM,即关于类的XML配置文件)或注解生成的能够持续自动将数据变动保存至数据库的实体对象。
  • 配置文件:即Hibernate配置文件hibernate.cfg.xml。
  • 映射文件:持久化对象的Java类对应的映射数据表文件object.hbm.xml(如student.hbm.xml),用于数据库数据与Java对象转化。

Hibernate执行流程如下:
Hibernate执行流程图

  1. 创建Configuration对象读取hibernate.cfg.xml配置文件
  2. 通过StandardServiceRegistryBuilder对象应用配置创建ServiceRegistry服务注册对象用于注册各项服务
  3. 创建MetaData元数据(数据库信息)对象,此步可省略,仅当需要执行表结构操作时使用
  4. 创建SessionFactory会话工厂对象,通过Configuration对象的 buildSessionFactory(serviceRegistry) 传入服务注册对象注册此工厂服务,用于生产封装了连接池中Connection对象的会话(Session)对象;若存在MetaData对象,也可调用其 buildSessionFactory() 方法
  5. 创建Session会话对象,用于数据存取操作
  6. 调用Session会话对象的 beginTransaction() 开启事务,通过回滚等操作避免部分修改造成数据不完整,确保数据库被正确修改
  7. 调用Session会话对象的数据库操作方法直接返回对象结果。

对比JNDI发现除去可省略的获取元数据对象步骤3,以及启动事务步骤6,基本大同小异。
依据流程图,写出如下代码:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
  1. public class HibernateUtil {
    private static Metadata metadata;
    public static SessionFactory sessionFactory;
    public static Session session;
    public static Transaction transaction;
    static {
    Configuration configuration = new Configuration().configure();
    ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).configure().build();
    metadata = new MetadataSources(serviceRegistry).buildMetadata();
    sessionFactory = metadata.buildSessionFactory();
    session = sessionFactory.openSession();
    transaction = session.beginTransaction();
    }
    }
    public class ProductDao {
    public Product getProduct(int id) throws Exception {
    try{
    Product product = HibernateUtil.session.get(Product.class, id);
    return product;
    } finally {
    try{
    HibernateUtil.transaction.commit();
    HibernateUtil.session.close();
    HibernateUtil.sessionFactory.close();
    } catch(SQLException e) {
    e.printStackTrace();
    }
    }
    }
    }

省时省力有木有😂!
Hibernate不但支持单个数据库JDBC连接方式,也支持多个数据库JTA连接方式,本文仅讨论单个数据库连接方式。

Hibernate使用

环境配置

  1. 创建Java或Java Web项目并导入与Hibrenate相关的Jar开发包。开发包下载地址
  2. 在src文件夹内创建hibernate.cfg.xml文件,该文件用于配置数据库连接方式、驱动程序包名、JDBC URL地址、用户名、密码等

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
    1. <?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="connection.url">jdbc:dataBase://localhost:port/schema</property>
      <property name="connection.driver_class">com.dataBase.jdbc.Driver</property>
      <!--属性dialect表示SQL方言,使用对应于数据库的SQL方言配置提高数据库操作效率-->
      <property name="dialect">org.hibernate.dialect.DataBaseDialect</property>
      <property name="connection.username">username</property>
      <property name="connection.password">password</property>
      <!--输出SQL语句至控制台,一般用于调试-->
      <property name="show_sql">true</property>
      <!--格式化SQL语句,即美化显示SQL语句-->
      <property name="format_sql">true</property>
      <!--最近会话的上下文(相关环境)类,仅当使用sessionFactory.getCurrentSession()时需要此配置-->
      <!--分thread、jta、指定事务管理类名三种取值,thread表示在当前线程事务管理对象中寻找最近会话,jta表示在分布式事务管理对象中寻找最近会话-->
      <!--由于本文不讨论JTA分布式数据库连接方式,故只能取值thread-->
      <property name="current_session_context_class">thread</property>
      <!--HBM(映射数据表)转DDL(数据库模式定义语言,即SQL关键词中的create alter drop ...)方式-->
      <!--分create、update两种取值,前者每次操作将重新创建表,后者仅修改表-->
      <property name="hbm2ddl.auto">update</property>

      </session-factory>
      </hibernate-configuration>
  3. 创建Hibernate工具类进行使用

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
    1. public class HibernateUtil {
      private static Metadata metadata;
      public static SessionFactory sessionFactory;
      public static Session session;
      public static Transaction transaction;
      static {
      Configuration configuration = new Configuration().configure();
      ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).configure().build();
      metadata = new MetadataSources(serviceRegistry).buildMetadata();
      sessionFactory = metadata.buildSessionFactory();
      //使用getCurrentSession()无需手动关闭会话,由会话管理上下文(即事务管理对象)控制
      session = sessionFactory.getCurrentSession();
      transaction = session.beginTransaction();
      }
      }

映射数据表

映射数据表文件内容模板如下:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  1. <?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">
    <!--属性package对应包名-->
    <hibernate-mapping package="pacakge">
    <!--属性name对应JavBean的类名-->
    <!--属性table对应数据库中存储此类数据的表名-->
    <class name="student" table="tb_table">

    </class>
    </hibernate-mapping>

id

id标签用于指定主键,例如:

  1. 1
  1. <id name="sid" column="id" type="java.lang.Integer"></id>

generator

generator标签用于指定主键生成策略(手动输入或自动增长等),例如:

  1. 1
    2
    3
    4
  1. <id name="id" column="sid" type="java.lang.Integer">
    <!--native表示主键值自动增长,根据数据库不同自动选择对应自增配置方式-->
    <generator class="native"/>
    </id>

以下为class属性值可选内容:

  • assigned:主键由程序生成
  • increment:主键为表中最大值加一
  • hilo:主键由高低位方式(high low)生成。先在表中增长high值,再在[0, max_low]区间中增长low值,最后计算high * (max_low + 1) + low得出主键值
  • seqhilo:同理hilo,使用序列存储high值
  • sequence:主键为序列中最大值加一
  • identity:主键由数据库自增字段生成
  • native:主键由identity或hilo或sequence生成
  • uuid:主键为通用唯一标识符(UUID)字符串
  • guid:类似uuid,主键为全球唯一标识符(GUID)字符串
  • foreign:主键为另一表中主键
  • select:主键由触发器生成

property

property标签用于指定普通属性,例如:

  1. 1
    2
    3
  1. <!--column表示该字段在数据库中对应列-->
    <!--not-null表示该字段在数据库中是否禁止为空-->
    <property name="name" column="sName" type="java.lang.String" not-null="true"/>

set

set标签用于指定集合属性,例如:

  1. 1
    2
  1. <!--lazy表示是否采用延迟加载,即读取时才查找表中数据组成集合-->
    <set name="scoreSet" lazy="true"></set>

key

key标签用于指定集合对应的外键列

  1. 1
    2
    3
  1. <set name="scoreSet" lazy="true">
    <key column="scoreId"/>
    </set>

one-to-one

one-to-one标签用于指定一对一关系,如学生信息表对身份证表

  1. 1
    2
    3
  1. <!--cascade表示进行关联操作的时机-->
    <!--分为none、save-update、delete、all四种取值,none为默认值,即不进行关联操作,save-update即save/update时进行关联操作,delete即delete时进行关联操作,all即save/update/delete时进行关联操作-->
    <one-to-one name="cardId" class="package.Identity" cascade="save-update"/>

one-to-many

one-to-many标签用于指定一对多关系,如教室表对学生信息表

  1. 1
    2
    3
    4
    5
  1. <!--写于Class.hbm.xml文件中-->
    <set name="userSet" inverse="true" lazy="true">
    <key column="sid"/>
    <one-to-many class="package.User"/>
    </set>

many-to-one

many-to-one标签用于指定多对一关系,如学生信息表对教室表

  1. 1
    2
    3
    4
    5
  1. <!--写于Student.hbm.xml文件中-->
    <set name="userSet" inverse="true" lazy="true">
    <key column="sid"/>
    <many-to-one class="package.User"/>
    </set>

many-to-many

many-to-many标签用于指定多对多关系,如学生信息表对教师表

  1. 1
    2
    3
    4
  1. <set name="userSet" inverse="true" lazy="true">
    <key column="sid"/>
    <many-to-many class="package.User" column="sid"/>
    </set>

注解

除某些表示类的注解外,适用于属性的注解既可置于属性上,也可置于对应get方法上,建议置于方法上
关系注解可组合使用,如@OneToMany与@ManyToOne可分别在两个类中同时出现。

@Entity

Entity注解用于表示类为实体(映射对象)类,例如:

  1. 1
    2
    3
    4
  1. @Entity
    public class TbStudentEntity {
    ...
    }

@Table

Table注解用于表示类对应的数据库表信息,例如:

  1. 1
    2
    3
    4
    5
  1. @Entity
    @Table(name = "tb_student", schema = "hibernate")
    public class TbStudentEntity {
    ...
    }

@Embeddable

Embeddable注解用于表示类为嵌入式实体(映射对象)类(即数据库表中嵌入其所有属性,而不单独创建表),常与@Embedded联用,例如:

  1. 1
    2
    3
    4
    5
    6
  1. @Embeddable
    public class Address {
    private String country;
    private String province;
    private String city;
    }

@Embedded

Embedded注解用于表示嵌入式属性约束(即数据库表中嵌入其所有属性,而不单独创建表),常与@Embeddable联用,例如:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  1. @Embeddable
    public class Address {
    private String country;
    private String province;
    private String city;
    }
    @Entity
    @Table(name = "tb_student", schema = "hibernate")
    public class TbStudentEntity {
    private Address address;

    public TbStudentEntity() {}
    @Embedded
    public int getAddress() { return address; }
    public void setAddress(Address address) { this.address = address; }
    }

@EmbeddedId

EmbeddedId注解用于表示嵌入式主键属性约束,类似@Embedded,例如:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  1. @Embeddable
    public class PrimaryKey {
    private int id;
    private String name;
    public PrimaryKey() {}
    }
    @Entity
    @Table(name = "tb_student", schema = "hibernate")
    public class TbStudentEntity {
    private PrimaryKey primaryKey;

    public TbStudentEntity() {}
    @EmbeddedId
    public int getPrimaryKey() { return primaryKey; }
    public void setPrimaryKey(PrimaryKey primaryKey) { this.primaryKey = primaryKey; }
    }

@Id

Id注解用于表示主键属性,例如:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  1. @Entity
    @Table(name = "tb_student", schema = "hibernate")
    public class TbStudentEntity {
    private int id;

    public TbStudentEntity() {}
    @Id
    @GeneratedValue(generator = "AutoInc")
    @GenericGenerator(name = "AutoInc", strategy = "native")
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    }

@GeneratedValue

GeneratedValue注解用于表示主键属性,例如:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  1. @Entity
    @Table(name = "tb_student", schema = "hibernate")
    public class TbStudentEntity {
    private int id;

    public TbStudentEntity() {}
    @Id
    // strategy表示默认主键生成方法,generator表示自定义主键生成器(默认为空,无需设定)
    @GeneratedValue(strategy = GenerationType.AUTO, generator="")
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    }

以下为strategy属性值可选内容:

  • GenerationType.AUTO:主键由程序生成
  • GenerationType.IDENTITY:主键由数据库自增字段生成
  • GenerationType.SEQUENCE:主键为序列中最大值加一
  • GenerationType.TABLE:主键为新建表中最大值加一

@GenericGenerator

由于GeneratedValue取值不够灵活
GenericGenerator注解用于定义自定义主键生成器,例如:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  1. @Entity
    @Table(name = "tb_student", schema = "hibernate")
    public class TbStudentEntity {
    private int id;

    public TbStudentEntity() {}
    @Id
    @GenericGenerator(name = "AutoInc", strategy = "native")
    @GeneratedValue(generator = "AutoInc")
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    }

@Column

Column注解用于表示属性对应列,例如:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  1. @Entity
    @Table(name = "tb_student", schema = "hibernate")
    public class TbStudentEntity {
    private int id;
    private String introduce;

    public TbStudentEntity() {}
    @Id
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    @Column(name = "column_introduce", length = 100)
    public String getIntroduce() { return introduce; }
    public void setIntroduce(String introduce) { this.introduce = introduce; }
    }

@JoinColumn

JoinColumn注解用于表示应用某种关系而加入主表的列,常与@OneToOne、@OneToMany、@ManyToOne、@ManyToMany注解一同使用

@JoinTable

JoinTable注解用于表示应用某种关系而加入的中间(关系)表,常与@JoinColumn、@ManyToMany注解一同使用

@OneToOne

OneToOne注解用于表示一对一关系,如学生信息表对身份证表,分以下两种情况:

  1. 默认身份证类不存储对应学生对象,代码如下

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
    1. @Entity
      @Table(name = "tb_identify", schema = "hibernate")
      public class Identify {
      private String cardId;

      public Identify() {}
      @Id
      public String getCardId() { return cardId; }
      public void setCardId(String cardId) { this.cardId = cardId; }
      }
      @Entity
      @Table(name = "tb_student", schema = "hibernate")
      public class TbStudentEntity {
      private int id;
      private Identify identify;

      public TbStudentEntity() {}
      @Id
      public int getId() { return id; }
      public void setId(int id) { this.id = id; }
      //cascade表示进行关联操作的时机
      //分为none、save-update、delete、all四种取值,none为默认值,即不进行关联操作,save-update即save/update时进行关联操作,delete即delete时进行关联操作,all即save/update/delete时进行关联操作
      @OneToOne(cascade = CascadeTtype.ALL)
      //加入主表tb_student的外键列
      //name表示列名
      //referencedColumnName表示对应外表列名
      //unique表示是否唯一,由于一对一关系不包含重复内容,故设置为true
      @JoinColum(name = "cardId", referencedColumnName = "cardId", unique = true)
      public Identify getIdentify() { return identify; }
      public void setIdentify(Identify identify) { this.identify = identify; }
      }

    此时数据库中,主表 tb_student 将添加 cardId 列作为外键关联于表 tb_identifycardId
    数据库表图

  2. 不改变数据库表使身份证类存储对应学生对象,代码如下

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
    1. @Entity
      @Table(name = "tb_identify", schema = "hibernate")
      public class Identify {
      private String cardId;
      private TbStudentEntity student;

      public Identify() {}
      @Id
      public String getCardId() { return cardId; }
      public void setCardId(String cardId) { this.cardId = cardId; }
      //mappedBy表示该属性为被控属性,内容由TbStudentEntity对象的getIdentify()方法控制,tb_identify表并不包含此列
      @OneToOne(mappedBy = "identify")
      @JoinColum(name = "cardId", unique = true)
      public TbStudentEntity getStudent() { return student; }
      public void setStudent(TbStudentEntity student) { this.student = student; }
      }
      @Entity
      @Table(name = "tb_student", schema = "hibernate")
      public class TbStudentEntity {
      private int id;
      private Identify identify;

      public TbStudentEntity() {}
      @Id
      public int getId() { return id; }
      public void setId(int id) { this.id = id; }
      //cascade表示进行关联操作的时机
      //分为none、save-update、delete、all四种取值,none为默认值,即不进行关联操作,save-update即save/update时进行关联操作,delete即delete时进行关联操作,all即save/update/delete时进行关联操作
      @OneToOne(cascade = CascadeTtype.ALL)
      //加入主表tb_student的外键列
      //name表示列名
      //referencedColumnName表示对应外表列名
      //unique表示是否唯一,由于一对一关系不包含重复内容,故设置为true
      @JoinColum(name = "cardId", referencedColumnName = "cardId", unique = true)
      public Identify getIdentify() { return identify; }
      public void setIdentify(Identify identify) { this.identify = identify; }
      }

    此时数据库中,主表 tb_student 将添加 cardId 列作为外键关联于表 tb_identifycardId
    数据库表图

@OneToMany

OneToMany注解用于表示一对多关系,如教室表对学生信息表,例如:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
  1. @Entity
    @Table(name = "tb_classroom", schema = "hibernate")
    public class ClassRoom {
    private int roomId;
    private Set<Student> students;

    public ClassRoom() {}
    @Id
    public int getRoomId() { return roomId; }
    public void setRoomId(int roomId) { this.roomId = roomId; }
    //cascade表示进行关联操作的时机
    //分为none、save-update、delete、all四种取值,none为默认值,即不进行关联操作,save-update即save/update时进行关联操作,delete即delete时进行关联操作,all即save/update/delete时进行关联操作
    //fetch表示进行关联加载的态度
    //分为eager、lazy两种取值,eager即积极态度立即进行关联加载,lazy即懒散态度用时进行关联加载
    //此处读取学生集合需大量查询故选择lazy
    @OneToMany(cascade = CascadeTtype.ALL, fetch = FetchType.LAZY)
    //加入主表tb_student的外键列
    //name表示列名
    //referencedColumnName表示对应外表列名
    @JoinColumn(name = "crId", referencedColumnName = "roomId")
    public Set<Student> getStduents() { return students; }
    public void setStduents(Set<Student> students) { this.students = students; }
    }
    @Entity
    @Table(name = "tb_student", schema = "hibernate")
    public class TbStudentEntity {
    private int id;

    public TbStudentEntity() {}
    @Id
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    }

  1. 此时数据库中,主表 `tb_student` 将添加 `crId` 列作为外键关联于表 `tb_classroom` `roomId`
  2. ![数据库表图](https://img.vim-cn.com/e7/ac6932a41a10bc21e451063cb10db9fdefa210.png)

@ManyToOne

ManyToOne注解用于表示多对一关系,如学生信息表对教室表,例如:

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
  1. @Entity
    @Table(name = "tb_classroom", schema = "hibernate")
    public class ClassRoom {
    private int roomId;

    public ClassRoom() {}
    @Id
    public int getRoomId() { return roomId; }
    public void setRoomId(int roomId) { this.roomId = roomId; }
    }
    @Entity
    @Table(name = "tb_student", schema = "hibernate")
    public class TbStudentEntity {
    private int id;
    private ClassRoom room;

    public TbStudentEntity() {}
    @Id
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    //cascade表示进行关联操作的时机
    //分为none、save-update、delete、all四种取值,none为默认值,即不进行关联操作,save-update即save/update时进行关联操作,delete即delete时进行关联操作,all即save/update/delete时进行关联操作
    //fetch表示进行关联加载的态度
    //分为eager、lazy两种取值,eager即积极态度立即进行关联加载,lazy即懒散态度用时进行关联加载
    //此处读取教室号无需大量查询故选择eager
    @ManyToOne(cascade = CascadeTtype.ALL, fetch = FetchType.EAGER)
    //加入主表tb_student的外键列
    //name表示列名
    //referencedColumnName表示对应外表列名
    @JoinColumn(name = "crId", referencedColumnName = "roomId")
    public ClassRoom getRoom() { return this.room; }
    public void setRoom(ClassRoom room) { this.room = room; }
    }

  1. 此时数据库中,主表 `tb_student` 将添加 `crId` 列作为外键关联于表 `tb_classroom` `roomId`
  2. ![数据库表图](https://img.vim-cn.com/e7/ac6932a41a10bc21e451063cb10db9fdefa210.png)

@ManyToMany

ManyToMany注解用于表示多对多关系,如学生信息表对教师表,分以下两种情况:

  1. 默认在数据库中添加学生为主的中间(关系)表使学生类存储对应教师对象集合,代码如下

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
    1. @Entity
      @Table(name = "tb_teacher", schema = "hibernate")
      public class Teacher {
      private int teacherId;

      public Teacher() {}
      @Id
      public int getTeacherId() { return teacherId; }
      public void setTeacherId(String teacherId) { this.teacherId = teacherId; }
      }
      @Entity
      @Table(name = "tb_student", schema = "hibernate")
      public class TbStudentEntity {
      private int id;
      private Set<Teacher> teachers;

      public TbStudentEntity() {}
      @Id
      public int getId() { return id; }
      public void setId(int id) { this.id = id; }
      //cascade表示进行关联操作的时机
      //分为none、save-update、delete、all四种取值,none为默认值,即不进行关联操作,save-update即save/update时进行关联操作,delete即delete时进行关联操作,all即save/update/delete时进行关联操作
      @ManyToMany(cascade = CascadeTtype.ALL)
      //加入的中间(关系)表
      //name表示表名
      //joinColumns表示主键对应外表列集合
      //inverseJoinColumns表示非主键对应外表列集合
      @JoinTable(
      name = "students_teachers",
      joinColumns = {@JoinColum(name = "sid", referencedColumnName = "id")},
      inverseJoinColumns = {@JoinColum(name = "tid", referencedColumnName = "teacherId")},
      )
      public Set<Teacher> getTeachers() { return teachers; }
      public void setTeachers(Set<Teacher> teachers) { this.teachers = teachers; }
      }

    此时数据库中,中间(关系)表 students_teachers 将添加 sid 列作为外键关联于表 tb_studentid 列和 teacherId 列作为外键关联于表 tb_teacherteacherId
    数据库表图

  2. 不添加数据库教师为主的中间(关系)表使教师类存储对应学生对象集合,代码如下

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
    1. @Entity
      @Table(name = "tb_teacher", schema = "hibernate")
      public class Teacher {
      private int teacherId;
      private Set<TbStudentEntity> students;

      public Teacher() {}
      @Id
      public int getTeacherId() { return teacherId; }
      public void setTeacherId(String teacherId) { this.teacherId = teacherId; }
      //mappedBy表示该属性为被控属性,内容由TbStudentEntity对象的getTeachers()方法控制,并不创建中间(关系)表
      @ManyToMany(mappedBy = "teachers")
      public Set<TbStudentEntity> getStudents() { return students; }
      public void setStudents(Set<TbStudentEntity> students) { this.students = students; }
      }
      @Entity
      @Table(name = "tb_student", schema = "hibernate")
      public class TbStudentEntity {
      private int id;
      private Set<Teacher> teachers;

      public TbStudentEntity() {}
      @Id
      public int getId() { return id; }
      public void setId(int id) { this.id = id; }
      //cascade表示进行关联操作的时机
      //分为none、save-update、delete、all四种取值,none为默认值,即不进行关联操作,save-update即save/update时进行关联操作,delete即delete时进行关联操作,all即save/update/delete时进行关联操作
      @ManyToMany(cascade = CascadeTtype.ALL)
      //加入的中间(关系)表
      //name表示表名
      //joinColumns表示主键对应外表列集合
      //inverseJoinColumns表示非主键对应外表列集合
      @JoinTable(
      name = "students_teachers",
      joinColumns = {@JoinColum(name = "sid", referencedColumnName = "id")},
      inverseJoinColumns = {@JoinColum(name = "tid", referencedColumnName = "teacherId")},
      )
      public Set<Teacher> getTeachers() { return teachers; }
      public void setTeachers(Set<Teacher> teachers) { this.teachers = teachers; }
      }

    此时数据库中,中间(关系)表 students_teachers 将添加 sid 列作为外键关联于表 tb_studentid 列和 teacherId 列作为外键关联于表 tb_teacherteacherId
    数据库表图

创建模型

本文将创建 TbStudentEntity 类作为JavaBrean

使用映射数据表

  1. 创建 TbStudentEntity 类,实现默认构造函数,以及get、set方法,代码如下

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
    1. public class TbStudentEntity {
      private int id;
      private String name;
      private String sex;
      private LocalDate birthday;
      private String address;

      public TbStudentEntity() {}
      public int getId() { return id; }
      public void setId(int id) { this.id = id; }
      public String getName() { return name; }
      public void setName(String name) { this.name = name; }
      public String getSex() { return sex; }
      public void setSex(String sex) { this.sex = sex; }
      public LocalDate getBirthday() { return birthday; }
      public void setBirthday(LocalDate birthday) { this.birthday = birthday; }
      public String getAddress() { return address; }
      public void setAddress(String address) { this.address = address; }
      @Override
      public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      TbStudentEntity that = (TbStudentEntity) o;
      return id == that.id &&
      Objects.equals(name, that.name) &&
      Objects.equals(sex, that.sex) &&
      Objects.equals(birthday, that.birthday) &&
      Objects.equals(address, that.address);
      }
      @Override
      public int hashCode() {
      return Objects.hash(id, name, sex, birthday, address);
      }
      }
  2. 同时,在 hibernate.cfg.xml 文件中写入如下内容:

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
    1. <?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 ...>

      <!--载入映射数据表,设置resource属性值为映射数据表文件路径-->
      <mapping resource="package/TbStudentEntity.hbm.xml" />
      </session-factory>
      </hibernate-configuration>
  3. 随后在 TbStudentEntity 类同级文件夹内创建 TbStudentEntity.hbm.xml 映射数据表文件

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
    1. <?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 package="pacakge">
      <class name="TbStudentEntity" table="tb_student">
      <id name="id" column="sid" type="java.lang.Integer">
      <generator class="native"/>
      </id>
      <property name="name" column="sname" type="java.lang.String"/>
      <property name="sex" column="ssex" type="java.lang.String"/>
      <property name="age" column="sage" type="java.lang.Integer"/>
      <property name="birthday" column="sbirthday" type="java.lang.LocalDate"/>
      <property name="address" column="saddress" type="java.lang.String"/>
      </class>
      </hibernate-mapping>

使用注解

XML文件手动编写并不方便,需用工具生成。所以Hibernate提供注解方式(注解属于JPA标准),无需编写HBM。本文建议使用注解,步骤如下:

  1. 创建 TbStudentEntity 类,实现默认构造函数,以及get、set方法,并加上注解,代码如下

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
    1. @Entity
      @Table(name = "tb_student", schema = "hibernate")
      public class TbStudentEntity {
      private int id;
      private String name;
      private String sex;
      private LocalDate birthday;
      private String address;

      public TbStudentEntity() {}
      @Id
      @GeneratedValue(generator = "AutoInc")
      @GenericGenerator(name = "AutoInc", strategy = "native")
      public int getId() { return id; }
      public void setId(int id) { this.id = id; }
      public String getName() { return name; }
      public void setName(String name) { this.name = name; }
      public String getSex() { return sex; }
      public void setSex(String sex) { this.sex = sex; }
      public LocalDate getBirthday() { return birthday; }
      public void setBirthday(LocalDate birthday) { this.birthday = birthday; }
      public String getAddress() { return address; }
      public void setAddress(String address) { this.address = address; }
      @Override
      public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      TbStudentEntity that = (TbStudentEntity) o;
      return id == that.id &&
      Objects.equals(name, that.name) &&
      Objects.equals(sex, that.sex) &&
      Objects.equals(birthday, that.birthday) &&
      Objects.equals(address, that.address);
      }
      @Override
      public int hashCode() {
      return Objects.hash(id, name, sex, birthday, address);
      }
      }
  2. 同时,在 hibernate.cfg.xml 文件中写入如下内容:

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
    1. <?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 ...>

      <!--载入映射数据表,设置class属性值为映射数据表类路径-->
      <mapping class="package.TbStudentEntity" />
      </session-factory>
      </hibernate-configuration>

操作方法

session.createQuery()

该方法传入HQL语句,返回 Query 对象,HQL具体语法此处不再累赘
调用 Query 对象的 list() 方法返回对应模型类对象链表

  1. 1
    2
    3
  1. Query query = session.createQuery("from TbStudentEntity where id = ?");
    query.setParameter(0, 1);
    List<TbStudentEntity> students = query.list();

session.get()

该方法传入模型类与主键,返回对应模型类对象

  1. 1
    2
  1. TbStudentEntity tbStudentEntity = session.get(tbStudentEntity.class, 1);
    System.out.println(tbStudentEntity.getClass());

session.load()

该方法传入模型类与主键,返回对应模型代理类的对象,取值时才对数据库进行查询

  1. 1
    2
    3
    4
    5
    6
  1. TbStudentEntity tbStudentEntity = session.load(tbStudentEntity.class, 1);
    //取值之前对象类为TbStudentEntity_$$_Javassist_0代理类
    System.out.println(tbStudentEntity.getClass().getName());
    System.out.println(tbStudentEntity.name);
    //取值之后对象类为TbStudentEntity模型类
    System.out.println(tbStudentEntity.getClass().getName());

session.save()

该方法传入模型对象,并自动保存关联对象数据

  1. 1
    2
  1. TbStudentEntity tbStudentEntity = new TbStudentEntity("小明", "男", LocalDate.of(2018, 4, 8), "光明小区");
    session.save(tbStudentEntity);

session.delete()

该方法传入模型对象,删除数据库中对应数据

  1. 1
    2
  1. TbStudentEntity tbStudentEntity = new TbStudentEntity("小明", "男", LocalDate.of(2018, 4, 8), "光明小区");
    session.delete(tbStudentEntity);

总结

至此,Hibernate框架基本使用方法掌握。

CC许可协议署名非商业性使用相同方式共享
本文采用 CC BY-NC-SA 3.0 Unported 协议进行许可