Mybatis用的什么连接池?

典型回答 Mybatis内置了三种数据源,分别是Pooled,Unpooled和JNDI,其中Pooled数据源是具有连接池的。同时Mybatis也可以使用三方数据源,如Druid,Hikari,C3P0等等 Mybatis数据源的类图如下所示: 可以看到,在Mybatis中,会通过工厂模式来获得对应的数据源,那么Mybatis是在执行的哪一步获取的呢? 答案是在执行SQL之前,Mybatis会获取数据库连接Connection,而此时获得的Connection则是应用的启动的时候,已经通过配置项中的文件加载到内存中了: 1 2 3 4 5 6 <dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSource"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> 一般情况下,我们不会使用Mybatis默认的PooledDataSource,而是会用Hikari,如果要增加Sql监控功能的话,也可以使用Druid,这是因为自带的数据库连接池有三个缺点: 空闲连接占用资源:连接池维护一定数量的空闲连接,这些连接会占用系统的资源,如果连接池设置过大,那么会浪费系统资源,如果设置过小,则会导致系统并发请求时连接不够用,影响系统性能。 连接池大小调优困难:连接池的大小设置需要根据系统的并发请求量、数据库的性能和系统的硬件配置等因素综合考虑,而这些因素都是难以预测和调整的。 连接泄漏:如果应用程序没有正确关闭连接,那么连接池中的连接就会泄漏,导致连接池中的连接数量不断增加,最终导致系统崩溃。 总的来说,专业的事情交给专业的组件来做,Mybatis功能的核心是ORM映射和缓存,数据库连接池这种东西,市场上已经有比它做的更好的,我们直接用那些更好的就行了。 知识扩展 什么是数据源,数据库和数据库连接池? 一般应用程序在连接数据库的时候,会有三步: 输入url,name,pwd产生一个connection 然后在这个connection中完成对应的sql操作 完成事务的提交或者回滚 为了对这三步进行抽象,诞生了数据源的概念,一般被定义为DataSource。数据源负责和实体数据库的连接,如(内存数据库,mysql等),所以数据源是被第三方数据库实现的。同时,因为通过数据源对数据库操作做了抽象,我们也可以在数据源中完成对数据库连接的池化,这就是数据库连接池。 借用javaDoc对DataSource的注释: 数据源用于连接到此DataSource对象所表示的物理数据源的工厂。作为DriverManager功能的替代方案,DataSource对象是获取连接的首选方式。实现DataSource接口的对象通常会向基于Java的命名服务注册™ 命名和目录(JNDI)API。 DataSource接口由驱动程序供应商实现。有三种类型的实现: 基本实现–生成一个标准Connection对象 连接池实现–生成一个Connection对象,该对象将自动参与连接池。此实现与中间层连接池管理器一起工作。 分布式事务实现–生成一个Connection对象,该对象可以用于分布式事务,并且几乎总是参与连接池。此实现使用中间层事务管理器,并且几乎总是使用连接池

March 22, 2026 · 1 min · santu

Mybatis的工作原理?

****典型回答 无论是Mybatis也好,Spring也罢,它们的执行过程无非可分为启动阶段和运行阶段: 启动阶段: 定义配置文件,如XML,注解 解析配置文件,将配置文件加载到内存当中 运行阶段: 读取内存中的配置文件,并根据配置文件实现对应的功能 对于执行SQL的逻辑来讲,有如下步骤: 当配置完成之后,假如说我们要执行一个下面一个sql,那么该如何执行呢? 1 2 TestMapper testMapper = session.getMapper(TestMapper.class); Test test = testMapper.findOne(1); 代理类的生成 首先Mybatis会根据我们传入接口通过JDK动态代理,生成一个代理对象TestMapper,生成逻辑如下所示: 1 2 3 4 5 6 7 8 9 public T newInstance(SqlSession sqlSession) { // mapperProxy实现了Invocationhandler接口,用于JDK动态代理 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } // 通过JDK动态代理生成对象 protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } 代理类的主要逻辑在MapperProxy中,而代理逻辑则是通过MapperMethod完成的。 ...

March 22, 2026 · 2 min · santu

Mybatis的缓存机制

典型回答 Mybatis的缓存机制有两种:一级缓存和二级缓存。Mybatis缓存的整体工作原理可以参考这篇文章 ✅Mybatis的工作原理? 一级缓存 在同一个会话中,Mybatis会将执行过的SQL语句的结果缓存到内存中,下次再执行相同的SQL语句时,会先查看缓存中是否存在该结果,如果存在则直接返回缓存中的结果,不再执行SQL语句。一级缓存是默认开启的,可以通过在Mybatis的配置文件中设置禁用或刷新缓存来控制缓存的使用。 工作流程如下: 对于一级缓存,有两点需要注意的是: MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺。 MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,换句话说,当一个SqlSession查询并缓存结果后,另一个SqlSession更新了该数据,其他缓存结果的SqlSession是看不到更新后的数据的。所以建议设定缓存级别为Statement。 二级缓存 二级缓存是基于命名空间的缓存,它可以跨会话,在多个会话之间共享缓存,可以减少数据库的访问次数。要使用二级缓存,需要在Mybatis的配置文件中配置相应的缓存实现类,并在需要使用缓存的Mapper接口上添加@CacheNamespace注解。二级缓存的使用需要注意缓存的更新和失效机制,以及并发操作的问题。 工作流程如下: 因为二级缓存是基于namespace的,所以一般情况下,Mybatis的二级缓存是不适合多表查询的情况的。举个例子: 我们有两个表:student和class,我们为这两个表创建了两个namespace去对这两个表做相关的操作。同时,为了进行多表查询,我们在namespace=student的空间中,对student和class两张表进行了关联查询操作(sqlA)。此时就会在namespace=student的空间中把sqlA的结果缓存下来,如果我们在namespace=class下更新了class表,namespace=student是不会更新的,这就会导致脏数据的产生。 不建议用 如前面介绍的,MyBatis 的二级缓存是一个功能强大的特性,它可以显著提高应用性能。but,他会带来数据一致性问题。即数据库发生了变化,但是缓存还是旧数据的情况。 所以,平时在工作中直接用Mybatis的二级缓存的场景会比较少。如果要用缓存,还不如直接用第三方的缓存,至少我们明确地知道这里有个缓存,而不是像Mybatis一样,可能开发者忽略了这里的缓存,导致出了问题不好排查。 如果我们使用第三方缓存解决方案,如 Redis、Memcached、以及应用层面的Guava,Caffeine等。这些工具提供了更强大的缓存功能,重要的是提供了更灵活的缓存策略和更精细的控制。 当然也不是完全不能用,在读多写少的场景也可以用。

March 22, 2026 · 1 min · santu

PageHelper分页的原理是什么?

典型回答 PageHelper是MyBatis中提供的分页插件,主要是用来做物理分页的。 当我们在代码中使用PageHelper.startPage(int pageNum, int pageSize)设置分页参数之后,其实PageHelper会把他们存储到ThreadLocal中。 PageHelper会在执行器的query方法执行之前,会从ThreadLocal中再获取分页参数信息,页码和页大小,然后执行分页算法,计算需要返回的数据块的起始位置和大小。最后,PageHelper会通过修改SQL语句的方式,在SQL后面动态拼接上limit语句,限定查询的数据范围,从而实现物理分页的效果。并且在查询结束后再清除ThreadLocal中的分页参数。

March 22, 2026 · 1 min · santu

什么是ORM,有哪些常用框架?

典型回答 Object Relational Mapping ,简称ORM,翻译过来是对象关系映射。一般用于实现面向对象编程语言里的对象和数据库中的之间的转换。 ORM 有下面这些优点: 数据模型都在一个地方定义,更容易更新和维护,也利于重用代码。 ORM 有现成的工具,很多功能都可以自动完成,比如数据消毒、预处理、事务等等。 数据消毒是指对用户输入的数据进行处理,以防止恶意数据或不合法数据对应用程序的攻击或错误影响。常见的数据消毒操作包括转义特殊字符、验证输入、限制输入长度等。ORM框架可以做参数化查询、数据类型验证等可以实现数据消毒 它迫使你使用 MVC 架构,ORM 就是天然的 Model,最终使代码更清晰。 基于 ORM 的业务代码比较简单,代码量少,语义性好,容易理解。 你不必编写性能不佳的 SQL。 但是,ORM 也有很突出的缺点: ORM 库不是轻量级工具,需要花很多精力学习和设置。 对于复杂的查询,ORM 要么是无法表达,要么是性能不如原生的 SQL。 ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。 常见的ORM框架有Hibernate、ibatis、Mybatis等。

March 22, 2026 · 1 min · santu

MyBatis与Hibernate有何不同?

典型回答 Hibernate和Mybatis都是ORM框架,都支持JDBC和JTA事务处理,它们创建的目的都是为了简化Java原生程序操作数据库的步骤。增加开发者的开发效率。 不同点有以下几点: **Hibernate是全自动的,Mybatis是半自动的。**在Hibernate当中,开发者只需要定义好数据库的表字段和Java DO的映射关系和规则即可,Hibernate会开放出来接口自动去处理数据库表的CURD,并按照规定好的规则映射到DO对象中,这个过程中操作者是完全不需要感知Sql逻辑的。但是在Mybatis中则完全不是这样,Mybatis不会帮助开发者编写sql逻辑,Mybatis只会按照定义好的规则将数据库字段映射到Java的DO中,但是具体的sql逻辑还是需要开发者自己编写的。 正是因为Mybatis需要自己编写Sql逻辑,这是一个包袱,因为开发者需要根据不同的DB,选择不同的SQL语句(DB移植性不高),而且正是因为要自己写SQL,项目初期的开发工作量要比Hibernate大一点。但是这也是优点,通过Mybatis开发者就可以定制sql,譬如多表join,sql优化等操作是Hibernate不具备的 Hibernate的缓存系统要优于Mybatis,如果二级缓存出现脏数据,Hibernate会及时报错,但是Mybatis就需要开发者自己去感知 总的来说,MyBatis 是一个小巧、方便、高效、简单、直接、半自动化的持久层框架,Hibernate 是一个强大、方便、高效、复杂、间接、全自动化的持久层框架。 对于性能要求不太苛刻的系统,比如管理系统、ERP 等推荐使用 Hibernate,而对于性能要求高、响应快、灵活的系统则推荐使用 MyBatis。

March 22, 2026 · 1 min · santu

Mybatis的优点有哪些?

典型回答 在回答这个问题之前,我们先看一段用JDBC和Mybatis写的代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public void testJdbc() { String url = "dbLink"; try(Connection conn = DriverManager.getConnection(url, "root", "password")){ // 加载MySQL驱动 Class.forName("com.mysql.cj.jdbc.Driver"); int author = 1; String date = "2018.06.10"; String sql = "SELECT id, title, content, create_time FROM article WHERE author_id = " + author + " AND create_time > '" + date + "'"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); List<Article> articles = new ArrayList<>(rs.getRow()); while (rs.next()) { Article article = new Article(); article.setId(rs.getInt("id")); article.setTitle(rs.getString("title")); article.setContent(rs.getString("content")); article.setCreateTime(rs.getDate("create_time")); articles.add(article); } System.out.println("Query SQL ==> " + sql); System.out.println("Query Result: "); articles.forEach(System.out::println); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void testMyBatis() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); inputStream.close(); try(SqlSession session = sqlSessionFactory.openSession()) { ArticleDao articleDao = session.getMapper(ArticleDao.class); List<Article> articles = articleDao. findByAuthorAndCreateTime(1, "2018-06-10"); System.out.println(articles); } } inteface ArticleDao { @Query({"SELECT id, title, content, create_time FROM article WHERE author_id = #{id} AND create_time > #{time}"}) List<Article> findByAuthorAndCreateTime(int id, String time); } 很明显,我们能看到使用Mybatis的代码,结构更清晰,代码量也比较少,这就是Mybatis最直观的优点: ...

March 22, 2026 · 2 min · santu

#和$的区别是什么?什么情况必须用$

典型回答 在Mybatis的mapper文件中,可以使用#{param}和${param}来作为动态参数的替换。 #{}和${}在预编译处理中是不一样的。#{}类似jdbc中的PreparedStatement,对于传入的参数,在预处理阶段会使用?代替,可以有效的避免SQL注入。 1 SELECT * FROM users WHERE id = #{userId} 这里的#{userId} 会被替换为 ?,并且 userId 的值在执行时会被安全地设置为参数值。 使用 $ 传递参数时,MyBatis 会将其视为字面量,并在构建 SQL 语句时直接替换成参数的实际值。这意味着参数值会直接拼接到 SQL 语句中。 1 SELECT * FROM ${tableName} 由于 $ 导致的是直接替换,如果参数内容是用户输入,这可能导致 SQL 注入的风险,因为恶意的输入可以被拼接成一部分 SQL 语句执行。 所以我们在Mybatis中,能使用#{}的地方应尽量使用#{},但是有一些情况是必须要用${}的,比如我们要把他用在order by、group by 等语句后面的时候。 1 2 3 4 order by ${sortParam} ${sortType} order by id desc; 当我们通过以上方式进行动态的选择排序字段的时候,就需要使用${}; 扩展知识 ${}的SQL注入问题 MyBatis 中的 $ 符号在 SQL 查询中是会出现潜在的 SQL 注入问题的,因为 $ 符号会直接将参数的值替换到 SQL 查询中,而不会进行参数值的安全转义或预处理。这意味着如果不小心构造了恶意的参数值,就可能导致 SQL 注入攻击。 ...

March 22, 2026 · 1 min · santu

Mybatis插件的运行原理?

典型回答 Mybatis插件的运行原理主要涉及3个关键接口:Interceptor、Invocation和Plugin。 Interceptor:拦截器接口,定义了Mybatis插件的基本功能,包括插件的初始化、插件的拦截方法以及插件的销毁方法。 Invocation:调用接口,表示Mybatis在执行SQL语句时的状态,包括SQL语句、参数、返回值等信息。 Plugin:插件接口,Mybatis框架在执行SQL语句时,会将所有注册的插件封装成Plugin对象,通过Plugin对象实现对SQL语句的拦截和修改。 插件的运行流程如下: 首先,当Mybatis框架运行时,会将所有实现了Interceptor接口的插件进行初始化。 初始化后,Mybatis框架会将所有插件和原始的Executor对象封装成一个InvocationChain对象。(这里使用的是责任链模式) 每次执行SQL语句时,Mybatis框架都会通过InvocationChain对象依次调用所有插件的intercept方法,实现对SQL语句的拦截和修改。 最后,Mybatis框架会将修改后的SQL语句交给原始的Executor对象执行,并将执行结果返回给调用方。 通过这种方式,Mybatis插件可以对SQL语句进行拦截和修改,实现各种功能,例如查询缓存、分页、分库分表等。 知识扩展 常见的Mybatis插件有哪些? 常见的插件如PageHelper,用于分页使用 ✅PageHelper分页的原理是什么? 自己有写过Mybatis插件吗? 笔者之前在做应用迁移的时候,通过Mybatis插件机制完成过一个功能,可以通过注册中心一键切换应用的数据源,伪代码如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Intercepts({@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}), @Signature(method = "query", type = Executor.class, args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), @Signature(method = "query", type = Executor.class, args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) public class MultipleDataSourceInterceptor implements Interceptor { private static String dataSourceKey = null; @Override public Object intercept(Invocation invocation) throws Throwable { // 每次请求,都去设置最新的数据源(至于数据源如何更换,则就是另一个话题了) if (dataSourceKey != null) { DataSourceHolder.setDataSource(dataSourceKey); } return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { //do nothing } public void registerCallback(String data) { dataSourceKey = data; } }

March 22, 2026 · 1 min · santu

Mybatis是如何实现字段映射的?

典型回答 Mybatis通过ResultSet对象来获取SQL查询返回的结果集,然后将结果集中的每行记录映射到Java对象中。在字段映射过程中,Mybatis提供了以下几种方式: 使用列名映射:Mybatis默认使用列名来映射查询结果集中的列与Java对象中的属性。如果列名和Java对象属性名不完全一致,可以通过在SQL语句中使用“AS”关键字或使用别名来修改列名。 使用别名映射:如果查询语句中使用了别名,则Mybatis会优先使用列别名来映射Java对象属性名,而不是列名。 使用ResultMap映射:ResultMap是Mybatis用来映射查询结果集和Java对象属性的关系。可以在映射文件中定义ResultMap,指定Java对象和列之间的映射关系。通过ResultMap,可以实现复杂的字段映射关系和转换。 自定义TypeHandler映射:如果默认的字段映射方式无法满足需求,可以通过实现TypeHandler接口来自定义字段映射规则。TypeHandler可以将查询结果集中的列类型转换为Java对象属性类型,并将Java对象属性类型转换为SQL类型。可以通过在映射文件中定义TypeHandler,来实现自定义映射。 总之,Mybatis提供了多种灵活的字段映射方式,可以满足不同场景下的需求。 扩展知识 字段映射的过程及原理 Mybatis实现字段映射的代码主要在ResultSetHandler类中。该类是Mybatis查询结果集处理的核心类,负责将JDBC ResultSet对象转换为Java对象,并进行字段映射。 Mybatis实现字段映射的原理可以简单描述为以下几个步骤: Mybatis通过JDBC API向数据库发送SQL查询语句,并获得查询结果集。 查询结果集中的所有数据封装到一个ResultSet对象中,Mybatis遍历ResultSet对象中的数据。 对于每一行数据,Mybatis根据Java对象属性名和查询结果集中的列名进行匹配。如果匹配成功,则将查询结果集中的该列数据映射到Java对象的相应属性中。 如果Java对象属性名和查询结果集中的列名不完全一致,Mybatis可以通过在SQL语句中使用“AS”关键字或使用别名来修改列名,或者使用ResultMap来定义Java对象属性和列的映射关系。 对于一些复杂的映射关系,例如日期格式的转换、枚举类型的转换等,可以通过自定义TypeHandler来实现。Mybatis将自定义TypeHandler注册到映射配置中,根据Java对象属性类型和查询结果集中的列类型进行转换。 最终,Mybatis将所有映射成功的Java对象封装成一个List集合,返回给用户使用。 总之,Mybatis通过查询结果集中的列名和Java对象属性名之间的映射关系,将查询结果集中的数据映射到Java对象中。Mybatis提供了多种灵活的映射方式,可以满足不同场景下的需求。

March 22, 2026 · 1 min · santu

留言给博主