六、配置iBatis
接下来,使用iBatis实现O/R Mapping。首先从http://www.ibatis.com下载iBatis 2.0,将所需的jar文件复制到web/WEB-INF/lib/目录下。iBatis使用XML配置数据库表到Java对象的映射,先编写一个sql-map-config.xml:
<?xml version="1.0" encoding="utf-8" ?
<!DOCTYPE sqlMapConfig
PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd"
<sqlMapConfig
<settings cacheModelsEnabled="false" enhancementEnabled="true"
lazyLoadingEnabled="true" maxRequests="32"
maxSessions="10" maxTransactions="5"
useStatementNamespaces="false"
/
<transactionManager type="JDBC"
<dataSource type="JNDI"
<property name="DataSource" value="jdbc/blog" /
</dataSource
</transactionManager
<!-- 假如有其他xml配置文件,可以包含进来 --
<sqlMap resource="Account.xml" /
</sqlMapConfig
将sql-map-config.xml放到web/WEB-INF/classes/目录下,iBatis就能搜索到这个配置文件,然后编写一个初始化类:
public class SqlConfig {
private SqlConfig() {}
private static final SqlMapClient sqlMap;
static {
try {
java.io.Reader reader = Resources.getResourceAsReader ("sql-map-config.xml");
sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Error initializing SqlConfig. Cause: " + e);
}
}
public static SqlMapClient getSqlMapInstance () {
return sqlMap;
}
}
SqlMapClient封装了访问数据库的大部分操作,可以直接使用SqlConfig.getSqlMapInstance()获得这个唯一实例。
七、使用DAO模1式
为了分离逻辑层和数据库持久层,定义一系列DAO接口:AccountDao,CategoryDao,ArticleDao……其实现类对应为SqlMapAccountDao,SqlMapCategoryDao,SqlMapArticleDao……这样就使得逻辑层完全脱离了数据库访问代码。假如将来需要使用其它的O/R Mapping方案,直接实现新的DAO接口替代现有的SqlMapXxxDao即可。以SqlMapAccountDao为例,实现一个login()方法是非常简单的:
public int login(String username, String passWord) throws AuthorizationException {
try {
Map map = new HashMap();
map.put("username", username);
map.put("password", password);
Integer I = (Integer)sqlMap.queryForObject("login", map);
if(I==null)
throw new RuntimeException("Failed: Invalid username or password.");
return I.intValue();
}
catch(SQLException sqle) {
throw new RuntimeException("Sql Exception: " + sqle);
}
}
在Account.xml配置文件中定义login查询:
<select id="login" parameterClass="java.util.Map" resultClass="int"
select [accountId] from [Account] where
[username] = #username# and password = #password#
</select
八、逻辑层设计
由于DAO模式已经实现了所有的数据库操作,业务逻辑主要是检查输入,调用DAO接口,因此业务逻辑就是一个简单的Facade接口:
public class FacadeImpl implements Facade {
private AccountDaoaccountDao;
private ArticleDaoarticleDao;
private CategoryDao categoryDao;
private FeedbackDao feedbackDao;
private ImageDaoimageDao;
private LinkDao linkDao;
private SequenceDao sequenceDao;
}
对于普通的getArticle()等方法,Facade仅仅简单地调用对应的DAO接口:
public Article getArticle(int articleId) throws QueryException {
return articleDao.getArticle(articleId);
}
对于需要身份验证的操作,如deleteArticle()方法,Facade需要首先验证用户身份:
public void deleteArticle(Identity id, int articleId) throws DeleteException {
Article article = getArticleInfo(articleId);
if(article.getAccountId()!=id.getAccountId())
throw new AuthorizationException("Permission denied.");
articleDao.deleteArticle(articleId);
}
要分离用户验证逻辑,可以使用Proxy模式,或者使用Spring的AOP,利用MethodInterceptor实现,不过,由于逻辑很简单,完全可以直接写在一块,不必使用过于复杂的设计。 至此,我们的Blog已经实现了所有的后台业务逻辑,并且提供统一的Facade接口。前台Web层仅仅依靠这个Facade接口,这样,Web层和后台耦合非常松散,即使替换整个Web层也非常轻易。
九、Web层设计:使用MVC模式
对于复杂的Web层,使用MVC模式是必不可少的。虽然Spring能轻易集成Struts,WebWorks等Web框架,但Spring本身就提供了一个非常好的Web框架,能完全实现MVC模式。
Spring使用一个DispatcherServlet,所有的特定请求都被转发到DispatcherServlet,然后由相应的Controller处理,Controller返回一个ModelAndView对象(因为Java语言的方法调用只能返回一个结果,而且不支持ref参数,所以将Model和View对象合在一起返回),Model是一个Java对象,通常是Map,View是视图的逻辑名字,通常是jsp文件名,但也可以使用Velocity等作为视图。返回的View通过viewResolver得到真正的文件名。首先配置Spring的MVC,在web.xml中声明DispatcherServlet,处理所有以.c结尾的请求:
<web-app
<servlet
<servlet-namedispatcher</servlet-name
<servlet-classorg.springframework.web.servlet.DispatcherServlet</servlet-class
<load-on-startup1</load-on-startup
</servlet
<servlet-mapping
<servlet-namedispatcher</servlet-name
<url-pattern*.c</url-pattern
</servlet-mapping
</web-app
Spring会在WEB-INF下查找一个名为dispatcher-servlet.xml的文件,我们需要创建这个文件:
<?xml version="1.0" encoding="utf-8"?
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd"
<beans
</beans
用到的所有的Java Bean组件都要在这个文件中声明和配置,以下是配置URL映射的Bean:
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"
<property name="mappings"
<props
<prop key="/article.c"articleController</prop
</props
</property
</bean
凡是匹配/article.c的Request都会被名为articleController的Bean处理,同样需要声明这个articleController:
<bean id="articleController" class="example.ViewArticleController"
</bean
ViewArticleController处理请求,然后生成Model,并选择一个View:
public class ViewArticleController implements Controller {
private Facade facade;
public void setFacade(Facade facade) { this.facade = facade; }
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
// 获得参数:
int articleId = Integer.parseInt(request.getParameter("articleId"));
// 使用facade处理请求:
Article article = facade.getArticle(articleId);
// 生成Model:
Map map = new HashMap();
map.put("article", article);
// 返回Model和视图名“SKIN/blueskysimple/article”:
return new ModelAndView("skin/blueskysimple/article", map);
}
}
最后,skin/bluesky/article视图会将结果显示给用户。
我们注重到,ViewArticleController并不自己查找或者创建Facade,而是由容器通过setFacade(Facade)方法设置的,这就是所谓的IoC(Inversion of Control)或者Dependency Injection。容器通过