做网站建设注册商标是多少类建设公司网站的好处
做网站建设注册商标是多少类,建设公司网站的好处,房地产网络营销论文,网站开发 模板 c大家好#xff0c;今天我们的主角是MyBatis#xff0c;作为当前国内最流行的ORM框架#xff0c;是我们这些crud选手最趁手的工具#xff0c;赶紧来看看面试都会问哪些问题吧。基础1.说说什么是MyBatis?MyBatis logo先吹一下#xff1a;Mybatis 是一个半 ORM#xff08;对… 大家好今天我们的主角是MyBatis作为当前国内最流行的ORM框架是我们这些crud选手最趁手的工具赶紧来看看面试都会问哪些问题吧。基础1.说说什么是MyBatis?MyBatis logo先吹一下Mybatis 是一个半 ORM对象关系映射框架它内部封装了 JDBC开发时只需要关注 SQL 语句本身不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程。程序员直接编写原生态 sql可以严格控制 sql 执行性能灵活度高。MyBatis 可以使用 XML 或注解来配置和映射原生信息将 POJO 映射成数据库中的记录避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。再说一下缺点SQL语句的编写工作量较大尤其当字段多、关联表多时对开发人员编写SQL语句的功底有一定要求SQL语句依赖于数据库导致数据库移植性差不能随意更换数据库ORM是什么?ORM简单示意图ORMObject Relational Mapping对象关系映射是一种为了解决关系型数据库数据与简单Java对象POJO的映射关系的技术。简单来说ORM是通过使用描述对象和数据库之间映射的元数据将程序中的对象自动持久化到关系型数据库中。为什么说Mybatis是半自动ORM映射工具它与全自动的区别在哪里Hibernate属于全自动ORM映射工具使用Hibernate查询关联对象或者关联集合对象时可以根据对象关系模型直接获取所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时需要手动编写SQL来完成所以被称之为半自动ORM映射工具。JDBC编程有哪些不足之处MyBatis是如何解决的JDBC编程的不足1、数据连接创建、释放频繁造成系统资源浪费从而影响系统性能解决在mybatis-config.xml中配置数据链接池使用连接池统一管理数据库连接。2、sql语句写在代码中造成代码不易维护解决将sql语句配置在XXXXmapper.xml文件中与java代码分离。3、向sql语句传参数麻烦因为sql语句的where条件不一定可能多也可能少占位符需要和参数一一对应。解决Mybatis自动将java对象映射至sql语句。4、对结果集解析麻烦sql变化导致解析代码变化且解析前需要遍历如果能将数据库记录封装成pojo对象解析比较方便。解决Mybatis自动将sql执行结果映射至java对象。2.Hibernate 和 MyBatis 有什么区别PS:直接用Hibernate的应该不多了吧毕竟大家都是“敏捷开发”但架不住面试爱问。相同点都是对jdbc的封装都是应用于持久层的框架。这还用说不同点映射关系MyBatis 是一个半自动映射的框架配置Java对象与sql语句执行结果的对应关系多表关联关系配置简单Hibernate 是一个全表映射的框架配置Java对象与数据库表的对应关系多表关联关系配置复杂SQL优化和移植性Hibernate 对SQL语句封装提供了日志、缓存、级联级联比 MyBatis 强大等特性此外还提供 HQLHibernate Query Language操作数据库数据库无关性支持好但会多消耗性能。如果项目需要支持多种数据库代码开发量少但SQL语句优化困难。MyBatis 需要手动编写 SQL支持动态 SQL、处理列表、动态生成表名、支持存储过程。开发工作量相对大些。直接使用SQL语句操作数据库不支持数据库无关性但sql语句优化容易。MyBatis和Hibernate的适用场景?Mybatis vs HibernateHibernate 是标准的ORM框架SQL编写量较少但不够灵活适合于需求相对稳定中小型的软件项目比如办公自动化系统MyBatis 是半ORM框架需要编写较多SQL但是比较灵活适合于需求变化频繁快速迭代的项目比如电商网站3.MyBatis使用过程生命周期MyBatis基本使用的过程大概可以分为这么几步Mybatis基本使用步骤1、 创建SqlSessionFactory可以从配置或者直接编码来创建SqlSessionFactoryString resource org/mybatis/example/mybatis-config.xml;
InputStream inputStream Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream);2、 通过SqlSessionFactory创建SqlSessionSqlSession会话可以理解为程序和数据库之间的桥梁SqlSession session sqlSessionFactory.openSession();3、 通过sqlsession执行数据库操作可以通过 SqlSession 实例来直接执行已映射的 SQL 语句Blog blog (Blog)session.selectOne(org.mybatis.example.BlogMapper.selectBlog, 101);更常用的方式是先获取Mapper(映射)然后再执行SQL语句BlogMapper mapper session.getMapper(BlogMapper.class);
Blog blog mapper.selectBlog(101);4、 调用session.commit()提交事务如果是更新、删除语句我们还需要提交一下事务。5、 调用session.close()关闭会话最后一定要记得关闭会话。MyBatis生命周期上面提到了几个MyBatis的组件一般说的MyBatis生命周期就是这些组件的生命周期。SqlSessionFactoryBuilder一旦创建了 SqlSessionFactory就不再需要它了。因此 SqlSessionFactoryBuilder 实例的生命周期只存在于方法的内部。SqlSessionFactorySqlSessionFactory 是用来创建SqlSession的相当于一个数据库连接池每次创建SqlSessionFactory都会使用数据库资源多次创建和销毁是对资源的浪费。所以SqlSessionFactory是应用级的生命周期而且应该是单例的。SqlSessionSqlSession相当于JDBC中的ConnectionSqlSession 的实例不是线程安全的因此是不能被共享的所以它的最佳的生命周期是一次请求或一个方法。Mapper映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的它的生命周期在sqlsession事务方法之内一般会控制在方法级。MyBatis主要组件生命周期当然万物皆可集成SpringMyBatis通常也是和Spring集成使用Spring可以帮助我们创建线程安全的、基于事务的 SqlSession 和映射器并将它们直接注入到我们的 bean 中我们不需要关心它们的创建过程和生命周期那就是另外的故事了。ps接下来看看Mybatis的基本使用不会有人不会吧不会吧这个应该会4.在mapper中如何传递多个参数mapper传递多个参数方法方法1顺序传参法public User selectUser(String name, int deptId);select idselectUser resultMapUserResultMapselect * from userwhere user_name #{0} and dept_id #{1}
/select#{}里面的数字代表传入参数的顺序。这种方法不建议使用sql层表达不直观且一旦顺序调整容易出错。方法2Param注解传参法public User selectUser(Param(userName) String name, int Param(deptId) deptId);select idselectUser resultMapUserResultMapselect * from userwhere user_name #{userName} and dept_id #{deptId}
/select#{}里面的名称对应的是注解Param括号里面修饰的名称。这种方法在参数不多的情况还是比较直观的推荐使用。方法3Map传参法public User selectUser(MapString, Object params);select idselectUser parameterTypejava.util.Map resultMapUserResultMapselect * from userwhere user_name #{userName} and dept_id #{deptId}
/select#{}里面的名称对应的是Map里面的key名称。这种方法适合传递多个参数且参数易变能灵活传递的情况。方法4Java Bean传参法public User selectUser(User user);select idselectUser parameterTypecom.jourwon.pojo.User resultMapUserResultMapselect * from userwhere user_name #{userName} and dept_id #{deptId}
/select#{}里面的名称对应的是User类里面的成员属性。这种方法直观需要建一个实体类扩展不容易需要加属性但代码可读性强业务逻辑处理方便推荐使用。推荐使用。5.实体类属性名和表中字段名不一样 怎么办?第1种通过在查询的SQL语句中定义字段名的别名让字段名的别名和实体类的属性名一致。select idgetOrder parameterTypeint resultTypecom.jourwon.pojo.Orderselect order_id id, order_no orderno ,order_price price form orders where order_id#{id};
/select第2种通过resultMap 中的result来映射字段名和实体类属性名的一一对应的关系。select idgetOrder parameterTypeint resultMaporderResultMapselect * from orders where order_id#{id}
/selectresultMap typecom.jourwon.pojo.Order idorderResultMap!–用id属性来映射主键字段–id propertyid columnorder_id!–用result属性来映射非主键字段property为实体类属性名column为数据库表中的属性–result property orderno column order_no/result propertyprice columnorder_price /
/reslutMap6.Mybatis是否可以映射Enum枚举类Mybatis当然可以映射枚举类不单可以映射枚举类Mybatis可以映射任何对象到表的一列上。映射方式为自定义一个TypeHandler实现TypeHandler的setParameter()和getResult()接口方法。TypeHandler有两个作用一是完成从javaType至jdbcType的转换二是完成jdbcType至javaType的转换体现为setParameter()和getResult()两个方法分别代表设置sql问号占位符参数和获取列查询结果。7.#{}和${}的区别?#{}和${}比较#{}是占位符预编译处理${}是拼接符字符串替换没有预编译处理。Mybatis在处理#{}时#{}传入参数是以字符串传入会将SQL中的#{}替换为?号调用PreparedStatement的set方法来赋值。#{} 可以有效的防止SQL注入提高系统安全性${} 不能防止SQL 注入#{} 的变量替换是在DBMS 中${} 的变量替换是在 DBMS 外8.模糊查询like语句该怎么写?concat拼接like1 ’%${question}%’ 可能引起SQL注入不推荐2 %#{question}% 注意因为#{…}解析成sql语句时候会在变量外侧自动加单引号’ 所以这里 % 需要使用双引号 不能使用单引号 ’ 不然会查不到任何结果。3 CONCAT(’%’,#{question},’%’) 使用CONCAT()函数推荐✨4 使用bind标签不推荐select idlistUserLikeUsername resultTypecom.jourwon.pojo.User
emsp;emsp;bind namepattern value% username % /
emsp;emsp;select id,sex,age,username,password from person where username LIKE #{pattern}
/select9.Mybatis能执行一对一、一对多的关联查询吗当然可以不止支持一对一、一对多的关联查询还支持多对多、多对一的关联查询。MyBatis级联一对一association比如订单和支付是一对一的关系这种关联的实现实体类:public class Order {private Integer orderId;private String orderDesc;/*** 支付对象*/private Pay pay;//……
}结果映射!-- 订单resultMap --
resultMap idpeopleResultMap typecn.fighter3.entity.Orderid propertyorderId columnorder_id /result propertyorderDesc columnorder_desc/!--一对一结果映射--association propertypay javaTypecn.fighter3.entity.Payid columnpayId propertypay_id/result columnaccount propertyaccount//association
/resultMap查询就是普通的关联查select idgetTeacher resultMapgetTeacherMap parameterTypeintselect * from order o left join pay p on o.order_idp.order_idwhere o.order_id#{orderId}/select一对多collection比如商品分类和商品是一对多的关系。查询查询就是一个普通的关联查询!-- 关联查询分类和产品表 --select idlistCategory resultMapcategoryBeanselect c.*, p.* from category_ c left join product_ p on c.id p.cid/select实体类public class Category {private int categoryId;private String categoryName;/*** 商品列表**/ListProduct products;//……
}结果映射resultMap typeCategory idcategoryBeanid columncategoryId propertycategory_id /result columncategoryName propertycategory_name /!-- 一对多的关系 --!-- property: 指的是集合属性的值, ofType指的是集合中元素的类型 --collection propertyproducts ofTypeProductid columnproduct_id propertyproductId /result columnproductName propertyproductName /result columnprice propertyprice //collection/resultMap那么多对一、多对多怎么实现呢还是利用association和collection篇幅所限这里就不展开了。10.Mybatis是否支持延迟加载原理Mybatis支持association关联对象和collection关联集合对象的延迟加载association指的就是一对一collection指的就是一对多查询。在Mybatis配置文件中可以配置是否启用延迟加载lazyLoadingEnabledtrue|false。它的原理是使用CGLIB创建目标对象的代理对象当调用目标方法时进入拦截器方法比如调用a.getB().getName()拦截器invoke()方法发现a.getB()是null值那么就会单独发送事先保存好的查询关联B对象的sql把B查询上来然后调用a.setB(b)于是a的对象b属性就有值了接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。当然了不光是Mybatis几乎所有的包括Hibernate支持延迟加载的原理都是一样的。11.如何获取生成的主键?新增标签中添加keyProperty ID 即可insert idinsert useGeneratedKeystrue keyPropertyuserId insert into user( user_name, user_password, create_time) values(#{userName}, #{userPassword} , #{createTime, jdbcType TIMESTAMP})
/insert这时候就可以完成回填主键mapper.insert(user);
user.getId;12.MyBatis支持动态SQL吗MyBatis中有一些支持动态SQL的标签它们的原理是使用OGNL从SQL参数对象中计算表达式的值根据表达式的值动态拼接SQL以此来完成动态SQL的功能。![if根据条件来组成where子句select idfindActiveBlogWithTitleLikeresultTypeBlogSELECT * FROM BLOGWHERE state ‘ACTIVE’if testtitle ! nullAND title like #{title}/if
/selectchoose (when, otherwise)这个和Java 中的 switch 语句有点像select idfindActiveBlogLikeresultTypeBlogSELECT * FROM BLOG WHERE state ‘ACTIVE’choosewhen testtitle ! nullAND title like #{title}/whenwhen testauthor ! null and author.name ! nullAND author_name like #{author.name}/whenotherwiseAND featured 1/otherwise/choose
/selecttrim (where, set)select idfindActiveBlogLikeresultTypeBlogSELECT * FROM BLOGwhereif teststate ! nullstate #{state}/ifif testtitle ! nullAND title like #{title}/ifif testauthor ! null and author.name ! nullAND author_name like #{author.name}/if/where
/selectset 可以用在动态更新的时候update idupdateAuthorIfNecessaryupdate Authorsetif testusername ! nullusername#{username},/ifif testpassword ! nullpassword#{password},/ifif testemail ! nullemail#{email},/ifif testbio ! nullbio#{bio}/if/setwhere id#{id}
/updatewhere可以用在所有的查询条件都是动态的情况foreach看到名字就知道了这个是用来循环的可以对集合进行遍历select idselectPostIn resultTypedomain.blog.PostSELECT *FROM POST Pwhereforeach itemitem indexindex collectionlistopenID in ( separator, close) nullabletrue#{item}/foreach/where
/select13.MyBatis如何执行批量操作MyBatis批量操作第一种方法使用foreach标签foreach的主要用在构建in条件中它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有itemindexcollectionopenseparatorclose。item 表示集合中每一个元素进行迭代时的别名随便起的变量名index 指定一个名字用于表示在迭代过程中每次迭代到的位置不常用open 表示该语句以什么开始常用“(”separator 表示在每次进行迭代之间以什么符号作为分隔符常用“,”close 表示以什么结束常用“)”。在使用foreach的时候最关键的也是最容易出错的就是collection属性该属性是必须指定的但是在不同情况下该属性的值是不一样的主要有以下3种情况如果传入的是单参数且参数类型是一个List的时候collection属性值为list如果传入的是单参数且参数类型是一个array数组的时候collection的属性值为array如果传入的参数是多个的时候我们就需要把它们封装成一个Map了当然单参数也可以封装成map实际上如果你在传入参数的时候在MyBatis里面也是会把它封装成一个Map的 map的key就是参数名所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key看看批量保存的两种用法!-- MySQL下批量保存可以foreach遍历 mysql支持values(),(),()语法 -- //推荐使用
insert idaddEmpsBatchINSERT INTO emp(ename,gender,email,did)VALUESforeach collectionemps itememp separator,(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})/foreach
/insert!-- 这种方式需要数据库连接属性allowMutiQueriestrue的支持如jdbc.urljdbc:mysql://localhost:3306/mybatis?allowMultiQueriestrue --
insert idaddEmpsBatchforeach collectionemps itememp separator; INSERT INTO emp(ename,gender,email,did)VALUES(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})/foreach
/insert第二种方法使用ExecutorType.BATCHMybatis内置的ExecutorType有3种默认为simple该模式下它为每个语句的执行创建一个新的预处理语句单条提交sql而batch模式重复使用已经预处理的语句并且批量执行所有更新语句显然batch性能将更优但batch模式也有自己的问题比如在Insert操作时在事务没有提交之前是没有办法获取到自增的id在某些情况下不符合业务的需求。具体用法如下//批量保存方法测试
Test
public void testBatch() throws IOException{SqlSessionFactory sqlSessionFactory getSqlSessionFactory();//可以执行批量操作的sqlSessionSqlSession openSession sqlSessionFactory.openSession(ExecutorType.BATCH);//批量保存执行前时间long start System.currentTimeMillis();try {EmployeeMapper mapper openSession.getMapper(EmployeeMapper.class);for (int i 0; i 1000; i) {mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), b, 1));}openSession.commit();long end System.currentTimeMillis();//批量保存执行后的时间System.out.println(执行时长 (end - start));//批量 预编译sql一次》设置参数》10000次》执行1次 677//非批量 预编译设置参数执行 》10000次 1121} finally {openSession.close();}
}mapper和mapper.xml如下public interface EmployeeMapper { //批量保存员工Long addEmp(Employee employee);
}mapper namespacecom.jourwon.mapper.EmployeeMapper!--批量保存员工 --insert idaddEmpinsert into employee(lastName,email,gender)values(#{lastName},#{email},#{gender})/insert
/mapper14.说说Mybatis的一级、二级缓存一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存其存储作用域为SqlSession各个SqlSession之间的缓存相互隔离当 Session flush 或 close 之后该 SqlSession 中的所有 Cache 就将清空MyBatis默认打开一级缓存。Mybatis一级缓存二级缓存与一级缓存其机制相同默认也是采用 PerpetualCacheHashMap 存储不同之处在于其存储作用域为 Mapper(Namespace)可以在多个SqlSession之间共享并且可自定义存储源如 Ehcache。默认不打开二级缓存要开启二级缓存使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置。Mybatis二级缓存示意图原理15.能说说MyBatis的工作原理吗我们已经大概知道了MyBatis的工作流程按工作原理可以分为两大步生成会话工厂、会话运行。MyBatis的工作流程MyBatis是一个成熟的框架篇幅限制这里抓大放小来看看它的主要工作流程。构建会话工厂构造会话工厂也可以分为两步构建会话工厂获取配置获取配置这一步经过了几步转化最终由生成了一个配置类Configuration实例这个配置类实例非常重要主要作用包括读取配置文件包括基础配置文件和映射文件初始化基础配置比如MyBatis的别名还有其它的一些重要的类对象像插件、映射器、ObjectFactory等等提供一个单例作为会话工厂构建的重要参数它的构建过程也会初始化一些环境变量比如数据源public SqlSessionFactory build(Reader reader, String environment, Properties properties) {SqlSessionFactory var5;//省略异常处理//xml配置构建器XMLConfigBuilder parser new XMLConfigBuilder(reader, environment, properties);//通过转化的Configuration构建SqlSessionFactoryvar5 this.build(parser.parse());}构建SqlSessionFactorySqlSessionFactory只是一个接口构建出来的实际上是它的实现类的实例一般我们用的都是它的实现类DefaultSqlSessionFactorypublic SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}会话运行会话运行是MyBatis最复杂的部分它的运行离不开四大组件的配合MyBatis会话运行四大关键组件Executor执行器Executor起到了至关重要的作用SqlSession只是一个门面相当于客服真正干活的是是Executor就像是默默无闻的工程师。它提供了相应的查询和更新方法以及事务方法。Environment environment this.configuration.getEnvironment();TransactionFactory transactionFactory this.getTransactionFactoryFromEnvironment(environment);tx transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);//通过Configuration创建executorExecutor executor this.configuration.newExecutor(tx, execType);var8 new DefaultSqlSession(this.configuration, executor, autoCommit);StatementHandler数据库会话器StatementHandler顾名思义处理数据库会话的。我们以SimpleExecutor为例看一下它的查询方法先生成了一个StatementHandler实例再拿这个handler去执行query。public E ListE doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt null;List var9;try {Configuration configuration ms.getConfiguration();StatementHandler handler configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt this.prepareStatement(handler, ms.getStatementLog());var9 handler.query(stmt, resultHandler);} finally {this.closeStatement(stmt);}return var9;}再以最常用的PreparedStatementHandler看一下它的query方法其实在上面的prepareStatement已经对参数进行了预编译处理到了这里就直接执行sql使用ResultHandler处理返回结果。public E ListE query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps (PreparedStatement)statement;ps.execute();return this.resultSetHandler.handleResultSets(ps);}ParameterHandler 参数处理器PreparedStatementHandler里对sql进行了预编译处理public void parameterize(Statement statement) throws SQLException {this.parameterHandler.setParameters((PreparedStatement)statement);}这里用的就是ParameterHandlersetParameters的作用就是设置预编译SQL语句的参数。里面还会用到typeHandler类型处理器对类型进行处理。public interface ParameterHandler {Object getParameterObject();void setParameters(PreparedStatement var1) throws SQLException;
}ResultSetHandler结果处理器我们前面也看到了最后的结果要通过ResultSetHandler来进行处理handleResultSets这个方法就是用来包装结果集的。Mybatis为我们提供了一个DefaultResultSetHandler通常都是用这个实现类去进行结果的处理的。public interface ResultSetHandler {E ListE handleResultSets(Statement var1) throws SQLException;E CursorE handleCursorResultSets(Statement var1) throws SQLException;void handleOutputParameters(CallableStatement var1) throws SQLException;
}它会使用typeHandle处理类型然后用ObjectFactory提供的规则组装对象返回给调用者。整体上总结一下会话运行会话运行的简单示意图PS以上源码分析比较简单在真正的源码大佬面前可能过不了关有条件的建议Debug一下MyBatis的源码。我们最后把整个的工作流程串联起来简单总结一下MyBatis整体工作原理图读取 MyBatis 配置文件——mybatis-config.xml 、加载映射文件——映射文件即 SQL 映射文件文件中配置了操作数据库的 SQL 语句。最后生成一个配置对象。构造会话工厂通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。创建会话对象由会话工厂创建 SqlSession 对象该对象中包含了执行 SQL 语句的所有方法。Executor 执行器MyBatis 底层定义了一个 Executor 接口来操作数据库它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句同时负责查询缓存的维护。StatementHandler数据库会话器串联起参数映射的处理和运行结果映射的处理。参数处理对输入参数的类型进行处理并预编译。结果处理对返回结果的类型进行处理根据对象映射规则返回相应的对象。16.MyBatis的功能架构是什么样的MyBatis功能架构我们一般把Mybatis的功能架构分为三层API接口层提供给外部使用的接口API开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。数据处理层负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。基础支撑层负责最基础的功能支撑包括连接管理、事务管理、配置加载和缓存处理这些都是共用的东西将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。17.为什么Mapper接口不需要实现类四个字回答动态代理我们来看一下获取Mapper的过程Mapper代理获取Mapper我们都知道定义的Mapper接口是没有实现类的Mapper映射其实是通过动态代理实现的。BlogMapper mapper session.getMapper(BlogMapper.class);七拐八绕地进去看一下发现获取Mapper的过程需要先获取MapperProxyFactory——Mapper代理工厂。public T T getMapper(ClassT type, SqlSession sqlSession) {MapperProxyFactoryT mapperProxyFactory (MapperProxyFactory)this.knownMappers.get(type);if (mapperProxyFactory null) {throw new BindingException(Type type is not known to the MapperRegistry.);} else {try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception var5) {throw new BindingException(Error getting mapper instance. Cause: var5, var5);}}}public class MapperProxyFactoryT {private final ClassT mapperInterface;……protected T newInstance(MapperProxyT mapperProxy) {return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);}public T newInstance(SqlSession sqlSession) {MapperProxyT mapperProxy new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);return this.newInstance(mapperProxy);}
}这里可以看到动态代理对接口的绑定它的作用就是生成动态代理对象占位而代理的方法被放到了MapperProxy中。MapperProxy里通常会生成一个MapperMethod对象它是通过cachedMapperMethod方法对其进行初始化的然后执行excute方法。public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);} catch (Throwable var5) {throw ExceptionUtil.unwrapThrowable(var5);}}MapperMethodMapperMethod里的excute方法会真正去执行sql。这里用到了命令模式其实绕一圈最终它还是通过SqlSession的实例去运行对象的sql。public Object execute(SqlSession sqlSession, Object[] args) {Object result;Object param;……case SELECT:if (this.method.returnsVoid() this.method.hasResultHandler()) {this.executeWithResultHandler(sqlSession, args);result null;} else if (this.method.returnsMany()) {result this.executeForMany(sqlSession, args);} else if (this.method.returnsMap()) {result this.executeForMap(sqlSession, args);} else if (this.method.returnsCursor()) {result this.executeForCursor(sqlSession, args);} else {param this.method.convertArgsToSqlCommandParam(args);result sqlSession.selectOne(this.command.getName(), param);if (this.method.returnsOptional() (result null || !this.method.getReturnType().equals(result.getClass()))) {result Optional.ofNullable(result);}}break;……}MapperProxyMapperProxyFactoryMapperProxyFactory的作用是生成MapperProxyMapper代理对象。18.Mybatis都有哪些Executor执行器Mybatis Executor类型Mybatis有三种基本的Executor执行器SimpleExecutor、ReuseExecutor、BatchExecutor。SimpleExecutor每执行一次update或select就开启一个Statement对象用完立刻关闭Statement对象。ReuseExecutor执行update或select以sql作为key查找Statement对象存在就使用不存在就创建用完后不关闭Statement对象而是放置于MapString, Statement内供下一次使用。简言之就是重复使用Statement对象。BatchExecutor执行update没有selectJDBC批处理不支持select将所有sql都添加到批处理中addBatch()等待统一执行executeBatch()它缓存了多个Statement对象每个Statement对象都是addBatch()完毕后等待逐一执行executeBatch()批处理。与JDBC批处理相同。作用范围Executor的这些特点都严格限制在SqlSession生命周期范围内。Mybatis中如何指定使用哪一种Executor执行器在Mybatis配置文件中在设置settings可以指定默认的ExecutorType执行器类型也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数如SqlSession openSession(ExecutorType execType)。配置默认的执行器。SIMPLE 就是普通的执行器REUSE 执行器会重用预处理语句prepared statementsBATCH 执行器将重用语句并执行批量更新。插件19.说说Mybatis的插件运行原理如何编写一个插件插件的运行原理Mybatis会话的运行需要ParameterHandler、ResultSetHandler、StatementHandler、Executor这四大对象的配合插件的原理就是在这四大对象调度的时候插入一些我我们自己的代码。MyBatis插件原理简图Mybatis使用JDK的动态代理为目标对象生成代理对象。它提供了一个工具类Plugin实现了InvocationHandler接口。Plugin中调用插件方法使用Plugin生成代理对象代理对象在调用方法的时候就会进入invoke方法在invoke方法中如果存在签名的拦截方法插件的intercept方法就会在这里被我们调用然后就返回结果。如果不存在签名方法那么将直接反射调用我们要执行的方法。如何编写一个插件我们自己编写MyBatis 插件只需要实现拦截器接口 Interceptor (org.apache.ibatis. plugin Interceptor 在实现类中对拦截对象和方法进行处理。实现Mybatis的Interceptor接口并重写intercept()方法这里我们只是在目标对象执行目标方法的前后进行了打印public class MyInterceptor implements Interceptor {Properties propsnull;Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println(before……);//如果当前代理的是一个非代理对象那么就会调用真实拦截对象的方法// 如果不是它就会调用下个插件代理对象的invoke方法Object objinvocation.proceed();System.out.println(after……);return obj;}
}然后再给插件编写注解确定要拦截的对象要拦截的方法Intercepts({Signature(type Executor.class, //确定要拦截的对象method update, //确定要拦截的方法args {MappedStatement.class,Object.class} //拦截方法的参数
)})
public class MyInterceptor implements Interceptor {Properties propsnull;Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println(before……);//如果当前代理的是一个非代理对象那么就会调用真实拦截对象的方法// 如果不是它就会调用下个插件代理对象的invoke方法Object objinvocation.proceed();System.out.println(after……);return obj;}
}最后再MyBatis配置文件里面配置插件pluginsplugin interceptorxxx.MyPluginproperty namedbType,valuemysql//plugin
/plugins20.MyBatis是如何进行分页的分页插件的原理是什么MyBatis是如何分页的MyBatis使用RowBounds对象进行分页它是针对ResultSet结果集执行的内存分页而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能也可以使用分页插件来完成物理分页。分页插件的原理是什么分页插件的基本原理是使用Mybatis提供的插件接口实现自定义插件拦截Executor的query方法在执行查询的时候拦截待执行的sql然后重写sql根据dialect方言添加对应的物理分页语句和物理分页参数。举例select * from student拦截sql后重写为select t.* from (select * from student) t limit 0, 10可以看一下一个大概的MyBatis通用分页拦截器Mybatis-通用分页拦截器参考[1]. MyBatis面试题2020最新版https://blog.csdn.net/ThinkWon/article/details/101292950)[2].mybatis官网https://mybatis.org/mybatis-3/zh/index.html[3].《深入浅出MyBatis基础原理与实战》[4].聊聊MyBatis缓存机制https://tech.meituan.com/2018/01/19/mybatis-cache.html[5].《MyBatis从入门到精通》
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/90865.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!