第二十一部分_Hibernate级联关系详解

接下来我们开始介绍Hibernate的数据类型,因为我们现在暂时只关注Hibernate这块,因此我们这次只建立一个Java Project,命名为hibernate2。

加入hibernate JAR包:

选择hibernate2项目,点击MyEclipse->Add Hibernate Capabilities, Hibernate Specification与风中页老师的相同,为Hibernate3.2,点击next,继续next,去掉Specify database connection details前面的√接着next,去掉Create SessionFactory class?前面的√点击Finish。

把上一个hibernate项目的hibernate.cfg.xml文件拷贝过来,覆盖掉当前src下面的hibernate.cfg.xml文件,修改mapping信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
 
<!-- Generated by MyEclipse Hibernate Tools.                   -->
<hibernate-configuration>
 
    <session-factory>
        <property name="show_sql">true</property> <!-- 属性之间没有上下关系,放在哪里都行 -->
         
        <property name="connection.url">jdbc:mysql://localhost:3306/myhibernate2</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
         
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    
        <mapping resource="Customers.hbm.xml"/> <!-- 将主配置文件包含对象-关系映射文件,之所以映射是因为hibernate启动时只会加载主配置文件 -->
    
    </session-factory>
 
</hibernate-configuration>

添加MySql驱动,从hibernate项目拷贝mysql-connector-java-5.1.34-bin.jar到hibernate根目录下。

创建表:(bigint即long类型;bit即boolean类型;timestamp也是一个日期类型的,比date精度更高,可以精确到毫秒;blob即二进制大型物件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> create table CUSTOMERS(
    -> ID bigint not null primary key,
    -> NAME varchar(15) not null,
    -> EMAIL varchar(128) not null,
    -> PASSWORD varchar(8) not null,
    -> PHONE int,
    -> ADDRESS varchar(255),
    -> SEX char(1),
    -> IS_MARRIED bit,
    -> DESCRIPTION text,
    -> IMAGE blob,
    -> BIRTHDAY date,
    -> REGISTERED_TIME timestamp
    -> );

新建com.test.bean包,在该包下面新建一个类Customer.java:

在src下面新建一个Customers.hbm.xml:

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 
<hibernate-mapping>
 
    <class name="com.test.bean.Customer" table="customers"> <!-- 将类与表相关联,使得类中的属性和表中的字段关联起来 -->
         
        <id name="id" column="id" type="long"> <!-- 类中id属性和映射到表中的id字段,类型为int/integer皆可 -->
            <generator class="increment"> <!-- 主键id的生成方式为自增 -->
            </generator>
        </id>
     
        <property name="name" column="name" type="string" not-null="true"></property> <!-- 如果不写字段名,则默认与类中的属性名相同 ;hibernate层和数据库层都可以对非空进行检查-->
        <property name="email" column="email" type="string" not-null="true"></property>
        <property name="password" column="password" type="string" not-null="true"></property>
        <property name="phone" column="phone" type="int"></property>
        <property name="address" column="address" type="string" ></property>
        <property name="sex" column="sex" type="character" ></property>
        <property name="married" column="is_married" type="boolean" ></property>
        <property name="description" column="description" type="text"></property>
        <property name="image" column="image" type="binary" ></property>
        <property name="birthday" column="birthday" type="date" ></property>
        <property name="registeredTime" column="registered_time" type="timestamp"></property>
 
    </class>
 
</hibernate-mapping>

在com.test.bean包下面,创建测试类HibernateTest.java同时放置一个photo.gif文件:

通过给一下几行代码添加注释的方式进行测试:

1
2
3
4
5
6
7
8
9
saveCustomer(customer);
 
findAllCustomers(out);
 
loadAndUpdateCustomer(customer.getId(), "Tianjin");
 
findAllCustomers(out);
 
deleteAllCustomers();

运行后在hibernate2根目录下面会生成一个photo_copy.gif图形文件。

上面的例子比较简单,下面我们看一个复杂的:表与表之间存在关联关系,类与类之间存在关联关系:

新建一个名为hibernate3的Java Project,导入相应的hibernate包(操作过程如之前所述)。

配置hibernate.cfg.xml:

接下来创建两个域模型,一个是Customer,一个是Order:

src下面新建包com.test,在该包下面新建类:Customer.java:

接着新建与Customer成多对一关系的Order.java类:

下面,在数据模型,写配置文件,在这之前需要建立两张表:

数据库Schema:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
create table customers(
    ID bigint not null,
    NAME varchar(15),
    primary key(ID)
);
 
create table orders(
    ID bigint not null,
    ORDER_NUMBER varchar(15),
    CUSTOMER_ID bigint not null,
    primary key(ID)
);
 
alter table orders add index IDX_CUSTOMER_ID(CUSTOMER_ID),
                   add constraint FK_CUSTOMER_ID foreign key (CUSTOMER_ID) references customers(ID);

接下来在src下创建Customer.hbm.xml:

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"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 
<hibernate-mapping>
 
    <class name="com.test.Customer" table="customers"> <!-- 将类与表相关联,使得类中的属性和表中的字段关联起来 -->
         
        <id name="id" column="id" type="long"> <!-- 类中id属性和映射到表中的id字段,类型为int/integer皆可 -->
            <generator class="increment"> <!-- 主键id的生成方式为自增 -->
            </generator>
        </id>
     
        <property name="name" type="string">
            <column name="name" length="15"></column> <!-- 第二种定义column的方式,可以进行精细化配置 -->
        </property>
         
        <set name="orders" cascade="save-update" inverse="true"> <!-- 反转属性为true,表示关联关系由多的一方维持,这是hibernate的一个最佳实践。 -->
            <key column="customer_id"></key> <!-- key元素设定与所关联的持久化类对应的表的外键 -->
            <one-to-many class="com.test.Order"/>
        </set>
 
    </class>
 
</hibernate-mapping>

继续在src下创建Order.hbm.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 
<hibernate-mapping>
 
    <class name="com.test.Order" table="orders">
         
        <id name="id" column="id" type="long">
            <generator class="increment">
            </generator>
        </id>
     
        <property name="orderNumber" type="string">
            <column name="order_number" length="15"></column>
        </property>
         
        <many-to-one name="customer" column="customer_id" class="com.test.Customer">
        </many-to-one>
 
    </class>
 
</hibernate-mapping>

最后,不要忘记引入mysql的驱动:mysql-connector-java-5.1.34-bin.jar

然后,为了验证我们的配置是否正确且如愿生效,我们编写一个测试类,在com.test包下面新建一个类Test.java:

执行结果:Console输出:

1
2
3
4
5
6
Hibernate: select max(id) from customers
Hibernate: select max(id) from orders
Hibernate: insert into customers (name, id) values (?, ?)
Hibernate: insert into orders (order_number, customer_id, id) values (?, ?, ?)
Hibernate: insert into orders (order_number, customer_id, id) values (?, ?, ?)
Hibernate: insert into orders (order_number, customer_id, id) values (?, ?, ?)

进入数据库中查看,我们发现orders表中的外键已经关联到customers表中的主键上了。

补充图例:

下面,继续进行探索,对于树模型的关系——找到一个节点,能找到它的多个子节点(或者没有)和唯一(或者没有)的父节点,如:

首先建立域模型:在hibernate3项目下的com.test包下建立类:Category.java:

建立表,数据库Schema:

1
2
3
4
5
6
7
8
9
create table categories(
    ID bigint not null,
    NAME varchar(15),
    CATEGORY_ID bigint,
    primary key(ID)
);
 
alter table categories add index IDX_CATEGORY_ID(CATEGORY_ID),
               add constraint FK_CATEGORY_ID foreign key(CATEGORY_ID) references categories(ID);

建立Category.hbm.xml文件:

接下来,在Test.java中增加方法——saveCategoryWithCascade和deleteCategoryWithCascade:

最后在主配置文件hibernate.cfg.xml中增加对Category.hbm.xml文件的映射:

运行Test.java程序,可以看到保存和删除都已经实现了级联操作。

下面我们来看如何用hibernate表示一对一和多对多关系:

一对一在实际开发中用的也比较多,比如一个人对应一个身份证。这种一对一的关系可以使用共用主键来表达。首先导入工程hibernate1(风中叶银行企陪day6)

Student 和 Certificate 类及其相关的映射文件

schema:

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
drop database if exists schoolproject;
create database schoolproject;
use schoolproject;
 
drop table if exists certificate;
CREATE TABLE certificate (
  id varchar(100) NOT NULL default '',
  `describe` varchar(100) default '',
   
  PRIMARY KEY  (id)
) ENGINE = InnoDB
CHARACTER SET utf8 COLLATE utf8_general_ci;
 
 
--
-- Dumping data for table 'certificate'
--
 
INSERT INTO certificate VALUES ('ff80808105416d3b0105416d3eca0001','tomclus');
INSERT INTO certificate VALUES ('ff808081054175b501054175b9190001','tom');
 
--
-- Table structure for table 'student'
--
drop table if exists student;
CREATE TABLE student (
  id varchar(100) NOT NULL default '',
  name varchar(20) default '',
  `cardId` varchar(20) NOT NULL default '',
  age int(11) default '0',
  PRIMARY KEY  (id)
) ENGINE = InnoDB
CHARACTER SET utf8 COLLATE utf8_general_ci;
 
--
-- Dumping data for table 'student'
--
 
INSERT INTO student VALUES ('ff80808105416d3b0105416d3eca0001','tomclus','200512345',33);
INSERT INTO student VALUES ('ff808081054175b501054175b9190001','tom','11111111',33);

BM.java(business manager) 

下面是与之相关的类BaseDAO、StudentDAO、HibernateUtil:

引入MySql驱动,执行BM.java方法,执行一次保存操作后表中存储的数据有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mysql> select * from student;
+----------------------------------+---------+-----------+-----+
| id                               | name    | cardId    | age |
+----------------------------------+---------+-----------+-----+
| 40281f815004a9ef015004a9f0860001 | spark   | 200211332 33 |
| ff80808105416d3b0105416d3eca0001 | tomclus | 200512345 33 |
| ff808081054175b501054175b9190001 | tom     | 11111111  33 |
+----------------------------------+---------+-----------+-----+
3 rows in set
 
mysql> select * from certificate;
+----------------------------------+----------+
| id                               | describe |
+----------------------------------+----------+
| 40281f815004a9ef015004a9f0860001 | spark    |
| ff80808105416d3b0105416d3eca0001 | tomclus  |
| ff808081054175b501054175b9190001 | tom      |
+----------------------------------+----------+
3 rows in set
 
mysql>

可以看到一对一的主键关联已经成功实现了。

一对一的第二种实现方式:其实还是通过外键来关联的(这种实现方式实际上就是退化了的一对多关联)。重命名当前工作空间下的hibernate2项目,导入风中叶老师的hibernate2(day6),可以看到Student.java、Certificate.java、Student.hbm.xml都没用任何变换,唯一变化的是id的生成方式,和删除了one-to-one标签增加了many-to-one标签。

Certificate.hbm.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
    <class name="model.Certificate" table="certificate" lazy="true">
         
        <id name="id">
            <generator class="uuid.hex" />
        </id>
         
         
        <property name="describe" column="`describe`" type="string" />
         
         
        <many-to-one name="stu"
            class="model.Student" 
            unique="true" <!-- 多对一,而多的一方又是唯一的,暗示着多对一已经退化成了一对一了 -->
            column="stu_id" <!-- 外键 -->
        />  <!-- 唯一的多对一,实际也就变成一对一关系了 -->
         
         
         
    </class>
</hibernate-mapping>

数据库schema:

执行BM.java,里面的代码跟之前的一模一样。可以看到certificate表的stu_id已经和student表的id关联起来了。

下面我们来看相对来说最复杂的一种:多对多的映射类型

重命名当前项目下的hibernate3,导入风中叶老师的hibernate3(day6),我们看一下学生和课程之间的多对多通过hibernate如何实现。

多对多在程序中如何体现:两个类Student、Course,分别定义两个集合类型的变量:

而在数据库中要想体现多对多的关系,就需要使用连接表,连接表中的内容就是stu_id和course_id。一条这样的记录,就表示一个映射:该学生选择了该课程,该课程被该学生所选。

Student.hbm.xml:

Course.hbm.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="model.Course" table="course"
        select-before-update="true"><!--把类和数表关联起来-->
         
        <id name="id" unsaved-value="null" ><!--id的产生方式是uuid.hex-->
            <generator class="uuid.hex" />
        </id>
         
        <property name="name" type="string" /><!--映射课程名-->
         
        <set name="students" table="student_course"
            cascade="save-update">
            <key column="course_id" />
            <many-to-many class="model.Student"
                column="stu_id" />
        </set>
    </class>
</hibernate-mapping>

数据库schema:

相关类BM.java、StudentDAO.java、BaseDAO、HibernateUtil.java:

执行BM.java类,学生表和课程表没有任何变化,唯一变化的是中间表多了一行记录,用于映射新建立的学生和课程之间的关系。

补充知识点:域对象在持久化层的三种状态

 

posted @   Code_Rush  阅读(270)  评论(0编辑  收藏  举报
编辑推荐:
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
阅读排行:
· dotnet 源代码生成器分析器入门
· 官方的 MCP C# SDK:csharp-sdk
· 一款 .NET 开源、功能强大的远程连接管理工具,支持 RDP、VNC、SSH 等多种主流协议!
· 一步一步教你部署ktransformers,大内存单显卡用上Deepseek-R1
· 一次Java后端服务间歇性响应慢的问题排查记录
点击右上角即可分享
微信分享提示