当前位置: 首页 > news >正文

天津网站建设是什么搜索引擎营销的概念

天津网站建设是什么,搜索引擎营销的概念,哪些网站的做的好看的,网站建设与维护实训目录 分页插件 Mybatis插件典型适用场景 实现思考 第一个问题 第二个问题 自定义分页插件 分页插件使用 添加pom依赖 插件注册 调用 代理和拦截是怎么实现的 PageHelper 原理 分页插件 MyBatis 通过提供插件机制,让我们可以根据自己的需要去增强MyBati…

目录

分页插件

Mybatis插件典型适用场景

实现思考

第一个问题

第二个问题

自定义分页插件

分页插件使用

添加pom依赖

插件注册

调用

代理和拦截是怎么实现的

PageHelper 原理


分页插件

MyBatis 通过提供插件机制,让我们可以根据自己的需要去增强MyBatis 的功能。需要注意的是,如果没有完全理解MyBatis 的运行原理和插件的工作方式,最好不要使用插件,因为它会改变系底层的工作逻辑,给系统带来很大的影响。

MyBatis 的插件可以在不修改原来的代码的情况下,通过拦截的方式,改变四大核心对象的行为,比如处理参数,处理SQL,处理结果。

Mybatis插件典型适用场景

分页功能

mybatis的分页默认是基于内存分页的(查出所有,再截取),数据量大的情况下效率较低,不过使用mybatis插件可以改变该行为,只需要拦截StatementHandler类的prepare方法,改变要执行的SQL语句为分页语句即可;

公共字段统一赋值

一般业务系统都会有创建者,创建时间,修改者,修改时间四个字段,对于这四个字段的赋值,实际上可以在DAO层统一拦截处理,可以用mybatis插件拦截Executor类的update方法,对相关参数进行统一赋值即可;

性能监控

对于SQL语句执行的性能监控,可以通过拦截Executor类的update, query等方法,用日志记录每个方法执行的时间;

其它

其实mybatis扩展性还是很强的,基于插件机制,基本上可以控制SQL执行的各个阶段,如执行阶段,参数处理阶段,语法构建阶段,结果集处理阶段,具体可以根据项目业务来实现对应业务逻辑。

实现思考

第一个问题

 不修改对象的代码,怎么对对象的行为进行修改,比如说在原来的方法前面做一点事情,在原来的方法后面做一点事情?

  答案:大家很容易能想到用代理模式,这个也确实是MyBatis 插件的原理。

第二个问题

我们可以定义很多的插件,那么这种所有的插件会形成一个链路,比如我们提交一个休假申请,先是项目经理审批,然后是部门经理审批,再是HR 审批,再到总经理审批,怎么实现层层的拦截?

  答案:插件是层层拦截的,我们又需要用到另一种设计模式——责任链模式。

在之前的源码中我们也发现了,mybatis内部对于插件的处理确实使用的代理模式,既然是代理模式,我们应该了解MyBatis 允许哪些对象的哪些方法允许被拦截,并不是每一个运行的节点都是可以被修改的。只有清楚了这些对象的方法的作用,当我们自己编写插件的时候才知道从哪里去拦截。在MyBatis 官网有答案,我们来看一下:

mybatis – MyBatis 3 | 配置

Executor 会拦截到CachingExcecutor 或者BaseExecutor。因为创建Executor 时是先创建CachingExcecutor,再包装拦截。从代码顺序上能看到。我们可以通过mybatis的分页插件来看看整个插件从包装拦截器链到执行拦截器链的过程。

  在查看插件原理的前提上,我们需要来看看官网对于自定义插件是怎么来做的,官网上有介绍:通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。这里本人踩了一个坑,在Springboot中集成,同时引入了pagehelper-spring-boot-starter 导致RowBounds参数的值被刷掉了,也就是走到了我的拦截其中没有被设置值,这里需要注意,拦截器出了问题,可以Debug看一下Configuration配置类中拦截器链的包装情况。

自定义分页插件

@Intercepts({@Signature(type = Executor.class,method = "query" ,args ={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} ), // 需要代理的对象和方法@Signature(type = Executor.class,method = "query" ,args ={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class} ) // 需要代理的对象和方法
})
public class MyPageInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println("简易版的分页插件:逻辑分页改成物理分页");// 修改sql 拼接Limit 0,10Object[] args = invocation.getArgs();// MappedStatement 对mapper映射文件里面元素的封装MappedStatement ms= (MappedStatement) args[0];// BoundSql 对sql和参数的封装Object parameterObject=args[1];BoundSql boundSql = ms.getBoundSql(parameterObject);// RowBounds 封装了逻辑分页的参数 :当前页offset,一页数limitRowBounds rowBounds= (RowBounds) args[2];// 拿到原来的sql语句String sql = boundSql.getSql();String limitSql=sql+ " limit "+rowBounds.getOffset()+","+ rowBounds.getLimit();//将分页sql重新封装一个BoundSql 进行后续执行BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(), limitSql, boundSql.getParameterMappings(), parameterObject);// 被代理的对象Executor executor= (Executor) invocation.getTarget();CacheKey cacheKey = executor.createCacheKey(ms, parameterObject, rowBounds, pageBoundSql);// 调用修改过后的sql继续执行查询return  executor.query(ms,parameterObject,rowBounds, (ResultHandler) args[3],cacheKey,pageBoundSql);}
}

拦截签名跟参数的顺序有严格要求,如果按照顺序找不到对应方法会抛出异常:

org.apache.ibatis.exceptions.PersistenceException: ### Error opening session. Cause: org.apache.ibatis.plugin.PluginException: Could not find method on interface org.apache.ibatis.executor.Executor named query

MyBatis 启动时扫描 标签, 注册到Configuration 对象的 InterceptorChain 中。property 里面的参数,会调用setProperties()方法处理。

分页插件使用

添加pom依赖

<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>1.2.15</version>
</dependency>

插件注册

在mybatis-config.xml 中注册插件

<configuration><plugins><!-- com.github.pagehelper为PageHelper类所在包名 --><plugin interceptor="com.github.pagehelper.PageHelper"><property name="helperDialect" value="mysql" /><!-- 该参数默认为false --><!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 --><!-- 和startPage中的pageNum效果一样 --><property name="offsetAsPageNum" value="true" /><!-- 该参数默认为false --><!-- 设置为true时,使用RowBounds分页会进行count查询 --><property name="rowBoundsWithCount" value="true" /><!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 --><!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型) --><property name="pageSizeZero" value="true" /><!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 --><!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 --><!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 --><property name="reasonable" value="true" /><!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 --><!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 --><!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 --><!-- 不理解该含义的前提下,不要随便复制该配置 --><property name="params" value="pageNum=start;pageSize=limit;" /></plugin></plugins>
</configuration>

调用

// 获取配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis/mybatis-config.xml");
// 通过加载配置文件获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {// Mybatis在getMapper就会给我们创建jdk动态代理EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);PageHelper.startPage(1, 5);List<Emp> list=mapper.selectAll(); PageInfo<ServiceStation> info = new PageInfo<ServiceStation>(list, 3);                   System.out.println("当前页码:"+info.getPageNum());System.out.println("每页的记录数:"+info.getPageSize());System.out.println("总记录数:"+info.getTotal());System.out.println("总页码:"+info.getPages());System.out.println("是否第一页:"+info.isIsFirstPage());System.out.println("连续显示的页码:");int[] nums = info.getNavigatepageNums();for (int i = 0; i < nums.length; i++) {System.out.println(nums[i]);}     
}  

代理和拦截是怎么实现的

上面提到的可以被代理的四大对象都是什么时候被代理的呢?Executor 是openSession() 的时候创建的; StatementHandler 是SimpleExecutor.doQuery()创建的;里面包含了处理参数的ParameterHandler 和处理结果集的ResultSetHandler 的创建,创建之后即调用InterceptorChain.pluginAll(),返回层层代理后的对象。代理是由Plugin 类创建。在我们重写的 plugin() 方法里面可以直接调用returnPlugin.wrap(target, this);返回代理对象。

当个插件的情况下,代理能不能被代理?代理顺序和调用顺序的关系? 可以被代理。

因为代理类是Plugin,所以最后调用的是Plugin 的invoke()方法。它先调用了定义的拦截器的intercept()方法。可以通过invocation.proceed()调用到被代理对象被拦截的方法。

调用流程时序图:

PageHelper 原理

先来看一下分页插件的简单用法:

PageHelper.startPage(1, 3);

List<Blog> blogs = blogMapper.selectBlogById2(blog); PageInfo page = new PageInfo(blogs, 3);

对于插件机制我们上面已经介绍过了,在这里我们自然的会想到其所涉及的核心类 :PageInterceptor。拦截的是Executor 的两个query()方法,要实现分页插件的功能,肯定是要对我们写的sql进行改写,那么一定是在 intercept 方法中进行操作的,我们会发现这么一行代码:

String pageSql = this.dialect.getPageSql(ms, boundSql, parameter, rowBounds, cacheKey);

 调用到 AbstractHelperDialect 中的  getPageSql 方法:

public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {// 获取sqlString sql = boundSql.getSql();//获取分页参数对象Page page = this.getLocalPage();return this.getPageSql(sql, page, pageKey);}

这里可以看到会去调用 this.getLocalPage(),我们来看看这个方法:

public <T> Page<T> getLocalPage() {return PageHelper.getLocalPage();
}
//线程独享
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
public static <T> Page<T> getLocalPage() {return (Page)LOCAL_PAGE.get();
}

可以发现这里是调用的是PageHelper的一个本地线程变量中的一个 Page对象,从其中获取我们所设置的  PageSize 与 PageNum,那么他是怎么设置值的呢?请看:

PageHelper.startPage(1, 3);public static <E> Page<E> startPage(int pageNum, int pageSize) {return startPage(pageNum, pageSize, true);
}public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {Page<E> page = new Page(pageNum, pageSize, count);page.setReasonable(reasonable);page.setPageSizeZero(pageSizeZero);Page<E> oldPage = getLocalPage();if (oldPage != null && oldPage.isOrderByOnly()) {page.setOrderBy(oldPage.getOrderBy());}//设置页数,行数信息setLocalPage(page);return page;
}protected static void setLocalPage(Page page) {//设置值LOCAL_PAGE.set(page);
}

在我们调用 PageHelper.startPage(1, 3); 的时候,系统会调用 LOCAL_PAGE.set(page) 进行设置,从而在分页插件中可以获取到这个本地变量对象中的参数进行 SQL 的改写,由于改写有很多实现,我们这里用的Mysql的实现:

在这里我们会发现分页插件改写SQL的核心代码,这个代码就很清晰了,不必过多赘述:

public String getPageSql(String sql, Page page, CacheKey pageKey) {StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);sqlBuilder.append(sql);if (page.getStartRow() == 0) {sqlBuilder.append(" LIMIT ");sqlBuilder.append(page.getPageSize());} else {sqlBuilder.append(" LIMIT ");sqlBuilder.append(page.getStartRow());sqlBuilder.append(",");sqlBuilder.append(page.getPageSize());pageKey.update(page.getStartRow());}pageKey.update(page.getPageSize());return sqlBuilder.toString();
}

PageHelper 就是这么一步一步的改写了我们的SQL 从而达到一个分页的效果。

关键类总结:

http://www.ritt.cn/news/28754.html

相关文章:

  • 最新深圳设计师建网站网络营销策划书8000字
  • 国家卫生计生委能力建设和继续教育中心网站数据分析培训班
  • 软件编程毕业设计代做网站seo工作流程
  • 网站服务器名是什么seo关键词排名技术
  • 石家庄网站开发培训搜索引擎优化指的是什么
  • 合肥 做网站的trinseo公司
  • dreamweaver网站建设和维护襄阳seo培训
  • 微信代运营的公司网站站长聚集地
  • 网站建设销售实训报告seo关键词优化如何
  • 企业网站建设是什么百度推广登录入口电脑
  • 中国建设银行ie下载网站seo站长优化工具
  • 郑州富士康要搬迁是真的吗广州seo网络推广员
  • 桂林论坛网站有哪些北京网站优化企业
  • 市地政府网站内容建设主管怎么免费建个人网站
  • 网站开发流程龙岩湖人队最新消息
  • 西安商城网站建设企业邮箱如何申请注册
  • 上海网站建设模板站霸网络网络营销ppt讲解
  • 建设网站呼叫中心有什么好处windows优化大师好吗
  • 制作自己的网站代码吗网站快速排名
  • 毕业设计网站建设题目成都百度推广公司电话
  • 万网的网站建设识别关键词软件
  • 提供微网站制作电话文大侠seo博客
  • seo做的好的网站 知乎广告开户南京seo
  • 张家港做网站多少钱搜索引擎分哪三类
  • 政府门户网站建设任务广告投放平台
  • 资讯网站的好处友情链接的网站
  • 网站建设小程序公众号推广开发百度搜索排名优化
  • 免费制作微信小程序平台站长工具seo综合查询 分析
  • java代码做网站正规接单赚佣金的app
  • 建立一个app平台需要多少钱北京百度搜索排名优化