SSM 框架学习: Mybatis 框架简介和基本使用 Published on May 3, 2024 in 日常 with 0 comment Java Mybatis ### 一. MyBatis 简介  #### 1.1 什么是 MyBatis Mybatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程和高级映射。其免除了几乎所有的JDBC代码以及参数设置和获取结果集的工作。它能够通过简单的 XML 或者注解来配置和映射原始类型、接口和 Java POLO 为数据库中的记录。 #### 1.2 快速入门 (1) 准备一个数据库脚本,并插入几条用于测试的数据。 ```sql create table emp ( emp_id int auto_increment primary key, emp_name varchar(100) not null, emp_salary double(10, 5) not null ); INSERT INTO emp (emp_name,emp_salary) VALUES ('tom',7531.5), ('jerry',8000.0), ('mike',6995.2); ``` (2) 搭建项目  修改 pom 文件,导入项目所需要的相关依赖: ```json 4.0.0 org.alen ssm-mybatis-part 1.0-SNAPSHOT pom mybatis-quick-01 8 8 UTF-8 org.mybatis mybatis 3.5.11 mysql mysql-connector-java 8.0.25 org.junit.jupiter junit-jupiter-api 5.3.1 ``` (3) 准备一个实体类 ```java public class Employee { private Integer empId; private String empName; private Double empSalary; public Integer getEmpId() { return empId; } public String getEmpName() { return empName; } public Double getEmpSalary() { return empSalary; } public void setEmpId(Integer empId) { this.empId = empId; } public void setEmpName(String empName) { this.empName = empName; } public void setEmpSalary(Double empSalary) { this.empSalary = empSalary; } } ``` (4) 准备 Mapper 接口和 MapperXML 文件 在 xxxMapper 中定义接口,规定方法。然后,在 xxxMapper.xml 文件中实现对应的 SQL 语句。 ```java public interface EmployeeMapper { // 根据 ID 查询员工信息 Employee queryById(Integer id); int deleteById(Integer id); } ``` xml 配置如下所示,这里需要注意的是在查询语句时列表字段需要和实体类的字段保持一致,否则查询返回的值为null。 ```xml select emp_id empId , emp_name empName, emp_salary empSalary from emp where emp_id = #{id} delete from emp where emp_id = #{id} ``` (5) 准备 MyBatis 配置文件 包括数据库连接信息、性能配置、mapper.xml 配置等。习惯上命名为 mybatis-config.xml。不过,后面整合 xml 后这个配置文件也可以省略。 ```xml ``` (6) 测试一下 ```java public class MybatisTest { // 通过 mybatis 的 api 进行调用 @Test public void test_01() throws IOException { // 1. 读取外部配置文件 InputStream ips = Resources.getResourceAsStream("mybatis-config.xml"); // 2. 创建 sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips); // 3. 根据 sqlSessionFactory 创建 sqlSession 用完就释放 SqlSession sqlSession = sqlSessionFactory.openSession(); // 4. 获取接口的代理对象 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); Employee employee = mapper.queryById(1); System.out.println("employee = " + employee); // 5. 提交事务 释放资源 sqlSession.commit(); sqlSession.close(); } } ```  ### 二. MyBatis 基本使用 #### 2.1 向 SQL 语句传参 (1)日志输出的配置 在 XML 文件中添加一个 settings 设置,如下所示: ```xml ``` 更多的配置项目可以直接参考 Mybatis 的参考文档进行实现:[https://mybatis.net.cn/configuration.html#settings](https://mybatis.net.cn/configuration.html#settings "https://mybatis.net.cn/configuration.html#settings") (2)取值符号 # 和 $ 的区别 ```xml ``` #### 2.2 数据输入 (1)简单类型参数 包括一个值的数据类型,如基本数据类型 int 、 float、short 等;基本数据类型的包装类型:Integer、Character、Double 等以及字符串类型 String。 ```xml delete from emp where emp_name = #{name}; ``` (2) 传入实体类型参数 例如在这个数据表中要插入一个员工的实体对象:``int insertEmp(Employee employee);`` ```xml insert into emp (emp_name, emp_salary) values (#{empName},#{empSalary}) ``` (3)传入多个简单类型的参数: 例如在数据表中根据姓名和薪水查询某个人的信息,在xml中可以如下配置: ```xml select emp_id empId, emp_name empName, emp_salary empSalary from emp where emp_name = #{a} and emp_salary = #{b} ``` 但是在这里,key 的表达不可以随便写。具体来说有两种实现方式。 一种是注解实现,在接口方法中添加 @Param 注解,如下所示: `Employee queryByList(@Param("a") String name, @Param("b") Double salary);` 另一种是利用 Mybatis 的默认机制,形参从左到右依次对应 arg0 、arg1 ...则刚才的 xml 需要更改成如下的形式: ```xml select emp_id empId, emp_name empName, emp_salary empSalary from emp where emp_name = #{arg0} and emp_salary = #{arg1} ``` (4)传入 Map 类型的参数 ```java // 插入员工 map name为员工名,salary为员工薪水 注意,mapper 接口不允许重载 int insertEmpMap(Map data); ``` ```xml insert into emp (emp_name, emp_salary) values (#{name}, #{salary}) ``` #### 2.3 数据输出 (1) 单个简单类型 案例:根据人员 ID 返回雇员的姓名信息。在 Mapper 接口中定义一个方法:`tring queryNameById(Integer id);` 编辑对应的 Mapper XML 文件,添加一个 select 标签,resultType 如下所示: ```xml select emp_name from emp where emp_id = #{id} ``` 在这里发现,一般数据库中命名是下划线形式,而在 Java 编程中更常见的是驼峰式命名法,这就导致在编写 SQL 时需要添加额外的列名信息。因而可以进行配置,实现两种命名方法的自动映射,在 config 文件中添加如下配置: ```xml ``` (2) 返回 Map 类型 一般情况下,如果想返回多个值,但是又没有与之关联的实体类,那么就可以使用 Map 的形式返回数据。在编写mapper.xml 时,仅需要把标签中的 resultType 值修改为 "map" 即可。 (3) 返回集合类型 案例:查询全部员工信息 `List queryAll();` 值得注意的是,返回值是集合。因而在 resultType 中不需要指定集合类型,只需要指定泛型即可。在 mapper 的 XML 文件中,增加如下的标签内容: ```xml select * from emp ``` (4)自增长主键回显与自动事务提交 还是举插入一名员工的例子,在 Mapper 接口中添加一个新增员工的方法: `int insertEmp(Employee employee);` 同时,在 XML 文件中添加一段标签: ```xml insert into emp (emp_name, emp_salary) values (#{empName},#{empSalary}) ``` 然后,编写一个测试方法: ```java @Test public void test_01() throws IOException { // 1. 读取外部配置文件 InputStream ips = Resources.getResourceAsStream("mybatis-config.xml"); // 2. 创建 sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips); // 3. 根据 sqlSessionFactory 创建 sqlSession 用完就释放 SqlSession sqlSession = sqlSessionFactory.openSession(); // 4. 获取接口的代理对象 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); Employee employee = new Employee(); employee.setEmpName("Jazmine"); employee.setEmpSalary(13213.51); int rows = mapper.insertEmp(employee); System.out.println("rows = " + rows); // 5. 提交事务 释放资源 //sqlSession.commit(); sqlSession.close(); } ``` 这个时候会发现,由于注释掉了 `commit` 事务提交,但是数据库并没有发生实际的变化。这个时候,有一个简单的办法就是增加一个自动提交的参数,即在 `openSession`中传入一个参数,使语句变成 `sqlSessionFactory.openSession(True);`即可 ,效果如下:  如果这个时候,希望在数据插入之后,获取到该条数据的主键值(前提是该列主键数据是 auto_increment 形式),只需要在 Mapper.XML 部分,编辑标签如下所示: ```xml insert into emp (emp_name, emp_salary) values (#{empName},#{empSalary}) ``` 测试效果: ```java Employee employee = new Employee(); employee.setEmpName("Jazmine"); employee.setEmpSalary(13213.51); System.out.println(employee.getEmpId()); System.out.println("--------------------"); int rows = mapper.insertEmp(employee); System.out.println("rows = " + rows); System.out.println(employee.getEmpId()); ```  (5)非自增长主键 准备一个新的数据表,名称为 worker,建表语句如下: ```sql create table worker ( id varchar(64) primary key , name varchar(20) ); ``` 准备一个对应的实现类: ```java public class Teacher { private String id; private String name; public String getId() { return id; } public String getName() { return name; } public void setId(String id) { this.id = id; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Teacher{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } } ``` 修改 Mybatis-config 配置文件, 把新建的 mapper 信息添加进去: ```xml ``` 编辑 WorkerMapper.xml 文件,添加一个 insert 标签。 ```xml insert into worker (id, name) values (#{id},#{name}) ``` 编写一个测试方法,看一下刚才的配置效果: ```java @Test public void test_02() throws IOException { // 1. 读取外部配置文件 InputStream ips = Resources.getResourceAsStream("mybatis-config.xml"); // 2. 创建 sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips); // 3. 根据 sqlSessionFactory 创建 sqlSession 用完就释放 SqlSession sqlSession = sqlSessionFactory.openSession(); // 4. 获取接口的代理对象 WorkerMapper mapper = sqlSession.getMapper(WorkerMapper.class); // 自己维护主键 String id = UUID.randomUUID().toString().replace("-",""); Worker worker = new Worker(); worker.setName("Jack"); worker.setId(id); int i = mapper.insertWorker(worker); System.out.println("rows = " + i); // 5. 提交事务 释放资源 sqlSession.commit(); sqlSession.close(); } ```  这里发现带有 UUID 的主键数据已经被插入到了数据库中。但是,非自增长的主键能否交给 Mybatis 来维护呢,答案是可以的。如下配置所示,再插入之前,可以先指定一段 SQL 语句,生成一个主键,如下所示: ```xml select replace(UUID(),'-',''); insert into worker (id, name) values (#{id},#{name}) ``` ### 三. MyBatis 多表映射 #### 3.1 实体类设计 实体类设计的一些技巧: (1)如果是一对一的关系,那么属性中包含对方对象即可。 (2)如果是一对多的关系,那么属性中包含对方对象集合。 ##### 3.1.1 对一映射 要求根据 ID 查询订单,以及订单关联的用户的信息。 (1) 顾客表设计 ```sql CREATE TABLE customer ( id INT auto_increment NOT NULL, name varchar(100) NOT null, primary key(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; ``` (2)订单表设计 ```sql CREATE TABLE ssm.`order` ( id INT auto_increment NOT null primary key, name varchar(100) NOT NULL, customer_id INT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; ``` (3)设计对应的实体类 ```java public class Order { private Integer id; private String name; private Integer customerId; // 用这个对象装客户信息 private Customer customer; public Integer getId() { return id; } public String getName() { return name; } public Integer getCustomerId() { return customerId; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public void setCustomerId(Integer customerId) { this.customerId = customerId; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } } public class Customer { private Integer id; private String name; public Integer getId() { return id; } public String getName() { return name; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } } ``` (4) 添加 Mapper 接口 ```xml SELECT * FROM ssm.`ORDER` JOIN ssm.CUSTOMER ON ssm.`ORDER`.CUSTOMER_ID = ssm.CUSTOMER.ID WHERE ssm.`ORDER`.ID = #{id} ``` (5) 配置 mybatis-config.xml 并测试上述代码 配置文件从刚才的项目中复制过来,然后编写如下的测试代码: ```java public class MybatisTest { private SqlSession session; @BeforeEach public void init() throws IOException { session = new SqlSessionFactoryBuilder() .build(Resources.getResourceAsStream("mybatis-config.xml")) .openSession(); } @AfterEach public void clean(){ session.close(); } @Test public void testOne(){ // 查询订单和对应的客户 OrderMapper mapper = session.getMapper(OrderMapper.class); Order order = mapper.queryOrderById(1); System.out.println("------order-------: "+order.getName()); } } ``` 在这个案例中,有几个需要注意的地方,首先不建议使用 order 这种和数据库关键字重复的单次作为表名,另外在 xml 配置中,对于 type 类型要么写全地址,要不就在配置文件中添加别名。 ##### 3.1.2 对多映射 案例:要求查询客户和与客户关联的全部订单信息。 (1) CustomerMapper 接口: `List queryList();` (2)编写实体类: ```java public class Customer { private Integer customerId; private String customerName; private List orderList; public Integer getCustomerId() { return customerId; } public String getCustomerName() { return customerName; } public void setCustomerId(Integer customerId) { this.customerId = customerId; } public void setCustomerName(String customerName) { this.customerName = customerName; } public List getOrderList() { return orderList; } public void setOrderList(List orderList) { this.orderList = orderList; } } public class Order { private Integer orderId; private String orderName; private Integer customerId; public Integer getOrderId() { return orderId; } public String getOrderName() { return orderName; } public Integer getCustomerId() { return customerId; } public void setOrderId(Integer orderId) { this.orderId = orderId; } public void setOrderName(String orderName) { this.orderName = orderName; } public void setCustomerId(Integer customerId) { this.customerId = customerId; } } ``` (3)创建对应的 Mapper: ```xml SELECT * FROM `SSM`.`ORDER` AS TOR JOIN CUSTOMER ON TOR.CUSTOMER_ID = CUSTOMER.ID ``` 最后在测试类上测试一下,看看效果: ```java public class MybatisTest { private SqlSession session; @BeforeEach public void init() throws IOException { session = new SqlSessionFactoryBuilder() .build(Resources.getResourceAsStream("mybatis-config.xml")) .openSession(); } @AfterEach public void clean(){ session.close(); } @Test public void testOne(){ // 查询订单和对应的客户 CustomerMapper mapper = session.getMapper(CustomerMapper.class); List customers = mapper.queryList(); System.out.println("------customerList-------: " + customers.toString()); } } ``` 这里没有报错,但是后续在编码的时候,库表列名设置的时候不要重复! ### 四. MyBatis 动态语句 经常遇到很多按照多条件查询的情况,传统的 JDBC 框架只能按照不同条件进行相应的拼接处理,操作比较复杂。但是使用 Mybatis,利用动态 SQL 就可以相对简单的实现这个需求。 > 这个章节引用了一篇比较不错的博客,原文在这里:https://blog.csdn.net/qq_39249094/article/details/107199696 #### 4.1 where - if 标签 ```java select count(*) from user where id = #{id} and username = 'xiaoming' ``` 在这里,如果传入的 id 不为 null, 那么才会 SQL 才会拼接 id = #{id}。在这里如果传入的 id 为 null,那么最终的 SQL 语句就变成了 `select count(*) from user where and username = 'xiaoming'`。这语句就会有问题,因而引入 where 标签来解决这个问题,语句修改为下方示例: ```java select count(*) from user id = #{id} and username = 'xiaoming' ``` where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入 WHERE 子句,否则会自动进行清除,防止报错。 #### 4.2 set 标签 这个标签一般用于更新语句当中,例如: ```java update emp emp_name = #{empName} , emp_salary = #{empSalary} where emp_id = #{empId} ``` 在这里,如果不加 set 标签,会发现如果 if 条件全部满足或者第二个条件满足的情况下才不会出错,否则会出现语法错误。而添加 set 标签后,会自动清除多余的逗号并添加 set 关键字。 #### 4.3 choose 标签 (1)当 id 和 username 都不为空的时候, 那么选择二选一(前者优先)。 (2)如果都为空,那么就选择 otherwise 中的。 (3)如果 id 和 username 只有一个不为空,那么就选择不为空的那个。 ```xml select count(*) from user and id = #{id} and username = #{username} and age = 18 ``` #### 4.4 其他标签 除此之外,还有 Foreach 批量操作标签、sql标签等,可以根据业务实际情况来使用。 ### 五. 拓展使用 #### 5.1 Mybatis 插件机制和分页插件 分页插件名称为 PageHelper,使用方法大致分为以下的几个步骤: (1)pom.xml 中引入该依赖。 ```java com.github.pagehelper pagehelper 5.1.1 ``` (2) mybatis-config.xml 配置分页文件 ```xml ``` (3) 编写 Mapper 接口和对应的 XML 文件: 接口内容:`List queryAll();` ```xml select * from emp ``` (4) 编写测试类: ```java @org.junit.jupiter.api.Test public void testOne(){ EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); // 调用之前 先设置分页参数,如当前是第几页 每页显示多少 PageHelper.startPage(1,2); List list = mapper.queryAll(); // 将分页情况封装到一个PageInfo 的分页实体类中 PageInfo pageInfo = new PageInfo<>(list); // 获取分页的数据 System.out.println(pageInfo); } ```  本文由 Alen 创作,采用 知识共享署名4.0 国际许可协议进行许可本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名最后编辑时间为: May 15, 2024 at 05:51 pm