通向架构师的道路(第二十天)万能框架spring(二)maven结合spring与ibatis

综合编程 2018-03-14

一、前言

上次讲了Struts结合Spring并使用Spring的JdbcTemplate来搭建工程框架后我们面临着jar库无法管理,工程发布不方便,jar包在工程内太占空间,jar包冲突,管理,甚至漏包都问题。于是我们在讲“万能框架spring(二)”前,传授了一篇番外篇,即讲利用maven来管理我们的jar库。

从今天开始我们将结合“万能框架spring(一)”与番外篇maven来更进一步丰富我们的ssx框架,那么今天讲的是使用iBatis3结合SS来构建我们的ssi框架,我们把这个框架命名为beta吧。

二、SSI框架

还记得我们在第十八天中讲到的我们的框架的架构图吗?上面这张是我们今天的架构图,除了Struts,Spring层,我们需要变换的是DAO层即把原来的SQL这部分换成iBatis,我们在次使用的是iBatis版本3。

由于我们在第十八天中已经说了这样的一个框架的好处其中就有:

层中相关技术的替换不影响到其它层面

所以对于我们来说我们需要改动的代码只有datasource.xml与dao层的2个接口两个类,那我们就一起来看看这个基于全注解的SSi框架是怎么样搭起来的吧。

三、搭建SSI框架

3.1建立工程

我们还是使用maven来建立我们的工程

建完后照着翻外篇《第十九天》中的“四、如何让Maven构建的工程在eclipse里跑起来”对工程进行设置。

3.2 增加iBatis3的jar相关包

打开pom.xml

第一步

找到“slf4j”,将它在pom中的描述改成如下内容:

    org.slf4j
    slf4j-api
    1.5.10

第二步

增加两个jar包

    org.slf4j
    slf4j-log4j12
    1.5.10



    org.apache.ibatis
    ibatis-core
    3.0

3.3 开始配置ibatis与spring结合

打开/src/main/resources/spring/datasource下的datasource.xml,增加如下几行


    

    









    

此处,我们需要4个类,它们是:

org.sky.ssi.ibatis.IBatis3SQLSessionFactoryBean类

package org.sky.ssi.ibatis;
 
import java.io.IOException;
import java.io.Reader;
import javax.sql.DataSource;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;


/**

 *

 * IBatis3SQLSessionFactoryBean is responsible for integrating iBatis 3 

* with spring 3. Since all environment configurations have been moved to

* spring, this class takes the responsibility to get environment information

* from spring configuration to generate SqlSessionFactory. * @author lifetragedy * */ public class IBatis3SQLSessionFactoryBean implements FactoryBean, InitializingBean{ rivate String configLocation; private DataSource dataSource; private SqlSessionFactory sqlSessionFactory; private boolean useTransactionAwareDataSource = true; private String environmentId = "development"; public String getConfigLocation() { return configLocation; } public void setConfigLocation(String configLocation) { this.configLocation = configLocation; } public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public SqlSessionFactory getSqlSessionFactory() { return sqlSessionFactory; } public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } public boolean isUseTransactionAwareDataSource() { return useTransactionAwareDataSource; } public void setUseTransactionAwareDataSource( boolean useTransactionAwareDataSource) { this.useTransactionAwareDataSource = useTransactionAwareDataSource; } public String getEnvironmentId() { return environmentId; } public void setEnvironmentId(String environmentId) { this.environmentId = environmentId; } public SqlSessionFactory getObject() throws Exception { return this.sqlSessionFactory; } public Class getObjectType() { return SqlSessionFactory.class; } public boolean isSingleton() { return true; } public void afterPropertiesSet() throws Exception { this.sqlSessionFactory = this.buildSqlSessionFactory(configLocation); } protected SqlSessionFactory buildSqlSessionFactory(String configLocation) throws IOException { if (configLocation == null) { throw new IllegalArgumentException( "configLocation entry is required"); } DataSource dataSourceToUse = this.dataSource; if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) { dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource); } Environment environment = new Environment(environmentId, new IBatisTransactionFactory(dataSourceToUse), dataSourceToUse); Reader reader = Resources.getResourceAsReader(configLocation); XMLConfigBuilder parser = new XMLConfigBuilder(reader, null, null); Configuration config = parser.parse(); config.setEnvironment(environment); return new DefaultSqlSessionFactory(config); } }

org.sky.ssi.ibatis.IBatisDAOSupport

package org.sky.ssi.ibatis;

import javax.annotation.Resource;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;


/**

 * Base class for all DAO class. The subclass extends this class to get

 * 

* DAO implementation proxy. * * @author lifetragedy * * @param */ public class IBatisDAOSupport { protected Logger log = Logger.getLogger(this.getClass()); @Resource private SqlSessionFactory ibatisSessionFactory; private T mapper; public SqlSessionFactory getSessionFactory() { return ibatisSessionFactory; } protected SqlSession getSqlSession() { return ibatisSessionFactory.openSession(); } public T getMapper(Class clazz) { mapper = getSqlSession().getMapper(clazz); return mapper; } public T getMapper(Class clazz, SqlSession session) { mapper = session.getMapper(clazz); return mapper; } /** * close SqlSession */ protected void closeSqlSession(SqlSession sqlSession) throws Exception { try { if (sqlSession != null) { sqlSession.close(); sqlSession = null; } } catch (Exception e) { } } }

org.sky.ssi.ibatis.IBatisTransaction

package org.sky.ssi.ibatis;

import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.ibatis.transaction.Transaction;
import org.springframework.jdbc.datasource.DataSourceUtils;

public class IBatisTransaction implements Transaction{

    private DataSource dataSource;

    private Connection connection;

    public IBatisTransaction(DataSource dataSource, Connection con, boolean autoCommit){

        this.dataSource = dataSource;

        this.connection = con;

    }



    public Connection getConnection(){

        eturn connection;

    }

 

    public void commit()

        throws SQLException{                        }

 

    public void rollback()

        throws SQLException{                        }

 

    public void close()

        throws SQLException{

            if(dataSource != null && connection != null){

            DataSourceUtils.releaseConnection(connection, dataSource);

            }

    }

}

org.sky.ssi.ibatis.IBatisTransactionFactory

package org.sky.ssi.ibatis;

 

import java.sql.Connection;

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.transaction.Transaction;

import org.apache.ibatis.transaction.TransactionFactory;


public class IBatisTransactionFactory implements TransactionFactory{
                     

     private DataSource dataSource;

  
     public IBatisTransactionFactory(DataSource dataSource){

        this.dataSource = dataSource;

     }

     

     public void setProperties(Properties properties){      }

     

     public Transaction newTransaction(Connection connection, boolean flag){

        return new IBatisTransaction(dataSource,connection,flag);

     } 

}

此三个类的作用就是在datasource.xml文件中描述的,把spring与datasource.xml中的datasource和transaction连接起来,此处尤其是“IBatis3SQLSessionFactoryBean”的写法,它通过spring中的“注入”特性,把iBatis的配置注入进spring并委托spring的context来管理iBatis(此属网上没有的资料,全部为本人在历年工程中的经验总结,并且已经在至少3个项目中进行了集成使用与相关测试)。

建立iBatis配置文件

我们先在/src/main/resources目录下建立一个叫sqlmap.xml的文件,内容如下:






    

        

        

    

然后我们在/src/main/resources 目录下建立index.xml与login.xml这2个xml文件。

看到这儿,有人会问了:为什么不把这两个xml文件也建立在spring目录下?

原因很简单:

在datasource.xml文件内我们已经通过


    

    

这样的方式把iBatis委托给了spring,iBatis的核心就是这个sqlmap.xml文件了,而在这个sqlmap.xml文件已经引用了login.xml与index.xml文件了。

而我们的web.xml文件里有这么一句:


    contextConfigLocation

    /WEB-INF/classes/spring/**/*.xml

因此如果我们再把ibatis/index.xml与ibatis/login.xml再建立到src/main/resources/spring目录下,spring于是会在容器启动时试图加载这两个xml文件,然后一看这两个xml文件不是什么spring的bean,直接抛错,对吧?

其们等一会再来看login.xml文件与index.xml文件,我们先来搞懂iBatis调用原理.

3.4 iBatis调用原理

1)iBatis就是一个dao层,它又被称为sqlmapping,它的sql是书写在一个.xml文件内的,在该xml文件内会将相关的sql绑定到相关的dao类的方法。

2)在调用结束时我们需要在finally块中关闭相关的sql调用。

我们来看一个例子。

login.xml文件






    

        

    

该DAO指向了一个接口org.sky.ssi.dao.LoginDAO,该dao接受一个sql,并且接受一个Map类型的参数。

那么我们来看该DAO

LoginDao.java

package org.sky.ssi.dao;

import java.util.Map;

public interface LoginDAO {

    public int validLogin(Map paraMap) throws Exception;

}

LoginImpl.java

package org.sky.ssi.dao.impl;


import java.util.Map;

import org.apache.ibatis.session.SqlSession;

import org.sky.ssi.dao.LoginDAO;

import org.sky.ssi.ibatis.IBatisDAOSupport;

import org.springframework.stereotype.Repository;
 

@Repository

public class LoginDAOImpl extends IBatisDAOSupport implements LoginDAO {

    public int validLogin(Map paraMap) throws Exception {

        SqlSession session = this.getSqlSession();

        try {

            return this.getMapper(LoginDAO.class, session).validLogin(paraMap);

        } catch (Exception e) {

            log.error(e.getMessage(), e);

            throw new Exception(e);

        } finally {

            this.closeSqlSession(session);

        }

    }

}

很简单吧,一切逻辑都在xml文件内。

一定记得不要忘了在finally块中关闭相关的sql调用啊,要不然将来工程出了OOM的错误不要怪我啊.

3.5 index模块

Index.xml文件






    

        

    



    

        insert into t_student(student_no, student_name)values(seq_student_no.nextval,#{stdName})

    

    

        delete from t_student where student_no=#{stdNo}

    

它指向了StudentDAO这个接口

StudentDAO.java

package org.sky.ssi.dao;


import org.sky.ssi.dbo.StudentDBO;

import org.sky.ssi.student.form.*;

import java.util.*;


public interface StudentDAO {


    public List getAllStudent() throws Exception;

    public void addStudent(Map paraMap) throws Exception;

    public void delStudent(Map paraMap) throws Exception;

}

StudentDAOImpl.java

package org.sky.ssi.dao.impl;

import java.util.List;

import java.util.Map;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.apache.ibatis.session.SqlSession;

import org.sky.ssi.dao.StudentDAO;

import org.sky.ssi.ibatis.IBatisDAOSupport;

import org.sky.ssi.dbo.StudentDBO;
 
import org.springframework.stereotype.Repository;


@Repository

public class StudentDAOImpl extends IBatisDAOSupport implements StudentDAO {


    @Override

    public List getAllStudent() throws Exception {

        SqlSession session = this.getSqlSession();

        try {

            return this.getMapper(StudentDAO.class, session).getAllStudent();

        } catch (Exception e) {

            throw new Exception(e);

        } finally {

            this.closeSqlSession(session);

        }

    }



    public void addStudent(Map paraMap) throws Exception {

        SqlSession session = this.getSqlSession();

        try {

            this.getMapper(StudentDAO.class, session).addStudent(paraMap);

        } catch (Exception e) {

            throw new Exception(e);

        } finally {

            this.closeSqlSession(session);

        }

    }



    public void delStudent(Map paraMap) throws Exception {

        SqlSession session = this.getSqlSession();

        try {

            this.getMapper(StudentDAO.class, session).delStudent(paraMap);

        } catch (Exception e) {

            throw new Exception(e);

        } finally {

            this.closeSqlSession(session);

        }

    }

}

3.6 Service接口微微有些改变

为了演示给大家看 iBatis接受多个参数的例子因此我们把原来的如:login(String loginId, String loginPwd)这样的方法改成了public int validLogin(Map paraMap) throws Exception;这样的结构,请大家注意。

四、beta工程中的增加功能

4.1 增加了一个filter

在我们的web.xml文件中


    LoginFilter

    org.sky.ssi.filter.LoginFilter

    

	    exclude

	    /jsp/login/login.jsp,

	                 /login.do

	    

    





    LoginFilter

    *.jsp





    LoginFilter

    /servlet/*





    LoginFilter

    *.do

有了这个filter我们就不用在我们的web工程中每一个action、每 个jsp里进行“用户是否登录”的判断了,它会自动根据配置除去“exclude”中的相关web resource,全部走这个“是否登录”的判断。

注意此处这个exclude是笔者自己写的,为什么要exclude?

如果你不exclude,试想一个用户在login.jsp中填入相关的登录信息后点一下login按钮跳转到了login.do,而这两个web resource由于没有被“排除”出“需要登录校验”,因此每次你一调用login.jsp, login.do这个filter就都会强制要求你再跳转到login.jsp,那么我们一个用户从login.jsp登录完后再跳回login.jsp再跳回,再跳回,如此重复,进入死循环。

4.2 增加了一个自动记录异常的日志功能

在我们的applicationContext.xml文件中






    

        

        

    

这样,我们的dao层、service层、有错就只管往外throw,框架一方面在接到相关的exception会进行数据库事务的自动回滚外,还会自动把service层抛出的exception记录在log文件中。

五、测试我们的工程

确认我们的StudentServiceImpl中删除学生的delStudent方法内容如下:

public void delStudent(String[] stdNo) throws Exception {

  for (String s : stdNo) {

         Map paraMap = new HashMap();

         paraMap.put("stdNo", s);

         studentDAO.delStudent(paraMap);

         throw new Exception("force system to throw a exception");

  }

}

我们把beta工程添加入我们在eclipse中配好的j2eeserver中去并启动起来。

在IE中输入: http://localhost:8080/beta/index.do 。 系统直接跳到login界面

我们输入相关的用户名写密码。

我们选中“13号学生高乐高”与“9号学生”,点“deletestudent”按钮。

后台抛错了,查看数据库内的数据

数据还在,说明我们的iBatis的事务已经在spring中启作用了.

再次更改StudentServiceImpl.java类中的delStudent方法,把“throw new Exception(“force system to throw a exception”);”注释掉,再来运行

我们再次选 中9号和13号学生,点deletestudent按钮,删除成功,这个够13的人终于被删了,呵呵。

您可能感兴趣的

一个mybatis开发框架,比JPA好用比MyBatis generator好用... fastmybatis fastmybatis是一个mybatis开发框架,目的为简化mybatis的开发,让开发更高效。 零配置快速上手 无需编写xml文件即可完成CRUD操作 支持mysql,sqlserver,oracle,postgresql,sqli...
Spring AOP 在前面的文章中已经和大家分享过关于spring IOC的知识,已经通过他的实现机制。我们都知道spring的两大核心:AOP(依赖注入)和IOC(控制反转),本篇我们就一起学习一下AOP的知识的。 这里分享一个问题?当我们软件开发完成后,需要给每一个方法添加操作日志,我们怎么操作呢?我想最简单的...
最基础的mybatis入门demo demo结构 数据库情况 (不会转sql语句 骚瑞) 数据库连接信息 jdbc.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mysql_d...
MyBatis7:MyBatis插件及示例—-打印每条SQL语句及其执行时间... Plugins 摘一段来自MyBatis官方文档的文字。 MyBatis允许你在某一点拦截已映射语句执行的调用。默认情况下,MyBatis允许使用插件来拦截方法调用 Executor(update、query、flushStatements、commint、...
码云周刊 | 码云周热门项目排行,企业服务持续大热... 众所周知,开源技术的大热往往预示着技术的热潮,码云发展至今,越来越多的开源作者选择在上面开源自己的作品,也希望通过码云每周最热门开源项目了解国内技术走向。此榜单根据 码云上开源项目的 Watch、Star、Fork 数量以及其他角度每周统计一次,从而得出每周前 5 名最热门的开源项目。希望大家...