在前两章我为大家详细分析了
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
。那么,我们下次继续。
相关文章
- VSCode插件开发全攻略(三)package.json详解
- 三国志战略版侠客军怎么玩 侠客军全方位详解
- 详解spring boot mybatis全注解化
- 详解:数据库名、实例名、ORACLE_SID、数据库域名、全局数据库名、服务名及手工脚本创建oracle数据库
- Redis最全详解(三)——SpringBoot整合2种方式
- Python urls.py的三种配置写法实例详解
- Symfony2使用第三方库Upload制作图片上传实例详解
- 最新最全面的Spring详解(三)——Resources,验证、数据绑定和类型转换与Spring表达式语言(SpEL)
- php获取POST数据的三种方法实例详解
- Python tkinter三种布局实例详解