Hebernate -- 映射继承关系
1. Employee 为基类, 派生出HourEmployee 和 SalaryEmployee两个类。
采用 subclass 元素的继承映射(1)
采用 subclass
的继承映射可以实现对于继承关系中父类和子类使用同一张表
因为父类和子类的实例全部保存在同一个表中,因此需要在该表内增加一列,使用该列来区分每行记录到底是哪个类的实例----这个列被称为辨别者列(discriminator).
在这种映射策略下,使用
subclass 来映射子类,使用class
或 subclass
的 discriminator-value
属性指定辨别者列的值
所有子类定义的字段都不能有非空约束。如果为那些字段添加非空约束,那么父类的实例在那些列其实并没有值,这将引起数据库完整性冲突,导致父类的实例无法保存到数据库中
示例代码::
Employee.java
public class Employee { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
HourEmployee.java
/** * 钟点工 */ public class HourEmployee extends Employee{ private Double rate; public Double getRate() { return rate; } public void setRate(Double rate) { this.rate = rate; } }
SalaryEmployee.java
/** * 正是员工 */ public class SalaryEmployee extends Employee { private Double salary; public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } }
Employee.hbm.xml 配置文件
<?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="cn.itcast.extends01.Employee" table="employee" discriminator-value="ee"> <id name="id" type="integer"> <column name="id"/> <generator class="increment"/> </id> <!--辨别者列,column="etype"用于区分员工的种类,该配置必须放置在id的后面--> <discriminator column="etype" type="string"/> <property name="name" type="string"> <column name="name"/> </property> <!--配置子类 discriminator-value: 子类对应的辨别者列的值 --> <subclass name="cn.itcast.extends01.HourEmployee" discriminator-value="he"> <property name="rate" column="rate" type="double"/> </subclass> <subclass name="cn.itcast.extends01.SalaryEmployee" discriminator-value="se"> <property name="salary" column="salary" type="double"/> </subclass> </class> </hibernate-mapping>
App.java
public class App { private static SessionFactory sf=null; static{ Configuration config=new Configuration(); config.configure("cn/itcast/extends01/hibernate.cfg.xml"); config.addClass(Employee.class); sf=config.buildSessionFactory(); } /* * 知识点1: 保存员工信息 */ @Test public void saveStudentAndCoure(){ Session session=sf.openSession(); Transaction tx=session.beginTransaction(); Employee e=new Employee(); e.setName("xxxx"); HourEmployee he=new HourEmployee(); he.setName("小昭"); he.setRate(10d); SalaryEmployee se=new SalaryEmployee(); se.setName("赵敏"); se.setSalary(3000d); session.save(e); session.save(he); session.save(se); tx.commit(); session.close(); } //知识点2: 查询钟点工信息 @Test public void findHourEmployee(){ Session session=sf.openSession(); Transaction tx=session.beginTransaction(); //HQL语句 可以使用子类HourEmployee Query query=session.createQuery("from HourEmployee"); query.list(); tx.commit(); session.close(); } }
hibernate.cfg.xml
<?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"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property> <!-- 配置数据库的方言,让hibernate知道连接的是哪个数据库--> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- 配置利用javaBean和映射文件生成数据库中的表 hibernate.hbm2ddl.auto值 * create:执行时,先查找该表是否存在,如存在先删除表,在创建表 * none:不能创建表,只能往表中插入数据,如表不存在抛出异常,默认值 * update:执行时, 情况一: 先查找该表是否存在,如果表存在,直接插入,如果表不存在,先创建表,再插入数据. 情况二: 先查找该表是否存在,如果表存在,但表的结构不一样,要修改表的结构 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 显示hibernate生成的sql语句 --> <property name="hibernate.show_sql">true</property> <!-- 显示格式化得sql语句 --> <property name="hibernate.format_sql">false</property> </session-factory> </hibernate-configuration>
采用 joined-subclass 元素的继承映射(2)
采用 joined-subclass
元素的继承映射可以实现每个子类一张表
采用这种映射策略时,父类实例保存在父类表中,子类实例由父类表和子类表共同存储。因为子类实例也是一个特殊的父类实例,因此必然也包含了父类实例的属性。于是将子类和父类共有的属性保存在父类表中,子类增加的属性,则保存在子类表中。
在这种映射策略下,无须使用鉴别者列,但需要为每个子类使用
key 元素映射共有主键,该主键必须与父类标识属性的列名相同。但如果继承树的深度很深,可能查询一个子类实例时,需要跨越多个表,因为子类的数据一次保存在多个父类中。
子类增加的属性可以添加非空约束。因为子类的属性和父类的属性没有保存在同一个表中
代码示例: 未贴出的和(1)相同
Employee.hbm.xml 配置文件
<?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="cn.itcast.extends02.Employee" table="e_emp" > <id name="id" type="integer"> <column name="id"/> <generator class="increment"/> </id> <property name="name" type="string"> <column name="name"/> </property> <!-- 使用joined-subclass映射子类 name="cn.itcast.extends02.HourEmployee":子类的类的完整路径 table="h_emp":子类对应的表 --> <joined-subclass name="cn.itcast.extends02.HourEmployee" table="h_emp"> <!--表示h_emp 表中的主键,同时又是外键--> <key column="hid"/> <property name="rate" column="rate" type="double"/> </joined-subclass> <joined-subclass name="cn.itcast.extends02.SalaryEmployee" table="s_emp"> <key column="sid"/> <property name="salary" column="salary" type="double"/> </joined-subclass> </class> </hibernate-mapping>
App.java
import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.junit.Test; public class App { private static SessionFactory sf=null; static{ Configuration config=new Configuration(); config.configure("cn/itcast/extends02/hibernate.cfg.xml"); config.addClass(Employee.class); sf=config.buildSessionFactory(); } /* * 知识点1: 保存员工信息 */ @Test public void saveStudentAndCoure(){ Session session=sf.openSession(); Transaction tx=session.beginTransaction(); Employee e=new Employee(); e.setName("xxxx"); HourEmployee he=new HourEmployee(); he.setName("小昭"); he.setRate(10d); SalaryEmployee se=new SalaryEmployee(); se.setName("赵敏"); se.setSalary(3000d); session.save(e); session.save(he); session.save(se); tx.commit(); session.close(); } //知识点2: 查询钟点工信息 @Test public void findHourEmployee(){ Session session=sf.openSession(); Transaction tx=session.beginTransaction(); //可以使用子类 Query query=session.createQuery("from HourEmployee"); query.list(); tx.commit(); session.close(); } //知识点5: 查询员工信息 @Test public void findEmployee(){ Session session=sf.openSession(); Transaction tx=session.beginTransaction(); //可以使用子类 Query query=session.createQuery("from Employee"); query.list(); tx.commit(); session.close(); } //知识点6: 删除员工信息(不用使用级联) 删除2号员工 @Test public void removeEmployee(){ Session session=sf.openSession(); Transaction tx=session.beginTransaction(); Employee e2=(Employee)session.get(Employee.class, 2); session.delete(e2); tx.commit(); session.close(); } //知识点7:查询唯一的员工,不是钟点工也不是正式员工 @Test public void findUniqueEmployee(){ Session session=sf.openSession(); Transaction tx=session.beginTransaction(); //sql SELECT * FROM e_emp e WHERE e.id NOT IN(SELECT hid FROM h_emp) AND e.id NOT IN(SELECT sid FROM s_emp) Query query=session.createQuery("from Employee e where e.id not in(select h.id from HourEmployee h) and e.id not in(select s.id from SalaryEmployee s)"); query.list(); tx.commit(); session.close(); } }