2025年高频mybatis的面试题及答案_第1页
2025年高频mybatis的面试题及答案_第2页
2025年高频mybatis的面试题及答案_第3页
2025年高频mybatis的面试题及答案_第4页
2025年高频mybatis的面试题及答案_第5页
已阅读5页,还剩16页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2025年高频mybatis的面试题及答案MyBatis的核心配置文件中,environment标签的作用是什么?它内部包含哪些关键子元素?environment标签用于配置MyBatis的运行环境,支持多环境切换(如开发、测试、生产)。其内部包含两个关键子元素:transactionManager(事务管理器)和dataSource(数据源)。transactionManager指定事务管理方式,MyBatis提供了JDBC(使用java.sql.Connection管理事务)和MANAGED(由容器管理事务,如Spring)两种实现;dataSource配置数据源信息,支持UNPOOLED(非连接池,每次请求新建连接)、POOLED(连接池,通过复用连接提升性能)和JNDI(从应用服务器获取数据源)三种类型。实际开发中,与Spring集成时,事务管理通常由Spring的PlatformTransactionManager接管,此时environment的transactionManager配置会被覆盖。{}和${}的本质区别是什么?什么场景下必须使用${}?{}是预编译处理,MyBatis会将其替换为占位符?,并通过PreparedStatement的参数绑定传递值,能有效防止SQL注入;${}是字符串拼接,直接将参数值插入SQL语句,存在SQL注入风险。必须使用${}的场景包括:动态表名或列名(如根据参数动态指定排序字段)、存储过程参数(部分数据库要求存储过程参数必须用字符串拼接)、原生SQL片段拼接(如动态拼接UNIONALL的表名)。例如,当需要根据用户传入的字段名排序时,ORDERBY${sortField}是唯一选择,但需在业务层对sortField进行严格的白名单校验,避免注入。动态SQL中<if>、<where>、<set>标签的协作逻辑是怎样的?举例说明常见错误。<if>标签用于条件判断,当test属性为true时包含内部SQL片段;<where>标签会自动添加WHERE关键字,并移除子元素中第一个AND/OR前缀(如<iftest="name!=null">ANDname={name}</if>会被处理为WHEREname={name});<set>标签用于UPDATE语句,自动添加SET关键字,并移除子元素中最后一个逗号(如<iftest="age!=null">age={age},</if>会被处理为SETage={age})。常见错误包括:在<where>标签内直接写条件而不加AND/OR(如<iftest="name!=null">name={name}</if>,会导致WHEREname={name},但如果没有条件满足时,<where>标签会被移除,避免提供"WHERE"空语句);在<set>标签内最后一个条件仍保留逗号(如<iftest="age!=null">age={age},</if><iftest="name!=null">name={name},</if>,此时<set>会自动删除最后一个逗号,但若所有条件都不满足,会导致SET后无内容,引发SQL错误,需配合<if>标签确保至少有一个条件成立)。MyBatis一级缓存和二级缓存的作用域、失效条件分别是什么?分布式环境下二级缓存需要注意什么?一级缓存(LocalCache)作用域为SqlSession,默认开启。当执行以下操作时缓存失效:调用SqlSession的update()、insert()、delete()方法(任何写操作);调用SqlSession.clearCache()手动清空;SqlSession关闭。二级缓存作用域为Mapper(namespace),需在全局配置中开启cacheEnabled=true,并在Mapper.xml中添加<cache/>标签(或通过@CacheNamespace注解)。其失效条件为:同一namespace内执行写操作(insert/update/delete),MyBatis会清空该namespace的二级缓存;手动调用Cache的clear()方法。分布式环境下使用二级缓存需注意:缓存数据需实现Serializable接口(MyBatis默认通过JDK序列化存储);不同节点的缓存需保持一致性(可通过Redis等分布式缓存替代MyBatis自带的二级缓存,避免缓存击穿/穿透);需控制缓存的过期时间(防止脏数据长期存在);高并发场景下需考虑缓存的并发控制(如使用Redis的分布式锁)。MyBatis如何实现多表关联查询?嵌套查询和嵌套结果映射的优缺点分别是什么?多表关联查询可通过两种方式实现:嵌套查询(通过association或collection标签的select属性调用另一个Mapper的查询)和嵌套结果映射(通过columnPrefix和多表JOIN后映射到对象)。嵌套查询的优点是SQL拆分清晰,适合复杂的分层数据(如用户-订单-商品三级嵌套),缺点是可能引发N+1问题(主查询1次,嵌套查询N次);嵌套结果映射的优点是通过一次JOIN查询完成,避免N+1,缺点是SQL复杂度高(需处理表别名、重复字段),且结果集过大时可能影响性能。例如,查询用户及其订单,嵌套查询写法为:```xml<selectid="getUser"resultMap="userMap">SELECTFROMuserWHEREid={id}</select><resultMapid="userMap"type="User"><collectionproperty="orders"column="id"select="getOrdersByUserId"/></resultMap><selectid="getOrdersByUserId"resultType="Order">SELECTFROMorderWHEREuser_id={userId}</select>```嵌套结果映射写法为:```xml<selectid="getUserWithOrders"resultMap="userOrderMap">SELECTu.,o.idasorder_id,o.amountasorder_amountFROMuseruLEFTJOINorderoONu.id=o.user_idWHEREu.id={id}</select><resultMapid="userOrderMap"type="User"><collectionproperty="orders"ofType="Order"columnPrefix="order_"><idproperty="id"column="id"/><resultproperty="amount"column="amount"/></collection></resultMap>```MyBatis插件的实现原理是什么?如何自定义一个统计SQL执行耗时的插件?MyBatis插件基于动态代理和责任链模式,通过拦截Executor、ParameterHandler、ResultSetHandler、StatementHandler四大核心接口的方法来实现功能扩展。自定义插件需实现Interceptor接口,重写intercept()、plugin()、setProperties()方法,并通过@Intercepts注解指定拦截的方法签名。例如,统计SQL执行耗时的插件可拦截StatementHandler的prepare()方法(该方法负责创建PreparedStatement并执行SQL):```java@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class,Integer.class})})publicclassSqlCostPluginimplementsInterceptor{@OverridepublicObjectintercept(Invocationinvocation)throwsThrowable{StatementHandlerstatementHandler=(StatementHandler)invocation.getTarget();//通过反射获取原始的BoundSql(包含SQL语句和参数)BoundSqlboundSql=statementHandler.getBoundSql();Stringsql=boundSql.getSql().replaceAll("\\s+","");longstart=System.currentTimeMillis();try{returnceed();//执行原始方法(即执行SQL)}finally{longcost=System.currentTimeMillis()start;System.out.printf("SQL执行耗时:%dms,SQL:%s%n",cost,sql);}}@OverridepublicObjectplugin(Objecttarget){returnPlugin.wrap(target,this);//提供代理对象}@OverridepublicvoidsetProperties(Propertiesproperties){//读取配置参数(如慢SQL阈值)}}```需注意:插件的拦截顺序由@Intercepts注解的声明顺序决定;多个插件会形成代理链,执行顺序为插件注册顺序的反序;拦截StatementHandler时,需通过反射获取被代理的目标对象(因为MyBatis内部可能对StatementHandler进行了多层代理)。MyBatis与Spring集成的核心组件有哪些?SqlSessionTemplate的作用是什么?集成核心组件包括:SqlSessionFactoryBean(用于创建SqlSessionFactory,通过dataSource、configLocation、mapperLocations等属性配置)、SqlSessionTemplate(线程安全的SqlSession实现,替代原生的SqlSession)、MapperFactoryBean(用于将Mapper接口提供代理对象,注入Spring容器)。SqlSessionTemplate的作用:封装原生SqlSession,实现线程安全(通过Spring的事务管理获取当前线程绑定的SqlSession);支持声明式事务(与@Transactional配合,自动管理SqlSession的生命周期);提供与SqlSession相同的API(如selectOne()、insert()),但内部通过代理处理异常(将MyBatis的PersistenceException转换为Spring的DataAccessException)。当使用@MapperScan注解时,Spring会为每个Mapper接口提供MapperFactoryBean实例,该实例通过SqlSessionTemplate获取Mapper代理对象(本质是调用SqlSession.getMapper())。MyBatis如何处理大字段(如CLOB、BLOB)?TypeHandler的自定义步骤是怎样的?MyBatis通过TypeHandler处理Java类型与JDBC类型的映射。对于CLOB字段,默认使用ClobTypeHandler(将Java的String或Clob类型映射到数据库的CLOB);BLOB字段默认使用BlobTypeHandler(映射Java的byte[]或Blob类型)。当需要自定义类型映射时(如将Java的LocalDateTime映射到数据库的TIMESTAMP),需:1.实现TypeHandler接口(或继承BaseTypeHandler);2.重写setParameter()(设置PreparedStatement参数)、getResult()(从ResultSet或CallableStatement获取结果)方法;3.在MyBatis配置中注册该TypeHandler(通过<typeHandlers>标签或@MappedTypes、@MappedJdbcTypes注解)。例如,自定义LocalDateTimeTypeHandler:```java@MappedTypes(LocalDateTime.class)@MappedJdbcTypes(JdbcType.TIMESTAMP)publicclassLocalDateTimeTypeHandlerextendsBaseTypeHandler<LocalDateTime>{@OverridepublicvoidsetNonNullParameter(PreparedStatementps,inti,LocalDateTimeparameter,JdbcTypejdbcType)throwsSQLException{ps.setTimestamp(i,Timestamp.valueOf(parameter));}@OverridepublicLocalDateTimegetNullableResult(ResultSetrs,StringcolumnName)throwsSQLException{Timestamptimestamp=rs.getTimestamp(columnName);returntimestamp!=null?timestamp.toLocalDateTime():null;}//重写其他getResult方法(如通过列索引、CallableStatement获取)}```然后在mybatis-config.xml中注册:```xml<typeHandlers><typeHandlerhandler="com.example.LocalDateTimeTypeHandler"/></typeHandlers>```或在SpringBoot中通过mybatis.type-handlers-package配置包扫描。MyBatis的批量操作如何实现?哪种方式性能最优?MyBatis批量操作有三种实现方式:1.原生SqlSession的批量执行:通过SqlSessionFactory.openSession(ExecutorType.BATCH)获取批量执行的SqlSession,多次调用insert/update/delete后统一提交。优点是代码简单,缺点是需手动管理SqlSession生命周期,且无法直接使用Mapper接口(需通过SqlSession的update()方法传入statementId)。2.使用<foreach>标签拼接批量SQL:在Mapper.xml中使用<foreachcollection="list"item="item">标签提供多条INSERT/UPDATE语句(如INSERTINTOtable(a,b)VALUES({item.a},{item.b}))。优点是无需特殊配置,缺点是SQL长度受数据库限制(如MySQL的max_allowed_packet),且可能影响可读性。3.MyBatis-Plus的批量方法:如saveBatch()、updateBatchById(),内部通过拼接批量SQL或使用批量执行器。优点是简化代码,缺点是依赖MyBatis-Plus库。性能对比:原生批量执行(ExecutorType.BATCH)最优,因为它通过JDBC的addBatch()和executeBatch()方法减少网络交互次数(多次操作一次提交);<foreach>拼接SQL次之(需提供一条长SQL,数据库一次解析);逐条执行最差(每次操作都需网络IO和SQL解析)。例如,使用批量执行器的代码:```javaSqlSessionsqlSession=sqlSessionFactory.openSession(ExecutorType.BATCH);UserMapperuserMapper=sqlSession.getMapper(UserMapper.class);list.forEach(user->userMapper.insert(user));sqlSmit();sqlSession.close();```MyBatis的延迟加载(懒加载)是如何实现的?需要满足哪些条件?延迟加载通过动态代理实现,MyBatis为关联对象提供代理类(默认使用Javassist,可配置为CGLIB),当首次访问关联对象的方法时(如调用user.getOrders().size()),代理类会触发实际的SQL查询。实现条件:1.全局配置中开启延迟加载(lazyLoadingEnabled=true)和积极延迟加载关闭(aggressiveLazyLoading=false,3.4.1+版本默认关闭,防止访问主对象任意属性时触发所有延迟加载);2.关联映射(association/collection)中设置fetchType="lazy"(或全局配置defaultFetchType="lazy");3.SqlSession需在调用延迟加载方法时保持开启状态(与Spring集成时,通过OpenSessionInView模式保持SqlSession生命周期)。例如,配置用户与订单的懒加载:```xml<resultMapid="userLazyMap"type="User"><collectionproperty="orders"column="id"select="getOrdersByUserId"fetchType="lazy"/></resultMap>```当调用user.getOrders()时,返回的是代理对象,实际查询在第一次访问orders的属性(如orders.get(0).getAmount())时触发。MyBatis的SQL执行流程是怎样的?核心组件的作用分别是什么?执行流程:1.客户端调用Mapper接口方法;2.Mapper代理对象(由ProxyFactory提供)调用SqlSession的对应方法(如selectOne());3.SqlSession通过Executor(默认SimpleExecutor,可配置为ReuseExecutor/BatchExecutor)执行SQL;4.Executor根据SQL的类型(查询/更新)调用StatementHandler;5.StatementHandler通过ParameterHandler设置SQL参数(处理{}占位符);6.执行JDBCStatement获取结果集;7.ResultSetHandler将结果集映射为Java对象(使用TypeHandler和ResultMap);8.结果返回客户端,同时将结果存入一级缓存(查询场景)。核心组件作用:SqlSession:提供数据库操作的核心API,管理Executor和事务。Executor:负责SQL的执行和缓存管理(一级/二级缓存),有三种实现:Simple(逐条执行)、Reuse(重用PreparedStatement)、Batch(批量执行)。StatementHandler:封装JDBCStatement的创建和执行,有四种实现(SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler、RoutingStatementHandler)。ParameterHandler:处理SQL参数绑定,将Java对象转换为JDBC类型。ResultSetHandler:处理结果集映射,将ResultSet转换为Java对象。Configuration:保存MyBatis的全局配置(如别名、插件、映射器),是所有组件的核心配置源。MyBatis在分布式事务中的处理方式是什么?与Seata集成时需要注意什么?MyBatis本身不直接支持分布式事务,需依赖外部事务管理器(如Seata、Atomikos)。与Seata集成时,MyBatis的数据源需通过Seata的DataSourceProxy包装,Seata会通过代理拦截数据库操作,记录回滚日志并参与全局事务协调。注意事项:1.需添加Seata的MyBatisStarter依赖(如seata-spring-boot-starter);2.配置Seata的事务组(transactionServiceGroup)与服务注册中心对应;3.使用@GlobalTransactional注解标记全局事务入口方法;4.确保所有参与全局事务的数据源都被DataSourceProxy代理(避免遗漏导致事务不一致);5.注意MyBatis的二级缓存可能与Seata的回滚日志冲突(需关闭二级缓存或使用分布式缓存并设置合理的过期时间)。MyBatis3.5.x版本后有哪些重要改进?对开发有什么影响?3.5.x版本后的重要改进包括:支持Java8的时间类型(LocalDateTime、LocalDate等),默认通过LocalDateTimeTypeHandler等处理,无需手动配置。RowBounds的改进:原生RowBounds(内存分页)标记为@Deprecated,推荐使用PageHelper等插件或数据库原生分页(如MySQL的LIMIT)。增强的TypeHandler:支持更灵活的类型映射,如通过@TypeHandler注解在ResultMap中指定特定字段的TypeHandler。批量插入的优化:支持更高效的批量插入语法(如IN

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论