目录:
学习笔记零 - 起源
学习笔记一 - 概念与入门
学习笔记二 - 基础配置
学习笔记三 - 高级技巧与进阶
最佳实践
1 iBATIS中的单元测试
iBATIS至少在3个方面可以使得单元测试变得容易:
- 测试映射层本身
- 测试DAO层
- 在DAO的消费层(使用层)进行测试
1.1 对映射层进行单元测试
测试用数据库实例
即数据库管理系统的实例,比如SQL SERVER。
JAVA程序员可以使用内存数据库,如HSQLDB。
数据库脚本
数据库模式以及测试数据的构造与创建。即通过数据库脚本进行创建,这些数据库脚本保持和程序源代码同步。
iBATIS配置文件
分别进行测试环境配置和产品环境配置最简单的方式就是,使用不同的iBATIS配置文件,两份配置文件引用相同的一组SQL配置文件。
iBATIS SqlMapClient单元测试
单元测试:
public class PersonMapTest extends TestCase {
private SqlMapClient sqlMapClient;
public void setup () {
sqlMapClient = SqlMapClientBuilder.
build("maps/TestSqlMapConfig.xml");
runSqlScript("scripts/drop-person-schema.sql");
runSqlScript("scripts/create-person-schema.sql");
runSqlScript("scripts/create-person-test-data.sql");
}
public void testShouldGetPersonWithIdOfOne() {
Person person = (Person) sqlMapClient.queryForObject("getPerson", new Integer(1));
assertNotNull("Expected to find a person.", person);
assertEquals("Expected person ID to be 1.",
new Integer(1), person.getId());
}
}
1.2 对DAO进行单元测试
DAO层是一个抽象层,通常分离为一个接口和一个实现,单测试接口没有作用,所以需要直接对DAO的实现进行测试。
利用模拟对象来对DAO进行单元测试
提供一个Dao实现:
public class SqlMapPersonDao implements PersonDao {
private SqlMapClient sqlMapClient;
public SqlMapPersonDao(SqlMapClient sqlMapClient) {
this.sqlMapClient = sqlMapClient;
}
public Person getPerson (int id) {
try {
return (Person)
sqlMapClient.queryForObject("getPerson", id);
} catch (SQLException e) {
throw new DaoRuntimeException("Error getting person. Cause: " + e, e);
}
}
}
使用JMock对象模拟框架
public void testShouldGetPersonFromDaoWithIDofOne() {
final Integer PERSON_ID = new Integer(1);
Mock mock = new Mock(SqlMapClient.class);
mock.expects(once()).method("queryForObject").with(eq("getPerson"),eq(PERSON_ID)).will(returnValue(new Person (PERSON_ID)));
PersonDao daoSqlMap = new SqlMapPersonDao((SqlMapClient)mock.proxy());
Person person = daoSqlMap.getPerson(PERSON_ID);
assertNotNull("Expected non-null person instance.",person);
assertEquals("Expected ID to be " + PERSON_ID,PERSON_ID, person.getId());
}
1.3 对DAO的消费层进行单元测试
应用程序中使用DAO的层的其他层就是DAO层的消费层。
简单的DAO接口:
public interface PersonDao extends Dao {
Person getPerson(Integer id);
}
使用DAO接口的服务:
public class PersonService {
private PersonDao personDao;
public PersonService(PersonDao personDao) {
this.personDao = personDao;
}
public Person getValidatedPerson(Integer personId) {
Person person = personDao.getPerson(personId);
validateAgainstPublicSystems(person);
validateAgainstPrivateSystems(person);
validateAgainstInternalSystems(person);
return person;
}
}
使用模拟DAO,避免访问数据库:
public void testShouldRetrieveAValidatedPerson (){
final Integer PERSON_ID = new Integer(1);
Mock mock = new Mock(PersonDao.class);
mock.expects(once()).method("getPerson").with(eq(PERSON_ID)).will(returnValue(new Person(PERSON_ID)));
PersonService service = new PersonService((PersonDao)mock.proxy());
service.isPersonalInformationValid(new Person(new Integer(1)), new Integer(1));
assertNotNull("Expected non-null person instance.",person);
assertEquals("Expected ID to be " + PERSON_ID,PERSON_ID, person.getId());
assertTrue("Expected valid person.",
person.isValid());
}
上例也使用了JUnit和JMock。
2 管理iBATIS配置文件
2.1 将其保存在类路径上
位置透明(location transparency) 是应用程序可维护性的一个重要方面。它可以简化应用程序的测试和部署。
保持位置透明的一个方法就是:不要在应用程序中使用静态的文件路径。
使用java类路径,应用程序就可以通过使用类加载器(classloader)在内部访问它。
将映射文件存储在类路径上之后,iBATIS就可以通过Resource实用类(iBATIS提供的类)来加载这些文件。如:
Reader reader = Resources.getResourceAsReader("config/maps/SqlMapConfig.xml");
2.2 集中放置文件
应把所有的映射文件集中放置,避免分散在类路径上。千万不要把它们放置在它们所处理的类的旁边,也不要存储在单独的包中,这会使得配置变得复杂,并且很难弄清楚有哪些映射文件可用。不要和其他XML文件混淆在一起
2.3 按返回类型来组织映射文件
刚开始时,最好利用语句的返回类型以及语句接受的参数类型来组织映射文件。
3 命名规范
iBATIS有很多东西需要命名:语句、结果映射、参数映射、SQL映射文件,以及XML文件都需要命名。因此,最好有一种命名规范。
3.1 语句的命名
语句命名通常遵循编程语言中方法的命名规范,如loadPerson,getPerson。
3.2 参数映射的命名
建议使用参数映射的语句的名字后添以Param后缀作为该参数映射的名字。
<select id="getPerson" parameterMap="getPersonParam" ... >
3.3 结果映射的命名
建议添加Result后缀
<resultMap id="PersonResult" type="com.domain.Person">
3.4 XML文件的命名
iBATIS中有两类XML文件,第一类是主配置文件,第二类是SQL映射文件。
主配置文件
推荐使用SqlMapConfig.xml。如果应用程序的不同模块有不同的配置文件,那么将该模块的名字作为前缀添加到配置文件名中。如WebSqlMapConfig.xml,GuiSqlMapConfig.xml。
SQL映射文件
取决于组织方式,如果是根据映射语句的返回类型组织的,那直接使用参数命名即可,如Person.xml。
如果某些应用程序需要对同一个映射语句做多个实现以匹配不同的数据库,那可以使用OraclePerson.xml这样的命名方式。
4 Bean、map还是XML
iBATIS支持多种类型的参数映射和结果映射,默认情况下推荐使用JavaBean
4.1 JavaBean
具有最好的性能、最大的灵活性和类型安全性
4.2 map
使用map需要谨慎,map非常低效,不具备安全性,耗费更多内存,难于预测与维护。
支持的原因:一,iBATIS使用map作为传递多个复杂参数给已映射语句。二数据库中的表扫描都不是,没有领域类,只是一堆名与值。
4.3 XML
在一个只需要一种可移植、可解析的格式快速转换数据的应用程序中,XML可能就是有用的。
XML不是领域模型的首选,是最慢、最不安全,最消耗内存的。它与数据的最终状态是最接近的,但这种优势是以其难以操纵和难以维护作为代价的。使用XML与map一样,要谨慎。
4.4 基本类型
安全且高效,但是数据不能过于复杂,如获取记录数这种需求,就可以使用基本类型。
综合案例研究
内容:
- 选择工具
- 设立工程
- 各种组件的综合应用
1 设计理念
设计一个游戏商店,该商店有4个组件:account,catalog(类别),cart(购物车)和order(订单)
1.1 账户
账户保存用户的相关信息,比如地址(address)、偏好(preference)等私人信息。
用户应该能创建和编辑自己的账户,该账户也用于处理用户登录时的安全信息。
1.2 目录
目录包含类别(catalog)、产品(product)和产品项(item)。一个类别包含一些产品,一个产品包含一些产品项。
1.3 购物车
购物车用于维护用户选择的所有产品。能够计算出购物车中产品项的总价。
1.4 订单
应用程序的订单部分用于结账。购物车将完成一系列过程,如购物信息确认、付账、形成账单、账单发送,最后确认。一旦订单完成,用户可以在他们的订单历史中浏览到他。
2 选择具体的实现技术
2.1 表现层
Struts、JSF、Spring和WebWork。作者使用Struts
2.2 服务层
作者使用iBATIS DAO
2.3 持久层
使用iBATIS