ssh框架-初识hibernate ROM技术 Hibernate简介 Hibernate原理 Hibernate使用 总结

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

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
public class  {
public static Connection getConnection() throws Exception {

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
    2
    3
    4
    5
    6
    7
    8
    9
    <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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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
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
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
    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
    <?xml version='1.0' encoding='utf-8'?>

    "-//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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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
2
3
4
5
6
7
8
9
10
11
12
<?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
<id name="sid" column="id" type="java.lang.Integer"></id>

generator

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

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

set

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

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

key

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

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

one-to-one

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

1
2
3
<!--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
2
3
4
5
<!--写于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
2
3
4
5
<!--写于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
2
3
4
<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
2
3
4
@Entity
public class TbStudentEntity {
...
}

@Table

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

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

@Embeddable

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

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

@Embedded

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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
2
3
4
5
6
7
8
9
10
11
12
@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
2
3
4
5
6
7
8
9
10
11
12
@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
2
3
4
5
6
7
8
9
10
11
12
@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
2
3
4
5
6
7
8
9
10
11
12
13
14
@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
    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
    @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
    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
    @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
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
@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; }
}

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

@ManyToOne

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

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
@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; }
}

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

@ManyToMany

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

  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
    @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
    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
    @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
    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
    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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version='1.0' encoding='utf-8'?>

    "-//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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?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
    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
    @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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version='1.0' encoding='utf-8'?>

    "-//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
2
3
Query query = session.createQuery("from TbStudentEntity where id = ?");
query.setParameter(0, 1);
List<TbStudentEntity> students = query.list();

session.get()

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

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

session.load()

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

1
2
3
4
5
6
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
2
TbStudentEntity tbStudentEntity = new TbStudentEntity("小明", "男", LocalDate.of(2018, 4, 8), "光明小区");
session.save(tbStudentEntity);

session.delete()

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

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

总结

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


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