java 面试总结(框架)

1.springmvc和springboot的区别?

   Spring MVC是什么?

     (1)Spring MVC是Spring提供的一个强大而灵活的模块式web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。
    (2)SpringMVC是一种基于Java的以请求为驱动类型的轻量级Web框架,其目的是将Web层进行解耦,即使用“请求-响应”模型,从工程结构上实现良好的分层,区分职责,简化Web开发。借助于注解,Spring MVC提供了几乎是POJO的开发模式,使得控制器的开发和测试更加简单。这些控制器一般不直接处理请求,而是将其委托给Spring上下文中的其他bean,通过Spring的依赖注入功能,这些bean被注入到控制器中。
     (3)Spring框架最核心的就是所谓的依赖注射和控制反转。完全解耦类之间的依赖关系,一个类如果要依赖什么,那就是一个接口。至于如何实现这个接口,这都不重要了。只要拿到一个实现了这个接口的类,就可以轻松的通过xml配置文件把实现类注射到调用接口的那个类里。所有类之间的这种依赖关系就完全通过配置文件的方式替代了

SpringMVC的优点:
    (1)使用简单,学习成本低。
    (2)很容易就可以写出性能优秀的程序.
    (3)灵活性强,Spring MVC的框架易扩展

SpringMVC的缺点:
    (1)Spring与MVC 的Servlet API 耦合,难以脱离容器独立运行
    (2)太过于细分,开发效率低
    (3)过度追求完美,有过度设计的危险

解决的问题领域是:网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。

     Spring Boot引入自动配置的概念,让项目设置变得很容易。Spring Boot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。Spring Boot只是承载者,辅助开发者简化项目搭建过程的。如果承载的是WEB项目,使用Spring MVC作为MVC框架,那么工作流程和SpringMVC的是完全一样的,因为这部分工作是Spring MVC做的而不是Spring Boot。

Spring Boot的核心功能:
  (1)独立运行的Spring项目
Spring Boot可以以jar包的形式进行独立的运行,使用:java -jar xx.jar 就可以成功的运行项目,或者在应用项目的主程序中运行main函数即可;
  (2)内嵌的Servlet容器
内嵌容器,使得我们可以执行运行项目的主程序main函数,并让项目的快速运行;
  (3)提供starter简化Manen配置
Spring Boot提供了一系列的starter pom用来简化我们的Maven依赖
  (4)自动配置Spring
Spring Boot会根据我们项目中类路径的jar包/类,为jar包的类进行自动配置Bean,这样一来就大大的简化了我们的配置。当然,这只是Spring考虑到的大多数的使用场景,在一些特殊情况,我们还需要自定义自动配置;
  (5)应用监控
Spring Boot提供了基于http、ssh、telnet对运行时的项目进行监控;

    springboot 要解决的问题:
    1.如何精简配置;如何让编码、部署、监控变简单;
    2.如何方便的让spring生态圈和其他工具链整合

    优点:
    对新手来说,无需任何门槛,只要懂Maven[的新手]会看文档就能亦步亦趋的开始一个新项目;
    对高手来说,改配置也是分分钟的事。另外fat jar的打包方式让部署方式变得优雅。

    缺点:
   就是简单的背后蕴藏了巨大的学习曲线。入门容易,但是如果没有完整学习spring的体系,碰到问题就一脸懵逼。如果没有一  定的经验,根本就不知道springboot自动做了什么。

扫描二维码关注公众号,回复: 3742242 查看本文章

   SpingMVC与SpringBoot的联系与区别:
   联系:
   Spring 最初利用“工厂模式”( DI )和“代理模式”( AOP )解耦应用组件。按照这种模式搞了一个 MVC 框架(一些用 Spring 解耦的组件),用开发 web 应用( SpringMVC )。后来发现每次开发都要搞很多依赖,写很多样板代码,使代码臃肿而麻烦,于是聪明的前人整理了一些懒人整合包( starter ),这套就是 Spring Boot 。
   区别:
   Spring MVC 是基于 Servlet 的一个 MVC框架 主要解决 WEB 开发的问题 但关于Spring 的配置比较 ;而Spring boot 的原则是:约定优于配置 ,可以极大地简化了 spring 的配置流程。

   2.hibernate和mybatis的区别

   1)hibernate是全自动,而mybatis是半自动

     hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。而mybatis仅有基本的字段映射,对象数据以及对象实际关系仍然需要通过手写sql来实现和管理。

    2)hibernate数据库移植性远大于mybatis

      hibernate通过它强大的映射结构和hql语言,大大降低了对象与数据库(oracle、mysql等)的耦合性,而mybatis由于需要手写sql,因此与数据库的耦合性直接取决于程序员写sql的方法,如果sql不具通用性而用了很多某数据库特性的sql语句的话,移植性也会随之降低很多,成本很高。 

    3)hibernate拥有完整的日志系统,mybatis则欠缺一些

      hibernate日志系统非常健全,涉及广泛,包括:sql记录、关系异常、优化警告、缓存提示、脏数据警告等;而mybatis则除了基本记录功能外,功能薄弱很多

    4)mybatis相比hibernate需要关心很多细节

        hibernate配置要比mybatis复杂的多,学习成本也比mybatis高。但也正因为mybatis使用简单,才导致它要比hibernate关心很多技术细节。mybatis由于不用考虑很多细节,开发模式上与传统jdbc区别很小,因此很容易上手并开发项目,但忽略细节会导致项目前期bug较多,因而开发出相对稳定的软件很慢,而开发出软件却很快。hibernate则正好与之相反。但是如果使用hibernate很熟练的话,实际上开发效率丝毫不差于甚至超越mybatis。

    5)sql直接优化上,mybatis要比hibernate方便很多

       由于mybatis的sql都是写在xml里,因此优化sql比hibernate方便很多。而hibernate的sql很多都是自动生成的,无法直接维护sql;虽有hql,但功能还是不及sql强大,见到报表等变态需求时,hql也歇菜,也就是说hql是有局限的;hibernate虽然也支持原生sql,但开发模式上却与orm不同,需要转换思维,因此使用上不是非常方便。总之写sql的灵活度上hibernate不及mybatis。

随着使用情况的不断增多,我又做了进一步的总结总结:

mybatis:小巧、方便、高效、简单、直接、半自动

hibernate:强大、方便、高效、复杂、绕弯子、全自动 

mybatis:

1. 入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。

2. 可以进行更为细致的SQL优化,可以减少查询字段。

3. 缺点就是框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。

4. 二级缓存机制不佳。

hibernate:

1. 功能强大,数据库无关性好,O/R映射能力强,如果你对Hibernate相当精通,而且对Hibernate进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。

2. 有更好的二级缓存机制,可以使用第三方缓存。

3. 缺点就是学习门槛不低,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate方面需要你的经验和能力都很强才行。

举个形象的比喻:

mybatis:机械工具,使用方便,拿来就用,但工作还是要自己来作,不过工具是活的,怎么使由我决定。

hibernate:智能机器人,但研发它(学习、熟练度)的成本很高,工作都可以摆脱他了,但仅限于它能做的事。

3.spring的特性,说说你对IOC和AOP的理解

   在面试中,经常会问,说说你对spring IOC和AOP的理解,问题很宽泛,似乎不知道从何说起。

   回答思路:1.先用通俗易懂的话解释下何为IOC和AOP---------》2.各自的实现原理-----------》3.自己的项目中如何使用

   1)IOC

       许多应用都是通过彼此间的相互合作来实现业务逻辑的,如类A要调用类B的方法,以前我们都是在类A中,通过自身new一个类B,然后在调用类B的方法,现在我们把new类B的事情交给spring来做,在我们调用的时候,容器会为我们实例化。

   2)IOC容器的初始化过程  

    资源定位,即定义bean的xml-------》载入--------》IOC容器注册,注册beanDefinition

    IOC容器的初始化过程,一般不包含bean的依赖注入的实现,在spring IOC设计中,bean的注册和依赖注入是两个过程,,依赖注入一般发生在应用第一次索取bean的时候,但是也可以在xm中配置,在容器初始化的时候,这个bean就完成了初始化。

   3)三种注入方式,构造器、接口、set注入,我们常用的是set注入

   4)bean是如何创建---  工厂模式

   5)数据是如何注入-------反射

   6)AOP   

     面向切面编程,在我们的应用中,经常需要做一些事情,但是这些事情与核心业务无关,比如,要记录所有update*方法的执行时间时间,操作人等等信息,记录到日志,

      通过spring的AOP技术,就可以在不修改update*的代码的情况下完成该需求。

    7)AOP的实现原理------代理

   

4.spring的动态代理是怎么实现的?

  在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:

  

InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

  每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

  我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?

  Object invoke(Object proxy, Method method, Object[] args) throws Throwable

  proxy:  指代我们所代理的那个真实对象

  method:  指代的是我们所要调用真实对象的某个方法的Method对象

  args:  指代的是调用真实对象某个方法时接受的参数

 如果不是很明白,等下通过一个实例会对这几个参数进行更深的讲解。

 接下来我们来看看Proxy这个类:

 Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

 Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载 interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了 h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

好了,在介绍完这两个接口(类)以后,我们来通过一个实例来看看我们的动态代理模式是什么样的:

首先我们定义了一个Subject类型的接口,为其声明了两个方法:

public interface Subject
{
    public void rent();
    
    public void hello(String str);
}

 5.springmvc的核心类,分别有什么作用

   控制器核心类:(入口类)

  org.springframework.web.servlet.DispatcherServlet  - 配置web.xml

  加载配置文件核心类:

  org.springframework.web.context.ContextLoaderListener – spring的配置文件

 处理url影射核心类:

 org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping-根据bean的名称请求一个bean. spring的配置文件- /abc

 处理视图资源核心类:

org.springframework.web.servlet.view.ResourceBundleViewResolver
return hello – 决定返回的字符串由哪一个页面来显示。
<context:component-scan package=“cn.hncu”/>用于替代上面的类。

方法动态调用核心类
org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver

6.springmvc的工作流程

      1). 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;

      2). DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

      3). DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)

      4).  提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

      HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

      数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

      数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

      数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

      5).  Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;

     6).  根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

      7). ViewResolver 结合Model和View,来渲染视图

      8). 将渲染结果返回给客户端。

7.Hibernate缓存机制和MyBatis缓存机制

   Mybatis缓存

   分为1级缓存和2级缓存,2级缓存在sessionfactory中,1级缓存在session中。

   1)session中的缓存在session连接关闭并被连接池回收时清理,或者手动清理

   2)sessionfactory中的缓存需要在mybatis配置文件中手动配置,配置如下:

   <setting name="cacheEnabled" value="true"/>

  <mapper namespace="dao.userdao">
   ...  select statement ...       <!-- Cache 配置 -->
    <cache        
        eviction="FIFO"
        flushInterval="60000"
        size="512"
        readOnly="true" />
  </mapper>

  flushInterval:刷新轮询时间,每隔这个时间后缓存被清理一次。

 Hiberante缓存

 Hibernate缓存的作用?

Hibernate是一个持久层框架,经常访问物理数据库,为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据Hibernate缓存分类:
Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存Hibernate一级缓存又称为“Session的缓存”,它是内置的,不能被卸载(不能被卸载的意思就是这种缓存不具有可选性,必须有的功能,不可以取消session缓存)。由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。第一级缓存是必需的,不允许而且事实上也无法卸除。在第一级缓存中,持久化类的每个实例都具有唯一的OID。 Hibernate二级缓存又称为“SessionFactory的缓存”,由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存是可选的,是一个可配置的插件,在默认情况下,SessionFactory不会启用这个插件。

什么样的数据适合存放到第二级缓存中?
1 很少被修改的数据
2 不是很重要的数据,允许出现偶尔并发的数据
3 不会被并发访问的数据
4 常量数据
不适合存放到第二级缓存的数据?
1 经常被修改的数据
2 .绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发
3 与其他应用共享的数据。
Hibernate查找对象如何应用缓存?
当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照ID放入到缓存,删除、更新、增加数据的时候,同时更新缓存。Hibernate管理缓存实例无论何时,当你给save()、update()或saveOrUpdate()方法传递一个对象时,或使用load()、 get()、list()、iterate() 或scroll()方法获得一个对象时, 该对象都将被加入到Session的内部缓存中。 当随后flush()方法被调用时,对象的状态会和数据库取得同步。 如果你不希望此同步操作发生,或者你正处理大量对象、需要对有效管理内存时,你可以调用evict() 方法,从一级缓存中去掉这些对象及其集合。

8.Mybatis配置外键的关键字,resultMap和resultType的区别

<!-- 外键关联  property="dbMenuType" 当前model中的外键属性  column="menuType" 当前表中作为关联的外键-->
<association property="dbMenuType" column="menuType" select="com.ejokovic.dao.DBMenuTypeDao.findById"></association>

mybatis中select元素有两个属性resultType和resultMap,工作中总是使用到他们,但是他们有什么区别呢?

就我的使用经验来说,对于单表查询映射或多表联合查询映射来说,他们都能达到要求,例如

package com.someapp.model;

public class User {

  private int id;

  private String username;

  private String hashedPassword;

  public int getId() {

    return id;

  }

  public void setId(int id) {

    this.id = id;

  }

  public String getUsername() {

    return username;

  }

  public void setUsername(String username) {

    this.username = username;

  }

  public String getHashedPassword() {

    return hashedPassword;

  }

  public void setHashedPassword(String hashedPassword) {

    this.hashedPassword = hashedPassword;

  }

}

<!-- In mybatis-config.xml file -->

<typeAlias type="com.someapp.model.User" alias="User"/>

使用resultType

<select id="selectUsers" parameterType="int" resultType="com.someapp.model.User">

  select id, username, hashedPassword

  from some_table

  where id = #{id}

</select>

这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,基于属性名来映射列到 JavaBean 的属性上。如果列名没有精确匹配,你可以在列名上使用 select 字句的别名(一个 基本的 SQL 特性)来匹配标签。比如:

<select id="selectUsers" parameterType="int" resultType="User">

  select

    user_id             as "id",

    user_name           as "userName",

    hashed_password     as "hashedPassword"

  from some_table

  where id = #{id}

</select>

使用resultMap

<resultMap id="userResultMap" type="User">

  <id property="id" column="user_id" />

  <result property="username" column="username"/>

  <result property="password" column="password"/>

</resultMap>

<select id="selectUsers" parameterType="int" resultMap="userResultMap">

  select user_id, user_name, hashed_password

  from some_table

  where id = #{id}

</select>

看出来了吧,resultType和resultMap都映射到了User对象中

说说不同点吧,resultType 和restltMap

restulyType:

1).对应的是java对象中的属性,大小写不敏感,

2).如果放的是java.lang.Map,key是查询语句的列名,value是查询的值,大小写敏感

3).resultMap:指的是定义好了的id的,是定义好的resyltType的引用

注意:用resultType的时候,要保证结果集的列名与java对象的属性相同,而resultMap则不用,而且resultMap可以用typeHander转换

4).type:java 对象对应的类,id:在本文件要唯一column :数据库的列名或别名,property:对应java对象的属性,jdbcType:java.sql.Types

查询语句中,resultMap属性指向上面那个属性的标签的id

parameterType:参数类型,只能传一个参数,如果有多个参数要封装,如封装成一个类,要写包名加类名,基本数据类型则可以省略

5).一对1、一对多时,若有表的字段相同必须写别名,不然查询结果无法正常映射,出现某属性为空或者返回的结果与想象中的不同,而这往往是没有报错的。

6).若有意外中的错误,反复检查以上几点,和认真核查自己的sql语句,mapper.xml文件是否配置正确。

另外还有resultMap 元素,它是 MyBatis 中最重要最强大的元素,它能提供级联查询,缓存等功能

9.mybatis是怎么实现分页的?

   mybatis有两种分页方法

   1)内存分页,也就是假分页。本质是查出所有的数据然后根据游标的方式,截取需要的记录。如果数据量大,开销大和内存溢出。

    使用方式:

    利用自动生成的example类,加入mybatis的RowBounds类,在调用的接口中添加给类的参数

        @Override
    public List<UserVo> selectSelective(UserVo userVo) {
        
        List<UserVo> listVo = new ArrayList<UserVo>();
        UserPoExample example = new UserPoExample();
        /**
         * 使用mybatis的RowBounds类,该类构造方法中要设置两个int类型参数
         * 第一个是从该条记录开始,第二个是开始后查询的条数
         */
        <strong>RowBounds rowBounds = new RowBounds(0, 5);</strong>
        Criteria criteria = example.createCriteria();
        criteria.andUsernameEqualTo("123");
        criteria.andRoleEqualTo(userVo.getRole());
        example.setOrderByClause("userId desc");//设置排序方式
        example.setStart(10);
        example.setLimit(10);
        UserPo userPo = new UserPo();
        userPo.setUsername("123");
        userPo.setRole(1);
        Page<UserVo> page = new Page<UserVo>();
        
        List<UserPo> listPo =null;
        try {
            //int countByExample = userPoMapper.countByExample(example);//按照条件查询总数
            //listPo = userPoMapper.selectBySelective(userPo,page);
            listPo = userPoMapper.selectByExample(example,<strong>rowBounds</strong>);
            
            for(UserPo po:listPo){
                UserVo vo = new UserVo();
                BeanUtils.copyProperties(po, vo);
                listVo.add(vo);
            }
        } catch (Exception e) {
            logger.error(e);
        }
        
        return listVo;
    }

第二中是,真正的物理分页

在自动生成的example对象中,加入两个成员变量start、和limit

public class UserPoExample {
    <strong>private int start;//设置分页开始
    
    private int limit;//设置分页的每页的数量</strong>
    
    public int getStart() {
        return start;
    }
 
    public void setStart(int start) {
        this.start = start;
    }
 
    public int getLimit() {
        return limit;
    }
 
    public void setLimit(int limit) {
        this.limit = limit;
    }
 

最后在对应的xml的方法中添加刚刚加入的条件,直接添加在自动生成的orderByClause后面

  <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
    
    <strong><if test="start !=0 or limit!=0">  
    limit #{start},#{limit}</if><span style="font-family: Arial, Helvetica, sans-serif;">  </span></strong>

通过日志可以看到是真正的分页查询

还有一种是使用分页拦截器实现的

首先在spring-dao的sqlsession工厂里面配置拦截器

<!-- sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 数据库连接池 -->
        <property name="dataSource" ref="dataSource" />
        
        <!-- 批量扫描别名 -->
        <property name="typeAliasesPackage" value="ssm.po" />  
        
        <!-- spring与mybatis整合不需要mybatis配置文件了,直接扫描mapper下的映射文件 -->
        <property name="mapperLocations" value="classpath:ssm/mapper/*.xml" />
        
        <!-- MybatisSpringPageInterceptor分页拦截器 -->
        <property name="plugins">  
            <bean class="ssm.utils.MybatisSpringPageInterceptor"></bean>
        </property>
         
    </bean>
拦截器代码:

package ssm.utils;
 
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
 
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
@Intercepts({ @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class }),
    @Signature(method = "query", type = Executor.class, args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })
public class MybatisSpringPageInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(MybatisSpringPageInterceptor.class);
 
    public static final String MYSQL = "mysql";
    public static final String ORACLE = "oracle";
 
    protected String databaseType;// 数据库类型,不同的数据库有不同的分页方法
 
    @SuppressWarnings("rawtypes")
    protected ThreadLocal<Page> pageThreadLocal = new ThreadLocal<Page>();
 
    public String getDatabaseType() {
        return databaseType;
    }
 
    public void setDatabaseType(String databaseType) {
        if (!databaseType.equalsIgnoreCase(MYSQL) && !databaseType.equalsIgnoreCase(ORACLE)) {
            throw new PageNotSupportException("Page not support for the type of database, database type [" + databaseType + "]");
        }
        this.databaseType = databaseType;
    }
 
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
 
    @Override
    public void setProperties(Properties properties) {
        String databaseType = properties.getProperty("databaseType");
        if (databaseType != null) {
            setDatabaseType(databaseType);
        }
    }
 
    @Override
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Object intercept(Invocation invocation) throws Throwable {
        if (invocation.getTarget() instanceof StatementHandler) { // 控制SQL和查询总数的地方
            Page page = pageThreadLocal.get();
            if (page == null) { //不是分页查询
                return invocation.proceed();
            }
 
            RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
            StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate");
            BoundSql boundSql = delegate.getBoundSql();
            
            Connection connection = (Connection) invocation.getArgs()[0];
            prepareAndCheckDatabaseType(connection); // 准备数据库类型
 
            if (page.getTotalPage() > -1) {
                if (log.isTraceEnabled()) {
                    log.trace("已经设置了总页数, 不需要再查询总数.");
                }
            } else {
                Object parameterObj = boundSql.getParameterObject();
                MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
                queryTotalRecord(page, parameterObj, mappedStatement, connection);
            }
 
            String sql = boundSql.getSql();
            String pageSql = buildPageSql(page, sql);
            if (log.isDebugEnabled()) {
                log.debug("分页时, 生成分页pageSql: " + pageSql);
            }
            ReflectUtil.setFieldValue(boundSql, "sql", pageSql);
 
            return invocation.proceed();
        } else { // 查询结果的地方
            // 获取是否有分页Page对象
            Page<?> page = findPageObject(invocation.getArgs()[1]);
            if (page == null) {
                if (log.isTraceEnabled()) {
                    log.trace("没有Page对象作为参数, 不是分页查询.");
                }
                return invocation.proceed();
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("检测到分页Page对象, 使用分页查询.");
                }
            }
            //设置真正的parameterObj
            invocation.getArgs()[1] = extractRealParameterObject(invocation.getArgs()[1]);
 
            pageThreadLocal.set(page);
            try {
                Object resultObj = invocation.proceed(); // Executor.query(..)
                if (resultObj instanceof List) {
                    /* @SuppressWarnings({ "unchecked", "rawtypes" }) */
                    page.setResults((List) resultObj);
                }
                return resultObj;
            } finally {
                pageThreadLocal.remove();
            }
        }
    }
 
    protected Page<?> findPageObject(Object parameterObj) {
        if (parameterObj instanceof Page<?>) {
            return (Page<?>) parameterObj;
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof Page<?>) {
                    return (Page<?>) val;
                }
            }
        }
        return null;
    }
 
    /**
     * <pre>
     * 把真正的参数对象解析出来
     * Spring会自动封装对个参数对象为Map<String, Object>对象
     * 对于通过@Param指定key值参数我们不做处理,因为XML文件需要该KEY值
     * 而对于没有@Param指定时,Spring会使用0,1作为主键
     * 对于没有@Param指定名称的参数,一般XML文件会直接对真正的参数对象解析,
     * 此时解析出真正的参数作为根对象
     * </pre>
     * @param parameterObj
     * @return
     */
    protected Object extractRealParameterObject(Object parameterObj) {
        if (parameterObj instanceof Map<?, ?>) {
            Map<?, ?> parameterMap = (Map<?, ?>) parameterObj;
            if (parameterMap.size() == 2) {
                boolean springMapWithNoParamName = true;
                for (Object key : parameterMap.keySet()) {
                    if (!(key instanceof String)) {
                        springMapWithNoParamName = false;
                        break;
                    }
                    String keyStr = (String) key;
                    if (!"0".equals(keyStr) && !"1".equals(keyStr)) {
                        springMapWithNoParamName = false;
                        break;
                    }
                }
                if (springMapWithNoParamName) {
                    for (Object value : parameterMap.values()) {
                        if (!(value instanceof Page<?>)) {
                            return value;
                        }
                    }
                }
            }
        }
        return parameterObj;
    }
 
    protected void prepareAndCheckDatabaseType(Connection connection) throws SQLException {
        if (databaseType == null) {
            String productName = connection.getMetaData().getDatabaseProductName();
            if (log.isTraceEnabled()) {
                log.trace("Database productName: " + productName);
            }
            productName = productName.toLowerCase();
            if (productName.indexOf(MYSQL) != -1) {
                databaseType = MYSQL;
            } else if (productName.indexOf(ORACLE) != -1) {
                databaseType = ORACLE;
            } else {
                throw new PageNotSupportException("Page not support for the type of database, database product name [" + productName + "]");
            }
            if (log.isInfoEnabled()) {
                log.info("自动检测到的数据库类型为: " + databaseType);
            }
        }
    }
 
    /**
     * <pre>
     * 生成分页SQL
     * </pre>
     * 
     * @param page
     * @param sql
     * @return
     */
    protected String buildPageSql(Page<?> page, String sql) {
        if (MYSQL.equalsIgnoreCase(databaseType)) {
            return buildMysqlPageSql(page, sql);
        } else if (ORACLE.equalsIgnoreCase(databaseType)) {
            return buildOraclePageSql(page, sql);
        }
        return sql;
    }
 
    /**
     * <pre>
     * 生成Mysql分页查询SQL
     * </pre>
     * 
     * @param page
     * @param sql
     * @return
     */
    protected String buildMysqlPageSql(Page<?> page, String sql) {
        // 计算第一条记录的位置,Mysql中记录的位置是从0开始的。
        int offset = (page.getPageNo() - 1) * page.getPageSize();
        return new StringBuilder(sql).append(" limit ").append(offset).append(",").append(page.getPageSize()).toString();
    }
 
    /**
     * <pre>
     * 生成Oracle分页查询SQL
     * </pre>
     * 
     * @param page
     * @param sql
     * @return
     */
    protected String buildOraclePageSql(Page<?> page, String sql) {
        // 计算第一条记录的位置,Oracle分页是通过rownum进行的,而rownum是从1开始的
        int offset = (page.getPageNo() - 1) * page.getPageSize() + 1;
        StringBuilder sb = new StringBuilder(sql);
        sb.insert(0, "select u.*, rownum r from (").append(") u where rownum < ").append(offset + page.getPageSize());
        sb.insert(0, "select * from (").append(") where r >= ").append(offset);
        return sb.toString();
    }
 
    /**
     * <pre>
     * 查询总数
     * </pre>
     * 
     * @param page
     * @param parameterObject
     * @param mappedStatement
     * @param connection
     * @throws SQLException
     */
    protected void queryTotalRecord(Page<?> page, Object parameterObject, MappedStatement mappedStatement, Connection connection) throws SQLException {
        BoundSql boundSql = mappedStatement.getBoundSql(page);
        String sql = boundSql.getSql();
        String countSql = this.buildCountSql(sql);
        if (log.isDebugEnabled()) {
            log.debug("分页时, 生成countSql: " + countSql);
        }
 
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, parameterObject);
        ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBoundSql);
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = connection.prepareStatement(countSql);
            parameterHandler.setParameters(pstmt);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                long totalRecord = rs.getLong(1);
                page.setTotalRecord(totalRecord);
            }
        } finally {
            if (rs != null)
                try {
                    rs.close();
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("关闭ResultSet时异常.", e);
                    }
                }
            if (pstmt != null)
                try {
                    pstmt.close();
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("关闭PreparedStatement时异常.", e);
                    }
                }
        }
    }
 
    /**
     * 根据原Sql语句获取对应的查询总记录数的Sql语句
     * 
     * @param sql
     * @return
     */
    protected String buildCountSql(String sql) {
        int index = sql.indexOf("from");
        return "select count(*) " + sql.substring(index);
    }
 
    /**
     * 利用反射进行操作的一个工具类
     * 
     */
    private static class ReflectUtil {
        /**
         * 利用反射获取指定对象的指定属性
         * 
         * @param obj 目标对象
         * @param fieldName 目标属性
         * @return 目标属性的值
         */
        public static Object getFieldValue(Object obj, String fieldName) {
            Object result = null;
            Field field = ReflectUtil.getField(obj, fieldName);
            if (field != null) {
                field.setAccessible(true);
                try {
                    result = field.get(obj);
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return result;
        }
 
        /**
         * 利用反射获取指定对象里面的指定属性
         * 
         * @param obj 目标对象
         * @param fieldName 目标属性
         * @return 目标字段
         */
        private static Field getField(Object obj, String fieldName) {
            Field field = null;
            for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
                try {
                    field = clazz.getDeclaredField(fieldName);
                    break;
                } catch (NoSuchFieldException e) {
                    // 这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
                }
            }
            return field;
        }
 
        /**
         * 利用反射设置指定对象的指定属性为指定的值
         * 
         * @param obj 目标对象
         * @param fieldName 目标属性
         * @param fieldValue 目标值
         */
        public static void setFieldValue(Object obj, String fieldName, String fieldValue) {
            Field field = ReflectUtil.getField(obj, fieldName);
            if (field != null) {
                try {
                    field.setAccessible(true);
                    field.set(obj, fieldValue);
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
 
    public static class PageNotSupportException extends RuntimeException {
 
        /** serialVersionUID*/
        private static final long serialVersionUID = 1L;
 
        public PageNotSupportException() {
            super();
        }
 
        public PageNotSupportException(String message, Throwable cause) {
            super(message, cause);
        }
 
        public PageNotSupportException(String message) {
            super(message);
        }
 
        public PageNotSupportException(Throwable cause) {
            super(cause);
        }
    }
 
}
package ssm.utils;
 
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
 
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
@Intercepts({ @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class }),
    @Signature(method = "query", type = Executor.class, args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })
public class MybatisSpringPageInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(MybatisSpringPageInterceptor.class);
 
    public static final String MYSQL = "mysql";
    public static final String ORACLE = "oracle";
 
    protected String databaseType;// 数据库类型,不同的数据库有不同的分页方法
 
    @SuppressWarnings("rawtypes")
    protected ThreadLocal<Page> pageThreadLocal = new ThreadLocal<Page>();
 
    public String getDatabaseType() {
        return databaseType;
    }
 
    public void setDatabaseType(String databaseType) {
        if (!databaseType.equalsIgnoreCase(MYSQL) && !databaseType.equalsIgnoreCase(ORACLE)) {
            throw new PageNotSupportException("Page not support for the type of database, database type [" + databaseType + "]");
        }
        this.databaseType = databaseType;
    }
 
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
 
    @Override
    public void setProperties(Properties properties) {
        String databaseType = properties.getProperty("databaseType");
        if (databaseType != null) {
            setDatabaseType(databaseType);
        }
    }
 
    @Override
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Object intercept(Invocation invocation) throws Throwable {
        if (invocation.getTarget() instanceof StatementHandler) { // 控制SQL和查询总数的地方
            Page page = pageThreadLocal.get();
            if (page == null) { //不是分页查询
                return invocation.proceed();
            }
 
            RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
            StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate");
            BoundSql boundSql = delegate.getBoundSql();
            
            Connection connection = (Connection) invocation.getArgs()[0];
            prepareAndCheckDatabaseType(connection); // 准备数据库类型
 
            if (page.getTotalPage() > -1) {
                if (log.isTraceEnabled()) {
                    log.trace("已经设置了总页数, 不需要再查询总数.");
                }
            } else {
                Object parameterObj = boundSql.getParameterObject();
                MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
                queryTotalRecord(page, parameterObj, mappedStatement, connection);
            }
 
            String sql = boundSql.getSql();
            String pageSql = buildPageSql(page, sql);
            if (log.isDebugEnabled()) {
                log.debug("分页时, 生成分页pageSql: " + pageSql);
            }
            ReflectUtil.setFieldValue(boundSql, "sql", pageSql);
 
            return invocation.proceed();
        } else { // 查询结果的地方
            // 获取是否有分页Page对象
            Page<?> page = findPageObject(invocation.getArgs()[1]);
            if (page == null) {
                if (log.isTraceEnabled()) {
                    log.trace("没有Page对象作为参数, 不是分页查询.");
                }
                return invocation.proceed();
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("检测到分页Page对象, 使用分页查询.");
                }
            }
            //设置真正的parameterObj
            invocation.getArgs()[1] = extractRealParameterObject(invocation.getArgs()[1]);
 
            pageThreadLocal.set(page);
            try {
                Object resultObj = invocation.proceed(); // Executor.query(..)
                if (resultObj instanceof List) {
                    /* @SuppressWarnings({ "unchecked", "rawtypes" }) */
                    page.setResults((List) resultObj);
                }
                return resultObj;
            } finally {
                pageThreadLocal.remove();
            }
        }
    }
 
    protected Page<?> findPageObject(Object parameterObj) {
        if (parameterObj instanceof Page<?>) {
            return (Page<?>) parameterObj;
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof Page<?>) {
                    return (Page<?>) val;
                }
            }
        }
        return null;
    }
 
    /**
     * <pre>
     * 把真正的参数对象解析出来
     * Spring会自动封装对个参数对象为Map<String, Object>对象
     * 对于通过@Param指定key值参数我们不做处理,因为XML文件需要该KEY值
     * 而对于没有@Param指定时,Spring会使用0,1作为主键
     * 对于没有@Param指定名称的参数,一般XML文件会直接对真正的参数对象解析,
     * 此时解析出真正的参数作为根对象
     * </pre>
     * @param parameterObj
     * @return
     */
    protected Object extractRealParameterObject(Object parameterObj) {
        if (parameterObj instanceof Map<?, ?>) {
            Map<?, ?> parameterMap = (Map<?, ?>) parameterObj;
            if (parameterMap.size() == 2) {
                boolean springMapWithNoParamName = true;
                for (Object key : parameterMap.keySet()) {
                    if (!(key instanceof String)) {
                        springMapWithNoParamName = false;
                        break;
                    }
                    String keyStr = (String) key;
                    if (!"0".equals(keyStr) && !"1".equals(keyStr)) {
                        springMapWithNoParamName = false;
                        break;
                    }
                }
                if (springMapWithNoParamName) {
                    for (Object value : parameterMap.values()) {
                        if (!(value instanceof Page<?>)) {
                            return value;
                        }
                    }
                }
            }
        }
        return parameterObj;
    }
 
    protected void prepareAndCheckDatabaseType(Connection connection) throws SQLException {
        if (databaseType == null) {
            String productName = connection.getMetaData().getDatabaseProductName();
            if (log.isTraceEnabled()) {
                log.trace("Database productName: " + productName);
            }
            productName = productName.toLowerCase();
            if (productName.indexOf(MYSQL) != -1) {
                databaseType = MYSQL;
            } else if (productName.indexOf(ORACLE) != -1) {
                databaseType = ORACLE;
            } else {
                throw new PageNotSupportException("Page not support for the type of database, database product name [" + productName + "]");
            }
            if (log.isInfoEnabled()) {
                log.info("自动检测到的数据库类型为: " + databaseType);
            }
        }
    }
 
    /**
     * <pre>
     * 生成分页SQL
     * </pre>
     * 
     * @param page
     * @param sql
     * @return
     */
    protected String buildPageSql(Page<?> page, String sql) {
        if (MYSQL.equalsIgnoreCase(databaseType)) {
            return buildMysqlPageSql(page, sql);
        } else if (ORACLE.equalsIgnoreCase(databaseType)) {
            return buildOraclePageSql(page, sql);
        }
        return sql;
    }
 
    /**
     * <pre>
     * 生成Mysql分页查询SQL
     * </pre>
     * 
     * @param page
     * @param sql
     * @return
     */
    protected String buildMysqlPageSql(Page<?> page, String sql) {
        // 计算第一条记录的位置,Mysql中记录的位置是从0开始的。
        int offset = (page.getPageNo() - 1) * page.getPageSize();
        return new StringBuilder(sql).append(" limit ").append(offset).append(",").append(page.getPageSize()).toString();
    }
 
    /**
     * <pre>
     * 生成Oracle分页查询SQL
     * </pre>
     * 
     * @param page
     * @param sql
     * @return
     */
    protected String buildOraclePageSql(Page<?> page, String sql) {
        // 计算第一条记录的位置,Oracle分页是通过rownum进行的,而rownum是从1开始的
        int offset = (page.getPageNo() - 1) * page.getPageSize() + 1;
        StringBuilder sb = new StringBuilder(sql);
        sb.insert(0, "select u.*, rownum r from (").append(") u where rownum < ").append(offset + page.getPageSize());
        sb.insert(0, "select * from (").append(") where r >= ").append(offset);
        return sb.toString();
    }
 
    /**
     * <pre>
     * 查询总数
     * </pre>
     * 
     * @param page
     * @param parameterObject
     * @param mappedStatement
     * @param connection
     * @throws SQLException
     */
    protected void queryTotalRecord(Page<?> page, Object parameterObject, MappedStatement mappedStatement, Connection connection) throws SQLException {
        BoundSql boundSql = mappedStatement.getBoundSql(page);
        String sql = boundSql.getSql();
        String countSql = this.buildCountSql(sql);
        if (log.isDebugEnabled()) {
            log.debug("分页时, 生成countSql: " + countSql);
        }
 
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, parameterObject);
        ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBoundSql);
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = connection.prepareStatement(countSql);
            parameterHandler.setParameters(pstmt);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                long totalRecord = rs.getLong(1);
                page.setTotalRecord(totalRecord);
            }
        } finally {
            if (rs != null)
                try {
                    rs.close();
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("关闭ResultSet时异常.", e);
                    }
                }
            if (pstmt != null)
                try {
                    pstmt.close();
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("关闭PreparedStatement时异常.", e);
                    }
                }
        }
    }
 
    /**
     * 根据原Sql语句获取对应的查询总记录数的Sql语句
     * 
     * @param sql
     * @return
     */
    protected String buildCountSql(String sql) {
        int index = sql.indexOf("from");
        return "select count(*) " + sql.substring(index);
    }
 
    /**
     * 利用反射进行操作的一个工具类
     * 
     */
    private static class ReflectUtil {
        /**
         * 利用反射获取指定对象的指定属性
         * 
         * @param obj 目标对象
         * @param fieldName 目标属性
         * @return 目标属性的值
         */
        public static Object getFieldValue(Object obj, String fieldName) {
            Object result = null;
            Field field = ReflectUtil.getField(obj, fieldName);
            if (field != null) {
                field.setAccessible(true);
                try {
                    result = field.get(obj);
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return result;
        }
 
        /**
         * 利用反射获取指定对象里面的指定属性
         * 
         * @param obj 目标对象
         * @param fieldName 目标属性
         * @return 目标字段
         */
        private static Field getField(Object obj, String fieldName) {
            Field field = null;
            for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
                try {
                    field = clazz.getDeclaredField(fieldName);
                    break;
                } catch (NoSuchFieldException e) {
                    // 这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
                }
            }
            return field;
        }
 
        /**
         * 利用反射设置指定对象的指定属性为指定的值
         * 
         * @param obj 目标对象
         * @param fieldName 目标属性
         * @param fieldValue 目标值
         */
        public static void setFieldValue(Object obj, String fieldName, String fieldValue) {
            Field field = ReflectUtil.getField(obj, fieldName);
            if (field != null) {
                try {
                    field.setAccessible(true);
                    field.set(obj, fieldValue);
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
 
    public static class PageNotSupportException extends RuntimeException {
 
        /** serialVersionUID*/
        private static final long serialVersionUID = 1L;
 
        public PageNotSupportException() {
            super();
        }
 
        public PageNotSupportException(String message, Throwable cause) {
            super(message, cause);
        }
 
        public PageNotSupportException(String message) {
            super(message);
        }
 
        public PageNotSupportException(Throwable cause) {
            super(cause);
        }
    }
 
}
分页的Page对象:

package ssm.utils;
 
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
public class Page<T> {
 
    public static final int DEFAULT_PAGE_SIZE = 10;
 
    protected int pageNo = 1; // 当前页, 默认为第1页
    protected int pageSize = DEFAULT_PAGE_SIZE; // 每页记录数
    protected long totalRecord = -1; // 总记录数, 默认为-1, 表示需要查询
    protected int totalPage = -1; // 总页数, 默认为-1, 表示需要计算
 
    protected List<T> results; // 当前页记录List形式
    
    public Map<String, Object> params = new HashMap<String, Object>();//设置页面传递的查询参数
 
    public Map<String, Object> getParams() {
        return params;
    }
 
    public void setParams(Map<String, Object> params) {
        this.params = params;
    }
 
    public int getPageNo() {
        return pageNo;
    }
 
    public void setPageNo(int pageNo) {
        this.pageNo = pageNo;
    }
 
    public int getPageSize() {
        return pageSize;
    }
 
    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
        computeTotalPage();
    }
 
    public long getTotalRecord() {
        return totalRecord;
    }
 
    public int getTotalPage() {
        return totalPage;
    }
 
    public void setTotalRecord(long totalRecord) {
        this.totalRecord = totalRecord;
        computeTotalPage();
    }
 
    protected void computeTotalPage() {
        if (getPageSize() > 0 && getTotalRecord() > -1) {
            this.totalPage = (int) (getTotalRecord() % getPageSize() == 0 ? getTotalRecord() / getPageSize() : getTotalRecord() / getPageSize() + 1);
        }
    }
 
    public List<T> getResults() {
        return results;
    }
 
    public void setResults(List<T> results) {
        this.results = results;
    }
 
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder().append("Page [pageNo=").append(pageNo).append(", pageSize=").append(pageSize)
                .append(", totalRecord=").append(totalRecord < 0 ? "null" : totalRecord).append(", totalPage=")
                .append(totalPage < 0 ? "null" : totalPage).append(", curPageObjects=").append(results == null ? "null" : results.size()).append("]");
        return builder.toString();
    }
 
}
对应的xml

<select id="selectBySelective" parameterType="ssm.utils.Page" resultType="ssm.po.UserPo">
    select * from tb_user
    <where>
        <if test="params.username != null and params.username !=''">
            userName = #{params.username}
            <!-- userName like '%${usesrname}%' -->
        </if>
        <if test="params.password != null and params.password !=''">
            and password = #{params.password}
        </if>
        <if test="params.sex != null and params.sex !=''">
            and sex = #{params.sex}
        </if>
        <if test="params.age != null and params.age !=''">
            and age = #{params.age}
        </if>
        <if test="params.email != null and params.email !=''">
            and email = #{params.email}
        </if>
        <if test="params.courseid != null and params.courseid !=''">
            and courseId = #{params.courseid}
        </if>
        <if test="params.role != null and params.role !=''">
            and role = #{params.role}
        </if>
        <if test="params.state != null and params.state !=''">
            and state = #{params.state}
        </if>
    </where>
        <if test="params.orderByClause != null and params.orderByClause !=''"></if>
            order by #{params.orderByClause}
      
  </select>
 

猜你喜欢

转载自blog.csdn.net/suchahaerkang/article/details/82990400