- 浏览: 337068 次
- 性别:
- 来自: 新加坡
文章分类
最新评论
-
xiuluocd:
ok,受益匪浅
[转贴]System.getProperty()参数大全 -
xiaohuafyle:
这个不能算是bug吧,weblogic用的jdk版本一直都很老 ...
项目积累:Weblogic -
macemers:
请问楼主 has_readed作为计数器,是如何防止网络阻塞的 ...
Socket、Thread的使用记录 -
xutao5641745:
higkoo 写道请教一个问题,我在编译Jmeter的时候,发 ...
[转贴]System.getProperty()参数大全 -
zhaoyu_h:
加油。。。啊。。。加油。。。 谢谢啦。。
[转贴]System.getProperty()参数大全
由于 Java™ 5 泛型的采用,有关泛型类型安全 Data Access Object (DAO) 实现的想法变得切实可行。在本文中,系统架构师 Per Mellqvist 展示了基于 Hibernate 的泛型 DAO 实现类。然后展示如何使用 Spring AOP introductions 将类型安全接口添加到类中以便于查询执行。
对于大多数开发人员,为系统中的每个 DAO 编写几乎相同的代码到目前为止已经成为一种习惯。虽然所有人都将这种重复标识为 “代码味道”,但我们大多数都已经学会忍受它。其实有解决方案。可以使用许多 ORM 工具来避免代码重复。例如,使用 Hibernate,您可以简单地为所有的持久域对象直接使用会话操作。这种方法的缺点是损失了类型安全。
为什么您要为数据访问代码提供类型安全接口?我会争辩说,当它与现代 IDE 工具一起使用时,会减少编程错误并提高生产率。首先,类型安全接口清楚地指明哪些域对象具有可用的持久存储。其次,它消除了易出错的类型强制转换的需要(这是一个在查询操作中比在 CRUD 中更常见的问题)。最后,它有效利用了今天大多数 IDE 具备的自动完成特性。使用自动完成是记住什么查询可用于特定域类的快捷方法。
在本文中,我将为您展示如何避免再三地重复 DAO 代码,而仍保留类型安全接口的优点。事实上,您需要为每个新 DAO 编写的只是 Hibernate 映射文件、无格式旧 Java 接口以及 Spring 配置文件中的 10 行。
DAO 模式对任何企业 Java 开发人员来说都应该很熟悉。但是模式的实现各不相同,所以我们来澄清一下本文提供的 DAO 实现背后的假设:
- 系统中的所有数据库访问都通过 DAO 进行以实现封装。
- 每个 DAO 实例负责一个主要域对象或实体。如果域对象具有独立生命周期,它应具有自己的 DAO。
- DAO 负责域对象的创建、读取(按主键)、更新和删除(creations, reads, updates, and deletions,CRUD)。
- DAO 可允许基于除主键之外的标准进行查询。我将之称为查找器方法 或查找器。查找器的返回值通常是 DAO 负责的域对象集合。
- DAO 不负责处理事务、会话或连接。这些不由 DAO 处理是为了实现灵活性。
-
泛型 DAO 的基础是其 CRUD 操作。下面的接口定义泛型 DAO 的方法:
清单 1. 泛型 DAO 接口
public interface GenericDao { /** Persist the newInstance object into database */ PK create(T newInstance); /** Retrieve an object that was previously persisted to the database using * the indicated id as primary key */ T read(PK id); /** Save changes made to a persistent object. */ void update(T transientObject); /** Remove an object from persistent storage in the database */ void delete(T persistentObject);}
用 Hibernate 实现清单 1 中的接口十分简单,如清单 2 所示。它只需调用底层 Hibernate 方法和添加强制类型转换。Spring 负责会话和事务管理。(当然,我假设这些函数已做了适当的设置,但该主题在 Hibernate 和 Springt 手册中有详细介绍。)
清单 2. 第一个泛型 DAO 实现
public class GenericDaoHibernateImpl implements GenericDao, FinderExecutor { private Class<t></t> type; public GenericDaoHibernateImpl(Class<t></t> type) { this.type = type; } public PK create(T o) { return (PK) getSession().save(o); } public T read(PK id) { return (T) getSession().get(type, id); } public void update(T o) { getSession().update(o); } public void delete(T o) { getSession().delete(o); } // Not showing implementations of getSession() and setSessionFactory() }
最后,在 Spring 配置中,我创建了
GenericDaoHibernateImpl
的一个实例。必须告诉GenericDaoHibernateImpl
的构造函数 DAO 实例将负责哪个域类。只有这样,Hibernate 才能在运行时知道由 DAO 管理的对象类型。在清单 3 中,我将域类Person
从示例应用程序传递给构造函数,并将先前配置的 Hibernate 会话工厂设置为已实例化的 DAO 的参数:
清单 3. 配置 DAO
<bean class="genericdao.impl.GenericDaoHibernateImpl" id="personDao"></bean> <constructor-arg></constructor-arg> <value></value>genericdaotest.domain.Person <property name="sessionFactory"> <ref bean="sessionFactory"></ref> </property>
我还没有完成,但我所完成的确实已经可以使用了。在清单 4 中,可以看到原封不动使用该泛型 DAO 的示例:
public void someMethodCreatingAPerson() { ... GenericDao dao = (GenericDao) beanFactory.getBean("personDao"); // This should normally be injected Person p = new Person("Per", 90); dao.create(p);}
现在,我有一个能够进行类型安全 CRUD 操作的泛型 DAO。让子类
GenericDaoHibernateImpl
为每个域对象添加查询能力将非常合理。因为本文的目的在于展示如何不为每个查询编写显式的 Java 代码来实现查询,但是,我将使用其他两个工具将查询引入 DAO,也就是 Spring AOP 和 Hibernate 命名的查询。可以使用 Spring AOP 中的 introductions 将功能添加到现有对象,方法是将功能包装在代理中,定义应实现的接口,并将所有先前未支持的方法指派到单个处理程序。在我的 DAO 实现中,我使用 introductions 将许多查找器方法添加到现有泛型 DAO 类中。因为查找器方法是特定于每个域对象的,因此将其应用于泛型 DAO 的类型化接口。
Spring 配置如清单 5 所示:
清单 5. FinderIntroductionAdvisor 的 Spring 配置
<bean class="genericdao.impl.FinderIntroductionAdvisor" id="finderIntroductionAdvisor"></bean><bean class="genericdao.impl.GenericDaoHibernateImpl" id="abstractDaoTarget" abstract="true"></bean> <property name="sessionFactory"> <ref bean="sessionFactory"></ref> </property><bean class="org.springframework.aop.framework.ProxyFactoryBean" id="abstractDao" abstract="true"></bean> <property name="interceptorNames"> <list></list> <value></value>finderIntroductionAdvisor </property>
在清单 5 的配置文件中,我定义了三个 Spring bean。第一个 bean 是 FinderIntroductionAdvisor,它处理引入到 DAO 的所有方法,这些方法在
GenericDaoHibernateImpl
类中不可用。我稍后将详细介绍 Advisor bean。第二个 bean 是 “抽象的”。在 Spring 中,这意味着该 bean 可在其他 bean 定义中被重用,但不被实例化。除了抽象特性之外,该 bean 定义只指出我想要
GenericDaoHibernateImpl
的实例以及该实例需要对SessionFactory
的引用。注意,GenericDaoHibernateImpl
类仅定义一个构造函数,该构造函数接受域类作为其参数。因为该 bean 定义是抽象的,所以我将来可以无数次地重用该定义,并将构造函数参数设置为合适的域类。最后,第三个也是最有趣的 bean 将
GenericDaoHibernateImpl
的 vanilla 实例包装在代理中,赋予其执行查找器方法的能力。该 bean 定义也是抽象的,不指定希望引入到 vanilla DAO 的接口。该接口对于每个具体的实例是不同的。注意,清单 5 显示的整个配置仅定义一次。当然,每个 DAO 的接口都基于
GenericDao
接口。我只需使该接口适应特定的域类并扩展该接口以包括查找器方法。在清单 6 中,可以看到为特定目的扩展的GenericDao
接口示例:public interface PersonDao extends GenericDao { List<person></person> findByName(String name);}
很明显,清单 6 中定义的方法旨在按名称查找
Person
。必需的 Java 实现代码全部是泛型代码,在添加更多 DAO 时不需要任何更新。因为 Spring 配置依赖于先前定义的 “抽象” bean,因此它变得相当简洁。我需要指出 DAO 负责哪个域类,并且需要告诉 Springs 该 DAO 应实现哪个接口(一些方法是直接使用,一些方法则是通过使用 introductions 来使用)。清单 7 展示了
PersonDAO
的 Spring 配置文件:<bean id="personDao" parent="abstractDao"></bean> <property name="proxyInterfaces"> <value></value>genericdaotest.dao.PersonDao </property> <property name="target"> <bean parent="abstractDaoTarget"></bean> <constructor-arg></constructor-arg> <value></value>genericdaotest.domain.Person </property>
在清单 8 中,可以看到使用了这个更新后的 DAO 版本:
public void someMethodCreatingAPerson() { ... PersonDao dao = (PersonDao) beanFactory.getBean("personDao"); // This should normally be injected Person p = new Person("Per", 90); dao.create(p); List<person></person> result = dao.findByName("Per"); // Runtime exception}
虽然清单 8 中的代码是使用类型安全
PersonDao
接口的正确方法,但 DAO 的实现并不完整。调用findByName()
会导致运行时异常。问题在于我还没有实现调用findByName()
所必需的查询。剩下要做的就是指定查询。为更正该问题,我使用了 Hibernate 命名查询。
使用 Hibernate,可以在 Hibernate 映射文件 (hbm.xml) 中定义 HQL 查询并为其命名。稍后可以通过简单地引用给定名称来在 Java 代码中使用该查询。该方法的优点之一是能够在部署时优化查询,而无需更改代码。您一会将会看到,另一个优点是无需编写任何新 Java 实现代码,就可以实现 “完整的” DAO。清单 9 是带有命名查询的映射文件的示例:
<hibernate-mapping package="genericdaotest.domain"></hibernate-mapping> <class name="Person"></class> <id name="id"></id> <generator class="native"></generator> <property name="name"></property> <property name="weight"></property> <query name="Person.findByName"></query>
清单 9 定义了域类
Person
的 Hibernate 映射,该域类具有两个属性:name
和weight
。Person
是具有上述属性的简单 POJO。该文件还包含一个在数据库中查找Person
所有实例的查询,其中 “name” 等于提供的参数。Hibernate 不为命名查询提供任何真正的名称空间功能。出于讨论目的,我为所有查询名称都加了域类的短(非限定)名称作为前缀。在现实世界中,使用包括包名称的完全类名可能是更好的主意。
您已经看到了为任何域对象创建和配置新 DAO 所必需的全部步骤。三个简单的步骤是:
- 定义一个接口,它扩展
GenericDao
并包含所需的任何查找器方法。 - 将每个查找器的命名查询添加到域对象的 hbm.xml 映射文件。
- 为 DAO 添加 10 行 Spring 配置文件。
查看执行查找器方法的代码(只编写了一次!)来结束我的讨论。
使用的 Spring advisor 和 interceptor 很简单,事实上它们的工作是向后引用
GenericDaoHibernateImplClass
。方法名以 “find” 打头的所有调用都传递给 DAO 和单个方法executeFinder()
。
清单 10. FinderIntroductionAdvisor 的实现
public class FinderIntroductionAdvisor extends DefaultIntroductionAdvisor { public FinderIntroductionAdvisor() { super(new FinderIntroductionInterceptor()); }}public class FinderIntroductionInterceptor implements IntroductionInterceptor { public Object invoke(MethodInvocation methodInvocation) throws Throwable { FinderExecutor genericDao = (FinderExecutor) methodInvocation.getThis(); String methodName = methodInvocation.getMethod().getName(); if (methodName.startsWith("find")) { Object[] arguments = methodInvocation.getArguments(); return genericDao.executeFinder(methodInvocation.getMethod(), arguments); } else { return methodInvocation.proceed(); } } public boolean implementsInterface(Class intf) { return intf.isInterface() && FinderExecutor.class.isAssignableFrom(intf); }}
清单 10 的实现中惟一缺少的是
executeFinder()
实现。该代码查看调用的类和方法的名称,并使用配置上的约定将其与 Hibernate 查询的名称相匹配。还可以使用FinderNamingStrategy
来支持其他命名查询的方法。默认实现查找叫做 “ClassName.methodName
” 的查询,其中ClassName
是不带包的短名称。清单 11 完成了泛型类型安全 DAO 实现:public List<t></t> executeFinder(Method method, final Object[] queryArgs) { final String queryName = queryNameFromMethod(method); final Query namedQuery = getSession().getNamedQuery(queryName); String[] namedParameters = namedQuery.getNamedParameters(); for(int i = 0; i < queryArgs.length; i++) { Object arg = queryArgs[i]; Type argType = namedQuery.setParameter(i, arg); } return (List<t></t>) namedQuery.list(); } public String queryNameFromMethod(Method finderMethod) { return type.getSimpleName() + "." + finderMethod.getName(); }
在 Java 5 之前,该语言不支持编写既类型安全又 泛型的代码,您必须只能选择其中之一。在本文中,您已经看到一个结合使用 Java 5 泛型与 Spring 和 Hibernate(以及 AOP)等工具来提高生产率的示例。泛型类型安全 DAO 类相当容易编写 —— 您只需要单个接口、一些命名查询和为 Spring 配置添加的 10 行代码 —— 而且可以极大地减少错误并节省时间。
几乎本文的所有代码都是可重用的。尽管您的 DAO 类可能包含此处没有实现的查询和操作类型(比如,批操作),但使用我所展示的技术,您至少应该能够实现其中的一部分。参阅 参考资料 了解其他泛型类型安全 DAO 类实现。
自 Java 语言中出现泛型以来,单个泛型类型安全 DAO 的概念已经成为主题。我曾在 JavaOne 2004 中与 Don Smith 简要讨论了泛型 DAO 的灵活性。本文使用的 DAO 实现类旨在作为示例实现,实际上还存在其他实现。例如,Christian Bauer 已经发布了带有 CRUD 操作和标准搜索的实现,Eric Burke 也在该领域做出了工作。我确信将会有更多的实现出现。我要额外感谢 Christian,他目睹了我编写泛型类型安全 DAO 的第一次尝试并提出改进建议。最后,我要感谢 Ramnivas Laddad 的无价帮助,他审阅了本文。
- 定义一个接口,它扩展
发表评论
-
复杂Struts Validation汇总
2008-11-16 21:57 3199不得不承认我用了太久的Struts1,但实际上并没有太多好的回 ... -
多种问题 一种解决方案:使用ANT管理部署应用服务器
2008-07-12 01:46 4034ANT在Java界和开源世界里面的地位大家都知道,虽然 ... -
Socket、Thread的使用记录
2008-04-03 16:35 2719做了太多时候的Web项目 ... -
项目积累:Weblogic
2007-08-22 17:54 2189新项目中最大的收获就是Weblogic的使用和管理,由于客户的 ... -
项目反思:DetachedCriteria需要吗?不需要吗?
2007-07-09 23:16 2982Hibernate3中提供了DetachedCri ... -
JUnit方案 DetachedCriteria模拟求解
2007-07-04 16:36 1843新项目客户对测试的要求比较严格,要求单元测试的覆盖率为 ... -
Spring 移植到WebLogic10上出现的Log4j.properties错误
2007-06-10 23:31 6276问题是在Tomcat移植到Weblogic10上出现的问题,主 ... -
[转贴]Clustering经典范文学习
2007-05-12 10:37 1930Cluster对于没有去实现的人还是相当神秘的东西,先从基础 ... -
JavaEye活动记实 Gavin King见面会以及Robbin Hibernate性能调优
2007-04-24 00:05 42494月19日,如期赶到交大 ... -
使用Velocity Swing制作的代码生成工具
2007-04-18 13:39 2865一个使用JEE开发的项目中将会存在大量的重复代码,这是每个开发 ... -
J2EE性能提高(Struts+Spring+Hibernate)
2007-04-17 22:33 3485新近的项目是一个BOSS级的计费系统,性能和稳定性的要求比较高 ... -
[转贴]System.getProperty()参数大全
2007-04-17 17:42 45591一直做Web应用一般不会用到这些东西,今天在搞一个代码生成器的 ... -
Spring2学习笔记
2007-04-15 22:33 1954学习和使用Rails正开心 ... -
[原创]Struts Validation框架中验证循环产生的Text框
2006-01-11 14:18 1897前几天做到一个validation验证使用<iterat ... -
[原创]JSP页面通过Form控制Beans属性
2006-01-14 16:29 1520这个问题我曾经在社区提问过,不知是当时没问清楚还是怎样,回答是 ... -
项目总结(Ajax+Struts+Spring+Hiberante+SQLServer2000) 第一部分
2006-08-06 16:09 1849总算将一个项目勉 ... -
我的乱码问题处理汇总
2006-08-06 22:32 3819乱码对于使用非英语文 ... -
项目总结(Ajax+Struts+Spring+Hiberante+SQLServer2000) 第三部分
2006-08-06 22:56 1573因为本次项目是政府项目,所以数据库服务器采用SQLServer ... -
[讨论]有关Spring+Hibernate更新数据库的操作
2006-09-20 00:05 2869最近老和同事争执一个S ... -
Hibernate事务管理读书笔记(不断添加中)
2006-09-20 12:28 6290昨天发了一个有关Hiberna ...
相关推荐
赠送jar包:jackson-datatype-jdk8-2.13.1.jar; 赠送原API文档:jackson-datatype-jdk8-2.13.1-javadoc.jar; 赠送源代码:jackson-datatype-jdk8-2.13.1-sources.jar; 赠送Maven依赖信息文件:jackson-datatype-...
赠送jar包:jackson-datatype-jdk8-2.11.4.jar; 赠送原API文档:jackson-datatype-jdk8-2.11.4-javadoc.jar; 赠送源代码:jackson-datatype-jdk8-2.11.4-sources.jar; 赠送Maven依赖信息文件:jackson-datatype-...
5. **安装JDK**:在基于RPM的Linux系统中,用户可以使用`rpm`命令安装此文件,例如`sudo rpm -ivh jdk-8u201-linux-x64.rpm`,这将自动处理依赖关系并安装JDK。 6. **环境变量设置**:安装完成后,可能需要配置`...
官方下载链接:https://download.oracle.com/otn/java/jdk/8u251-b08/3d5a2bb8f8d4428bbe94aed7ec7ae784/jdk-8u251-windows-x64.exe 觉得慢的话可以用txt里百度网盘下载,附件为...jdk8 jdk-8u251-windows-x64 网盘下载
这个"jdk-8u211-windows-x64.zip"压缩包包含的是针对Windows 64位系统的JDK 8更新211版本。JDK 8是Java历史上的一个关键版本,引入了大量新特性,优化了性能,并且对开发者友好性进行了改进。 1. **Java语言新特性*...
Java Development Kit(JDK)是...总之,"jdk-8u151-linux-x64.tar.gz"提供了在Linux环境下进行Java开发所需的基础,它的特性改进极大地提升了开发效率和程序性能。无论是初学者还是经验丰富的开发者,都能从中受益。
linux下离线安装jdk的依赖包,jdk-8u181-linux-x64.rpm
本主题涉及两个特定于Linux平台的JDK版本:jdk-8u171-linux-x64.rpm和jdk-8u171-linux-x64.tar.gz。这两个文件分别对应于两种不同的安装方式,适应于不同类型的Linux系统需求。 首先,`jdk-8u171-linux-x64.rpm`是...
jdk-8u11-linux-x64.rpm
在本例中,我们讨论的是JDK的特定版本——"jdk-8u371-windows-x64",这是一个面向Windows 64位操作系统的更新版本8u371。Java是由Sun Microsystems公司(后被Oracle公司收购)开发的一种广泛使用的编程语言和平台,...
- 首先,你需要将`jdk-8u131-linux-x64.tar.gz`文件下载到你的Linux机器上,通常是在`~/Downloads`目录下。 - 使用`tar`命令进行解压。例如,可以运行`tar -zxvf jdk-8u131-linux-x64.tar.gz`。这将创建一个名为`...
在这个场景中,我们有两个版本的JDK 8u341针对Linux操作系统的安装包:`jdk-8u341-linux-x64.rpm` 和 `jdk-8u341-linux-x64.tar.gz`。 1. **RPM 包格式**: `jdk-8u341-linux-x64.rpm` 是一个遵循Red Hat Package ...
1. 首先,下载名为“jdk-7u79-linux-x64.tar.gz”的压缩文件,这是针对Linux 64位系统的JDK 1.7的归档文件。 2. 使用解压命令(如tar -zxvf jdk-7u79-linux-x64.tar.gz)将内容解压到指定目录。 3. 设置环境变量,...
这个"jdk-8u281-windows-x64"是专为64位Windows操作系统设计的JDK安装程序。JDK是开发和运行Java应用程序的基础,它包含了Java编译器、Java运行时环境(JRE)、调试工具和其他必要的软件组件。 **JDK的主要组成部分...
标题“jdk-8u381-windows-x64”指的是Java Development Kit(JDK)的特定版本,适用于Windows操作系统64位平台。JDK是Oracle公司提供的用于开发和运行Java应用程序的重要工具集,包含了Java编译器、Java虚拟机(JVM...
`jdk-8u181-windows-x64.zip` 是Oracle公司发布的JDK 8更新181的64位Windows版本的压缩包。这个版本在Java社区中被广泛认为是一个稳定且功能丰富的版本,对于许多开发者来说,它仍然是首选的开发环境。 JDK 8是Java...
- 解压下载的`jdk-7u79-linux-x64`压缩包到适当目录。 - 配置环境变量`JAVA_HOME`,`PATH`和`CLASSPATH`,使系统能够找到JDK。 - 使用`java -version`命令检查安装是否成功。 5. **开发和运行Java程序**: - ...
在下载"jdk-8u221-linux-x64.tar.gz"之后,你需要在Linux环境下解压这个文件。可以使用以下命令来完成: ```bash tar -zxvf jdk-8u221-linux-x64.tar.gz ``` 这将会创建一个名为"jdk-8u221-linux-x64"的目录,其中...
jdk-8u211-windows-x64安装包 百度网盘链接:https://pan.baidu.com/s/1Md5NbPblXG-P-PDse
本篇主要针对"jdk-8u71-windows-x64.exe"这个特定的JDK版本进行详细解析。 "jdk-8u71-windows-x64.exe"是Oracle JDK 8 Update 71的64位Windows版本安装程序。这个版本发布于2016年,它包含了Java运行时环境(Java ...