SSH框架-初识Hibernate
概述
早期的应用程序均直接调用数据库软件提供的方法手册读取数据,当需要对数据库进行更换、升级时,必须重写与数据库的通讯方法,这对开发人员是个巨大的挑战。因此孕育出JDBC解决方案。
JDBC(Java DataBase Connectivity)方案是Java连接数据库的标准规范。通过增加一层固定的标准类与接口,使得开发人员无需关注数据库操作方法,只需调用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
48public 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为例,操作如下:
配置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>在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>创建JNDI工具类用于连接数据库
1
2
3
4
5
6
7
8
9
10public 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执行流程图如下:
- 创建Context对象读取配置文件
- 通过DataSource数据资源对象应用配置创建数据库连接池
- 创建Connection数据库连接对象,连接数据库
- 创建Statement数据库操作对象,用于数据存取操作
- 调用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原理如图所示:
- 持久化对象:依据映射数据表(HBM,即关于类的XML配置文件)或注解生成的能够持续自动将数据变动保存至数据库的实体对象。
- 配置文件:即Hibernate配置文件hibernate.cfg.xml。
- 映射文件:持久化对象的Java类对应的映射数据表文件object.hbm.xml(如student.hbm.xml),用于数据库数据与Java对象转化。
Hibernate执行流程如下:
- 创建Configuration对象读取hibernate.cfg.xml配置文件
- 通过StandardServiceRegistryBuilder对象应用配置创建ServiceRegistry服务注册对象用于注册各项服务
- 创建MetaData元数据(数据库信息)对象,此步可省略,仅当需要执行表结构操作时使用
- 创建SessionFactory会话工厂对象,通过Configuration对象的
buildSessionFactory(serviceRegistry)
传入服务注册对象注册此工厂服务,用于生产封装了连接池中Connection对象的会话(Session)对象;若存在MetaData对象,也可调用其buildSessionFactory()
方法 - 创建Session会话对象,用于数据存取操作
- 调用Session会话对象的
beginTransaction()
开启事务,通过回滚等操作避免部分修改造成数据不完整,确保数据库被正确修改 - 调用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
30public 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使用
环境配置
- 创建Java或Java Web项目并导入与Hibrenate相关的Jar开发包。开发包下载地址
在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-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>创建Hibernate工具类进行使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public 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'?>
<!--属性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
public class TbStudentEntity {
...
}
@Table
Table注解用于表示类对应的数据库表信息,例如:1
2
3
4
5
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
...
}
@Embeddable
Embeddable注解用于表示类为嵌入式实体(映射对象)类(即数据库表中嵌入其所有属性,而不单独创建表),常与@Embedded联用,例如:1
2
3
4
5
6
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
public class Address {
private String country;
private String province;
private String city;
}
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private Address address;
public TbStudentEntity() {}
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
public class PrimaryKey {
private int id;
private String name;
public PrimaryKey() {}
}
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private PrimaryKey primaryKey;
public TbStudentEntity() {}
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
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
public TbStudentEntity() {}
"AutoInc") (generator =
"AutoInc", strategy = "native") (name =
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
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
public TbStudentEntity() {}
// strategy表示默认主键生成方法,generator表示自定义主键生成器(默认为空,无需设定)
"") (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
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
public TbStudentEntity() {}
"AutoInc", strategy = "native") (name =
"AutoInc") (generator =
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
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
private String introduce;
public TbStudentEntity() {}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
"column_introduce", length = 100) (name =
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
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
"tb_identify", schema = "hibernate") (name =
public class Identify {
private String cardId;
public Identify() {}
public String getCardId() { return cardId; }
public void setCardId(String cardId) { this.cardId = cardId; }
}
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
private Identify identify;
public TbStudentEntity() {}
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时进行关联操作
(cascade = CascadeTtype.ALL)
//加入主表tb_student的外键列
//name表示列名
//referencedColumnName表示对应外表列名
//unique表示是否唯一,由于一对一关系不包含重复内容,故设置为true
"cardId", referencedColumnName = "cardId", unique = true) (name =
public Identify getIdentify() { return identify; }
public void setIdentify(Identify identify) { this.identify = identify; }
}此时数据库中,主表
tb_student
将添加cardId
列作为外键关联于表tb_identify
的cardId
列
不改变数据库表使身份证类存储对应学生对象,代码如下
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
"tb_identify", schema = "hibernate") (name =
public class Identify {
private String cardId;
private TbStudentEntity student;
public Identify() {}
public String getCardId() { return cardId; }
public void setCardId(String cardId) { this.cardId = cardId; }
//mappedBy表示该属性为被控属性,内容由TbStudentEntity对象的getIdentify()方法控制,tb_identify表并不包含此列
"identify") (mappedBy =
"cardId", unique = true) (name =
public TbStudentEntity getStudent() { return student; }
public void setStudent(TbStudentEntity student) { this.student = student; }
}
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
private Identify identify;
public TbStudentEntity() {}
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时进行关联操作
(cascade = CascadeTtype.ALL)
//加入主表tb_student的外键列
//name表示列名
//referencedColumnName表示对应外表列名
//unique表示是否唯一,由于一对一关系不包含重复内容,故设置为true
"cardId", referencedColumnName = "cardId", unique = true) (name =
public Identify getIdentify() { return identify; }
public void setIdentify(Identify identify) { this.identify = identify; }
}此时数据库中,主表
tb_student
将添加cardId
列作为外键关联于表tb_identify
的cardId
列
@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
"tb_classroom", schema = "hibernate") (name =
public class ClassRoom {
private int roomId;
private Set<Student> students;
public ClassRoom() {}
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
(cascade = CascadeTtype.ALL, fetch = FetchType.LAZY)
//加入主表tb_student的外键列
//name表示列名
//referencedColumnName表示对应外表列名
"crId", referencedColumnName = "roomId") (name =
public Set<Student> getStduents() { return students; }
public void setStduents(Set<Student> students) { this.students = students; }
}
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
public TbStudentEntity() {}
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
"tb_classroom", schema = "hibernate") (name =
public class ClassRoom {
private int roomId;
public ClassRoom() {}
public int getRoomId() { return roomId; }
public void setRoomId(int roomId) { this.roomId = roomId; }
}
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
private ClassRoom room;
public TbStudentEntity() {}
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
(cascade = CascadeTtype.ALL, fetch = FetchType.EAGER)
//加入主表tb_student的外键列
//name表示列名
//referencedColumnName表示对应外表列名
"crId", referencedColumnName = "roomId") (name =
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
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
"tb_teacher", schema = "hibernate") (name =
public class Teacher {
private int teacherId;
public Teacher() {}
public int getTeacherId() { return teacherId; }
public void setTeacherId(String teacherId) { this.teacherId = teacherId; }
}
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
private Set<Teacher> teachers;
public TbStudentEntity() {}
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时进行关联操作
(cascade = CascadeTtype.ALL)
//加入的中间(关系)表
//name表示表名
//joinColumns表示主键对应外表列集合
//inverseJoinColumns表示非主键对应外表列集合
(
name = "students_teachers",
joinColumns = {"sid", referencedColumnName = "id")}, (name =
inverseJoinColumns = {"tid", referencedColumnName = "teacherId")}, (name =
)
public Set<Teacher> getTeachers() { return teachers; }
public void setTeachers(Set<Teacher> teachers) { this.teachers = teachers; }
}此时数据库中,中间(关系)表
students_teachers
将添加sid
列作为外键关联于表tb_student
的id
列和teacherId
列作为外键关联于表tb_teacher
的teacherId
列
不添加数据库教师为主的中间(关系)表使教师类存储对应学生对象集合,代码如下
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
"tb_teacher", schema = "hibernate") (name =
public class Teacher {
private int teacherId;
private Set<TbStudentEntity> students;
public Teacher() {}
public int getTeacherId() { return teacherId; }
public void setTeacherId(String teacherId) { this.teacherId = teacherId; }
//mappedBy表示该属性为被控属性,内容由TbStudentEntity对象的getTeachers()方法控制,并不创建中间(关系)表
"teachers") (mappedBy =
public Set<TbStudentEntity> getStudents() { return students; }
public void setStudents(Set<TbStudentEntity> students) { this.students = students; }
}
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
private Set<Teacher> teachers;
public TbStudentEntity() {}
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时进行关联操作
(cascade = CascadeTtype.ALL)
//加入的中间(关系)表
//name表示表名
//joinColumns表示主键对应外表列集合
//inverseJoinColumns表示非主键对应外表列集合
(
name = "students_teachers",
joinColumns = {"sid", referencedColumnName = "id")}, (name =
inverseJoinColumns = {"tid", referencedColumnName = "teacherId")}, (name =
)
public Set<Teacher> getTeachers() { return teachers; }
public void setTeachers(Set<Teacher> teachers) { this.teachers = teachers; }
}此时数据库中,中间(关系)表
students_teachers
将添加sid
列作为外键关联于表tb_student
的id
列和teacherId
列作为外键关联于表tb_teacher
的teacherId
列
创建模型
本文将创建 TbStudentEntity
类作为JavaBrean
使用映射数据表
创建
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
34public 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; }
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);
}
public int hashCode() {
return Objects.hash(id, name, sex, birthday, address);
}
}同时,在
hibernate.cfg.xml
文件中写入如下内容:1
2
3
4
5
6
7
8
9
10
11
12<?xml version='1.0' encoding='utf-8'?>
<hibernate-configuration>
<session-factory>
<property ...>
<!--载入映射数据表,设置resource属性值为映射数据表文件路径-->
<mapping resource="package/TbStudentEntity.hbm.xml" />
</session-factory>
</hibernate-configuration>随后在
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'?>
<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。本文建议使用注解,步骤如下:
创建
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
"tb_student", schema = "hibernate") (name =
public class TbStudentEntity {
private int id;
private String name;
private String sex;
private LocalDate birthday;
private String address;
public TbStudentEntity() {}
"AutoInc") (generator =
"AutoInc", strategy = "native") (name =
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; }
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);
}
public int hashCode() {
return Objects.hash(id, name, sex, birthday, address);
}
}同时,在
hibernate.cfg.xml
文件中写入如下内容:1
2
3
4
5
6
7
8
9
10
11
12<?xml version='1.0' encoding='utf-8'?>
<hibernate-configuration>
<session-factory>
<property ...>
<!--载入映射数据表,设置class属性值为映射数据表类路径-->
<mapping class="package.TbStudentEntity" />
</session-factory>
</hibernate-configuration>
操作方法
session.createQuery()
该方法传入HQL语句,返回 Query
对象,HQL具体语法此处不再累赘
调用 Query
对象的 list()
方法返回对应模型类对象链表1
2
3Query query = session.createQuery("from TbStudentEntity where id = ?");
query.setParameter(0, 1);
List<TbStudentEntity> students = query.list();
session.get()
该方法传入模型类与主键,返回对应模型类对象1
2TbStudentEntity tbStudentEntity = session.get(tbStudentEntity.class, 1);
System.out.println(tbStudentEntity.getClass());
session.load()
该方法传入模型类与主键,返回对应模型代理类的对象,取值时才对数据库进行查询1
2
3
4
5
6TbStudentEntity 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
2TbStudentEntity tbStudentEntity = new TbStudentEntity("小明", "男", LocalDate.of(2018, 4, 8), "光明小区");
session.save(tbStudentEntity);
session.delete()
该方法传入模型对象,删除数据库中对应数据1
2TbStudentEntity tbStudentEntity = new TbStudentEntity("小明", "男", LocalDate.of(2018, 4, 8), "光明小区");
session.delete(tbStudentEntity);
总结
至此,Hibernate框架基本使用方法掌握。
本文采用 CC BY-NC-SA 3.0 Unported 协议进行许可