- 浏览: 1527292 次
- 性别:
- 来自: 厦门
博客专栏
-
Spring 3.x企业实...
浏览量:464213
文章分类
最新评论
-
JyeChou:
学习Spring必学的Java基础知识(1)----反射 -
hhzhaoheng:
...
《Spring4.x企业应用开发实战》光盘资料下载 -
renlongnian:
//assertReflectionEquals(user1, ...
单元测试系列之3:测试整合之王Unitils -
骑着蜗牛超F1:
huang_yong 写道我的经验是,只需定义三层:1.ent ...
Spring的事务管理难点剖析(2):应用分层的迷惑 -
wangyudong:
工具地址貌似更新了哦https://github.com/Wi ...
几种常用的REST webservice客户端测试工具
问题
1.mybatis默认分页是内存分页的,谁用谁崩溃啊!
类似于下面的DAO签名方法,只要有RowBounds入参,Mybatis即会自动内存分页:
我们必须将其转换为物理分页,也即数据库分页。
2.分页一般都需要自动计算出总行数,而在mybatis中,你必须手动发起两次请求,烦人。
解决思路
1.Mybatis的拦截器是我们动手动脚的地方
Mybatis的架构是非常漂亮的,它允许对多个接口的多个方法定义拦截器(Interceptor),以下是Mybatis的调用粗线:
我们不但可以对Excutor的方法编写插件,还可以对StatementHandler或ResultSetHandler的方法编写插件。以下是一个Mybatis的插件:
注意PaginationInterceptor类的@Intercepts注解,如上所示,它将对StatementHandler的prepare(Connection connection)方法进行拦截。
2.怎样将mybatis的语句转换为分页的语句呢
这得求助Hibernate的org.hibernate.dialect.Dialect接口及其实现类。我们知道不同数据库分页的方法是不一样的。mysql是limit x,y,而Oracle要用一个子查询,使用rownum来做到。Hibernater的org.hibernate.dialect.Dialect的以下方法可以非常方便地让我们做到这点:
以下就是使用该方法完成的转换:
SELECT * FROM cartan_common.t_app s WHERE s.author = ?
对应的分页SQL:
SELECT * FROM cartan_common.t_app s WHERE s.author = ? limit ?, ?
3.怎样生成SQL对应的总条数SQL呢?
通过以下的类的MybatisHepler.getCountSql(originalSql)方法即可:
好了,下面给出PaginationInterceptor的完整代码:
如何在保证分页接口签名不变的情况下,将总行数传回去呢?
下面是一个Service层的方法:
由于DAO appMapper按正常签名只返回一个List,对应的总行数我怎么获取呢?这里我用到了ThreadLocal,因为它让我们可以跨类访问,毕竟Service调用DAO,它们都位于同一个Thread中:
这样,以上Service的方法就可以改成:
改进
但是每个Service都要手工调用setTotalRows(CountHelper.getTotalRowCount())是不是有点多余呢?
这里我们可以使用Spring AOP自动做这个事,这样①处的代码就可以不用手工写了。
为些,我写了一个Advice:
在Spring配置文件中,将TotalRowValueMount#setTotalRows(Page page)方法植入到所有返回值类型为Page的方法上,在方法返回时,自动调用setTotalRows(CountHelper.getTotalRowCount());
怎么配置mybatis以应用拦截器呢
在mybatis配置文件中,添加拦截器即可:
稍后再提交整个代码。
1.mybatis默认分页是内存分页的,谁用谁崩溃啊!
类似于下面的DAO签名方法,只要有RowBounds入参,Mybatis即会自动内存分页:
@Select("SELECT * FROM cartan_common.t_app s WHERE s.author = #{param.author}") ArrayList<App> queryList(@Param("param")AppQueryParam appQueryParam,RowBounds rowBounds);
我们必须将其转换为物理分页,也即数据库分页。
2.分页一般都需要自动计算出总行数,而在mybatis中,你必须手动发起两次请求,烦人。
解决思路
1.Mybatis的拦截器是我们动手动脚的地方
Mybatis的架构是非常漂亮的,它允许对多个接口的多个方法定义拦截器(Interceptor),以下是Mybatis的调用粗线:
我们不但可以对Excutor的方法编写插件,还可以对StatementHandler或ResultSetHandler的方法编写插件。以下是一个Mybatis的插件:
package com.ridge.dao.mybatis; import org.apache.commons.lang.reflect.FieldUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ibatis.executor.statement.PreparedStatementHandler; 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.ParameterMapping; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.RowBounds; import org.hibernate.dialect.Dialect; import java.sql.Connection; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 默认情况下 * 数据库的类型 * * @author : chenxh * @date: 13-7-5 */ @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})}) public class PaginationInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { ... } }
注意PaginationInterceptor类的@Intercepts注解,如上所示,它将对StatementHandler的prepare(Connection connection)方法进行拦截。
2.怎样将mybatis的语句转换为分页的语句呢
这得求助Hibernate的org.hibernate.dialect.Dialect接口及其实现类。我们知道不同数据库分页的方法是不一样的。mysql是limit x,y,而Oracle要用一个子查询,使用rownum来做到。Hibernater的org.hibernate.dialect.Dialect的以下方法可以非常方便地让我们做到这点:
getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit())
以下就是使用该方法完成的转换:
引用
SELECT * FROM cartan_common.t_app s WHERE s.author = ?
对应的分页SQL:
SELECT * FROM cartan_common.t_app s WHERE s.author = ? limit ?, ?
3.怎样生成SQL对应的总条数SQL呢?
通过以下的类的MybatisHepler.getCountSql(originalSql)方法即可:
package com.ridge.dao.mybatis; import com.ridge.dao.Paging; import org.apache.ibatis.session.RowBounds; import java.util.regex.Matcher; import java.util.regex.Pattern; public class MybatisHepler { /** * 获取查询总数对应的SQL * @param querySelect * @return */ public static String getCountSql(String querySelect) { querySelect = compress(querySelect); int orderIndex = getLastOrderInsertPoint(querySelect); int formIndex = getAfterFormInsertPoint(querySelect); String select = querySelect.substring(0, formIndex); //如果SELECT 中包含 DISTINCT 只能在外层包含COUNT if (select.toLowerCase().indexOf("select distinct") != -1 || querySelect.toLowerCase().indexOf("group by") != -1) { return new StringBuffer(querySelect.length()).append( "select count(1) count from (").append( querySelect.substring(0, orderIndex)).append(" ) t") .toString(); } else { return new StringBuffer(querySelect.length()).append( "select count(1) count ").append( querySelect.substring(formIndex, orderIndex)).toString(); } } /** * 得到最后一个Order By的插入点位置 * * @return 返回最后一个Order By插入点的位置 */ private static int getLastOrderInsertPoint(String querySelect) { int orderIndex = querySelect.toLowerCase().lastIndexOf("order by"); if (orderIndex == -1) { orderIndex = querySelect.length(); }else{ if(!isBracketCanPartnership(querySelect.substring(orderIndex, querySelect.length()))){ throw new RuntimeException("My SQL 分页必须要有Order by 语句!"); } } return orderIndex; } /** * 将{@code paging}转换为{@link org.apache.ibatis.session.RowBounds}对象 * @param paging * @return */ public static final RowBounds toRowBounds(Paging paging){ return new RowBounds(paging.getRowOffset(),paging.getPageSize()); } /** * 得到分页的SQL * * @param offset 偏移量 * @param limit 位置 * @return 分页SQL */ public static String getPagingSql(String querySelect, int offset, int limit) { querySelect = compress(querySelect); String sql = querySelect.replaceAll("[^\\s,]+\\.", "") + " limit " + offset + " ," + limit; return sql; } /** * 将SQL语句压缩成一条语句,并且每个单词的间隔都是1个空格 * @param sql SQL语句 * @return 如果sql是NULL返回空,否则返回转化后的SQL */ private static String compress(String sql) { return sql.replaceAll("[\r\n]", " ").replaceAll("\\s{2,}", " "); } /** * 得到SQL第一个正确的FROM的的插入点 */ private static int getAfterFormInsertPoint(String querySelect) { String regex = "\\s+FROM\\s+"; Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(querySelect); while (matcher.find()) { int fromStartIndex = matcher.start(0); String text = querySelect.substring(0, fromStartIndex); if (isBracketCanPartnership(text)) { return fromStartIndex; } } return 0; } /** * 判断括号"()"是否匹配,并不会判断排列顺序是否正确 * * @param text 要判断的文本 * @return 如果匹配返回TRUE, 否则返回FALSE */ private static boolean isBracketCanPartnership(String text) { if (text == null || (getIndexOfCount(text, '(') != getIndexOfCount(text, ')'))) { return false; } return true; } /** * 得到一个字符在另一个字符串中出现的次数 * * @param text 文本 * @param ch 字符 */ private static int getIndexOfCount(String text, char ch) { int count = 0; for (int i = 0; i < text.length(); i++) { count = (text.charAt(i) == ch) ? count + 1 : count; } return count; } }
好了,下面给出PaginationInterceptor的完整代码:
package com.ridge.dao.mybatis; import org.apache.commons.lang.reflect.FieldUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ibatis.executor.statement.PreparedStatementHandler; 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.ParameterMapping; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.RowBounds; import org.hibernate.dialect.Dialect; import java.sql.Connection; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 默认情况下 * 数据库的类型 * * @author : chenxh * @date: 13-7-5 */ @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})}) public class PaginationInterceptor implements Interceptor { private final static Log logger = LogFactory.getLog(PaginationInterceptor.class); public static final String CONFIGURATION = "configuration"; private static Dialect dialect = null; private static final String ROW_BOUNDS = "rowBounds"; private static final String BOUND_SQL = "boundSql"; private static final String DIALECT = "dialect"; private static final String SQL = "sql"; private static final String OFFSET = "offset"; private static final String LIMIT = "limit"; public static final String DELEGATE = "delegate"; private static final int CONNECTION_INDEX = 0; private static final String INTERCEPTOR_CONF = "<plugins>\n" + "<plugin interceptor=\"" + PaginationInterceptor.class.getCanonicalName() + "\">\n" + "<property name=\"dialect\" value=\"" + DialetHelper.getSupportDatabaseTypes() + "\"/>\n" + "</plugin>\n" + "</plugins>"; @Override public Object intercept(Invocation invocation) throws Throwable { RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation.getTarget(); PreparedStatementHandler preparedStatHandler = (PreparedStatementHandler) FieldUtils.readField(statementHandler, DELEGATE, true); final Object[] queryArgs = invocation.getArgs(); Connection connection = (Connection) queryArgs[CONNECTION_INDEX]; RowBounds rowBounds = (RowBounds) FieldUtils.readField(preparedStatHandler, ROW_BOUNDS, true); BoundSql boundSql = (BoundSql) FieldUtils.readField(preparedStatHandler, BOUND_SQL, true); Configuration configuration = (Configuration) FieldUtils.readField(preparedStatHandler, CONFIGURATION, true); //没有分页,直接返回原调用 if (rowBounds == null || rowBounds == RowBounds.DEFAULT) { return invocation.proceed(); } //有分页 String originalSql = boundSql.getSql(); //1.获取总行数,将行数绑定到当前线程中 String countSql = MybatisHepler.getCountSql(originalSql); CountHelper.getCount(countSql, preparedStatHandler, configuration, boundSql, connection); //2.获取分页的结果集 ////////////////////////////////////////////////////////////////////////////////////////////// String pagingSql = dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()); FieldUtils.writeField(boundSql, SQL, pagingSql, true); int size = 0; size = getPageParamNum(originalSql, pagingSql); if (size == 1) { ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, LIMIT, Integer.class); boundSql.getParameterMappings().add(builder.build()); boundSql.setAdditionalParameter(LIMIT, rowBounds.getLimit()); } if (size == 2) { ParameterMapping.Builder builder = new ParameterMapping.Builder( configuration, OFFSET, Integer.class); boundSql.getParameterMappings().add(builder.build()); boundSql.setAdditionalParameter(OFFSET, rowBounds.getOffset()); builder = new ParameterMapping.Builder(configuration, LIMIT, Integer.class); boundSql.getParameterMappings().add(builder.build()); boundSql.setAdditionalParameter(LIMIT, rowBounds.getLimit()); } FieldUtils.writeField(rowBounds, OFFSET, RowBounds.NO_ROW_OFFSET, true); FieldUtils.writeField(rowBounds, LIMIT, RowBounds.NO_ROW_LIMIT, true); if (logger.isDebugEnabled()) { logger.debug("\n" + originalSql + "\n对应的分页SQL:\n" + boundSql.getSql() + "\n对应的count SQL:\n" + countSql); } return invocation.proceed(); } /** * 获取用于控制分页的问号的个数 * * @param originalSql * @param pagingSql * @return */ private int getPageParamNum(String originalSql, String pagingSql) { int size = 0; String addSql = pagingSql.replace(originalSql, ""); Pattern pattern = Pattern.compile("[?]"); Matcher matcher = pattern.matcher(addSql); while (matcher.find()) { size++; } return size; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { if (PaginationInterceptor.dialect == null) { String dialect = properties.getProperty(DIALECT); if (dialect == null) { throw new RuntimeException("拦截器未提供dialect的配置,正确配置参见:\n" + INTERCEPTOR_CONF); } PaginationInterceptor.dialect = DialetHelper.getDialect(dialect); } } }
如何在保证分页接口签名不变的情况下,将总行数传回去呢?
下面是一个Service层的方法:
public Page<App> queryAppList(AppQueryParam queryParam,Paging paging){ List<App> apps = appMapper.queryList(queryParam, MybatisHepler.toRowBounds(paging)); Page<App> appPage = new Page<App>(); appPage.setResult(apps); appPage.setPageSize(paging.getPageSize()); return appPage; }
由于DAO appMapper按正常签名只返回一个List,对应的总行数我怎么获取呢?这里我用到了ThreadLocal,因为它让我们可以跨类访问,毕竟Service调用DAO,它们都位于同一个Thread中:
package com.ridge.dao.mybatis; import org.apache.commons.lang.reflect.FieldUtils; import org.apache.ibatis.executor.statement.PreparedStatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.ibatis.session.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; /** * @author : chenxh * @date: 13-7-8 */ public class CountHelper { private static final String MAPPED_STATEMENT = "mappedStatement"; private static Logger logger = LoggerFactory.getLogger(CountHelper.class); /** * 保存计算总行数的值 */ private static ThreadLocal<Integer> totalRowCountHolder = new ThreadLocal<Integer>(); /** * 获取查询对象的总行数 * @param sql 获取总行数的SQL * @param statementHandler * @param configuration * @param boundSql * @param connection * @throws Throwable */ static void getCount(String sql, PreparedStatementHandler statementHandler, Configuration configuration, BoundSql boundSql, Connection connection) throws Throwable{ Object parameterObject = statementHandler.getParameterHandler().getParameterObject(); if (logger.isDebugEnabled()) { logger.debug("Total count SQL [{}] ", sql); logger.debug("Total count Parameters: {} ", parameterObject); } PreparedStatement countStmt = null; ResultSet rs = null; try { countStmt = connection.prepareStatement(sql); final BoundSql countBS = new BoundSql(configuration, sql, boundSql.getParameterMappings(), parameterObject); MappedStatement mappedStatement = (MappedStatement)FieldUtils.readField(statementHandler, MAPPED_STATEMENT, true); DefaultParameterHandler handler = new DefaultParameterHandler(mappedStatement, parameterObject, countBS); handler.setParameters(countStmt); rs = countStmt.executeQuery(); int count = 0; if (rs.next()) { count = rs.getInt(1); } if (logger.isDebugEnabled()) { logger.debug("Total count: {}", count); } totalRowCountHolder.set(count); } finally { try { if (rs != null) { rs.close(); } } finally { if (countStmt != null) { countStmt.close(); } } } } /** * 获取当前线程对应的分页查询的总行数 * * @return */ public static int getTotalRowCount() { return totalRowCountHolder.get(); } }
这样,以上Service的方法就可以改成:
public Page<App> queryAppList(AppQueryParam queryParam,Paging paging){ List<App> apps = appMapper.queryList(queryParam, MybatisHepler.toRowBounds(paging)); Page<App> appPage = new Page<App>(); appPage.setResult(apps); appPage.setPageSize(paging.getPageSize()); appPage.setTotalRows(CountHelper.getTotalRowCount());//①注意这里!! return appPage; }
改进
但是每个Service都要手工调用setTotalRows(CountHelper.getTotalRowCount())是不是有点多余呢?
这里我们可以使用Spring AOP自动做这个事,这样①处的代码就可以不用手工写了。
为些,我写了一个Advice:
package com.ridge.dao.mybatis; import com.ridge.dao.Page; /** * @author : chenxh * @date: 13-7-8 */ public class TotalRowValueMount { public void setTotalRows(Page page){ page.setTotalRows(CountHelper.getTotalRowCount()); } }
在Spring配置文件中,将TotalRowValueMount#setTotalRows(Page page)方法植入到所有返回值类型为Page的方法上,在方法返回时,自动调用setTotalRows(CountHelper.getTotalRowCount());
<!-- 所有分页查询的方法自动设置总行数 --> <aop:config> <aop:aspect id="pagingQueryAspect" ref="totalRowCounter"> <aop:pointcut id="pagingQueryMethods" expression="execution(public com.ridge.dao.Page *(..))"/> <aop:after-returning pointcut-ref="pagingQueryMethods" returning="page" method="setTotalRows"/> </aop:aspect> </aop:config> <bean id="totalRowCounter" class="com.ridge.dao.mybatis.TotalRowValueMount"/>
怎么配置mybatis以应用拦截器呢
在mybatis配置文件中,添加拦截器即可:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- changes from the defaults --> <setting name="lazyLoadingEnabled" value="false" /> </settings> <plugins> <plugin interceptor="com.ridge.dao.mybatis.PaginationInterceptor"> <property name="dialect" value="MYSQL"/> </plugin> </plugins> <mappers> <package name="com.xxx.yyy"/> </mappers> </configuration>
稍后再提交整个代码。
发表评论
-
10 个最受欢迎的 Java 开发的 CMS 系统
2014-03-08 11:06 5176转于:http://www.oschina.net/news ... -
一个常见的Spring IOC疑难症状
2013-07-25 14:14 5061Case 请看下面的IOC实例: 1)Aa ... -
如何配置一个带泛型的Bean
2013-07-24 14:31 2760希望在Spring容器中配置一个带泛型的Bean,直接配置如下 ... -
安装Redis完整过程
2013-07-11 16:09 53589概述 首先报告一下 ... -
HyperSQL 2.0
2012-11-01 15:15 1962HyperSQL 2.0于本月7日发布了。此次发布距HSQLD ... -
Google Guava API学习笔记(2):集合
2012-08-31 18:42 8514不可变集合 Immutable Collections 例 ... -
Google Guava API学习笔记(1):基础
2012-08-31 17:52 8716Guava项目包括了多个Google的核心库,包括col ... -
学习Spring必学的Java基础知识(9)----HTTP请求报文
2012-06-09 16:02 13943引述要学习Spring框架的技术内幕,必须事先掌握一些基本的J ... -
学习Spring必学的Java基础知识(4)----XML基础知识
2012-05-12 15:33 8560引述要学习Spring框架的 ... -
学习Spring必学的Java基础知识(3)----PropertyEditor
2012-05-12 15:13 16846引述要学习Spring框架的 ... -
学习Spring必学的Java基础知识(2)----动态代理
2012-05-02 13:03 9713引述要学习Spring框架的 ... -
学习Spring必学的Java基础知识(1)----反射
2012-04-25 13:57 89790引述要学习Spring框架的技术内幕,必须事先掌握一些基本的J ... -
透透彻彻IoC(你没有理由不懂!)
2012-04-18 11:01 94214引述:IoC(控制反转:I ... -
单元测试系列之5:使用unitils测试Service层
2012-04-14 10:48 18446引述:Spring 的测试框架为我们提供一个强大的测试环境,解 ... -
如何用Spring读取JAR中的文件
2012-04-13 17:22 18399使用如下方式读取JAR中的文件出错 类路径下 ... -
单元测试系列之3:测试整合之王Unitils
2012-04-09 14:11 15660引述:程序测试对保障应用程序正确性而言,其重要性怎么样强调都不 ... -
单元测试系列之1:开发测试的那些事儿
2012-03-28 12:52 10014引述:程序测试对保障应用程序正确性而言,其重要性怎 ... -
单元测试的那些事儿
2012-03-28 12:48 2------------------------------- ... -
Spring 3.0的新功能
2012-03-26 09:30 71942009年9月发布Spring ... -
Spring的事务管理难点剖析(7):数据连接泄漏
2012-03-07 10:53 6843底层连接资源的访问问题 对于应用开发者来说,数据连接泄 ...
相关推荐
在本文中,我们将深入探讨如何将Spring MVC 3.1与MyBatis 3.1框架集成,并讨论其中涉及的关键技术,如事务管理、分页和JSON数据交换。此外,我们还将简要提及开发环境中使用的工具,如Eclipse 4.3、Maven 3.0.5和...
网上提供的好多Mybatis教程不详细完整,这个Mybatis3.1实战绝对完整版本。声明:该作品版权归原作者所有,仅供网友参考交流,如若喜欢请购买正版图书。
标题中的"MyBatis3.1 jar"指的是MyBatis框架的3.1版本的jar包。这个版本相对于3.0有了一些显著的变化和改进,使得开发者在处理数据库操作时更加便捷高效。 在描述中提到,MyBatis3.1与3.0相比有显著不同。这可能...
在"mybatis3.1-lib"这个压缩包中,我们可以预见到它包含了MyBatis 3.1版本所需的所有核心库文件。这个版本的MyBatis引入了一些改进和新特性,以提供更好的性能和开发者体验。以下是关于MyBatis 3.1的一些关键知识点...
本文将深入探讨"spring3.2.2+mybatis3.1-lib"的整合过程及其相关知识点。 首先,Spring是一个全面的后端开发框架,它提供了依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP...
要使用 MyBatis 自动分页实现,需要在 Spring 配置文件中添加拦截器配置。例如,在 Spring 配置文件中添加以下配置: ```xml <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> ...
springMVC3.0 + MyBatis3.1 花了2天整合成功的DEMO Spring 用的是基于注解的 MyBatis 用的是基于XML的 带表结构 有增删改查的小例子(含事务) 个人认为这套小框架有有兴趣的人 完全可以自己修改修改, 作为自己以后...
**SpringMVC与Mybatis简介** ...通过上述步骤,我们可以构建一个基于SpringMVC3.1和Mybatis3.1的简单J2EE应用程序。这个Demo项目适合初学者了解这两个框架的基本使用和整合方式,为进一步深入学习和实践打下基础。
基于struts2.2+spring3.1+Mybatis3.1(SSI框架)框架开发下的JAR包整合!
06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo...
Mybatis3.1版本引入了一些改进和优化,旨在提升开发效率和性能。本实战指南将通过一系列实例,帮助你掌握如何在实际项目中运用Mybatis。 2. **什么是Mybatis** Mybatis是一个半自动的ORM框架,不同于Hibernate的...
自己下载源码做的Mybatis3.1 api文档,现在分享给大家,希望能对大家有所帮助
下面我们将深入探讨Spring 5.2.0.RELEASE和MyBatis 3.1的源码编译过程,以及这两个框架的核心知识点。 首先,让我们了解Spring 5.2.0.RELEASE的主要改进和特性: 1. **响应式编程**:Spring 5引入了对响应式编程的...
Mybatis通用分页插件通过自动处理分页参数,极大地简化了这一过程。 该插件的核心功能包括: 1. **智能分页**:根据不同的数据库(如MySQL、Oracle、SQL Server等)自动生成对应的分页SQL,无需手动编写LIMIT或...
Struts 2.3.7、Spring 3.2.0 和 MyBatis 3.1 是三个在Java企业级开发中广泛使用的开源框架,它们分别负责不同的职责,共同构建了一个强大的后端服务架构。 **Struts 2.3.7** 是一个基于MVC(Model-View-Controller...
在给定的压缩包文件中,包含的是MyBatis 3.1版本的相关文档和库文件,这对于开发者来说是非常宝贵的资源。以下是这些文件的具体内容和相关知识点: 1. **mybatis参考文档.CHM**: 这是MyBatis的官方中文参考文档,...
在这个项目中,我们看到的是一个基于Struts2.3、Spring3.2和MyBatis3.1的集成应用,其中使用了注释式的事务管理。 首先,让我们来详细了解一下这三个框架: 1. **Struts2**:这是一个基于MVC设计模式的Java Web...
在本文中,我们将深入探讨如何使用JSP、Servlet和MyBatis这三种技术来实现一个分页查询的功能。这是一个常见的需求,在许多Web应用程序中,为了提高用户体验,通常需要将大量数据分批次展示,而不是一次性加载所有...
springMVC+Mybatis3.1+spring3.1.2(包含事务详解,代码诠释,含数据库文件) 展示了增、删、改、查、注解、拦截器、spring事务配置(亲测成功),sql文 件!!赶紧来下载给好评!!! web project 完全可以跑起来!...