> 文章列表 > 【Mybatis】2—Mybatis基本用法

【Mybatis】2—Mybatis基本用法

【Mybatis】2—Mybatis基本用法

⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
笔记链接👉https://github.com/A-BigTree/Code_Learning
⭐⭐⭐⭐⭐⭐

如果可以,麻烦各位看官顺手点个star~😊

如果文章对你有所帮助,可以点赞👍收藏⭐支持一下博主~😆


文章目录

  • 3 Mybatis基本用法
    • 3.1 HelloWorld
      • 3.1.1 物理建模
      • 3.1.2 逻辑建模
        • 创建Maven项目加入依赖
        • 创建Java实体类
      • 3.1.3 搭建框架环境
        • 导入依赖
        • 配置文件
      • 3.1.4 测试代码
    • 3.2 HelloWorld强化
      • 3.2.1 加入日志
        • 加入依赖
        • 加入配置文件
        • 修改测试代码
      • 3.2.2 关联外部属性文件
        • 创建属性文件
        • 引入属性文件中的值
        • 引用属性文件中的值
      • 3.2.3 用上Mapper接口
        • 测试类中抽取代码
        • 声明Mapper接口
        • 测试方法
    • 3.3 给SQL语句传参
      • 3.3.1 `#{}`形式
      • 3.3.2 `${}`形式
    • 3.4 数据输入
      • 3.4.1 MyBatis总体机制概括
      • 3.4.2 概括说明
      • 3.4.3 单个简单类型参数
        • 抽象方法声明
        • SQL语句
      • 3.4.4 实体类型参数
        • 抽象方法声明
        • SQL语句
      • 3.4.5 零散的简单数据类型
        • 抽象方法声明
        • SQL语句
      • 3.4.6 Map类型参数
        • 抽象方法声明
        • SQL语句
        • 测试
        • 使用场景
    • 3.5 数据输出
      • 3.5.1 返回单个简单类型数据
        • 抽象方法
        • SQL语句
      • 3.5.2 返回实体类对象
        • 抽象方法
        • SQL语句
        • 增加配置自动识别对应关系
      • 3.5.3 返回Map类型
        • 抽象方法
        • SQL语句
      • 3.5.4 返回List类型
        • 抽象方法
        • SQL语句
      • 3.5.5 返回自增主键
        • 返回场景
        • SQL语句
        • 不支持自增主键的数据库
      • 3.5.6 使用resultMap

3 Mybatis基本用法

3.1 HelloWorld

3.1.1 物理建模

CREATE DATABASE `mybatis-example`;USE `mybatis-example`;CREATE TABLE `t_emp`(emp_id INT AUTO_INCREMENT,emp_name CHAR(100),emp_salary DOUBLE(10,5),PRIMARY KEY(emp_id)
);INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("tom",200.33);
INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("jerry",666.66);
INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("andy",777.77);

3.1.2 逻辑建模

创建Maven项目加入依赖

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version><scope>compile</scope>
</dependency>

创建Java实体类

实体类是和现实世界中某一个具体或抽象的概念对应,是软件开发过程中,为了管理现实世界中的数据而设计的模型。 实体类的多个不同的叫法:

  • domain:领域模型;
  • entity:实体;
  • POJO:Plain Old Java Object;
  • Java bean:一个Java类;
/*** 和数据库表 t_emp 对应的实体类* emp_id INT AUTO_INCREMENT* emp_name CHAR(100)* emp_salary DOUBLE(10,5)** Java 的实体类中,属性的类型不要使用基本数据类型,要使用包装类型。因为包装类型可以赋值为null,表示空,而基本数据类型不可以。*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {private Integer empId;private String empName;private Double empSalary;}

3.1.3 搭建框架环境

导入依赖

<!-- Mybatis核心 -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version>
</dependency><!-- junit测试 -->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope>
</dependency><!-- MySQL驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.3</version>
</dependency>

配置文件

Mybatis全局配置文件:

习惯上命名为 mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 --><environments default="development"><!-- environment表示配置Mybatis的一个具体的环境 --><environment id="development"><!-- Mybatis的内置的事务管理器 --><transactionManager type="JDBC"/><!-- 配置数据源 --><dataSource type="POOLED"><!-- 建立数据库连接的具体信息 --><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/><property name="username" value="root"/><property name="password" value="atguigu"/></dataSource></environment></environments><mappers><!-- Mapper注册:指定Mybatis映射文件的具体位置 --><!-- mapper标签:配置一个具体的Mapper映射文件 --><!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 --><!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 --><mapper resource="mappers/EmployeeMapper.xml"/></mappers></configuration>
  • 注意:配置文件存放的位置是src/main/resources目录下

Mybatis 映射配置文件:

相关概念:ORM(Object Relationship Mapping)对象关系映射。

  • 对象:Java的实体类对象;
  • 关系:关系型数据库;
  • 映射:二者之间的对应关系;

下表列举的是最简单的单表映射(一个表和一个类):

Java概念 数据库概念
属性 字段/列
对象 记录/行

注意:EmployeeMapper.xml所在的目录要和mybatis-config.xml中使用mapper标签配置的一致。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- mapper是根标签,namespace属性:在Mybatis全局范围内找到一个具体的Mapper配置 -->
<!-- 引入接口后,为了方便通过接口全类名来找到Mapper配置文件,所以通常将namespace属性设置为接口全类名 -->
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper"><!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 --><!-- resultType属性:指定封装查询结果的Java实体类的全类名 --><select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee"><!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符,在#{}内部还是要声明一个见名知意的名称 -->select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}</select>
</mapper>

3.1.4 测试代码

@Test
public void testSelectEmployee() throws IOException {// 1.创建SqlSessionFactory对象// ①声明Mybatis全局配置文件的路径String mybatisConfigFilePath = "mybatis-config.xml";// ②以输入流的形式加载Mybatis配置文件InputStream inputStream = Resources.getResourceAsStream(mybatisConfigFilePath);// ③基于读取Mybatis配置文件的输入流创建SqlSessionFactory对象SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 2.使用SqlSessionFactory对象开启一个会话SqlSession session = sessionFactory.openSession();// 3.根据Mapper配置文件的名称空间+SQL语句的id找到具体的SQL语句// 格式是:名称空间.SQL语句的idString statement = "com.atguigu.mybatis.dao.EmployeeMapper.selectEmployee";// 要传入SQL语句的参数Integer empId = 1;// 执行SQL语句Object result = session.selectOne(statement, empId);System.out.println("o = " + result);// 4.关闭SqlSessionsession.close();}

说明:

  • SqlSession:代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的会话);
  • SqlSessionFactory:是“生产”SqlSession的“工厂”;
  • 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象;

在这里插入图片描述

3.2 HelloWorld强化

3.2.1 加入日志

在Mybatis工作过程中,通过打印日志的方式,将要执行的SQL语句打印出来。

加入依赖

<!-- 日志 -->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version>
</dependency>

加入配置文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true"><!-- 指定日志输出的位置,ConsoleAppender表示输出到控制台 --><appender name="STDOUT"class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- 日志输出的格式 --><!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 --><pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern><charset>UTF-8</charset></encoder></appender><!-- 设置全局日志级别。日志级别按顺序分别是:TRACE、DEBUG、INFO、WARN、ERROR --><!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 --><root level="INFO"><!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender --><appender-ref ref="STDOUT" /></root><!-- 根据特殊需求指定局部日志级别 --><logger name="com.atguigu.mybatis" level="DEBUG" />
</configuration>

修改测试代码

这里我们仅仅修改查询结果的打印方式,为此需要做两件事:

  • 在类上加@Slf4j注解;
  • 使用Logger对象打印数据;
if (result != null) {log.info(result.toString());
}

3.2.2 关联外部属性文件

在实际开发时,同一套代码往往会对应多个不同的具体服务器环境。使用的数据库连接参数也不同。为了更好的维护这些信息,我们建议把数据库连接信息提取到Mybatis全局配置文件外边。

创建属性文件

wechat.dev.driver=com.mysql.jdbc.Driver
wechat.dev.url=jdbc:mysql://192.168.198.100:3306/mybatis-example
wechat.dev.username=root
wechat.dev.password=atguiguwechat.test.driver=com.mysql.jdbc.Driver
wechat.test.url=jdbc:mysql://192.168.198.150:3306/mybatis-example
wechat.test.username=root
wechat.test.password=atguiguwechat.product.driver=com.mysql.jdbc.Driver
wechat.product.url=jdbc:mysql://192.168.198.200:3306/mybatis-example
wechat.product.username=root
wechat.product.password=atguigu

引入属性文件中的值

Mybatis全局配置文件中指定外部jdbc.properties文件的位置。

<properties resource="jdbc.properties"/>

引用属性文件中的值

在需要具体属性值的时候使用${key}格式引用属性文件中的键

<dataSource type="POOLED"><!-- 建立数据库连接的具体信息(引用了外部属性文件中的数据) --><property name="driver" value="${wechat.dev.driver}"/><property name="url" value="${wechat.dev.url}"/><property name="username" value="${wechat.dev.username}"/><property name="password" value="${wechat.dev.password}"/>
</dataSource>

3.2.3 用上Mapper接口

Mybatis中的Mapper接口相当于以前的Dao。但是区别在于,Mapper仅仅只是建接口即可,我们不需要提供实现类。该用法的思路如下图所示:

在这里插入图片描述

测试类中抽取代码

这一步和Mapper接口没关系,只是对代码本身的优化:

    private SqlSession session;// junit会在每一个@Test方法前执行@Before方法@Beforepublic void init() throws IOException {session = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")).openSession();}// junit会在每一个@Test方法后执行@After方法@Afterpublic void clear() {session.commit();session.close();}

声明Mapper接口

public interface EmployeeMapper {Employee selectEmployee(Integer empId);}
  • 方法名和SQL的id一致;
  • 方法返回值和resultType一致;
  • 方法的参数和SQL的参数一致;
  • 接口的全类名和映射配置文件的名称空间一致;

测试方法

 @Test
public void testUsrMapperInterface() {// 1.根据EmployeeMapper接口的Class对象获取Mapper接口类型的对象EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);// 2.调用EmployeeMapper接口的方法完成对数据库的操作Employee emp = employeeMapper.selectEmployee(1);// 3.打印查询结果if (emp != null)log.info(emp.toString());
}

3.3 给SQL语句传参

3.3.1 #{}形式

Mybatis会将SQL语句中的#{}转换为问号占位符。

3.3.2 ${}形式

${}形式传参,底层Mybatis做的是字符串拼接操作。

通常不会采用${}的方式传值。 一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用${}。 结论:实际开发中,能用#{}实现的,肯定不用${}

3.4 数据输入

3.4.1 MyBatis总体机制概括

在这里插入图片描述

3.4.2 概括说明

这里数据输入具体是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。

  • 简单类型:只包含一个值的数据类型
    • 基本数据类型:int、byte、short、double、……
    • 基本数据类型的包装类型:Integer、Character、Double、……
    • 字符串类型:String
  • 复杂类型:包含多个值的数据类型
    • 实体类类型:Employee、Department、……
    • 集合类型:List、Set、Map、……
    • 数组类型:int[]、String[]、……
    • 复合类型:List、实体类中包含集合……

3.4.3 单个简单类型参数

抽象方法声明

Employee selectEmployee(Integer empId);

SQL语句

<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}</select>

单个简单类型参数,在#{}中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名

3.4.4 实体类型参数

抽象方法声明

int insertEmployee(Employee employee);

SQL语句

<insert id="insertEmployee">insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})</insert>

在这里插入图片描述

Mybatis会根据#{}中传入的数据,加工成getXxx()方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}解析后的问号占位符这个位置。

3.4.5 零散的简单数据类型

抽象方法声明

int updateEmployee(@Param("empId") Integer empId,@Param("empSalary") Double empSalary);

SQL语句

<update id="updateEmployee">update t_emp set emp_salary=#{empSalary} where emp_id=#{empId}</update>

3.4.6 Map类型参数

抽象方法声明

int updateEmployeeByMap(Map<String, Object> paramMap);

SQL语句

<update id="updateEmployeeByMap">update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey}</update>

测试

@Test
public void testUpdateEmpNameByMap() {EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);Map<String, Object> paramMap = new HashMap<>();paramMap.put("empSalaryKey", 999.99);paramMap.put("empIdKey", 5);int result = mapper.updateEmployeeByMap(paramMap);log.info("result = " + result);}

使用场景

有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。

3.5 数据输出

数据输出总体上有两种形式:

  • 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
  • 查询操作的查询结果

3.5.1 返回单个简单类型数据

抽象方法

int selectEmpCount();

SQL语句

<select id="selectEmpCount" resultType="int">select count(*) from t_emp</select>

Mybatis内部给常用的数据类型设定了很多别名。 以int类型为例,可以写的名称有:intintegerIntegerjava.lang.IntegerIntINTINTEGER等等。

3.5.2 返回实体类对象

抽象方法

Employee selectEmployee(Integer empId);

SQL语句

<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee"><!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 --><!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}</select>

通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来。

增加配置自动识别对应关系

在 Mybatis 全局配置文件中,做了下面的配置,select语句中可以不给字段设置别名

<!-- 在全局范围内对Mybatis进行配置 -->
<settings><!-- 具体配置 --><!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 --><!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 --><!-- 规则要求数据库表字段命名方式:单词_单词 --><!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 --><setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

3.5.3 返回Map类型

适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。

抽象方法

Map<String,Object> selectEmpNameAndMaxSalary();

SQL语句

<!-- Map<String,Object> selectEmpNameAndMaxSalary(); -->
<!-- 返回工资最高的员工的姓名和他的工资 -->
<select id="selectEmpNameAndMaxSalary" resultType="map">SELECTemp_name 员工姓名,emp_salary 员工工资,(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资FROM t_emp WHERE emp_salary=(SELECT MAX(emp_salary) FROM t_emp)</select>

3.5.4 返回List类型

查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。

抽象方法

List<Employee> selectAll();

SQL语句

<!-- List<Employee> selectAll(); --><select id="selectAll" resultType="com.atguigu.mybatis.entity.Employee">select emp_id empId,emp_name empName,emp_salary empSalaryfrom t_emp</select>

3.5.5 返回自增主键

返回场景

例如:保存订单信息。需要保存Order对象和List<OrderItem>。其中,OrderItem对应的数据库表,包含一个外键,指向Order对应表的主键。

在保存List<OrderItem>的时候,需要使用下面的SQL:

insert into t_order_item(item_name,item_price,item_count,order_id) values(...)

这里需要用到的order_id,是在保存Order对象时,数据库表以自增方式产生的,需要特殊办法拿到这个自增的主键值。至于,为什么不能通过查询最大主键的方式解决这个问题,参考下图:

在这里插入图片描述

SQL语句

<!-- int insertEmployee(Employee employee); -->
<!-- useGeneratedKeys属性字面意思就是“使用生成的主键” -->
<!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 -->
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId">insert into t_emp(emp_name,emp_salary)values(#{empName},#{empSalary})</insert>

Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回

不支持自增主键的数据库

而对于不支持自增型主键的数据库(例如 Oracle),则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用

<insert id="insertEmployee" parameterType="com.atguigu.mybatis.beans.Employee"  databaseId="oracle"><selectKey order="BEFORE" keyProperty="id" resultType="integer">select employee_seq.nextval from dual </selectKey>  insert into orcl_employee(id,last_name,email,gender) values(#{id},#{lastName},#{email},#{gender})</insert>

或者是

<insert id="insertEmployee" parameterType="com.atguigu.mybatis.beans.Employee"  databaseId="oracle"><selectKey order="AFTER" keyProperty="id" resultType="integer">select employee_seq.currval from dual </selectKey>  insert into orcl_employee(id,last_name,email,gender) values(employee_seq.nextval,#{lastName},#{email},#{gender})</insert>

3.5.6 使用resultMap

使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系

<!-- 专门声明一个resultMap设定column到property之间的对应关系 -->
<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee"><!-- 使用id标签设置主键列和主键属性之间的对应关系 --><!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 --><id column="emp_id" property="empId"/><!-- 使用result标签设置普通字段和Java实体类属性之间的关系 --><result column="emp_name" property="empName"/><result column="emp_salary" property="empSalary"/></resultMap><!-- Employee selectEmployeeByRM(Integer empId); -->
<select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}</select>