1、一对一(会用即可)
主表:没有外键 有主键 从表:拥有外键的是从表 先操作从表,再去操作主表 one-to-one ,many-to-one 只是表达多个表之间的关系。 外键:目的是多表连接 主键:目的是唯一标识数据
示例: 一个人对应一个身份证 ,一夫一妻制, 一个萝卜一个坑。。。。
一对一的关系用程序如何描述:
示例:一夫一妻制
Husband.java
public class Husband{
private Integer marryId; //结婚编号
private String name; //名称
private Integer age; //年龄
private Integer hid; //丈夫的身份证号
private Wife wife; //丈夫和妻子有一对一的关系
set/get....
}
Wife.java
public class Wife{
private Integer marryId; //妻子的身份证号
private String name; //名称
private Integer age; //年龄
private Integer wid; //结婚编号
private Husband husband;//妻子和丈夫有一对一的关系
set/get....
}
1.1 方式一: 按照主键映射 (主键都是一样的)
用已有的主键充当外键去连接表来表示关系
元数据配置文件:
1.1.1 Husband.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.qf.domain">
<class name="Husband" table="husband">
<id name="marryId">
<generator class="native"></generator>
</id>
<property name="name"></property>
<property name="age"></property>
<property name="hid"></property>
<!-- 表达的是一对一的关系 constrained="true"表示外键约束-->
<one-to-one name="wife" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
1.1.2 Wife.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.qf.domain">
<class name="Wife" table="wife">
<id name="marryId">
<generator class="native"></generator>
</id>
<property name="name"></property>
<property name="age"></property>
<property name="wid"></property>
<!-- 表达一对一的关系:
当前Wife和name变量代指的类映射的表建立一对一的关系。 -->
<one-to-one name="husband"></one-to-one>
</class>
</hibernate-mapping>
1.1.3 测试
/**
* one-to-one表示一对一的关系。
* constrained="true"表示外键约束。当前类映射表的主键作为外键去连接另外一个表的主键。
* 示例: husband的主键作为外键去连接wife的主键。建立了一对一的关系(夫妻关系)。
*
* 主表 wife
* 副表(从表) husband 维护外键,通过外键去连接主表的主键
*
* 手动删除表的先删除副表。再删除主表。
*
* 方式一: 按照主键关系映射的(用已有的主键充当外键去连接关系) 上午重点:
* 方式二:按照外键关系映射的。(给表插入额外的一个列作为外键去连接另外一个表)
*/
@Test
public void test01() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
try {
Husband husband =new Husband();
husband.setName("丈夫");
Wife wife =new Wife();
wife.setName("妻子");
//表达一对一的关系
husband.setWife(wife);
wife.setHusband(husband);
//有约束关系的表后插入(该表插入的时候要 找对应的关系的那条数据)
//先插入主表
session.save(wife);
//后插入副表
session.save(husband);
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if (transaction!=null) {
transaction.rollback();
}
}
}
1.2 按照外键映射
给表额外插入一个列作为外键去连接另一个表
java持久化类
Wife.java
public class Wife{
private Integer wid; //妻子的身份证号(主键)
private String name; //名称
private Integer age; //年龄
private Integer marryId; //结婚编号
private Husband husband;//妻子和丈夫有一对一的关系
set/get....
}
Husband.java
public class Husband{
private Integer hid; //丈夫的身份证号 (主键)
private String name; //名称
private Integer age; //年龄
private Integer marryId; //结婚编号
private Wife wife; //丈夫和妻子有一对一的关系
set/get....
//理解: 多个表中主键都有各自的含义及内容的时候(主键不一样)
//给husband额外插入一个列wife_id1,作为外键去连接wife表
}
1.2.1 Husband.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.qf.domain">
<class name="Husband" table="husband">
<id name="hid">
<generator class="native"></generator>
</id>
<property name="name"></property>
<property name="age"></property>
<property name="marryId"></property>
<!-- 目的: 表达的是一对一的关系
many-to-one原本:多对一,使用unique="true"属性进行转化变成一对一
column:声明一个外键,去连接另一个表 (值随意定义,见名之意)
-->
<many-to-one name="wife" unique="true" column="wife_id1"></many-to-one>
</class>
</hibernate-mapping>
1.2.2 Wife.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.qf.domain">
<class name="Wife" table="wife">
<id name="wid">
<generator class="native"></generator>
</id>
<property name="name"></property>
<property name="age"></property>
<property name="marryId"></property>
<!-- 表达一对一的关系:
当前Wife和name变量代指的类映射的表建立一对一的关系。 -->
<one-to-one name="husband"></one-to-one>
</class>
</hibernate-mapping>
1.2.3 测试
@Test
public void test01() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
try {
Husband husband =new Husband();
husband.setName("丈夫1");
Wife wife =new Wife();
wife.setName("妻子1");
//表达一对一的关系
husband.setWife(wife);
wife.setHusband(husband);
//有约束关系的表后插入(该表插入的时候要 找对应的关系的那条数据)
//先插入主表
session.save(wife);
//后插入副表
session.save(husband);
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if (transaction!=null) {
transaction.rollback();
}
}
}
2、一对多|多对一(最常见的,重点掌握)
示例:客户和联系人为例:
一对多: 一个客户对应多个联系人
多对一:多个联系人对应一个客户
2.1 持久化类
package com.qf.domain;
//从表(拥有外键。)
public class Contact {
private Integer contact_id; //主键
private String contact_name;
private Integer contact_cust_id;//外键(表达关系的时候声明外键即可)
private String contact_gender;
private String contact_phone;
private String contact_mobile;
private String contact_email;
private String contact_qq;
private String contact_position;
private String contact_memo;//备注
//表达多(Contact)对一(Customer)关系
private Customer customer;
set/get.....
}
package com.qf.domain;
import java.util.HashSet;
import java.util.Set;
//主表
public class Customer {
private Integer cust_id;//主键
private String cust_name;
private Integer cust_user_id;
private Integer cust_create_id;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_linkman;
private String cust_phone;
private String cust_mobile;
//表达一(Customer)对多(Contact)的关系
private Set<Contact> contacts =new HashSet<>();
set/get.....
}
2.2 映射文件
Contact.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.qf.domain">
<class name="Contact" table="contact">
<id name="contact_id">
<generator class="native"></generator>
</id>
<property name="contact_name" ></property>
<property name="contact_gender" ></property>
<property name="contact_phone" ></property>
<property name="contact_mobile" ></property>
<property name="contact_email" ></property>
<property name="contact_qq" ></property>
<property name="contact_position" ></property>
<property name="contact_memo" ></property>
<!-- 表达多对一关系
column:声明外键-->
<many-to-one name="customer" column="contact_cust_id"></many-to-one>
</class>
</hibernate-mapping>
Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.qf.domain">
<class name="Customer" table="customer">
<id name="cust_id" column="cust_id">
<generator class="native"></generator>
</id>
<property name="cust_name" column="cust_name"></property>
<property name="cust_user_id" column="cust_user_id"></property>
<property name="cust_create_id" column="cust_create_id"></property>
<property name="cust_source" column="cust_source"></property>
<property name="cust_industry" column="cust_industry"></property>
<property name="cust_level" column="cust_level"></property>
<property name="cust_linkman" column="cust_linkman"></property>
<property name="cust_phone" column="cust_phone"></property>
<property name="cust_mobile" column="cust_mobile"></property>
<!-- 表达一对多的关系 -->
<set name="contacts">
<!-- column外键:说明哪一个外键要连接当前的表 -->
<key column="contact_cust_id"></key>
<!-- 表达关系 -->
<one-to-many class="Contact"/>
</set>
</class>
</hibernate-mapping>
2.3 主配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- 4(数据库信息)+1(数据库方言)+2(生成sql格式化并打印)+1(数据库表生成策略)+1(加载映射文件)+
1(事务隔离级别)+1(配置current_session_context) -->
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/1715_hibernate05</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
<!-- JBoss Tools 提示工具 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 隔离级别 -->
<property name="hibernate.connection.isolation">4</property>
<!-- 目的:要使用getCurrentSession()获取当前线程绑定的Session,必须要做如下配置:-->
<property name="hibernate.current_session_context_class">thread</property>
<mapping resource="com/qf/domain/Customer.hbm.xml"/>
<mapping resource="com/qf/domain/Contact.hbm.xml"/>
</session-factory>
</hibernate-configuration>
2.4 测试
@Test
public void test001() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
try {
//1个客户
Customer customer= new Customer();
customer.setCust_name("阿里巴巴");
//多个联系人,示例2个
Contact contact1 =new Contact();
contact1.setContact_name("马云");
Contact contact2 =new Contact();
contact2.setContact_name("李总");
//表达多表关系
//一对多
//把联系人给客户添加上
customer.getContacts().add(contact1);
customer.getContacts().add(contact2);
//多对一
contact1.setCustomer(customer);
contact2.setCustomer(customer);
//持久化保存
session.save(customer);
session.save(contact1);
session.save(contact2);
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if (transaction!=null) {
transaction.rollback();
}
}
}
//给指定客户添加新的联系
//持久态的对象会自动更新,已经在数据库中
//瞬时状态和游离状态必须通过方法转化成持久态才可以保存到数据
@Test
public void test002() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
try {
//1.先找出客户
Customer customer = session.get(Customer.class, 1);
//2.新加联系人
Contact contact =new Contact();
contact.setContact_name("刘总");
//表达一对多的关系
customer.getContacts().add(contact);
//表达多对一的关系
contact.setCustomer(customer);
//保存
session.save(contact);
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if (transaction!=null) {
transaction.rollback();
}
}
}
3、多对多(常见的,掌握)
示例1:多个学生可以被多个老师教,多个老师可以教多个 学生;
示例2:多个用户可以有多个角色,多个角色可以有多个用户。
例如:马云既是老板又是演员。李彦宏既是企业家又是老板。
1.User.java
public class User {
private Integer user_id;
private String user_name;
private String user_pwd;
private String user_state;//用户状态
//表达User和Role的多对多的关系
private Set<Role> roles =new HashSet<>();
set/get.......
}
2.Role.java
public class Role {
private Integer role_id;//角色编号
private String role_name;//角色名
private String role_memo;//角色备注
//表达Role和User的多对多的关系
private Set<User> users =new HashSet<>();
set/get.......
}
3.User.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.qf.domain">
<class name="User" table="user">
<id name="user_id">
<generator class="native"></generator>
</id>
<property name="user_name"></property>
<property name="user_pwd"></property>
<property name="user_state"></property>
<!-- 表达多对多的关系 table:表示中间表的名称
inverse:true 不维护关系(放弃维护关系)
false:默认false,维护关系
-->
<set name="roles" table="user_role" inverse="true" >
<!-- 引用已经存在的键连接当前表User-->
<key column="user_id1"></key>
<!-- class:与当前表要建立关系的实体类名称
column:声明外键.表示在中间表中生成一个新的列。
作为外键去连接与当前表要建立关系的表
-->
<many-to-many class="Role" column="role_id1"></many-to-many>
</set>
</class>
</hibernate-mapping>
4.Role.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.qf.domain">
<class name="Role" table="role">
<id name="role_id">
<generator class="native"></generator>
</id>
<property name="role_name"></property>
<property name="role_memo"></property>
<!-- 表达多对多的关系 table:表示中间表的名称 -->
<set name="users" table="user_role" >
<!-- 引用已经存在的键连接当前表Role-->
<key column="role_id1"></key>
<!-- class:与当前表要建立关系的实体类名称
column:声明外键.表示在中间表中生成一个新的列。
作为外键去连接与当前表要建立关系的表
-->
<many-to-many class="User" column="user_id1"></many-to-many>
</set>
</class>
</hibernate-mapping>
5.hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- 4(数据库信息)+1(数据库方言)+2(生成sql格式化并打印)+1(数据库表生成策略)+1(加载映射文件)+
1(事务隔离级别)+1(配置current_session_context) -->
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/1715_hibernate06</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
<!-- JBoss Tools 提示工具 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 隔离级别 -->
<property name="hibernate.connection.isolation">4</property>
<!-- 目的:要使用getCurrentSession()获取当前线程绑定的Session,必须要做如下配置:-->
<property name="hibernate.current_session_context_class">thread</property>
<mapping resource="com/qf/domain/User.hbm.xml"/>
<mapping resource="com/qf/domain/Role.hbm.xml"/>
</session-factory>
</hibernate-configuration>
6.测试
//插入
@Test
public void test01() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
try {
User user1 =new User();
user1.setUser_name("马云11111");
User user2 =new User();
user2.setUser_name("李彦宏1111");
Role role1 =new Role();
role1.setRole_name("企业家11111");
Role role2 =new Role();
role2.setRole_name("演员11111");
//表达多对多的关系
//从user角度出发表达关系
user1.getRoles().add(role1);
user1.getRoles().add(role2);
user2.getRoles().add(role1);
user2.getRoles().add(role2);
//从role角度出发表达关系
role1.getUsers().add(user1);
role1.getUsers().add(user2);
role2.getUsers().add(user1);
role2.getUsers().add(user2);
session.save(user1);
session.save(user2);
session.save(role1);
session.save(role2);
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if (transaction!=null) {
transaction.rollback();
}
}
}
执行以上代码报错:约束异常。
解决方式一:去除掉一方关系表达维护,例如:删除掉30-34行代码
解决方式二:代码不用改变,在不需要维护关系的地方添加inverse="true"(谁放弃给谁加)
示例:User维护User和Role的关系。 Role放弃关系维护。
<set name="roles" table="user_role" inverse="true" >
<!-- 引用已经存在的键连接当前表User-->
<key column="user_id1"></key>
<!-- class:与当前表要建立关系的实体类名称
column:声明外键.表示在中间表中生成一个新的列。
作为外键去连接与当前表要建立关系的表
-->
<many-to-many class="Role" column="role_id1"></many-to-many>
</set>
备注:inverse
true:放弃维护关系
false:维护关系 ,默认
7.cascade级联操作(多表关系)
/* cascade:级联操作
* save-update(重点): 级联更新 .持久化当前对象的同时要持久化与之相关联的对象。
* 示例:session.save(User) 含义:持久化User的同时在持久化与User关联的类Role
在以上案例中可以去除40行和41行
* delete: 级联删除 删除一个对象的同时会删除掉与之相关连的对象 (不建议使用,慎重使用)
* all: save-update和delete结合一起
*/
//删除用户(使用cascade="delete"会删除调用当前数据和与之关联的所有数据)
@Test
public void test02() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
try {
//删除一个用户
User user = session.get(User.class, 3);
session.delete(user);//删除user的同时删除了该user关联的role角色
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if (transaction!=null) {
transaction.rollback();
}
}
}
//给用户删除指定角色(不使用cascade="delete"删除)
@Test
public void test03() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
try {
//一个用户
User user = session.get(User.class, 5);
//给该用户删除一个角色
Role role= session.get(Role.class, 6);
//从用户移除角色
user.getRoles().remove(role);//内连接查询
session.delete(role);
transaction.commit();
} catch (Exception e) {
// TODO: handle exception
if (transaction!=null) {
transaction.rollback();
}
}
}