一. 映射对象标识符 OID

  1. 先看一个测试
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
}

过一下hibernate4-3_主键


2. OID原理

Hibernate的session根据OID来标识一个ORM对象,借助session缓存来减少对数据库的访问。

过一下hibernate4-3_主键_02

二. Hibernate对象主键生成策略

  1. 主键的分类
  • 业务主键
    具有业务上的含义,如学号。
  • 代理主键
    没有业务上的含义,如ID。在业务变动较大的表格中一般使用无意义的代理主键。
  1. 主键生成策略

策略

含义

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. 一对多映射(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 + "]";
}

}
  • 自动生成的外键
  • 过一下hibernate4-3_xml_03


  • 过一下hibernate4-3_xml_04

  • 外键在“多方”——要先于“一方”删除
  • 过一下hibernate4-3_xml_05

  • 测试
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如何从“一方”操作“多方”的数据?

  1. 在“一方”添加数据,然后在“多方”级联添加相应的数据。
  • “一方”的实体类
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);
}
  1. 在“一方”直接查询得到“多方”的数据。
    基于前一步的实体类和配置,直接测试。
@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);
}
}
  1. 外键关联“一方”和“多方”数据
  • 孤立添加“一方”和“多方”的数据
@Test
public void test04() {
Class c = new Class();
c.setName("丐帮");

Student s1 = new Student();
s1.setName("洪七公");

session.save(c);
session.save(s1);
}

过一下hibernate4-3_hibernate_06


过一下hibernate4-3_主键_07

  • 外键关联“一方”和“多方”的数据
@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);
}

过一下hibernate4-3_hibernate_08


发现SQL有冗余

过一下hibernate4-3_xml_09


在“一方”的配置文件中添加属性做优化:

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>

过一下hibernate4-3_xml_10


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

注意:该操作属于高危操作,一般不用。