一. 映射对象标识符 OID
- 先看一个测试
public void testSessionOID() {
Session session = sf.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Student s1 = (Student) session.get(Student.class, Long.valueOf(1));
Student s2 = (Student) session.get(Student.class, Long.valueOf(2));
Student s3 = (Student) session.get(Student.class, Long.valueOf(1));
System.out.println(s1 == s2);
System.out.println(s1 == s3);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
2. OID原理
Hibernate的session根据OID来标识一个ORM对象,借助session缓存来减少对数据库的访问。
二. Hibernate对象主键生成策略
- 主键的分类
- 业务主键
具有业务上的含义,如学号。 - 代理主键
没有业务上的含义,如ID。在业务变动较大的表格中一般使用无意义的代理主键。
- 主键生成策略
策略 | 含义 |
increment | 由 Hibernate 自动以递增的方式生成标识(ID),适用于代理主键。 |
identity | 由底层数据库生成标识(ID)。适用于代理主键。 |
sequcence | 由 Hibernate 根据底层数据库的序列来生成标识(ID)。适用于代理主键。 |
hilo | Hibernate 根据 high/low 算法来生成标识(ID)。适用于代理主键。 |
native | 根据底层数据库对自动生成标识(ID)的支持能力来选择 identity,sequence 或 hilo。适用于代理主键。 |
例如:Student.hbm.xml
<?xml version="1.0"?>
<!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.bee.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"> <----这里
</generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
注解方式
@Id
@GeneratedValue(generator = "_native")
@GenericGenerator(name = "_native", strategy = "native") <----这里
public long getId() {
return id;
}
三. 关联关系
- 一对多映射(1:n)
以学生表,班级表为例。
- 主配置文件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">
<hibernate-configuration>
<session-factory>
<!--数据库连接设置 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<!-- 方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 控制台显示SQL -->
<property name="show_sql">true</property>
<!-- 自动更新表结构 -->
<property name="hbm2ddl.auto">update</property>
<!-- 映射项目中的配置文件 -->
<mapping resource="com/bee/model/Student.hbm.xml"/>
<mapping resource="com/bee/model/Class.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- 实体类——“1方”
配置文件Class.hbm.xml(与Class.java同目录)
<?xml version="1.0"?>
<!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.bee.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
</class>
</hibernate-mapping>
实体类
package com.bee.model;
public class Class {
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 实体类——“n方”
配置文件Student.hbm.xml(与Student.java同目录)
<?xml version="1.0"?>
<!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.bee.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name" column="stuName"></property>
<!-- 定义外键关联:外键一定在“n方”——t_student -->
<many-to-one name="c" column="classId" class="com.bee.model.Class" cascade="save-update"></many-to-one>
</class>
</hibernate-mapping>
实体类
package com.bee.model;
public class Student {
private long id;
private String name;
private Class c; //外键
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class getC() {
return c;
}
public void setC(Class c) {
this.c = c;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
- 自动生成的外键
- 外键在“多方”——要先于“一方”删除
- 测试
import com.bee.model.Class;
...
@Test
public void test12n() {
Session session = sf.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Class c = new Class();
c.setName("全真教");
session.save(c);
Student s1 = new Student();
s1.setName("马钰");
s1.setC(c);
Student s2 = new Student();
s2.setName("丘处机");
s2.setC(c);
session.save(s1);
session.save(s2);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
- 新的JUnit测试类及级联保存更新
package com.bee.service;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.bee.model.Class;
import com.bee.model.Student;
import com.bee.utils.HibernateUtil;
public class MyJUnitTest02 {
private SessionFactory sf = HibernateUtil.getSessionFactory();
private Session session;
@Before
public void setUp() throws Exception {
session = sf.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
}
@After
public void tearDown() throws Exception {
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void test01() {
Class c = new Class();
c.setName("全真教");
//注意:只有save()之后的对象才持久化到数据库中。
//如果注释掉下边对c对象的save,那么c对象将成为临时对象(transient instance),且不能持久化到数据库。
//session.save(c);
Student s1 = new Student();
s1.setName("谭处端");
s1.setC(c);
Student s2 = new Student();
s2.setName("王处一");
s2.setC(c);
//那么不持久化c对象而只持久化(save)s1和s2对象会发生错误,除非...
//在配置文件Student.hbm.xml中启用cascade="save-update",即级联保存更新,以保证在保存1:n的“多方”时级联持久化(save)“一方”的数据。
session.save(s1);
session.save(s2);
}
}
四. 对于1:n如何从“一方”操作“多方”的数据?
- 在“一方”添加数据,然后在“多方”级联添加相应的数据。
- “一方”的实体类
public class Class {
private long id;
private String name;
private Set<Student> students = new HashSet<Student>(); //增加操作“多方”的“抓手”
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}
- 修改Class.hbm.xml配置
<?xml version="1.0"?>
<!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.bee.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
<!-- 关键是下边的:cascade="save-update" -->
<set name="students" cascade="save-update">
<key column="classId"></key>
<one-to-many class="com.bee.model.Student"/>
</set>
</class>
</hibernate-mapping>
- 测试
@Test
public void test02() {
Class c = new Class();
c.setName("江南");
Student s1 = new Student();
s1.setName("柯镇恶");
Student s2 = new Student();
s2.setName("朱聪");
c.getStudents().add(s1);
c.getStudents().add(s2);
session.save(c);
}
- 在“一方”直接查询得到“多方”的数据。
基于前一步的实体类和配置,直接测试。
@Test
public void test03() {
Class c = (Class) session.get(Class.class, Long.valueOf(6));
Set<Student> students = c.getStudents();
Iterator it = students.iterator();
while (it.hasNext()) {
Student s = (Student) it.next();
System.out.println(s);
}
}
- 外键关联“一方”和“多方”数据
- 孤立添加“一方”和“多方”的数据
@Test
public void test04() {
Class c = new Class();
c.setName("丐帮");
Student s1 = new Student();
s1.setName("洪七公");
session.save(c);
session.save(s1);
}
- 外键关联“一方”和“多方”的数据
@Test
public void test05() {
Class c = (Class) session.get(Class.class, Long.valueOf(7));
Student s = (Student) session.get(Student.class, Long.valueOf(7));
s.setC(c);
c.getStudents().add(s);
}
发现SQL有冗余
在“一方”的配置文件中添加属性做优化:
Class.hbm.xml
<?xml version="1.0"?>
<!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.bee.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
<!-- 在“一方”加inverse=true,从而只让“多方”生成外键关联的SQL。 -->
<set name="students" cascade="save-update" inverse="true">
<key column="classId"></key>
<one-to-many class="com.bee.model.Student"/>
</set>
</class>
</hibernate-mapping>
4. 在“一方”删除,从而级联删除“多方”数据。
- 修改配置文件Class.hbm.xml
<?xml version="1.0"?>
<!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.bee.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
<!-- 设置cascade="delete"以保证级联删除 -->
<set name="students" cascade="delete">
<key column="classId"></key>
<one-to-many class="com.bee.model.Student"/>
</set>
</class>
</hibernate-mapping>
- 测试
@Test
public void test06() {
Class c = (Class) session.get(Class.class, Long.valueOf(6));
session.delete(c);
}
注意:该操作属于高危操作,一般不用。