几个月前,我在博问里面发了一个问题:http://q.cnblogs.com/q/64900/,但是一直没有找到好的答案,关闭问题以后才自己解决了,在这里分享一下。

首先我重复一下场景,博问里面举的动物的例子,这篇文章里为了和我的代码对应,换一个例子。假设要做一个企业的人员管理系统,有各种各样的用户,有的身份是老板,有的身份是员工,有的身份是保安,等等,这些用户可能有非常多非常多通用的行为,比如说修改年龄,比如说按姓名或者工号在全公司查找,并且大多数情况下我们不关心一个人的具体身份,这时候我希望采取这样一种结构:

Person类是一个父类,拥有所有员工的公有属性,包括员工号和姓名,Staff和Employer是两种身份的人,都具有员工号和姓名这样的特征,并且Employer呢还有一个座右铭,Staff还有一个薪水。

class Person implements Serializable {id, name}
class Employer extends Person {motto}
class Staff extends Person {salary}

这是一种继承映射的关系,映射到数据表,就是

person表
id name
员工号,主键 员工姓名
employer表
id motto
员工号,主键,外键指向person表 座右铭
staff表
id salary
员工号,主键,外键指向person表 薪水

我现在要查询名字叫“张三”的所有员工,那么我可以用下面的方法查询:

Criteria criteria = getCurrentSession().createCriteria(Person.class);
criteria.add(Restrictions.eq("name", "张三"));
List<Person> person = criteria.list();

此时查询到的Person列表,所有的对象都是Employer或者Staff的具体化对象,Hibernate区分应该是哪一种对象的方式可以参考它生成的SQL语句:

Hibernate:
select
person0_.id as id1_3_0_,
person0_.name as name2_3_0_,
person0_1_.motto as motto1_0_0_,
person0_2_.salary as salary1_1_0_,
case
when person0_1_.id is not null then 1
when person0_2_.id is not null then 2
when person0_.id is not null then 0
end as clazz_0_
from
person person0_
left outer join
Employer person0_1_
on person0_.id=person0_1_.id
left outer join
Staff person0_2_
on person0_.id=person0_2_.id
where
person0_.name=?

这种多态查询非常的方便,如果只想查Staff,那么只需要写查询的时候,改一下class就可以,这样就只有staff left outer join person,而没有Employer的事情了:

Criteria criteria = getCurrentSession().createCriteria(Staff.class);

但是,毋庸置疑,当子表众多的时候,在Person上做多态查询,join的大量使用非常影响性能,而在这种要把Employer和Staff都查询出来存到一个List的情况下,我们一般只关心父表里存储的公有字段,所以我之前一直在寻找能够只查父表的方法,但是却没有找到。

尝试直接通过annotation禁用多态查询无果后,使用一种替代的解决方案:

以前是Employer和Staff都去继承Person,现在加一层,Person类是变成一个抽象类,不映射数据库,PersonUntyped和PersonTyped都映射同一个数据表"person"表,需要查Employer或者Staff的时候,还像以前一样,调用:

Criteria criteria = getCurrentSession().createCriteria(Employer.class);
List<Person> ems= criteria.list();
criteria = getCurrentSession().createCriteria(Staff.class);
List<Person> stas= criteria.list();

如果需要查父类对象,那么就调用:

criteria = getCurrentSession().createCriteria(PersonUntyped.class);
List<Person> pers= criteria.list();

Hibernate只解析当前类的字段,所以默认情况下PersonUntyped和PersonTyped映射person表时hibernate是不认识Person中的id和name的,需要在Person类上面加MappedSuperclass注解才可以:

@MappedSuperclass
public abstract class Person {

为了达到在Controller中使用同一个操作Person的接口,让Controller中完全感受不到我们加的这一层的存在,首先和以前一样为Employer、Staff和PersonUntyped分别建一个DAO,然后在Service中封装:

@Service
@Transactional
public class PersonServiceImpl implements PersonService { private PersonDao<Person> personDao; public Person findById(int type, int id) {
wireBean(type);
return personDao.findById(id);
} public void wireBean(int type) {
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
// 省略了try/trach,beanName根据type选择是personDao、employerDao还是staffDao
personDao = wac.getBean(beanName);
}
}

真正数据库设计的时候,父表里还应该有一个type,每一个Service方法的第一个参数规定都是type或者是一个Person对象,可以用person.getType()来获取到这个type,每个Service方法的第一行都要先根据type装载personDao,如果type是1或者2,分别装载employerDao和staffDao,如果是0,就装载只查父表的personDao。

这样一来这个问题就解决了,但是,当Service类扩充功能的时候,开发人员还要留心在每个函数开始都加一行装载的调用,非常麻烦,如果能在每个方法一开始的时候自动调用装载的代码多么好,非常显然,AOP可以完成这个工作,在PersonService中删除wireBean,新建aspect包,创建一个PersonAspect类:

@Component
@Aspect
public class PersonServiceAspect { @Autowired
private WebApplicationContext wac; @Before("execution(* org.zhangfc.demo4ssh.service.PersonServiceImpl.*(..))")
public void wirePersonDao(JoinPoint joinPoint) {
Object [] args = joinPoint.getArgs();
Object service = joinPoint.getTarget();
Field dao = null;
try {
dao = service.getClass().getDeclaredField("personDao");
if (!dao.isAccessible()) {
dao.setAccessible(true);
}
} catch (NoSuchFieldException | SecurityException e) {
return;
}
if (args.length == 0 || args[0].equals(0)) {
setBean(dao, service, "personDao");
}
if (args.length > 1 && args[0].equals(1)) {
setBean(dao, service, "employerDao");
}
if (args.length > 1 && args[0].equals(2)) {
setBean(dao, service, "staffDao");
}
} public boolean setBean(Field dao, Object service, String beanName){
try {
dao.set(service, wac.getBean(beanName));
return true;
} catch (BeansException | IllegalArgumentException
| IllegalAccessException e) {
return false;
}
} }

这样,在Service中就已经看不到了不同Person对象的不同。AOP的配置不再多做描述,可以参看我以前的文章,或者下载本例源码查看。

【JavaEE】Hibernate继承映射,不用多态查询只查父表的方法的更多相关文章

  1. SSH开发实践part3:hibernate继承映射

    0 大家好.上次讲了关于hibernate中双向1-N的映射配置,可以参考:http://www.cnblogs.com/souvenir/p/3784510.html 实际项目中,对象间的关系比较复 ...

  2. web进修之—Hibernate 继承映射(5)

    先看三个类的继承关系,Payment是父类,CashPayment和CreditCardPayment是Payment的子类:   view plaincopy to clipboardprint p ...

  3. Hibernate关联映射及高级查询

    一.Hibernate中的关联关系 1.1.单向一对多关联关系 按照以下步骤配置hibernate中持久化类的一对多对象关联: (1).持久化类添加关联类的相关属性及getter/setter方法. ...

  4. Hibernate继承映射(@Inheritance)

    继承映射在 Annotation 中使用 @Inheritance 注解,并且需要使用 strategy 属性指定继承策略,继承策略有 SINGLE_TABLE.TABLE_PER_CLASS 和 J ...

  5. hibernate 继承映射关系( SINGLE_TABLE)

    三种继承映射关系.   1,SINGLE_TABLE   person student  teacher 在一个表中,student和teacher继承自person,通过一个Discriminato ...

  6. Hibernate继承映射

    在面向对象的程序领域中,类与类之间是有继承关系的,例如Java世界中只需要extends关键字就可以确定这两个类的父子关系,但是在关系数据库的世界中,表与表之间没有任何关键字可以明确指明这两张表的父子 ...

  7. Hibernate 继承映射可能会遇到的错误

    问题: 我们在配置hibernate的时候,默认是会配置下面的两个属性的 <property name="hibernate.default_catalog">hibe ...

  8. hibernate 继承映射关系( JOINED)

    一个主表,其他的表每个都有自己的表来装填自己特有的部分,共同的部分就放在主表中.   package com.bjsxt.hibernate; import javax.persistence.Ent ...

  9. hibernate 继承映射关系( TABLE_PER_CLASS)

    Person,Student,Teacher各创建一个表,主键用一个中间表生成.   package com.bjsxt.hibernate; import javax.persistence.Ent ...

随机推荐

  1. Dennis与Ken爷爷的UNIX/C世界

     沉寂了很久了,时间在不断地逝去,转眼又到了新的一年,2013的发生了太多,Beta版本.辞职.职位转换.ARM.Driver.初级厨艺.Dx11.GPU.CPU.登山.GNU/Linux.Cross ...

  2. 实现让Lync client也能够"潜水"隐身聊天

    看到MSN或QQ,都支持隐身聊天. Lync Server  2013也是支持的.   1.Server端:Lync 2013 Server 缺省策略是没有设置显示脱机功能.(设置前截图)   2.直 ...

  3. LED子系统剖析

    写之前,先看一张图: 上次说了LED驱动程序,Linux自身也携带了LED驱动,且是脱离平台的,即LED子系统.操作起来十分简单.但是它的实质却不是那么容易,研究了一个晚上,终于明白了其中一个文件的功 ...

  4. GNOME3任务栏、标题栏过宽问题

    Debian 7.0 默认安装的是GNOME 3.4.2桌面系统,缺省状态下,用户会发现桌面系统的桌面任务栏及标题栏宽度太大,影响美观,同时也浪费屏幕显示的有效宽度,针对这个问题我们可以通过以下方式进 ...

  5. Robot Framework自动化测试(五)--- 开发系统关键字

    最近一直在用robot framework 做自动化测试项目,老实说对于习惯直接使用python的情况下,被框在这个工具里各种不爽,当然,使用工具的好处也很多,降低了使用成本与难度:当然,在享受工具带 ...

  6. MySQL安全问题(防范必知)

    对于任何一种数据库来说,安全问题都是非常重要的.如果数据库出现安全漏洞,轻则数据被窃取,重则数据被破坏,这些后果对于一些重要的数据库都是非常严重的.下面来从操作系统和数据库两个层对MySQL的安全问题 ...

  7. 负载均衡服务器session共享的解决方案 (转载)

    http://luanzhz.blog.163.com/blog/static/58023129201101811445262/ 在ASP.NET的程序中要使用Session对象时,必须确保页面的@p ...

  8. 奥特曼小分队之四(Work Breakdown Structure)

    写在前面的话:游戏介绍http://www.cnblogs.com/atmxfd/p/5415107.html 需求 我们的游戏是一款基于局域网的游戏,用户只需将服务端和客户端置于同一局域网下即可使用 ...

  9. Sprint回顾

    1.回顾组织 主题:“我们下次怎么样才能更加认真对待?” 时间:设定为1至2个小时. 参与者:整个团队. 场所:能够在不受干扰的情况下讨论. 秘书:指定某人当秘书,筹备.记录.整理.  2.回顾流程 ...

  10. Node.js爬虫数据抓取乱码问题总结

    一.非UTF-8页面处理 1.背景 windows-1251编码 比如俄语网站:https://vk.com/cciinniikk 可耻地发现是这种编码 所有这里主要说的是 Windows-1251( ...