Struts2.1.6+Spring2.5.6+Hibernate3.3.1全注解实例详解(三)

时间:2021-10-20 00:43:56
  在前两章我为大家详细分析了 JAR 包的选择和必须的配置文件,那么这一章,我就对例子的层次结构进行说明,并实现除 WEB 层的功能代码。
     第三部分:建立框架代码
     工程结构
    
     大家可以看到,本例一共分为: dao entity service web 四层。另外在这些层次下,还以业务功能再进行分包,这样做是为了方便在以后的功能扩展中,能更好的管理和维护代码。如果将所有类都直接集中在这 4 个包下面,随着类的增加,会越来越难以维护,而且查找起来也很费劲。
    
HibernateDao
    
在本例中,我是通过继承 Spring 提供的 HibernateDaoSupport 来实现 持久层的基类。同时引入泛型参数,封装了一些基本操作方法。
    
     这是 HibernateDao 的部分代码,引入的这个泛型参数,其实就是实体类( User Role )。通过传递这个实体类,在构造方法中利用反射特性将它从 JVM 中取出来。
    
     这里的 getClass() 方法是获得继承 HibernateDao 的类( UserDao RoleDao
    getGenericSuperclass()
方法就是通过这些继承了 HibernateDao 的类,来得到父类(父类就是 HibernateDao )的泛型。注意这个方法的返回值为 Type ,这是一个类型接口。请参考 API
    
因为在继承 HibernateDao 的时候,会给它加一个泛型参数。比如, User Role 实体类。因此超类是参数化类型,所以返回的 Type 对象包含所使用的实际类型参数。这里返回的 Type 对象是 ParameterizedType 接口的实现类 ParameterizedTypeImpl ,所以要将返回类型转型为 ParameterizedType
    getActualTypeArguments()
方法是 ParameterizedType 接口中的,它的作用就是获得实际类型参数 Type 对象的数组,因为我们这里只定义了一个泛型参数,数组里面也只有一个值,所以需要在数组下标处填 0 。然后最后一步转型千万别忘记了,因为这个方法返回的可是一个 Type 数组喔。
    
如果对于这部分的说明还有点不理解的话,请到时候有了代码,设个断点跟踪一下,就会全部清楚了。关于 java 反射,它已经超出本文的范围。大象只对本例中用到的部分进行讲解。
    
使用这种写法,是方便我们进行类型转换与类型检查。另外还可以简化某些方法的写法。比如: createCriteria(Criterion... criterions) 这个方法。参数是 Criterion 类型的可变参数,它是用来设置查询条件。如果要进行对象化查询,那么最简单的写法就可以直接写成 createCriteria() 。另外还有重载的方法,可以根据传入 class 类型来创建自定义查询。
    
dao
    
持久层的 Dao 类是根据实体类定义,一般是一个实体类就会有一个对应的 Dao 类。当然这要跟业务需求来设计,不是绝对的。另外你也可以为了简便而去掉 dao 层,将持久化操作与业务逻辑全部写在 service 层。
    
     这些定义的方法是供 service 层调用,在业务层,将不会看到一行与持久层有关的代码,降低藕合性是这样做的目的。 @Repository 注解的作用就是标注这个 UserDao 是一个持久层组件。还记得前一章讲到的扫描器吗? component-scan 它就是用来将标有 @Repository @Service 这样的注解类扫描到 Spring 的容器里,并且同时对标有 @Autowired 注解的 Bean 启用了自动注入功能。这就是 Spring 2.5 开始提供的新特性。我们使用注解的方法,就可以告别那繁琐的配置文件定义。
     entity
     关于实体的定义就是使用 JPA 注解,关于这部分,我以前写过一篇文章专门讲这个,如果有不清楚的朋友可以先参考一下。 学习JPA——Hibernate Annotation使用实例
     本例中,我有两点要讲下。
    
第一、管理主键的表 generator_table 去掉原来单独定义的那个 ID 主键,把 g_key 设为主键,整个表将只有两个字段, g_key g_value
    
第二、在 User 实体中,我将角色 ID role_id )与角色实体( Role )做了一个多对一关联。这一点是原来文章中没有讲过的。
    
    
请一定注意 role_id user 表的字段。我在本例中设定的是一个角色可以对应多个人员,所以这个 role_id 存的就是 role id 的值。 fetch = FetchType. LAZY 指定采用延迟检索,如果当你取得了 User 对象,但又不想取 Role 中的信息,这时, User 对象中的 role 属性是代理状态。 Role 对象中的值都是空的。只有当你使用 role.id role.name 进行取值的时候, hibernate 才会去数据库中查找对应的记录,因此在一定程度上降低了资源消耗。不过这里有点要注意,采用延迟检索的时候,需要加上前一篇讲到的 OpenSessionInViewFilter 过滤器。否则会遇到 session 关闭的异常。
    
service
    
    
@Service 表示这是 业务层组件。 在业务层需要对 UserDao 加上 @Autowired 注解,大象在这里将业务层的方法名与持久层的方法名定义为一样的,是我的一种习惯,大家可以按自己的想法来做。
    
测试
    
既然有了这么多代码,那就来测试一下吧,看看有没有问题。
    
    
好吧,为了照顾那些坚定的 JUnit 拥护者,再写一个 JUnit 测试。本例使用 junit-4.4.jar
    
    
    
@BeforeClass 注解的方法表示在类实例化之前,也就是在类的构造方法执行之前就会执行。而且使用这个注解的方法必须是 static void
    
@Test 标明这是测试方法,方法名不用像以前那样必须按照规则进行命名,可以*定义。
    
上图显示大象使用 JUnit 方式测试也通过了(如果不会通过我写它干嘛?嘿嘿)。
    
假如我将张三改成张四,再来看看测试结果。
    
    
这个截图可以很明显的说明所有东西。
    
这一篇是给大家讲怎么用代码来实现除 web 层之外的全注解步骤。当然,我主要是讲思路,其实思路比代码重要得多。这一个系列的最后,我会放上所有源码供大家下载。现在这样慢慢分析,是想给大家讲道理。我们应该努力提升自己的境界与层次,而不要只把眼光放在代码上面。下一章将会着重介绍 web 层,以及 struts2 的注解插件 struts2-convention 。那么,我们下次继续。