1.Mybatis框架简述

:::tips MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。是一个半自动的ORM持久层框架。 ::: 优点:
1、与JDBC相比,减少了50%的代码量
2、 最简单的持久化框架,简单易学
3、SQL代码从程序代码中彻底分离出来,可以重用
4、提供XML标签,支持编写动态SQL
5、提供映射标签,支持对象与数据库的ORM字段关系映射
支持缓存、连接池、数据库移植….
缺点:
1、SQL语句编写工作量大,熟练度要高
2、数据库移植性比较差,如果需要切换数据库的话,SQL语句会有很大的差异

同类产品Hibenate框架相比,具有学习成本低,灵活性高等特点。

2.Mybatis框架使用

2.1.引入依赖 :::tips 创建maven项目并添加mybatis所需依赖 :::

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>org.example</groupId>
  7. <artifactId>mybatis-study</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <dependencies>
  10. <!--mybatis依赖-->
  11. <dependency>
  12. <groupId>org.mybatis</groupId>
  13. <artifactId>mybatis</artifactId>
  14. <version>3.5.4</version>
  15. </dependency>
  16. <!--mysql驱动依赖-->
  17. <dependency>
  18. <groupId>mysql</groupId>
  19. <artifactId>mysql-connector-java</artifactId>
  20. <version>8.0.19</version>
  21. </dependency>
  22. </dependencies>
  23. <build>
  24. <!--这个元素描述了项目相关的所有资源路径列表,例如和项目相关的属性文件,这些资源被包含在最终的打包文件里。-->
  25. <resources>
  26. <resource>
  27. <!-- 描述存放资源的目录,该路径相对POM路径-->
  28. <directory>src/main/java</directory>
  29. <includes>
  30. <include>**/*.xml</include>
  31. </includes>
  32. </resource>
  33. </resources>
  34. </build>
  35. </project>

2.2.创建配置类 :::tips 分别创建mybatis-config.xml与config.properties,如下所示: :::

  1. driver=com.mysql.cj.jdbc.Driver
  2. url=jdbc:mysql://localhost:3306/spring_db?serverTimezone=GMT%2B8
  3. username=root
  4. password=123456
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <!--引入外部配置-->
  7. <properties resource="config.properties"/>
  8. <settings>
  9. <!--开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。-->
  10. <setting name="mapUnderscoreToCamelCase" value="true"/>
  11. </settings>
  12. <environments default="development">
  13. <environment id="development">
  14. <transactionManager type="JDBC"/>
  15. <dataSource type="POOLED">
  16. <property name="driver" value="${driver}"/>
  17. <property name="url" value="${url}"/>
  18. <property name="username" value="${username}"/>
  19. <property name="password" value="${password}"/>
  20. </dataSource>
  21. </environment>
  22. </environments>
  23. <mappers>
  24. <mapper resource="UserMapper.xml"/>
  25. </mappers>
  26. </configuration>

2.3.创建实体与mapper接口

  1. @Data
  2. public class ProductEntity {
  3. private String productId;
  4. private String productName;
  5. private BigDecimal productPrice;
  6. private BigDecimal productDisPrice;
  7. private String originAddress;
  8. private String orderId;
  9. }
  1. import java.util.List;
  2. public interface UserMapper {
  3. List<ProductEntity> selectAll();
  4. }

2.4.测试类

  1. import org.apache.ibatis.session.SqlSession;
  2. import org.apache.ibatis.session.SqlSessionFactory;
  3. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.util.List;
  7. public class MybatisApp {
  8. public static void main(String[] args) throws IOException {
  9. String resource = "mybatis-config.xml";
  10. InputStream inputStream = MybatisApp.class.getClassLoader().getResourceAsStream(resource);
  11. // InputStream inputStream = Resources.getResourceAsStream(resource);
  12. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  13. try (SqlSession session = sqlSessionFactory.openSession()) {
  14. UserMapper mapper = session.getMapper(UserMapper.class);
  15. List<ProductEntity> blog = mapper.selectAll();
  16. System.out.println(blog);
  17. }
  18. }
  19. }

2.5.包结构如下
image.png
以上就是mybatis初体验,mybatis是不是用起来很方便?

3.Mybatis进阶使用

mybatis的强大特性之一还是其动态sql。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

  1. <select id="findActiveBlogWithTitleLike"
  2. resultType="Blog">
  3. SELECT * FROM BLOG
  4. WHERE state = ‘ACTIVE’
  5. <if test="title != null">
  6. AND title like #{title}
  7. </if>
  8. </select>

如果希望通过 “title” 和 “author” 两个参数进行可选搜索该怎么办呢?首先,我想先将语句名称修改成更名副其实的名称;接下来,只需要加入另一个条件即可。

  1. <select id="findActiveBlogLike"
  2. resultType="Blog">
  3. SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  4. <if test="title != null">
  5. AND title like #{title}
  6. </if>
  7. <if test="author != null and author.name != null">
  8. AND author_name like #{author.name}
  9. </if>
  10. </select>

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员挑选的 Blog)。

  1. <select id="findActiveBlogLike"
  2. resultType="Blog">
  3. SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  4. <choose>
  5. <when test="title != null">
  6. AND title like #{title}
  7. </when>
  8. <when test="author != null and author.name != null">
  9. AND author_name like #{author.name}
  10. </when>
  11. <otherwise>
  12. AND featured = 1
  13. </otherwise>
  14. </choose>
  15. </select>

trim、where、set

前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。

  1. <select id="findActiveBlogLike"
  2. resultType="Blog">
  3. SELECT * FROM BLOG
  4. WHERE
  5. <if test="state != null">
  6. state = #{state}
  7. </if>
  8. <if test="title != null">
  9. AND title like #{title}
  10. </if>
  11. <if test="author != null and author.name != null">
  12. AND author_name like #{author.name}
  13. </if>
  14. </select>

如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

  1. SELECT * FROM BLOG
  2. WHERE

这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

  1. SELECT * FROM BLOG
  2. WHERE
  3. AND title like ‘someTitle’

这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。
MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:

  1. <select id="findActiveBlogLike"
  2. resultType="Blog">
  3. SELECT * FROM BLOG
  4. <where>
  5. <if test="state != null">
  6. state = #{state}
  7. </if>
  8. <if test="title != null">
  9. AND title like #{title}
  10. </if>
  11. <if test="author != null and author.name != null">
  12. AND author_name like #{author.name}
  13. </if>
  14. </where>
  15. </select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

  1. <trim prefix="WHERE" prefixOverrides="AND |OR ">
  2. ...
  3. </trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

  1. <update id="updateAuthorIfNecessary">
  2. update Author
  3. <set>
  4. <if test="username != null">username=#{username},</if>
  5. <if test="password != null">password=#{password},</if>
  6. <if test="email != null">email=#{email},</if>
  7. <if test="bio != null">bio=#{bio}</if>
  8. </set>
  9. where id=#{id}
  10. </update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
来看看与 set 元素等价的自定义 trim 元素吧:

  1. <trim prefix="SET" suffixOverrides=",">
  2. ...
  3. </trim>

注意,我们覆盖了后缀值设置,并且自定义了前缀值。

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

  1. <select id="selectPostIn" resultType="domain.blog.Post">
  2. SELECT *
  3. FROM POST P
  4. WHERE ID in
  5. <foreach item="item" index="index" collection="list"
  6. open="(" separator="," close=")">
  7. #{item}
  8. </foreach>
  9. </select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
至此,我们已经完成了与 XML 配置及映射文件相关的讨论。下一章将详细探讨 Java API,以便你能充分利用已经创建的映射配置。

script

要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。比如:

  1. @Update({"<script>",
  2. "update Author",
  3. " <set>",
  4. " <if test='username != null'>username=#{username},</if>",
  5. " <if test='password != null'>password=#{password},</if>",
  6. " <if test='email != null'>email=#{email},</if>",
  7. " <if test='bio != null'>bio=#{bio}</if>",
  8. " </set>",
  9. "where id=#{id}",
  10. "</script>"})
  11. void updateAuthorValues(Author author);

bind

bind 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:

  1. <select id="selectBlogsLike" resultType="Blog">
  2. <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  3. SELECT * FROM BLOG
  4. WHERE title LIKE #{pattern}
  5. </select>

多数据库支持

如果配置了 databaseIdProvider,你就可以在动态代码中使用名为 “_databaseId” 的变量来为不同的数据库构建特定的语句。比如下面的例子:

  1. <insert id="insert">
  2. <selectKey keyProperty="id" resultType="int" order="BEFORE">
  3. <if test="_databaseId == 'oracle'">
  4. select seq_users.nextval from dual
  5. </if>
  6. <if test="_databaseId == 'db2'">
  7. select nextval for seq_users from sysibm.sysdummy1"
  8. </if>
  9. </selectKey>
  10. insert into users values (#{id}, #{name})
  11. </insert>

4.MyBatis-Spring

:::tips MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。 ::: 引入依赖

  1. <dependency>
  2. <groupId>org.mybatis</groupId>
  3. <artifactId>mybatis-spring</artifactId>
  4. <version>2.0.7</version>
  5. </dependency>

这里不做赘述,参考我的CSDN文章:SSM整合

5.Mybatis-SpringBoot

:::tips springboot项目集成mybatis,参考我的文章:Springboot之SSM ::: mybatis-spring-boot-starter依赖

  1. <!--mybatis整合springboot必须依赖-->
  2. <dependency>
  3. <groupId>org.mybatis.spring.boot</groupId>
  4. <artifactId>mybatis-spring-boot-starter</artifactId>
  5. <version>1.3.5</version>
  6. </dependency>
  7. <!--使用mysql数据库连接驱动-->
  8. <dependency>
  9. <groupId>mysql</groupId>
  10. <artifactId>mysql-connector-java</artifactId>
  11. <version>8.0.11</version>
  12. </dependency>

创建mybatis-config.xml

  1. <?xml version="1.0" encoding="GBK"?>
  2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
  3. <configuration>
  4. </configuration>

创建application.yml

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.cj.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/spring_db?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
  5. username: root
  6. password: 123456
  7. mybatis:
  8. mapper-locations: classpath:mybatis/mapper/*.xml
  9. type-aliases-package: com.itmck.microicrrisk.dao.entity

6.Mybatis-plus-SpringBoot

:::tips Mybatis-plus是mybatis的增强版.引入之后我们就不需要再做基本的CRUD操作。这里详细说一下当前方式。
推荐使用当前的架构:SpringBoot+Mybatis-plus+Mysql/Oracle ::: mybatis-plus依赖

  1. <!--这里就是主要引入的依赖-->
  2. <dependency>
  3. <groupId>com.baomidou</groupId>
  4. <artifactId>mybatis-plus-boot-starter</artifactId>
  5. <version>3.2.0</version>
  6. </dependency>
  7. <!--如果使用mysql引入如下依赖-->
  8. <dependency>
  9. <groupId>mysql</groupId>
  10. <artifactId>mysql-connector-java</artifactId>
  11. <scope>runtime</scope>
  12. </dependency>

application.yml配置

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.cj.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/spring_db?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
  5. username: root
  6. password: 123456
  7. mybatis-plus:
  8. mapper-locations: classpath*:mybatis/mapper/*.xml
  9. type-aliases-package: com.itmck.microicrrisk.dao.entity
  10. configuration:
  11. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

创建sql语句

  1. CREATE TABLE `user` (
  2. `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  3. `name` varchar(30) DEFAULT NULL COMMENT '姓名',
  4. `age` int(11) DEFAULT NULL COMMENT '年龄',
  5. `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=InnoDB AUTO_INCREMENT=1173113442471559178 DEFAULT CHARSET=utf8;

mybatis-plus中的分页插件

  1. /**
  2. * Create by it_mck 2019/9/15 14:50
  3. *
  4. * @Description: 使用mybatis-plus分页的时候要设置此项
  5. * @Version: 1.0
  6. */
  7. @Configuration
  8. public class MybatisplusConfig {
  9. @Bean
  10. public PaginationInterceptor paginationInterceptor() {
  11. PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
  12. // paginationInterceptor.setLimit(你的最大单页限制数量,默认 500 条,小于 0 如 -1 不受限制);
  13. paginationInterceptor.setDialectType("mysql");//指定 MySQL 方言,否则它可能不知道怎么写分页函数
  14. return paginationInterceptor;
  15. }
  16. }

测试

  1. @Data
  2. @TableName(value = "user")
  3. public class User {
  4. @TableId(value = "id",type = IdType.AUTO)
  5. private Long id;
  6. private String name;
  7. private Integer age;
  8. private String email;
  9. }
  1. @Mapper
  2. public interface UserMapper extends BaseMapper<User> {
  3. }
  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class MybatisPlusDemoApplicationTests {
  4. @Autowired
  5. private UserMapper userMapper;
  6. @Test
  7. public void testSelect() {
  8. System.out.println(("----- selectAll method test ------"));
  9. List<User> userList = userMapper.selectList(null);
  10. Assert.assertEquals(5, userList.size());
  11. userList.forEach(System.out::println);
  12. }
  13. @Test
  14. public void testSelect2() {
  15. System.out.println(("----- selectByPage method test ------"));
  16. //使用分页之前,必须先制定分页插件,否则分页无法使用
  17. //1-----------制定分页插件
  18. PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
  19. paginationInterceptor.setDialectType("mysql");//设置使用mysql作为分页
  20. //2-----------设置分页参数
  21. Page<User> userPage = new Page<>();
  22. userPage.setCurrent(1);
  23. userPage.setSize(5);
  24. //3----------------查询 SELECT id,name,email,age FROM user LIMIT ?,?
  25. IPage<User> userIPage = userMapper.selectPage(userPage, null);
  26. List<User> records = userIPage.getRecords();
  27. for (User record : records) {
  28. System.out.println("======================="+record);
  29. }
  30. }
  31. @Test
  32. public void testInsert() {
  33. System.out.println(("----- insert method test ------"));
  34. User user = new User();
  35. user.setName("wxp123");
  36. user.setAge(24);
  37. user.setEmail("956411425@qq.com");
  38. int i=0;
  39. while (i<5){
  40. int insert = userMapper.insert(user);//INSERT INTO user ( id, name, email, age ) VALUES ( ?, ?, ?, ? )
  41. i++;
  42. }
  43. }
  44. @Test
  45. public void testUpdate() {
  46. System.out.println(("----- update method test ------"));
  47. User user = new User();
  48. user.setName("mck");
  49. user.setAge(24);
  50. user.setEmail("347451331@qq.com");
  51. QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
  52. userQueryWrapper.eq("id", 1173108471533436929L);
  53. /**
  54. * 参数1:新的值
  55. */
  56. int update = userMapper.update(user, userQueryWrapper);//UPDATE user SET name=?, email=?, age=? WHERE (id = ?)
  57. Assert.assertEquals(1, update);
  58. if (update == 1){
  59. System.out.println("update成功");
  60. }else{
  61. System.out.println("update失败");
  62. }
  63. }
  64. @Test
  65. public void testDelete(){
  66. System.out.println(("----- delete method test ------"));
  67. // int i = userMapper.deleteById(1173113442471559170L);//删除一个 DELETE FROM user WHERE id=?
  68. // Assert.assertEquals(1, i);
  69. // HashMap<String, Object> map = new HashMap<>();
  70. // map.put("name", "wxp");
  71. // int i1 = userMapper.deleteByMap(map);//DELETE FROM user WHERE name = ?
  72. ArrayList<Long> longs = new ArrayList<>();
  73. longs.add(1173108471533436929L);
  74. longs.add(1173113442471559171L);
  75. int i = userMapper.deleteBatchIds(longs);//批量删除 DELETE FROM user WHERE id IN ( ? , ? )
  76. Assert.assertEquals(2, i);
  77. }
  78. @Test
  79. public void findAllByPage(){
  80. PageParam mySqlPage = new PageParam();
  81. mySqlPage.setCurrentPage(2);
  82. mySqlPage.setLimit(5);
  83. System.out.println("offset:"+mySqlPage.getOffset());
  84. List<User> list = userMapper.findAllByPage(mySqlPage);
  85. for (User user : list) {
  86. System.out.println("------------"+user);
  87. }
  88. }
  89. }