原文链接: http://www.java3z.com/cwbwebhome/article/article8/81137.html
在前两章我为大家详细分析了 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 。???
原文链接: http://www.java3z.com/cwbwebhome/article/article8/81137.html