网站推广的方案设计怎么写,国内可用的免费云端服务器,怎么做网站的图片,wordpress引用视频文章目录 说说 MyBatis 执行流程#xff1f;1. 加载配置文件和映射文件2. 构建 SqlSessionFactory3. 创建 SqlSession4. 调用 Mapper 方法5. 处理参数和结果映射6. 事务管理7. 释放资源简化流程图#xff1a; MyBatis 和 Hibernate 有什么不同#xff1f;1. **对象关系映射层… 文章目录 说说 MyBatis 执行流程1. 加载配置文件和映射文件2. 构建 SqlSessionFactory3. 创建 SqlSession4. 调用 Mapper 方法5. 处理参数和结果映射6. 事务管理7. 释放资源简化流程图 MyBatis 和 Hibernate 有什么不同1. **对象关系映射层次**2. **SQL 控制**3. **开发复杂度**4. **性能**5. **缓存机制**6. **数据库无关性**7. **适用场景**8. **学习曲线**总结 ${} 和 #{} 有什么区别1. **${}字符串拼接**2. **#{}预编译处理**3. **对比总结****什么时候用哪个**示例 什么是 SQL 注入SQL 注入示例SQL 注入的常见类型SQL 注入的危害防止 SQL 注入的最佳实践 如何解决实体类属性和表中字段不一致的问题解决方案1. **使用注解映射**2. **使用 XML 配置映射适用于 MyBatis** MyBatis 中如何实现分页1. **手动编写分页 SQL**2. **使用 MyBatis-Plus 的分页插件**1) 引入 MyBatis-Plus 依赖2) 配置分页插件3) 使用分页查询 3. **使用 PageHelper 插件**1) 引入 PageHelper 依赖2) 配置 PageHelper3) 使用 PageHelper 进行分页 HashMap 和 Hashtable 可以作为查询结果吗HashMap 和 Hashtable 作为查询结果1. **HashMap**2. **Hashtable**3. **推荐使用 HashMap 而非 Hashtable** 何时使用 HashMap 而不是实体类总结 说一说动态SQL动态 SQL 的作用动态 SQL 标签1. **if 标签**2. **choose、when 和 otherwise 标签**3. **where 标签**4. **trim 标签**5. **set 标签**6. **foreach 标签**7. **bind 标签** 动态 SQL 的优势 说一下 MyBatis 的缓存机制1. 一级缓存Local Cache2. 二级缓存Global Cache缓存实现缓存策略缓存的注意事项总结 MyBatis 中有哪些设计模式1. **单例模式Singleton Pattern**2. **代理模式Proxy Pattern**3. **工厂模式Factory Pattern**4. **适配器模式Adapter Pattern**5. **模板方法模式Template Method Pattern**6. **建造者模式Builder Pattern**7. **责任链模式Chain of Responsibility Pattern**总结 说说 MyBatis 执行流程
MyBatis 是一个优秀的持久层框架支持自定义 SQL、存储过程和高级映射。它的执行流程大致如下
1. 加载配置文件和映射文件
加载核心配置文件mybatis-config.xml MyBatis 会读取核心配置文件以初始化环境信息比如数据库连接池、事务管理、映射器等。加载映射文件Mapper XML 文件 MyBatis 读取每个映射文件中的 SQL 语句定义并将其映射到相应的 Java 接口或类的方法上。
2. 构建 SqlSessionFactory
创建 SqlSessionFactory 通过 SqlSessionFactoryBuilder 构建 SqlSessionFactory 对象。SqlSessionFactory 是 MyBatis 的核心对象之一它负责创建 SqlSession 对象。
3. 创建 SqlSession
获取 SqlSession SqlSessionFactory 创建 SqlSession 对象。SqlSession 是 MyBatis 与数据库交互的关键对象用来执行 SQL 查询、插入、更新和删除操作。
4. 调用 Mapper 方法 获取 Mapper 接口的代理对象 MyBatis 会为每个 Mapper 接口生成代理对象当你调用接口方法时实际上是调用代理对象的方法。 执行 SQL 操作 代理对象根据 Mapper 文件中的 SQL 映射信息动态构建 SQL 语句发送到数据库执行。
5. 处理参数和结果映射 参数处理 MyBatis 将传入的参数进行处理根据 Mapper 中的配置如 #{} 和 ${} 占位符将参数嵌入到 SQL 语句中。 执行 SQL 并获取结果 MyBatis 通过 JDBC 执行构建的 SQL 语句并将返回的结果集转换为 Java 对象。 结果映射 如果结果需要映射到自定义对象例如 POJOMyBatis 会根据配置将数据库字段映射到 Java 对象的属性。
6. 事务管理
事务控制 SqlSession 提供了事务控制的方法如 commit() 和 rollback()。如果操作成功需要手动提交事务或者使用自动提交否则可以回滚。
7. 释放资源
关闭 SqlSession 当操作完成后需要关闭 SqlSession 以释放数据库连接等资源。这可以通过手动关闭或者使用 try-with-resources 来自动关闭。
简化流程图
加载配置 -构建 SqlSessionFactory -创建 SqlSession -获取 Mapper 代理 -调用 Mapper 方法 -执行 SQL 并映射结果 -事务管理 -关闭 SqlSession
这个执行流程展现了 MyBatis 如何在配置、动态 SQL 映射和事务管理之间无缝衔接以简化与数据库的交互。
MyBatis 和 Hibernate 有什么不同
MyBatis 和 Hibernate 都是 Java 的 ORM对象关系映射框架尽管它们都处理数据库交互但其实现方式和适用场景有很大差异。
1. 对象关系映射层次 MyBatis: MyBatis 是 半自动化的 ORM 框架它的核心在于 SQL 语句的控制。开发者需要手动编写 SQL 语句MyBatis 提供了丰富的映射配置用于将 SQL 查询结果映射到 Java 对象上。因此它提供了对 SQL 更大的控制和灵活性但需要开发者自己编写 SQL。 Hibernate: Hibernate 是 全自动化的 ORM 框架基于 POJOPlain Old Java Objects和映射元数据它自动生成 SQL 并处理对象与数据库表之间的映射。开发者通常不需要手动编写 SQLHibernate 会根据实体类和数据库之间的映射关系来生成并执行 SQL 语句。
2. SQL 控制 MyBatis: MyBatis 允许开发者完全控制 SQL 语句这意味着你可以编写自定义的、优化的 SQL 语句包括复杂的查询、插入、更新和删除操作。对于需要高度优化的 SQL 场景MyBatis 更合适。 Hibernate: Hibernate 尽可能屏蔽了底层的 SQL 细节它基于 HQLHibernate Query Language或 Criteria API 进行查询并自动将 HQL 翻译成 SQL。如果开发者希望直接编写 SQLHibernate 也支持原生 SQL 查询但通常不推荐频繁使用。
3. 开发复杂度 MyBatis: MyBatis 对 SQL 的控制力高但这意味着开发者需要对 SQL 和数据库有深入的了解尤其是需要手动维护 SQL 语句和结果映射这可能在项目规模增大时带来额外的复杂性。 Hibernate: Hibernate 通过自动化映射和对象持久化减少了手动编写 SQL 的需求对于数据模型简单或业务逻辑与数据关系明确的项目开发难度较低。然而由于 Hibernate 隐藏了 SQL 细节复杂的查询可能不容易优化。
4. 性能 MyBatis: 因为 SQL 是手动编写的MyBatis 提供了对 SQL 语句的完全控制因此可以编写高效的 SQL 并优化性能。然而性能优化的责任完全在开发者身上。 Hibernate: Hibernate 有自己的缓存机制一级缓存、二级缓存和自动化的批量操作优化。Hibernate 的自动生成 SQL 在一些简单场景下可以提高开发效率和性能但对于复杂查询生成的 SQL 可能不够高效导致性能问题。
5. 缓存机制 MyBatis: MyBatis 支持一级和二级缓存。一级缓存是基于 SqlSession 的本地缓存而二级缓存是跨 SqlSession 的全局缓存。但缓存机制不像 Hibernate 那么自动化和丰富更多依赖开发者的手动配置。 Hibernate: Hibernate 具有更强大的缓存机制默认开启一级缓存并且可以很方便地使用二级缓存插件如 EHCache、Infinispan。它自动化的缓存管理能显著提升性能特别是在读取频繁的场景中。
6. 数据库无关性 MyBatis: MyBatis 并没有内置的数据库无关性支持开发者需要为不同数据库编写不同的 SQL 语句因此在跨数据库操作时需要更多的工作。 Hibernate: Hibernate 通过自动生成 SQL 实现了较好的数据库无关性开发者不需要为不同数据库编写不同的 SQL它会根据底层数据库方言自动生成合适的 SQL 语句。
7. 适用场景 MyBatis: 适用于对 SQL 控制要求高、需要复杂查询或多表联合查询、或已有复杂的数据库设计的项目。MyBatis 的灵活性非常适合对 SQL 优化要求高的场景。 Hibernate: 适用于简单的数据模型或 CRUD 操作比较多的应用场景。对于需要较多的数据库自动化管理和面向对象设计的项目Hibernate 是一个不错的选择。Hibernate 的自动化机制可以帮助开发者快速开发原型和减少代码量。
8. 学习曲线 MyBatis: 对开发者的 SQL 和数据库知识要求较高。由于它不完全是 ORM 解决方案开发者必须在 SQL 和 Java 对象映射中找到平衡。 Hibernate: 虽然 Hibernate 提供了许多自动化功能简化了数据库操作但它的学习曲线较陡尤其是在复杂场景下如优化、缓存管理、批量操作等。开发者需要理解其底层原理和行为以避免潜在的问题。
总结
MyBatis: 灵活、可控性强适合对 SQL 有较高要求和复杂查询的项目。Hibernate: 自动化程度高、面向对象映射良好适合 CRUD 操作较多且开发者希望避免直接与 SQL 打交道的场景。
两者各有优缺点选择使用哪一个框架需要根据项目需求、团队技能和对 SQL 控制的需求来权衡。
${} 和 #{} 有什么区别
${} 和 #{} 是 MyBatis 中用来在 SQL 语句中引用参数的两种方式它们的区别主要体现在 SQL 解析、参数处理、安全性和性能等方面。
1. ${}字符串拼接
作用: 直接将参数的值拼接到 SQL 语句中类似于字符串替换。解析方式: ${} 在 MyBatis 执行 SQL 时参数不会被预处理它会将传入的参数直接嵌入到生成的 SQL 语句中。使用场景: 适用于动态生成 SQL 语句的场景例如动态表名、列名或某些 SQL 关键字。
SELECT * FROM ${tableName};在这里tableName 的值将直接替换 ${tableName}。如果 tableName users最终生成的 SQL 将是
SELECT * FROM users;优点: 灵活适用于动态 SQL 生成。缺点: 存在 SQL 注入风险参数不会被 MyBatis 进行预处理和转义。
2. #{}预编译处理
作用: 将参数作为预编译 SQL 语句的占位符类似于 JDBC 的 ?MyBatis 会将 #{} 中的参数值传递给 JDBC 处理并将其安全地设置为 SQL 语句的参数。解析方式: #{} 在 MyBatis 中会被解析为 ?并使用 PreparedStatement 将参数值绑定到这个占位符。参数会被自动转义以防止 SQL 注入攻击。使用场景: 适用于传递普通的 SQL 参数如查询条件、插入值等。
SELECT * FROM users WHERE id #{userId};在这里#{userId} 会被解析为 ?MyBatis 会将 userId 的值绑定到这个占位符上。如果 userId 1最终生成的 SQL 是
SELECT * FROM users WHERE id ?;然后 MyBatis 会通过 PreparedStatement 绑定参数将 1 传递给 SQL。
优点: 安全性高防止 SQL 注入。MyBatis 会自动处理数据类型和转义。缺点: 不适用于动态生成 SQL 语句的场景如动态表名、列名等。
3. 对比总结
特性${}#{}工作原理直接将参数值拼接到 SQL 中使用占位符 ? 预编译处理适用场景动态 SQL如表名、列名等普通 SQL 参数传递安全性存在 SQL 注入风险防止 SQL 注入性能每次执行都重新编译 SQL预编译后重复执行效率高转义处理无自动转义自动转义避免特殊字符问题
什么时候用哪个
使用 #{} 主要是为了传递普通参数特别是对于用户输入的数据这样可以避免 SQL 注入问题。使用 ${} 主要用于拼接 SQL 动态部分比如表名、列名等不能用于传递用户输入的数据因为这种方式不会进行转义和预处理。
示例
假设你有一段 SQL 查询代码
SELECT * FROM ${tableName} WHERE name #{name};如果 tableName users 且 name John生成的 SQL 会是
SELECT * FROM users WHERE name ?;然后 MyBatis 会安全地将 name 的值 John 绑定到 ? 处。
在这种组合场景下${} 用于动态表名而 #{} 用于安全的参数传递。
什么是 SQL 注入
SQL 注入SQL Injection是一种网络攻击技术攻击者通过在输入字段中插入恶意的 SQL 代码使应用程序生成恶意的 SQL 查询从而操纵数据库的执行行为。SQL 注入可以导致严重的安全问题包括数据泄露、数据篡改、删除数据甚至获取系统控制权。
SQL 注入示例
假设有一个登录功能接受用户名和密码并在数据库中验证用户凭证。SQL 查询可能是这样写的
SELECT * FROM users WHERE username admin AND password password123;如果这个查询中的 username 和 password 直接来自用户的输入并且没有适当的处理那么攻击者可以输入恶意的 SQL 代码。
假设攻击者在用户名字段输入 OR 1 1而在密码字段随意输入数据那么生成的 SQL 查询可能会变成
SELECT * FROM users WHERE username OR 1 1 AND password whatever;这段 SQL 会返回所有用户的数据因为 1 1 始终为真导致 SQL 查询条件总是成立。攻击者因此可以绕过登录验证并以系统管理员或其他用户的身份登录。
SQL 注入的常见类型 基于错误的注入Error-Based Injection 攻击者通过故意输入错误的数据来获取数据库错误信息进而推断数据库结构和内容。 联合查询注入Union-Based Injection 攻击者利用 UNION 语句将恶意查询与原查询合并获取数据库的额外信息。 盲注入Blind SQL Injection 当数据库不会返回错误信息时攻击者通过逐步测试布尔条件或时间延迟来推测数据库信息。 基于时间的注入Time-Based Injection 通过让数据库查询执行时间延迟的方式推断数据库是否执行了某条 SQL 语句进而获取数据。
SQL 注入的危害
数据泄露: 攻击者可以读取敏感数据例如用户信息、财务数据等。数据篡改: 攻击者可以修改数据库中的信息如更改密码、篡改记录。删除数据: 攻击者可以执行 DELETE 操作删除数据库中的数据。执行系统命令: 在某些情况下攻击者可以通过 SQL 注入执行数据库系统命令甚至接管整个服务器。
防止 SQL 注入的最佳实践 使用预编译语句和参数化查询 避免直接拼接 SQL 字符串使用像 PreparedStatement 或其他框架如 MyBatis 中的 #{}来处理参数。这可以有效防止 SQL 注入。 String sql SELECT * FROM users WHERE username ? AND password ?;
PreparedStatement stmt connection.prepareStatement(sql);
stmt.setString(1, username);
stmt.setString(2, password);
ResultSet rs stmt.executeQuery();使用 ORM 框架 使用 Hibernate、MyBatis 等 ORM 框架它们通常会自动处理参数化查询减少 SQL 注入的风险。
如何解决实体类属性和表中字段不一致的问题
在开发中数据库表的字段名和实体类的属性名不一致是常见的问题。为了解决这种不一致性常见的 ORM 框架如 MyBatis、Hibernate 等提供了多种机制来将数据库字段与实体类的属性进行映射。
解决方案
1. 使用注解映射
如果使用的是基于注解的 ORM 框架可以通过注解明确地指定实体类属性与数据库表字段之间的映射。 在 MyBatis 中 可以使用 Results 和 Result 注解来指定属性和数据库字段的映射关系。 Select(SELECT id, user_name FROM users WHERE id #{id})
Results({Result(property id, column id),Result(property username, column user_name)
})
User selectUserById(int id);在这个例子中username 是实体类的属性而 user_name 是数据库中的字段名通过 Result 注解将它们对应起来。 在 Hibernate 中 使用 Column 注解来指定实体类属性和数据库字段之间的映射。 Entity
public class User {Idprivate Long id;Column(name user_name)private String username;// getter, setter
}Column(name user_name) 表示实体类的 username 属性与数据库表中的 user_name 字段对应。
2. 使用 XML 配置映射适用于 MyBatis
MyBatis 还支持使用 XML 来配置实体类属性与数据库字段的映射。
resultMap iduserMap typecom.example.Userresult propertyid columnid/result propertyusername columnuser_name/
/resultMapselect idselectUserById resultMapuserMapSELECT id, user_name FROM users WHERE id #{id}
/select这里的 resultMap 定义了实体类属性与数据库字段之间的映射property 是实体类的属性column 是数据库表的字段。
MyBatis 中如何实现分页
在 MyBatis 中实现分页可以有多种方式常见的方法包括手动编写分页 SQL、使用分页插件等。下面介绍几种实现分页的常见方式。
1. 手动编写分页 SQL
分页的原理是通过 SQL 的 LIMIT 和 OFFSET 子句来实现的这些子句通常用于 MySQL、PostgreSQL 等数据库。你可以手动编写分页 SQL将分页参数如页码和每页大小传递到 SQL 语句中。
select idselectUsers resultTypecom.example.UserSELECT * FROM usersLIMIT #{limit} OFFSET #{offset}
/select在 Mapper 中可以定义如下方法
ListUser selectUsers(Param(limit) int limit, Param(offset) int offset);在实际调用时可以根据当前页数和每页条目数计算出 limit 和 offset
int page 1; // 当前页码
int pageSize 10; // 每页大小
int limit pageSize;
int offset (page - 1) * pageSize;ListUser users userMapper.selectUsers(limit, offset);2. 使用 MyBatis-Plus 的分页插件
MyBatis-Plus 是 MyBatis 的增强工具提供了丰富的功能分页就是其中之一。MyBatis-Plus 提供了分页插件 PageHelper可以自动生成分页 SQL开发者只需要传递分页参数即可。
1) 引入 MyBatis-Plus 依赖
在 pom.xml 中添加 MyBatis-Plus 依赖
dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.1/version !-- 版本号可以根据需要调整 --
/dependency2) 配置分页插件
在 Spring Boot 项目中通常通过配置类来启用分页插件
Configuration
public class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 指定数据库类型return interceptor;}
}3) 使用分页查询
使用 MyBatis-Plus 提供的 Page 对象来进行分页查询
public IPageUser selectUserPage(PageUser page) {return userMapper.selectPage(page, null);
}调用分页查询时
PageUser page new Page(1, 10); // 第 1 页每页 10 条
IPageUser userPage userService.selectUserPage(page);
ListUser users userPage.getRecords();
long total userPage.getTotal(); // 总条数MyBatis-Plus 自动会根据分页参数生成 LIMIT 和 OFFSET 的 SQL 语句。
3. 使用 PageHelper 插件
PageHelper 是 MyBatis 的一个常用分页插件提供了分页功能的自动处理。
1) 引入 PageHelper 依赖
在 pom.xml 中添加 PageHelper 依赖
dependencygroupIdcom.github.pagehelper/groupIdartifactIdpagehelper-spring-boot-starter/artifactIdversion1.4.6/version
/dependency2) 配置 PageHelper
PageHelper 的配置可以通过 application.yml 来完成
pagehelper:helper-dialect: mysqlreasonable: truesupport-methods-arguments: true3) 使用 PageHelper 进行分页
在 Mapper 方法中调用分页逻辑
PageHelper.startPage(1, 10); // 第 1 页每页 10 条
ListUser users userMapper.selectUsers();
PageInfoUser pageInfo new PageInfo(users); // 封装分页信息PageHelper.startPage(pageNum, pageSize) 会拦截并修改 SQL自动添加分页参数。PageInfo 对象可以获取分页的详细信息如总条数、总页数等。
HashMap 和 Hashtable 可以作为查询结果吗
在 MyBatis 或其他类似的 ORM 框架中HashMap 和 Hashtable 可以用作查询结果的返回类型但它们在使用中有一些区别和限制取决于具体的使用场景和框架支持情况。
HashMap 和 Hashtable 作为查询结果
1. HashMap
HashMap 是常用的集合类它允许存储键值对并且线程不安全。HashMap 作为查询结果返回时通常用于结果集中包含动态或非结构化数据比如表的字段名作为键字段值作为值。
常见场景
当查询结果的字段与实体类无法直接对应时可以使用 HashMap 来存储结果。查询结果中字段是动态的无法确定字段名时使用 HashMap 是一个可行的解决方案。
示例
select idselectUserAsMap resultTypehashmapSELECT id, username, email FROM users WHERE id #{id}
/select在 Mapper 接口中返回类型可以是 HashMap
MapString, Object selectUserAsMap(int id);查询后结果会以键值对的形式存储在 HashMap 中键是数据库字段名值是对应的字段值。
2. Hashtable
Hashtable 和 HashMap 类似也是一个存储键值对的集合类不过 Hashtable 是线程安全的。尽管如此Hashtable 在现代 Java 开发中较少使用因为 HashMap 提供了更好的性能且一般不需要对每个操作进行同步。
在 MyBatis 中理论上你也可以将查询结果存储在 Hashtable 中但是这种用法比较少见且不推荐。Hashtable 的线程安全性和旧式设计使得它不如 HashMap 高效而且大部分场景下并不需要如此严格的线程安全保障。
示例
select idselectUserAsHashtable resultTypehashtableSELECT id, username, email FROM users WHERE id #{id}
/select在 Mapper 接口中返回类型可以是 Hashtable
HashtableString, Object selectUserAsHashtable(int id);3. 推荐使用 HashMap 而非 Hashtable
大多数情况下建议使用 HashMap 而不是 Hashtable。HashMap 在查询操作中更为高效并且在 MyBatis 或其他框架中使用更为广泛和灵活。只有在极特殊的情况下才会考虑使用 Hashtable 作为查询结果类型。
何时使用 HashMap 而不是实体类
动态查询结果如果查询的字段无法确定或者查询的列不固定如动态 SQL使用 HashMap 可以很好地处理这些情况。简化数据处理如果数据处理逻辑很简单且不需要进行复杂的对象映射操作使用 HashMap 可以避免定义实体类。
总结
HashMap在 MyBatis 中广泛使用可以用来存储动态的、非结构化的查询结果。它适合大多数场景尤其是字段名和字段数动态变化的情况下。Hashtable尽管可以作为查询结果类型但现代开发中很少使用HashMap 更具效率优势。除非有特别的线程安全需求一般不推荐使用 Hashtable。
说一说动态SQL
动态 SQL 的作用
动态 SQL 的主要目的是根据业务逻辑条件动态拼接 SQL 语句避免在代码中手动拼接 SQL提高代码的可读性、可维护性并减少 SQL 重复。动态 SQL 常用于以下场景
根据不同条件生成不同的查询。动态添加 WHERE 条件、ORDER BY 排序、GROUP BY 聚合等。动态更新、插入或删除操作。
动态 SQL 标签
MyBatis 提供了一些常见的动态 SQL 标签以下是这些标签的使用方式和示例。
1. if 标签
if 标签用于根据条件动态生成 SQL 片段。
示例
select idfindUsers parameterTypemap resultTypeUserSELECT * FROM userswhereif testusername ! nullAND username #{username}/ifif testemail ! nullAND email #{email}/if/where
/select这个查询会根据传入的 username 或 email 动态生成带有不同 WHERE 条件的 SQL 语句。
2. choose、when 和 otherwise 标签
这些标签可以模拟 switch-case 逻辑根据多个条件选择执行哪个 SQL 片段。
示例
select idfindUsers parameterTypemap resultTypeUserSELECT * FROM userswherechoosewhen testusername ! nullAND username #{username}/whenwhen testemail ! nullAND email #{email}/whenotherwiseAND status active/otherwise/choose/where
/select在这个例子中如果 username 不为空则使用 username 条件否则如果 email 不为空则使用 email 条件如果都不满足则使用默认的 status 条件。
3. where 标签
where 标签可以自动处理 SQL 语句中的 WHERE 和 AND 等条件拼接问题。它会智能地处理条件确保 SQL 语句的正确性。
示例
select idfindUsers parameterTypemap resultTypeUserSELECT * FROM userswhereif testusername ! nullusername #{username}/ifif testemail ! nullAND email #{email}/if/where
/selectwhere 会自动处理 WHERE 和 AND如果条件存在它会自动添加 WHERE 关键字如果有多个条件它会自动在这些条件之间添加 AND。
4. trim 标签
trim 标签允许手动控制 SQL 的前后缀内容比如去掉多余的 AND、OR 等或者添加特定的前后缀。
示例
select idfindUsers parameterTypemap resultTypeUserSELECT * FROM userstrim prefixWHERE prefixOverridesAND | ORif testusername ! nullAND username #{username}/ifif testemail ! nullAND email #{email}/if/trim
/select在这个例子中trim 标签会自动去掉前面多余的 AND 或 OR并确保 SQL 语句的前缀为 WHERE。
5. set 标签
set 标签专门用于 UPDATE 语句中它会自动处理逗号分隔的问题确保 SQL 语句的正确性。
示例
update idupdateUser parameterTypeUserUPDATE userssetif testusername ! nullusername #{username},/ifif testemail ! nullemail #{email},/ifif teststatus ! nullstatus #{status}/if/setWHERE id #{id}
/updateset 标签会自动处理逗号问题确保生成的 SQL 语句在最后一个字段后不会出现多余的逗号。
6. foreach 标签
foreach 标签用于处理集合类型的参数比如 List、Set 或数组常用于批量插入或 IN 查询等场景。
示例
select idfindUsersByIds parameterTypelist resultTypeUserSELECT * FROM users WHERE id INforeach itemid collectionlist open( separator, close)#{id}/foreach
/selectforeach 标签会根据传入的集合自动生成 IN 子句。
7. bind 标签
bind 标签允许将表达式结果绑定到一个变量中并在后续 SQL 语句中使用该变量。
示例
select idfindUsersLike parameterTypestring resultTypeUserbind namepattern value% username %/SELECT * FROM users WHERE username LIKE #{pattern}
/selectbind 标签将 username 拼接为一个模糊查询的字符串并绑定到 pattern 变量中。
动态 SQL 的优势
灵活性能够根据不同的业务逻辑条件动态生成 SQL避免大量重复代码。可维护性将复杂 SQL 的拼接逻辑交由 MyBatis 动态处理开发者无需手动拼接 SQL 字符串减少了拼写错误的可能性。效率可以避免不必要的查询条件从而提升查询效率。
说一下 MyBatis 的缓存机制
MyBatis 的缓存机制用于提高数据库操作的效率通过缓存查询结果来减少对数据库的访问。MyBatis 提供了一级缓存和二级缓存两种缓存机制分别适用于不同的场景和需求。
1. 一级缓存Local Cache
基本概念
一级缓存是 SqlSession 级别的缓存也称为本地缓存。它用于缓存当前 SqlSession 会话中的查询结果。一级缓存的生命周期与 SqlSession 一致即在同一 SqlSession 中的查询会使用一级缓存。
特点
默认启用一级缓存是 MyBatis 默认启用的不需要额外配置。作用范围缓存的作用范围限定在单个 SqlSession 会话内。不同 SqlSession 会话之间的缓存是不共享的。缓存刷新在 SqlSession 会话中如果执行了 insert、update 或 delete 操作这些操作会导致缓存被刷新保证缓存中的数据与数据库一致。
示例
try (SqlSession session sqlSessionFactory.openSession()) {User user1 session.selectOne(com.example.UserMapper.selectUser, 1);User user2 session.selectOne(com.example.UserMapper.selectUser, 1);// user1 和 user2 会从一级缓存中读取相同的数据
}2. 二级缓存Global Cache
基本概念
二级缓存是 SqlSessionFactory 级别的缓存也称为全局缓存。它在多个 SqlSession 之间共享。二级缓存的作用范围是跨 SqlSession 的所有使用同一个 SqlSessionFactory 的 SqlSession 都可以访问二级缓存。
特点
需要配置二级缓存默认是关闭的需要在配置文件中显式启用。缓存实现MyBatis 支持多种缓存实现包括内存缓存如 EHCache、Redis和自定义缓存。缓存刷新在对数据进行修改后例如 insert、update、delete缓存会被刷新以确保数据一致性。
配置示例 启用二级缓存 在映射文件中配置 cache 标签以启用二级缓存。 mapper namespacecom.example.UserMappercache/!-- 其他映射配置 --
/mapper配置二级缓存 可以在 mybatis-config.xml 文件中配置二级缓存的全局设置。 configurationsettingssetting namecacheEnabled valuetrue//settings
/configuration自定义缓存实现 configurationcache typecom.example.MyCustomCache/
/configuration缓存实现 内存缓存 EHCacheMyBatis 可以集成 EHCache 作为二级缓存的实现提供强大的缓存管理功能。Redis可以使用 Redis 作为二级缓存实现分布式缓存。 自定义缓存 可以通过实现 org.apache.ibatis.cache.Cache 接口创建自定义的缓存实现。 示例 public class MyCustomCache implements Cache {// 实现缓存接口的方法
}缓存策略 缓存失效 在进行 insert、update、delete 操作时相关的缓存会被刷新确保缓存中的数据与数据库一致。可以通过配置映射文件中的 flushCache 属性来控制缓存的刷新策略。 缓存配置 可以在映射文件中配置缓存的大小、过期时间等属性以优化缓存性能。 示例 cache evictionLRU flushInterval60000 size512 readOnlytrue/eviction缓存淘汰策略如 LRU、FIFO。flushInterval缓存刷新间隔毫秒。size缓存的最大大小。readOnly缓存是否为只读。
缓存的注意事项 N1 查询问题 延迟加载可能导致 N1 查询问题即在加载集合时会执行 N 次查询。可以通过优化 SQL 语句和使用批量加载来减少查询次数。 缓存一致性 二级缓存可能会存在缓存一致性问题尤其在分布式环境下。需要确保缓存的更新和失效机制正确以避免脏读问题。 性能影响 不合理的缓存配置或过度缓存可能会导致性能下降。需要根据实际需求合理配置缓存策略和大小。
总结
MyBatis 的缓存机制通过一级缓存和二级缓存提高了数据库操作的效率。一级缓存是 SqlSession 级别的缓存用于缓存单个会话中的查询结果。二级缓存是 SqlSessionFactory 级别的缓存用于跨会话共享缓存。二级缓存需要显式配置支持多种缓存实现和自定义缓存策略。合理使用缓存可以显著提高性能但需要注意缓存一致性和性能影响。
MyBatis 中有哪些设计模式
MyBatis 中使用了多种设计模式来实现其功能和优化性能。以下是一些关键的设计模式在 MyBatis 中的应用
1. 单例模式Singleton Pattern
应用场景
SqlSessionFactory 是 MyBatis 中的核心工厂用于创建 SqlSession 对象。通常一个应用只需要一个 SqlSessionFactory 实例以避免重复创建和配置。
实现方式
SqlSessionFactory 的实例通常是通过单例模式来管理的。SqlSessionFactoryBuilder 类负责创建 SqlSessionFactory 实例确保应用中只有一个 SqlSessionFactory 实例。
示例
public class MyBatisUtil {private static SqlSessionFactory sqlSessionFactory;static {try {InputStream inputStream Resources.getResourceAsStream(mybatis-config.xml);sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {throw new RuntimeException(e.getMessage(), e);}}public static SqlSessionFactory getSqlSessionFactory() {return sqlSessionFactory;}
}2. 代理模式Proxy Pattern
应用场景
MyBatis 使用代理模式来实现 Mapper 接口的动态代理。Mapper 接口的方法在运行时被动态代理生成实现了对 SQL 语句的调用和映射。
实现方式
MyBatis 通过 JDK 动态代理或 CGLIB 库生成 Mapper 接口的代理对象。这些代理对象会在调用方法时执行对应的 SQL 语句。
示例
public interface UserMapper {User selectUser(int id);
}UserMapper 接口的代理对象会执行 selectUser 方法并发起 SQL 查询。
3. 工厂模式Factory Pattern
应用场景
工厂模式用于创建 SqlSession 和 SqlSessionFactory 实例。它使得对象创建与使用分离提高了代码的灵活性和可维护性。
实现方式
SqlSessionFactoryBuilder 和 SqlSessionFactory 是工厂模式的具体实现。SqlSessionFactoryBuilder 负责创建 SqlSessionFactory 实例而 SqlSessionFactory 负责创建 SqlSession 实例。
示例
public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) {// 创建 SqlSessionFactory 实例的逻辑}
}4. 适配器模式Adapter Pattern
应用场景
适配器模式用于将 MyBatis 的功能与不同的数据源或缓存系统进行适配。例如将 MyBatis 与 EHCache 或 Redis 进行集成。
实现方式
MyBatis 提供了接口和适配器允许开发者将第三方库或自定义实现集成到 MyBatis 中从而实现对不同缓存系统或数据源的支持。
示例
cache typeorg.mybatis.caches.ehcache.EhcacheCache/5. 模板方法模式Template Method Pattern
应用场景
模板方法模式用于定义一个操作的算法骨架将一些步骤延迟到子类中实现。在 MyBatis 中这种模式用于封装数据库操作的常见步骤。
实现方式
SqlSession 类提供了数据库操作的模板方法例如 selectOne、selectList、insert、update 和 delete。这些方法封装了常见的数据库操作步骤用户只需关注具体的 SQL 语句和参数。
示例
public interface SqlSession {T T selectOne(String statement, Object parameter);E ListE selectList(String statement, Object parameter);int insert(String statement, Object parameter);int update(String statement, Object parameter);int delete(String statement, Object parameter);
}6. 建造者模式Builder Pattern
应用场景
建造者模式用于构建复杂的对象在 MyBatis 中主要用于构建 SqlSessionFactory 实例。
实现方式
SqlSessionFactoryBuilder 类实现了建造者模式允许通过链式调用配置不同的设置选项然后构建 SqlSessionFactory 实例。
示例
public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) {// 配置和创建 SqlSessionFactory 实例}
}7. 责任链模式Chain of Responsibility Pattern
应用场景
责任链模式用于处理请求的传递和处理MyBatis 中的拦截器机制就是这种模式的应用。
实现方式
MyBatis 的拦截器可以在 SQL 执行的各个阶段插入自定义逻辑例如请求拦截、结果处理等。多个拦截器可以按顺序链式处理请求。
示例
public class ExampleInterceptor implements Interceptor {Overridepublic Object intercept(Invocation invocation) throws Throwable {// 处理请求的逻辑}
}总结
MyBatis 采用了多种设计模式来实现其功能和优化性能包括单例模式、代理模式、工厂模式、适配器模式、模板方法模式、建造者模式和责任链模式。这些设计模式帮助 MyBatis 实现了对象管理、SQL 映射、缓存管理、数据库操作封装等功能提高了系统的灵活性、可维护性和性能。