关系映射
.多对一(Employee-Department)
1.建表示在员工表中添加一个外键
表示Employee-Department之间的多对一的关系,可以在Employee中建一个departid字段。
缺点:想知道部门信息还得现查一遍
解决:是在Employee类中建一个Department类型的成员变量,这才体现出面向对象的感觉
分析:hibernate不是单纯的把column="depart_id"赋值给name="depart" ,而是hibernate会根据这个一对多的关系反射找到Department这个类,之后定位到相应的映射文件,将Department表的数据查出来,经行封装赋值。数据会根据主外键寻找取出。
pk:主键
fk:外键
<?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 package="cn.itcast.domain">
<class name="Employee">
<!-- 对象标示符,类型可以不写,hibernate自己识别 -->
<id name="id" column="id">
<!-- 指定主键生成方式。
native根据方言判定生成主键的方式
-->
<generator class="native"/>
</id>
<property name="name" column="name" unique="true" not-null="true"/><!--标示name唯一性,非空的限制 -->
<!-- private Department depart是一个复杂的属性,所以不能用 property映射-->
<!--Employee类中Department的对象名 -->
<many-to-one name="depart" column="depart_id" not-null="true" ></many-to-one>
</class>
</hibernate-mapping>
2.缺省映射的(什么都不给):
就是一个对象的外键,必定是另一个表的主键。
如果不是就得添加一个属性引用, property-ref=“关联表字段”
如:
<many-to-one name="depart" column="depart_id" not-null="true" property-ref=“name”></many-to-one>
解释:depart_id对应另一个表(实体)的name
一般我们不使用,以为这是一种数据库不良的设置,为了避免这种情况我们才添加了这个没有什么用处的id列
3.以下是剩余代码
员工类:
注意:为了实现多对一的关联关系,添加private Department depart对象
package cn.itcast.domain;
/**
* 员工类
*
* @author Mars
*
*/
public class Employee {
private int id;
private String name;
private Department depart;
public int getId() {
return id;
}
public Department getDepart() {
return depart;
}
public void setDepart(Department depart) {
this.depart = depart;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
部门类:
package cn.itcast.domain;
/**
* 部门类
* @author Mars
*
*/
public class Department {
private int id;
private String 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;
}
}
映射文件:
<?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 package="cn.itcast.domain">
<class name="Department" >
<!-- 对象标示符,类型可以不写,hibernate自己识别 -->
<id name="id" column="id">
<!-- 指定主键生成方式。
native根据方言判定生成主键的方式
-->
<generator class="native"/>
</id>
<property name="name" column="name" unique="true" not-null="true"/><!--标示name唯一性,非空的限制 -->
</class>
</hibernate-mapping>
工具类:
管理session
package cn.itcast.dao;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
*(1)不想让其他类继承工具类
*(2)不能让它创建对象,所以属性全部private,还得有个private的无参数构造
* @author Mars
*
*/
public final class HibernateUtil {
private static SessionFactory sessionFactory;
private HibernateUtil(){
}
/**
* 细节1:Configuration:是一个配置类
* Configuration的对象会找hibernate.cfg.xml,完成hibernate的初始化
*
* 细节2:hibernate的配置文件有两种hibernate.cfg.xml和hibernate.properties
* 两种存在一种即可,当然如果都存在的话,hibernate.cfg.xml中的配置信息会覆盖hibernate.properties的配置信息
*
* 细节3:初始化工作只尽量只初始化一次,耗时
*/
static{
//1。读取并解析配置文件
Configuration cfg = new Configuration();
//如果hibernate.cfg.xml不是放在类路径下,就需要此时指定路径
//cfg.configure("filename");
cfg.configure();
//可以使用代码来设置配置信息,但是不便于管理,不建议使用
//cfg.setProperty("hibernate.connection.driver_class", "oracle.jdbc.driver.OracleDriver");
//2。读取并解析映射信息,创建SessionFactory
//所有的配置信息都可以在SessionFactory中找到,映射文件的信息都能找到
sessionFactory = cfg.buildSessionFactory();
}
/**
* 获取session
* @return
*/
public static Session getSession(){
// 3。打开Session
return sessionFactory.openSession();
}
/**
* 获取SessionFactory对象
* @return
*/
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
}
测试类:
package cn.itcast.RelationalMapping;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import cn.itcast.dao.HibernateUtil;
import cn.itcast.domain.Department;
import cn.itcast.domain.Employee;
public class Many2one {
/**
* @param args
*/
public static void main(String[] args) {
Department dept = add();
Employee emp =query(1);
//外面使用牵扯到懒加载,可在查询方法中添加代理初始化
System.out.println("deoart name:"+emp.getDepart().getName());
}
static Employee query(int empId){
Session s = null;
Transaction tx =null;
try {
s=HibernateUtil.getSession();
tx=s.beginTransaction();
Employee emp = (Employee)s.get(Employee.class, empId);
//查询分成两步:1.先查询雇员的
// 2.之后按照dept_id查询部门信息
//如果一开始我们将Employee中的部门设成int类型的dept_id,则部门信息就得手工完成,现在由部门信息完成,代码简洁很多
// System.out.println("deoart name:"+emp.getDepart().getName());
//代理初始化,涉及懒加载
Hibernate.initialize(emp.getDepart());
return emp;
} finally{
if(s!=null){
s.close();
}
}
}
/**
* 多对一添加方法
* @return
*/
static Department add(){
Session s = null;
Transaction tx =null;
try {
Department depart = new Department();
depart.setName("depart name");
Employee emp = new Employee();
emp.setDepart(depart);//对象模型,建立两个对象的关联
emp.setName("emp name");
s=HibernateUtil.getSession();
tx=s.beginTransaction();
s.save(emp);
s.save(depart);
tx.commit();
return depart;
} finally{
if(s!=null){
s.close();
}
}
}
}
4.编写时遇到的异常:
Exception in thread "main" org.hibernate.MappingException: Unknown entity: cn.itcast.domain.Department
at org.hibernate.internal.SessionFactoryImpl.getEntityPersister(SessionFactoryImpl.java:1141)
at org.hibernate.internal.SessionImpl.getEntityPersister(SessionImpl.java:1433)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:116)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:206)
at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:55)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:191)
at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:49)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:764)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:756)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:752)
at cn.itcast.RelationalMapping.Many2one.add(Many2one.java:31)
at cn.itcast.RelationalMapping.Many2one.main(Many2one.java:16)
分析:在hibernate.cfg.xml中没有配置相应的映射xml
5.疑问:
s.save(depart);
s.save(emp);
如果将这句颠倒,会保存成功?
猜想:不会成功,保存emp时会由于找不到相应的dept主键,保存不成功
结果与分析:结果成功了
hibernate的执行是这样的Hibernate:
insert
into
Employee
(name, depart_id, id)
values
(?, ?, ?)
Hibernate:
insert
into
Department
(name, id)
values
(?, ?)
Hibernate:
update
Employee
set
name=?,
depart_id=?
where
id=?
先向emp中插入一个dept_id=null的记录,之后检测到相应的dept_id,再执行一个update语句。
本质上:hibernate对三种状态的监控。hibernate很强大啊!
(前提是在Employee.hbm.xml中没有非空限制<many-to-one name="depart" column="depart_id" not-null="true" ></many-to-one>)
预告:尽请期待
一对多(Employee-Department)
一对一(Person-idcard)
多对对(teacher-student)
组件映射(User-Name)
集合映射(set,list,map,bag)
inverse和cascade(Employee-Department)