struts2+spring3+mybatis整合案例

3 篇文章 0 订阅
2 篇文章 0 订阅

最近公司要做新项目,让我负责搭建项目的主体框架,说实话,之前没做过类似的工作,但接触出ibatis,也有相关封装后的代码,但领导就要求用mybatis,而且还要求必须用注解来搞。是一件很蛋疼的事。以前的开发模式是在xml基础上完成的,做什么都通过配置,是挺容易的,现在的领导要求用注解,也没办法,不能否则注解的开发效率还是挺高的。


但头疼的事发生了,这个框架我该怎么搭建呢,从网上找过很多资料,都没有特别全的,我只能靠我自己了。首先先搭建一个简单的吧。最后我们再来讨论下目前框架的不足和缺点。


新建工程,讲jar包全部导入,当然可以用maven。这里讲解不用这个。



1、从配置spring开始

空的配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/context  
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">

</beans>

我们依次在配置文件配置:数据源,sessionFactory,事务等配置项:

	<context:annotation-config />
	<context:component-scan base-package="cn.damai"/>
	<aop:aspectj-autoproxy proxy-target-class="true" />

	<!-- 数据源默认配置  -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"/>      
    	<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>      
   	 	<property name="user" value="root"/>      
    	<property name="password" value="123456"/>
	</bean>

	<!-- mybatiss -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:Mybatis_Configuration.xml"/>
	</bean>

	<!---->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="cn.damai.mplus.*" />
	</bean>
	
	
	<!-- 事物配置开始 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource"/>
    </bean>
	<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
	
	<!-- 常见事务设置
		propagation:事务传播行为,默认REQUIRED
			REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务(最常见选择)
			SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
			MANDATORY:支持当前事务,如果当前没有事务,就抛出异常
			REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
			NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
			NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
		isolation:事务隔离级别,默认DEFAULT
		read-only:事务是否只读,默认false
	-->
2、搭建mybatis

搭建mybatis我们需要两个重要的jar包:


中间有个mybatisext的jar包,这个不是mybatis的必须包,是我自己封装的一个包。至于干什么用的,下面我们来说。

在applicationcontext.xml文件中,我们已经配置了mybatis的内容如下:

	<!-- mybatiss -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:Mybatis_Configuration.xml"/>
	</bean>

	<!---->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="cn.damai.mplus.*" />
	</bean>

这两个配置的作用是,sqlSessionFactory 这个都很清楚,就是session工厂,用于操作数据库的工厂类,我们需要将数据源和配置信息告诉他。他帮助我们获取需要的链接,帮助我们对数据库做操作。下面的MapperScannerConfigurer类的作用是:

为了代替手工使用 SqlSessionDaoSupport 或 SqlSessionTemplate 编写数据访问对象 (DAO)的代码,MyBatis-Spring 提供了一个动态代理的实现:MapperFactoryBean。这个类 可以让你直接注入数据映射器接口到你的 service 层 bean 中。当使用映射器时,你仅仅如调 用你的 DAO 一样调用它们就可以了,但是你不需要编写任何 DAO 实现的代码,因为 MyBatis-Spring 将会为你创建代理。

使用注入的映射器代码,在 MyBatis,Spring 或 MyBatis-Spring 上面不会有直接的依赖。 MapperFactoryBean 创建的代理控制开放和关闭 session,翻译任意的异常到 Spring 的 DataAccessException 异常中。此外,如果需要或参与到一个已经存在活动事务中,代理将 会开启一个新的 Spring 事务。



然后,不管我们用ibatis还是mybatis,我们都需要面对的一个问题就是他们对分页的支持问题。这个我们随便百度就能发现,mybatis的分页是基于内存分页的,查找出所有记录再取出偏移量的记录,如果jdbc驱支持absolute定位或者rs.next()到指定偏移位置),其实这样的分页实现基本没用,特别是大量数据情况下。所以说白了,我们需要对mybatis做改造,如何改造,百度上就现成的解决方案,我也是参考的他们的。但其实来讲 想找了现成的也不是很好找,反正是我找到了,我是在这个网站上下载下来经过一些改造后使用的:

https://github.com/miemiedev/mybatis-paginator


改造后,打成了一个mybatis-ext的jar包,放在了classpath下。这个包的作用是能够帮助我们再执行分页查询时对sql进行拦截,真正生成Statement并执行sql的语句是StatementHandler接口的某个实现,这样就可以写个插件对StatementHandler的行为进行拦截,具体原理可以参考网址

http://www.cnblogs.com/jcli/archive/2011/08/09/2132222.html


现在假设我们的分页插件也搞完了,接下来要做的就是开发我们的baseDao完成对数据库操作的封装,刚说了领导要求我们用注解来做,现在我先用xml来稍微封装下,差不多就行啊,之后再来用注解来搞搞,用xml完成baseDao封装如下:

import java.util.List;

import javax.annotation.Resource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Component;

import cn.damai.mplus.bean.User;
import cn.damai.mplus.framework.common.Page;
import cn.damai.mplus.framework.common.PageBounds;
import cn.damai.mplus.framework.common.PageInfo;

@Component
public class XmlDao<T> {
	
	@Resource
	private SqlSessionFactory sessionFactory;
	public void setSessionFactory(SqlSessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	public void insert(String stateName,T t){
		this.sessionFactory.openSession().insert(stateName,t);
	}
	
	public void delete(String stateName,T t){
		this.sessionFactory.openSession().delete(stateName,t);
	}
	
	public void update(String stateName,T t){
		this.sessionFactory.openSession().delete(stateName,t);
	}
	
	public Page<T> queryForPage(String stateName,T t, PageInfo pageInfo){
		int rowCount = (Integer)this.sessionFactory.openSession().selectOne(stateName+"_count");
		pageInfo.setRowCount(rowCount);
		return new Page(pageInfo,
			this.sessionFactory.openSession().
			selectList(stateName, t, new PageBounds(pageInfo.getCurrentPage(), pageInfo.getPageSize())));
    }
	
	public List<T> queryForParam(String stateName,T param){
    	return this.sessionFactory.openSession().selectList(stateName, param);
    }
	
	public List<T> query(String stateName){
    	return this.sessionFactory.openSession().selectList(stateName);
    }
}

其中我们封装了关于分页的对象Page,在public Page<T> queryForPage(String stateName,T t, PageInfo pageInfo)方法中,我们将pageInfo对象传递过来,这个对象我们只需要提供两个属性值当前页和每页大小即可:根据currentPage和pageSize我就可以计算出offset了。然后再查询结果中,再将rowCount总记录数提供给它,那一共多少页啊等信息也可以计算出来了。

PageInfo pinfo = new PageInfo();
pinfo.setCurrentPage(2);
pinfo.setPageSize(5);

PageInfo这个类的源代码如下:

import java.io.Serializable;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

/**
 * 分页器,根据page,limit,totalCount用于页面上分页显示多项内容,计算页码和当前页的偏移量,方便页面分页使用.
 *
 * @author badqiu
 * @author miemiedev
 */
public class PageInfo implements Serializable {
    private static final long serialVersionUID = 6089482156906595931L;

    private static final int DEFAULT_SLIDERS_COUNT = 7;

    
    private static final int CURR_PAGE = 1;
	private static final int PAGE_SIZE = 10;
    /**
     * 分页大小
     */
    public int pageSize = PAGE_SIZE;
    /**
     * 页数
     */
    public int currentPage = CURR_PAGE;
    
    /**
     *  起始索引
     */
    public int offset = 0;
    /**
     * 总记录数
     */
    public int rowCount;

    public PageInfo() {}
    
    public PageInfo(int currentPage, int pageSize, int totalCount) {
        super();
        this.pageSize = pageSize;
        this.rowCount = totalCount;
        this.currentPage = computePageNo(currentPage);
    }

    public PageInfo(HttpServletRequest request){
    	String currentPage = request.getParameter("currpage");
		String pageSize = request.getParameter("pagesize");
		
		if (StringUtils.isNotBlank(currentPage)) {
			this.setCurrentPage(Integer.parseInt(currentPage));
		} else {
			this.setCurrentPage(1);
		}
		
		if (StringUtils.isNotBlank(pageSize)) {
			this.setPageSize(Integer.parseInt(pageSize));
		}
    }
    
    
    /**
     * 取得当前页。
     */

    /**
     * 取得总项数。
     *
     * @return 总项数
     */
    public int getPageSize() {
		return pageSize;
	}

	public int getRowCount() {
		return rowCount;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public int getCurrentPage() {
		return currentPage;
	}

	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage;
	}

	public void setRowCount(int rowCount) {
		this.rowCount = rowCount;
	}

	/**
     * 是否是首页(第一页),第一页页码为1
     *
     * @return 首页标识
     */
    public boolean isFirstPage() {
        return currentPage <= 1;
    }

    /**
     * 是否是最后一页
     *
     * @return 末页标识
     */
    public boolean isLastPage() {
        return currentPage >= getTotalPages();
    }

    public int getPrePage() {
        if (isHasPrePage()) {
            return currentPage - 1;
        } else {
            return currentPage;
        }
    }

    public int getNextPage() {
        if (isHasNextPage()) {
            return currentPage + 1;
        } else {
            return currentPage;
        }
    }

    /**
     * 判断指定页码是否被禁止,也就是说指定页码超出了范围或等于当前页码。
     *
     * @param page 页码
     * @return boolean  是否为禁止的页码
     */
    public boolean isDisabledPage(int currentPage) {
        return ((currentPage < 1) || (currentPage > getTotalPages()) || (currentPage == this.currentPage));
    }

    /**
     * 是否有上一页
     *
     * @return 上一页标识
     */
    public boolean isHasPrePage() {
        return (currentPage - 1 >= 1);
    }

    /**
     * 是否有下一页
     *
     * @return 下一页标识
     */
    public boolean isHasNextPage() {
        return (currentPage + 1 <= getTotalPages());
    }

    /**
     * 开始行,可以用于oracle分页使用 (1-based)。
     */
    public int getStartRow() {
        if (getPageSize() <= 0 || rowCount <= 0) return 0;
        return currentPage > 0 ? (currentPage - 1) * getPageSize() + 1 : 0;
    }

    /**
     * 结束行,可以用于oracle分页使用 (1-based)。
     */
    public int getEndRow() {
        return currentPage > 0 ? Math.min(pageSize * currentPage, getRowCount()) : 0;
    }

    /**
     * offset,计数从0开始,可以用于mysql分页使用(0-based)
     */
    public int getOffset() {
        return currentPage > 0 ? (currentPage - 1) * getPageSize() : 0;
    }



    /**
     * 得到 总页数
     *
     * @return
     */
    public int getTotalPages() {
        if (rowCount <= 0) {
            return 0;
        }
        if (pageSize <= 0) {
            return 0;
        }

        int count = rowCount / pageSize;
        if (rowCount % pageSize > 0) {
            count++;
        }
        return count;
    }

    protected int computePageNo(int currentPage) {
        return computePageNumber(currentPage, pageSize, rowCount);
    }

    /**
     * 页码滑动窗口,并将当前页尽可能地放在滑动窗口的中间部位。
     *
     * @return
     */
    public Integer[] getSlider() {
        return slider(DEFAULT_SLIDERS_COUNT);
    }

    /**
     * 页码滑动窗口,并将当前页尽可能地放在滑动窗口的中间部位。
     * 注意:不可以使用 getSlider(1)方法名称,因为在JSP中会与 getSlider()方法冲突,报exception
     *
     * @return
     */
    public Integer[] slider(int slidersCount) {
        return generateLinkPageNumbers(getCurrentPage(), (int) getTotalPages(), slidersCount);
    }

    private static int computeLastPageNumber(int totalItems, int pageSize) {
        if (pageSize <= 0) return 1;
        int result = (int) (totalItems % pageSize == 0 ?
                totalItems / pageSize
                : totalItems / pageSize + 1);
        if (result <= 1)
            result = 1;
        return result;
    }

    private static int computePageNumber(int page, int pageSize, int totalItems) {
        if (page <= 1) {
            return 1;
        }
        if (Integer.MAX_VALUE == page
                || page > computeLastPageNumber(totalItems, pageSize)) { //last page
            return computeLastPageNumber(totalItems, pageSize);
        }
        return page;
    }

    private static Integer[] generateLinkPageNumbers(int currentPageNumber, int lastPageNumber, int count) {
        int avg = count / 2;

        int startPageNumber = currentPageNumber - avg;
        if (startPageNumber <= 0) {
            startPageNumber = 1;
        }

        int endPageNumber = startPageNumber + count - 1;
        if (endPageNumber > lastPageNumber) {
            endPageNumber = lastPageNumber;
        }

        if (endPageNumber - startPageNumber < count) {
            startPageNumber = endPageNumber - count;
            if (startPageNumber <= 0) {
                startPageNumber = 1;
            }
        }

        java.util.List<Integer> result = new java.util.ArrayList<Integer>();
        for (int i = startPageNumber; i <= endPageNumber; i++) {
            result.add(new Integer(i));
        }
        return result.toArray(new Integer[result.size()]);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("page");
        sb.append("{currentPage=").append(currentPage);
        sb.append(", pageSize=").append(pageSize);
        sb.append(", rowCount=").append(rowCount);
        sb.append('}');
        return sb.toString();
    }
    
}

好,现在我们用xml的方式将baseDao给搞定了,然后我们再serviceImpl中通过注解的方式可以获取baseDao的实例,就可以来操作这些方法了:

@Service
@Transactional
public class UserServiceImpl implements UserService{
	
	@Autowired
	private UserDao userDao;
	
	@Autowired
	private XmlDao<User> xmlDao;

	
	@Override
	public List<User> queryUsers() {
		return xmlDao.query("queryAll");
	}
	

	@Override
	public List<User> queryUsers(User user) {
		return xmlDao.queryForParam("queryUsersForParam",user);
	}

	@Override
	public Page<User> queryUsersForPage(User user,PageInfo pageInfo) {
		return xmlDao.queryForPage("queryUsersForPage", user, pageInfo);
	}

这里我们看到我们将@transaction的注解放在了类上,表示说这个类的所有操作都是有事务的,当然了,具体的配置可以通过属性来配置,这里不讲了。

下面,我们再来看一下用注解怎么开发呢,先不说怎么封装了,现在你让我用注解的方式完成crud,我也不怎么熟悉,所以咱先看如何用注解完成最简单的crud:

import java.util.List;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

import cn.damai.mplus.bean.User;
import cn.damai.mplus.framework.base.AnnotationDao;
import cn.damai.mplus.framework.common.PageInfo;
	
public interface UserDao extends AnnotationDao<User>{
	@Insert(value="insert into d_user values (#{username},#{password})")
	public void addUser(User user);
	
	@Select(value="select * from d_user where username=#{username}")
	public List<User> getUser(User user);
	
	@Select(value="select * from d_user limit #{offset},#{pageSize}")
	public List<User> getUsersByPage(PageInfo pageInfo);	
}

很简单,所以我就不过多的解释了,insert代表插入,select是查询。value是sql,除此之外,我们还可以配置@result等注解,这些可以自行了解一下。


现在问题是如果我们不自己用注解开发,而是用注解的思想完成一个baseMaper我们应该怎么做呢,我也没什么思路,但我借鉴了这个网站并用了一下感觉还可以:

http://blog.csdn.net/beiouwolf/article/details/7347797


缺点是没有提供通用查询的方法。本来想自己搞搞看看能不能写出一个来,搞来搞去也没成功。唉。希望高人指路啊。


所以,暂时也不管了,现在我们的baseMapper(注解)和baseDao(xml)假设都搞定了,spring也ok了,就剩下struts2了,这个也很简单了:

import java.util.List;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.ExceptionMapping;
import org.apache.struts2.convention.annotation.ExceptionMappings;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.convention.annotation.Result;
import org.springframework.beans.factory.annotation.Autowired;

import cn.damai.mplus.bean.User;
import cn.damai.mplus.framework.base.BaseAction;
import cn.damai.mplus.framework.common.Page;
import cn.damai.mplus.framework.common.PageInfo;
import cn.damai.mplus.service.impl.UserServiceImpl;


@ParentPackage("struts-default") 
@Namespace("/user") 
@Results({
	@Result(name = "success", location = "/index.jsp"),
	@Result(name = "error", location = "/error.jsp")
}) 
@ExceptionMappings({
	@ExceptionMapping(exception = "java.lang.RuntimeException", result = "error")
})
public class UserAction extends BaseAction{
	
	private static final long serialVersionUID = 1L;

	@Autowired
	private UserServiceImpl userService;
	
	private User user;
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	
	@Action(value = "add", results = { @Result(name = "success", location = "/success1.jsp") })
	public String add(){
		this.userService.addUser(user);
		List<User> users = this.userService.queryUsers();
		this.setRequestAttribute("users", users);
//		Map request = (Map)ActionContext.getContext().get("request");
//		request.put("users", users);
		return "success";
	}

配置文件也没用,直接用注解就搞定了,当然你用注解,得导入相关的jar包:



这里也提供一下用xml方式配置struts2的样式:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <package name="user" extends="damai-default">
    	<action name="user_*" class="userAction" method="{1}">
    		<result name="success">/success.jsp</result>
    	</action>
    </package>
</struts>

这样,我们的ss-mybatis的框架就搭建完成了,随便测试下


ok!!!









  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
封装通用的Spring3+Struts2+MyBatis3的CRUD+条件分页查询,Spring+Quartz调度,FunctionCharts图像化工具 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="temperMonitorTimerJob" class="cn.sup.cd.listener.TemperatureMonitorTaskJob"></bean> <!-- 政策调度--> <bean id="temperMonitorTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <!-- 调用的类 --> <property name="targetObject"> <ref bean="temperMonitorTimerJob"/> </property> <!-- 调用类中的方法 --> <property name="targetMethod"> <value>temperatureMonitorTimer</value> </property> </bean> <!-- BOOK定义触发时间 几秒后执行monitor.start.time 每隔monitor.interval.time执行--> <bean id="getPolicyTime" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail"> <ref bean="temperMonitorTask"/> </property> <!-- cron表达式 --> <property name="cronExpression"> <value>${monitor.start.time}/${monitor.interval.time} * * * * ?</value> </property> </bean> <!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序 --> <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="getPolicyTime"/> </list> </property> </bean> </beans>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值