什么是Mybatis
mybatis是JAVA领域中的一款持久化框架。它的主要功能是让我们轻松的在JAVA对象和数据库之间建立联系。通过这种联系,开发者可以很方便的存储、检索和操作数据。以下是Mybatis和其他ORM框架相比的一些特点:
- Mybatis强调对SQL的可控性,在使用的时候,可以直接编写SQL语句,提供更精准的优化查询。还可充分利用数据库的特性,处理各种复杂的业务逻辑。在某些情况下,能够带来更好的性能。
- Mybatis不会自动将对象和数据库表进行完全的映射,这跟其他ORM框架的做法不同。在使用Mybatis时,需要手动编写映射配置,把JAVA对象的属性和数据表的列进行关联。虽说这种方式需要更多的操作,但也正是这种方式让映射过程更加可控,尤其适用于处理复杂的数据结构。
相对于其他ORM框架,MyBatis更加灵活。它允许开发者直接操控SQL语句,并手动控制对象与数据库表之间的映射关系。这使得MvBatis在需要精细控制数据库操作以及处理复杂映射关系场景中更加具有优势。而其他一些ORM框架则更适合简化开发流程,减少样板代码的编写。
Mybatis的优点和缺点
优点:
- Mybatis是使用SQL语句进行编程,非常灵活。可以将SQL写在XML中,这样可以避免和代码或数据库设计产生冲突,也便于集中管理。Mybatis还提供XML标签,用来编写动态SQL语句,而且可以重复利用。可根据不同情况灵活的生成SQL。
- 与使用JDBC相比,Mybatis能减少超过50%的代码,也省去了很多JDBC冗余的操作,不需要手动来回打开关闭数据库连接,减轻了开发负担。
- Mybatis能够很好的兼容各种数据库,因为它是基于JDBC连接数据库,只要是JDBC支持的数据库,Mybatis都能支持。这使得在不同的项目中,甚至不同类型的数据库之间切换变得非常方便。
- Mybatis能够轻松与Spring框架集成,给项目带来很大得便利。
- 对象个数据库之间得映射,Mybatis提供了映射标签()可以方便得将对象和数据库的字段关系映射起来。此外对象关系映射标签还支持对象关系组件的维护,这是一个很有用的功能。
缺点:
- mybatis虽然强大,但是编写SQL语句可能会相对繁琐,特别是当涉及多个字段或多个关联表时。这就要求开发人员在SQL编写方面有一定的功底。
- 由于SQL语句依赖于特定的数据库,如果想要更换数据库移植性会受到一定的影响,这意味着不能轻易更改数据库,可能需要进行一些适应性的修改。
Mybatis与JPA有哪些不同
- 编程模型的不同:Mybatis和JPA采用不同的方式来处理数据操作。Mybatis采用基于SQL的编程模型,意味着开发者需要自己编写SQL语句,并且将其映射到java方法。JPA采用基于对象的编程模型,用户只需定义实体类并使用注解将实体映射到数据表中,JPA会自动生成SQL语句】开发人员无需过关心底层SQL的细节。
- SQL控制:Mybatis可以编写和优化SQL语句,在需要特定优化或使用数据库特性时非常有用。而JPA则将大部分SQL细节隐藏,自动生成SQL语句,在某些情况下可能会影响性能或限制操作。
- 灵活性和控制:Mybatis提供了更多的灵活性,适用于需定制化SQL查询或调用存储过程的场景。JPA则提供了更高层次的抽象,用于简化常见的数据库操作。在某些高级或者是复杂的情况下会产生一些限制。
- 查询语言:Mybatis使用原生的SQL作为查询语言,要求开发人员对SQL有一定的了解。JPA则引入了JPQL作为查询语句,更加的面向对象,类似于SQL,但是操作的是实体对象。
- 缓存方面:Mybatis的缓存控制更加精细,可以更准确的控制缓存的行为。JPA虽然也支持缓存,但通常对缓存的控制较少,更多的由框架自身控制。
- 学习曲线:Mybatis对熟悉SQL的开发人员来说,上手较快。JPA的学习曲线比较平缓,因为隐藏了更多的细节,一些高级特性需要长时间来理解。
Mybatis的核心组件有哪些
- SqlSessionFactoryBuilder (构造器):使用Builder模式根据mybatis-config.xml配置或者代码来生成SqISessionFactory。
- SqlSessionFactory:SqlSessionFactory是一个会话工厂,主要的作用是用来创建SqlSession对象,这个对象是我们与数据库交互的主要途径。SqlSessionFactory还可以帮助配置数据库连接信息和事务管理等。该工厂一旦被创建就会加载一些必要配置和映射文件,为后续的数据库操作提供一个可靠的基础。
- SqlSession:代表一次数据库的会话,提供了一系列方法来执行Sql语句,提交或事务回滚,还可以获取Mapper接口实例。不过SqlSession的生命周期是短暂的,通常在数据库操作完成之后就需要关闭,以释放资源。
- Mapper:Mybatis的重要组件,由一个XML文件和一个jAVA接口组成,用于定义SQL语句的结果映射规则。Mapper接口方法和SQL映射语句之间存在一一对应的关系。
Mybatis的工作流程
- 加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select | update | delete | insert>标签项。
- SqlSessionFactoryBuilder通过Configuration对象生成SqlSessionFactory,用来开启SqlSession。
- SqlSession对象完成和数据库的交互:
- 用户程序调用mybatis接口层api(即Mapper接口中的方法)
- SqlSession通过调用api的Statement ID找到对应的MappedStatement对象
- 通过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象
- JDBC执行sql。
- 借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。
Mybatis的一级缓存和二级缓存
一级缓存:
- 一级缓存是Mybatis默认开启并且不可关闭的,是针对SqlSession级别的缓存。生命周期与SqlSession的生命周期一致。
- 当执行查询时,首先会从一级缓存中检查是否存在相同查询语句的结果,如果有则直接返回结果,避免对数据库进行访问提高查询效率。
- 缓存键:一级缓存的键是由SQL语句、参数值和结果映射组成。这意味着相同的SQL语句和参数值会公用缓存。
- 缓存失效:当SqlSession关闭或提交后,以及执行了任何更新操作(插入、更新、删除)之后缓存会失效。
二级缓存:
- 缓存范围:二级缓存的作用域是Mapper,级别是SqlSessionFactory级别的,即同一个Mapper对象中的查询结果可以被多个SqlSession共享。
- 缓存的配置:你可以在 Mapper 的 XML 文件或注解中配置二级缓存的开启与关闭,以及缓存的刷新策略、缓存大小等。
- 缓存对象的序列化:为了能在多个 SqlSession 间共享缓存数据,二级缓存中的对象需要支持序列化。你可以自定义实现
Serializable
接口的对象或使用 MyBatis 提供的缓存插件。 - 缓存的更新:当执行更新操作时,MyBatis 会清除相关的二级缓存,以保证缓存数据的一致性。
启用二级缓存:
-
application.properties中配置开启全局缓存
mybatis.configuration.cacheEnabled=true
-
Mapper 文件中,你可以添加 `` 元素来启用二级缓存
<!-- mapper/UserMapper.xml --> <mapper namespace="com.example.demo.mapper.UserMapper"> <cache evication="LRU" flushInterval="60000" size="1024" readOnly="true"/> <!-- 查询语句 --> </mapper>
-
或者是使用注解的方式
@CacheNamespace public interface UserMapper { // 查询方法 }
Mybatis如何处理延迟加载
延迟加载是一种优化方法,目标是为了在查询数据库的时候,尽量不读取多余的数据,从而提高我们应用的表现和节约资源。在Mybatis里,延迟加载的技巧主要是用于在处理对象关系映射的时候,也就是ORM。
延迟加载的好处:
- 提高性能:在对象关系较为复杂或者是数据多的情况下,延迟加载可以减少一次性加载大量数据,让查询更快,应答更快。
- 节省资源:可以按需加载,不一开始加载所有关联数据,节约了内存和网络资源。
- 避免不必要的查询:如果有些关联数据在当前情景下用不上,用延迟加载就能避免没不要的数据查询,不累积数据库的负担。
但要注意,虽然延迟加载能提升性能,可别用得过了,免得碰上懒加载的N+1问题,就是要查很多次才能拿到关联数据,结果性能就拖垮了。所以用延迟加载的时候,得根据实际情况合理配置和使用。
Mybatis中的动态Sql是什么
动态SQL是Mybatis中的一个强大的特性,允许根据不同的条件在运行时动态构建不同的SQL语句。
假设用户需要根据不同的条件搜索商品,例如商品名,价格或者是分类,可以使用动态SQL构建一个灵活的查询语句,在用户提供相关条件的时候包含这些条件。
在Mybatis中可以使用、、、等标签构建动态SQL。
动态SQL使得构建更加的灵活,适应不同情况的查询变得方便。减少代码的重复,提高代码的可维护性和可读性。
Mybatis中的XML映射有哪些标签
除了常见的select、insert、update、delete标签,Mybatis的XML映射文件中还存在一些其他的标签用于复杂的操作和配置。下面是一些常见的标签:
- resultMap:用于定义查询结果与JAVA对象之间的映射关系,可以在多个查询中重复使用。
- association和collection:用于在resultMap中定义关联关系,用于处理一对一一对多的关系。
- sql:用于定义可重用的SQL片段,然后在其他地方引用,用于减少重复编写的SQL语句
- include:用于在SQL语句中引入外部定义的SQL片段,提高可维护性。
- if、choose、when、otherwise:用于在SQL中进行条件判断和逻辑控制,用于动态的条件构建。
- trim、where、set:用于在SQL语句中添加固定的SQL片段,如where和set关键字,用于动态的条件构建。
- foreach:用于在SQL语句中进行集合迭代,适用于生成IN语句等。
- bind:用于SQL语句中申明并且绑定一个变量,适合生成IN语句。
- cache:用于配置二级缓存。
- selectKey:用于在插入操作后获取生成的主键值。
Mybatis中模糊查询语句怎么写
-
字符串拼接方式
<select id = "searchUser" resultMap = "userResultMap"> SELECT * FROM users WHERE username LIKE CONCAT('%',#{keyword},'%') </select>
-
动态SQL方式
<select id = "searchUser" resultMap = "userResultMap"> SELECT * FROM users <where> <if> AND username LIKE CONCAT('%',#{keyword},'%') </if> </where> </select>
#{}和${}的区别是什么?
- #{}(预编译或者是占位符)
- #{}是用于预编译的参数标记,当使用#{}时候,Mybatis会将参数值放入一个预编译的PreparedStatement中并确保参数值被正确的转义和引用,从而防止SQL注入攻击。
- ${}(字符串替换)
- 是用于字符串替换的参数标记。当使用 {}是用于字符串替换的参数标记。当使用 是用于字符串替换的参数标记。当使用{}时,Mybatis会直接将参数值嵌入SQL语句中,不会进行预编译或转义。这存在潜在的安全问题,有SQL注入的风险。
总结区别:
#{}用于预编译,提供参数安全性,适合大多数情况
${}用于字符串替换,潜在安全风险较高,仅在特定情况下使用,确保参数值安全。在实际使用中,推荐优先使用 #{}来处理参数,以确保数据库操作的安全性和可靠性。只有在确保参数值不会引发安全问题的情况下,才应该考虑使用 ${}。
Mybatis中二级缓存的缺点
- 数据不一致性:如果某个地方改了数据,但是缓存里的数据没有及时更新,其他地方拿到的数据就可能是过时的,这样可能会引发错误。
- 占用内存空间:使用二级缓存需要一些内存空间来存储数据。如果数据很多,可能会占用比较多的内存,这有可能会影响引用程序的运行速度。
- 维护成本:在使用二级缓存的时候,需要考虑数据什么时候更新、什么时候删除等等,这会让代码变得复杂,需要更多的开发和测试工作。
Mybatis中二级缓存的适用场景
- 如果我们的应用程序更多是读取数据,写操作相对较少,那么二级缓存就能够很有效地减少数据库访问的次数,提升性能。
- 还有,如果我们的数据变化不是很频繁,即使缓存里的数据不是最新的,也不会对应用产生太大的影响,这时候也可以考虑用二级缓存。
- 另外,如果某些查询的耗时比较长,但是查询结果又不怎么变化,用了二级缓存就可以加快这些查询的速度。
Mybatis中二级缓存的不适用场景
- 如果应用程序对数据的实时性要求很高,二级缓存可能就不太适合,因为缓存里的数据可能会有点滞后。
- 如果我们应用里有很多频繁的写操作,可能会导致缓存一直失效,这样就得不偿失了,可能还会增加数据库的压力。
- 如果涉及到复杂的数据模型或者数据之间的关联关系,使用缓存的维护和数据一致性可能会变得更加复杂。
Mybatis为什么是半ORM框架
- mybatis被称为ORM框架是因为它在数据库操作方面提供了一些对象关系映射的功能,但是Mybatis需要手动编写SQL语句来执行数据库操作。
- 相比下 全ORM框架更加自动化,它会完全代替用户生成SQL语句进行数据库操作。在某些情况下可以提高开发效率。但是全ORM框架可能会造成性能方面的影响。因为可能会生成复杂的SQL,导致查询效率降低。
- Mybatis相对较灵活,可以精确的控制要执行的SQL语句。这对于需要优化查询性能的场景很有帮助。并且Mybatis可以在映射文件中明确的指定每个字段的关系,可以更好的控制数据库表和JAVA对象之间的对应关系。
最后再来总结一下,Mybatis被称为半ORM框架,是因为它结合了ORM的部分特性,但相对于全ORM框架,5更注重灵活性和精确控制。在选择使用哪种方式时,需要根据具体的项目需求和性能要求来考虑。
Mybatis如何实现动态数据源的切换
Mybatis如何处理懒加载和预加载
懒加载和预加载其实就是Mybatis在获取数据库数据的时候如何处理关联对象的加载方式。
懒加载:
懒加载是一种延迟加载的技术,即在需要访问关联对象的时候才会加载相关的数据,这意味着,当从数据库中获取一个主对象的时候,其关联对象不会立即加载到内存中。只有当实际尝试访问这些关联对象的时候,Mybatis才会从数据库中加载并填充这些关联对象数据。
在MyBatis中,默认情况下,和元素的fetchType属性值为lazy,这意味着它们将采用懒加载的方式。例如:
<select id="selectUser" parameterType="int" resultType="com.example.User">
select * from user where id = #{id}
<association property="address" javaType="com.example.Address" fetchType="lazy">
select * from address where userId = #{id}
</association>
</select>
预加载:
预加载是在获取主对象时同时加载其关联对象的技术。当你获取主对象时,它的所有关联对象也会被一并加载到内存中,从而避免了多次数据库查询。
在MyBatis中,如果你想使用预加载,可以将fetchType属性设置为eager:
<select id="selectUserWithAddress" parameterType="int" resultType="com.example.User">
select * from user where id = #{id}
<association property="address" javaType="com.example.Address" fetchType="eager">
select * from address where userId = #{id}
</association>
</select>
然而,使用fetchType="eager"
在某些情况下可能会导致N+1查询问题,即除了主查询外,还会为每个关联对象执行额外的查询。为了避免这种情况,你可以使用嵌套查询或嵌套结果(nested query or nested result)来优化预加载,其中嵌套查询会在主查询中包含一个子查询来一次性加载所有关联数据。
Mybatis的工作原理
- 配置文件:配置Mybatis的配置文件,用于定义数据源、事务管理以及其他一些设置。
- SQL映射文件:映射SQL查询到Java对象,需要在XML文件中写出SQL。
- SqlSessionFactory:Mybatis初始化时,会调用SqlSessionFactoryBuilder根据上述的配置文件创建出SqlSessionFactory对象,该对象只会创建一次,用于创建与数据库交互的会话SqlSession.
- SqlSession:Mybatis的关键组件,当我们和数据库进行交互的时候,就需要通过SqlSessionFactory创建出SqlSession,这个会话包括了所有执行SQL的方法,例如Insert、delete、update、select
- 接口映射器Mapper:为了使代码简洁,我们经常使用接口来代表SQL映射。这些接口方法对应了上述SQL映射文件中的SQL。使得我们能够像调用普通的方法一样执行SQL。
- Executor:负责SQL的执行和结果的处理。
- ParameterHandler: ParameterHandler 负责设置 SQL 参数。
- StatementHandler: StatementHandler 负责处理 JDBC 的 Statement,包括预编译 SQL 语句和执行 SQL。
- ResultSetHandler: ResultSetHandler 负责处理数据库的 ResultSet 并将其映射到 Java 对象。
那么,当你在应用中调用一个映射器方法时,这里发生了什么?
- MyBatis会找到对应的SQL语句。
- 使用给定的参数,MyBatis会为这条SQL语句创建一个PreparedStatement
- 执行这个PreparedStatement.
- 如果这是一个查询操作,MyBatis会将查询结果映射到Java对象或集合中。
- 最后,返回这个结果。这就是MyBatis的基本工作原理。使用MyBatis,我们可以更方便地与数据库交互,同时避免大部分重复和模板化的代码。
如何在Mybatis中进行分页查询?
基本分页查询:
Mybatis提供简单的方式来提供分页查询,主要涉及两个参数:offset和limit。offset表示从结果集的哪一行开始读取数据,而Limit则表示每页显示多少条数据。
<select id="getPagedData" resultType="User">
Select * from user Limit #{offset},#{limlt}
</select>
其中,offset可以通过公式(pageNum-1)*pageSize计算得出,其中pageNum表示页码,从1开始,pageSize表示每页显示的记录数。
使用插件:
PageHelper插件:是一个流行的Mybatis分页插件,简化了分页查询的操作,只需在查询方法前调用PageHelper.startPage(pageNum,pageSize),然后执行查询语句,PageHelper就会自动处理分页逻辑。
使用如下:
application.yml
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countSql
helper-dialect
: 设置数据库方言,比如 MySQL。reasonable
: 开启合理模式,分页条数小于 0 或大于总页数时,返回空数据。support-methods-arguments
: 支持通过方法参数传递分页参数。params
: 设置分页参数,count=countSql
表示 count 查询的 SQL 语句。
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class YourService {
@Resource
private YourMapper yourMapper;
public PageInfo<YourEntity> findPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<YourEntity> list = yourMapper.selectList();
return new PageInfo<>(list);
}
}
Mybatis的插件能够在哪些地方进行拦截
- Executor层面的拦截:在SQL语句的执行层面,可以在SQL语句的执行前后进行拦截。
- StatementHandler层面拦截:在SQL语句的处理层面,插件可以在SQL语句执行之前进行拦截,例如修改参数值。
- ParameterHandler层面的拦截:可以在参数传递个给SQL语句之前拦截
- ResultSetHandler层面的拦截:可以在结果返回给调用方之前拦截。
如何自定义Mybatis插件并使用
在Spring Boot中使用自定义的MyBatis插件涉及几个关键步骤:创建插件类、配置插件、以及确保插件被MyBatis识别和使用。下面是一步一步的指导:
- 创建自定义插件类
首先,你需要创建一个实现org.apache.ibatis.plugin.Interceptor
接口的类。在这个类中,你需要实现intercept
方法,该方法将在MyBatis执行SQL语句前后被调用。你还可以实现Plugin
接口的setProperties
方法来接收配置参数。
Java
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class CustomMybatisPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 在这里实现你的插件逻辑
System.out.println("Custom plugin intercepted the method.");
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 读取插件配置参数
}
}
- 配置自定义插件
为了使MyBatis能够识别和使用你的自定义插件,你需要在Spring Boot的配置中注册这个插件。你可以通过创建一个配置类并使用@Bean
注解来实现这一点。
Java
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisConfig {
@Autowired
private DataSource dataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
// 添加自定义插件
Interceptor[] plugins = new Interceptor[]{new CustomMybatisPlugin()};
factoryBean.setPlugins(plugins);
return factoryBean.getObject();
}
}
- 验证插件是否工作
在你的应用中执行一些数据库操作,看看你的自定义插件是否按预期工作。例如,如果你的插件打印了一条日志信息,你应该能够在日志中看到这条信息。
注意事项
- 确保你的插件类正确实现了
Interceptor
接口,并且使用了正确的@Signature
注解来指明你想要拦截的MyBatis组件和方法。 - 在配置插件时,记得在
SqlSessionFactoryBean
的setPlugins
方法中传递插件数组。 - 测试你的插件以确认它按照你的期望工作,特别是当涉及到更复杂的业务逻辑时。