Mybatis入门学习之基本用法

目录

第一章 前言

1、总体技术体系

①单一架构

②分布式架构

2、框架的概念

3、Mybatis历史

4、Mybatis下载地址

5、Mybatis特性

6、和其它持久化层技术对比

第二章 Mybatis基本用法 

第一节 HelloWorld

1、物理建模

2、逻辑建模

①创建Maven module

②创建 Java 实体类

3、搭建框架开发环境

①导入依赖

②准备配置文件

4、junit测试代码

5、修正一个误区

①误区

②图解

③源码

第二节 HelloWorld强化

1、加入日志

①目的

②操作

③日志的级别

④STDOUT

⑤打印效果

2、关联外部属性文件

①需求

②做法

3、用上 Mapper 接口

①思路

②调整junit代码

③完成Mapper接口

④最终的junit测试方法

4、增删改操作

①insert

②delete

③update

第三节 给SQL语句传参

1、#{}方式

2、${}方式

①SQL语句

②Mapper接口

③junit测试

④实际打印的SQL

⑤应用场景举例

第四节 数据输入

1、Mybatis总体机制概括

2、概念说明

3、单个简单类型参数

①Mapper接口中抽象方法的声明

②SQL语句

4、实体类类型参数

①Mapper接口中抽象方法的声明

②SQL语句

③对应关系

④结论

5、零散的简单类型数据

①Mapper接口中抽象方法的声明

②SQL语句

③对应关系

6、Map类型参数

①Mapper接口中抽象方法的声明

②SQL语句

③junit测试

④对应关系

⑤使用场景

第五节 数据输出

1、返回单个简单类型数据

①Mapper接口中的抽象方法

②SQL语句

③junit测试

2、返回实体类对象

①Mapper接口的抽象方法

②SQL语句

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

3、返回Map类型

①Mapper接口的抽象方法

②SQL语句

③junit测试

4、返回List类型

①Mapper接口中抽象方法

②SQL语句

③junit测试

5、返回自增主键

①使用场景

②在Mapper配置文件中设置方式

④注意

⑤不支持自增主键的数据库

6、数据库表字段和实体类属性对应关系

①别名

②全局配置自动识别驼峰式命名规则

③使用resultMap


第一章 前言

1、总体技术体系

①单一架构

一个项目,一个工程,导出为一个war包,在一个Tomcat上运行。也叫all in one。

②分布式架构

一个项目(对应 IDEA 中的一个 project),拆分成很多个模块,每个模块是一个 IDEA 中的一个 module。每一个工程都是运行在自己的 Tomcat 上。模块之间可以互相调用。每一个模块内部可以看成是一个单一架构的应用。

2、框架的概念

框架=jar包+配置文件

3、Mybatis历史

MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。

iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。

4、Mybatis下载地址

https://github.com/mybatis/mybatis-3

5、Mybatis特性

  • MyBatis支持定制化SQL、存储过程以及高级映射
  • MyBatis避免了几乎所有的JDBC代码和手动设置参数以及结果集解析操作
  • MyBatis可以使用简单的XML或注解实现配置和原始映射;将接口和Java的POJO(Plain Ordinary Java Object,普通的Java对象)映射成数据库中的记录
  • Mybatis是一个半自动的ORM(Object Relation Mapping)框架

6、和其它持久化层技术对比

  • JDBC
    • SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
    • 维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
    • 代码冗长,开发效率低
  • Hibernate 和 JPA
    • 操作简便,开发效率高
    • 程序中的长难复杂 SQL 需要绕过框架
    • 内部自动生成的 SQL,不容易做特殊优化
    • 基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
    • 反射操作太多,导致数据库性能下降
  • MyBatis
    • 轻量级,性能出色
    • SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
    • 开发效率稍逊于 HIbernate,但是完全能够接收

第二章 Mybatis基本用法 

第一节 HelloWorld

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);

2、逻辑建模

①创建Maven module

②创建 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,表示空,而基本数据类型不可以。
 */
public class Employee {
    
    private Integer empId;
    
    private String empName;
    
    private Double empSalary;
    
    public Employee() {
    
    }
    
    public Integer getEmpId() {
        return empId;
    }
    
    public void setEmpId(Integer empId) {
        this.empId = empId;
    }
    
    public String getEmpName() {
        return empName;
    }
    
    public void setEmpName(String empName) {
        this.empName = empName;
    }
    
    public Double getEmpSalary() {
        return empSalary;
    }
    
    public void setEmpSalary(Double empSalary) {
        this.empSalary = empSalary;
    }
    
    @Override
    public String toString() {
        return "Employee{" +
                "empId=" + empId +
                ", empName='" + empName + '\'' +
                ", empSalary=" + empSalary +
                '}';
    }
    
    public Employee(Integer empId, String empName, Double empSalary) {
        this.empId = empId;
        this.empName = empName;
        this.empSalary = empSalary;
    }
}

3、搭建框架开发环境

①导入依赖

<dependencies>
    <!-- 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>
</dependencies>

②准备配置文件

[1]Mybatis 全局配置文件

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//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.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目录下。

[2]Mybatis 映射文件

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

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

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

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//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>

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

4、junit测试代码

@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语句的id
    String statement = "com.atguigu.mybatis.dao.EmployeeMapper.selectEmployee";
    
    // 要传入SQL语句的参数
    Integer empId = 1;
    
    // 执行SQL语句
    Object result = session.selectOne(statement, empId);
    
    System.out.println("o = " + result);
    
    // 4.关闭SqlSession
    session.close();
}

说明:

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

5、修正一个误区

①误区

刚开始接触框架,我们会认为Java程序会转入XML配置文件中执行,但其实框架会在初始化时将XML文件读取进来,封装到对象中,再然后就都是Java代码的执行了,XML中的配置是没法执行的。

②图解

③源码

[1]封装Configuration对象

所在类:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory

[2]准备去获取已映射的指令

所在类:org.apache.ibatis.session.defaults.DefaultSqlSession

[3]正式获取已映射的指令

所在类:org.apache.ibatis.session.Configuration

[4]mappedStatements对象结构

mappedStatements对象的类型:Configuration类中的一个静态内部类:StrictMap

第二节 HelloWorld强化

1、加入日志

①目的

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

②操作

[1]加入依赖

<!-- log4j日志 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

[2]加入log4j的配置文件

支持 XML 和 properties 属性文件两种形式。无论使用哪种形式,文件名是固定的:

  • log4j.xml
  • log4j.properties
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
    
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m  (%F:%L) \n" />
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug" />
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info" />
    </logger>
    <root>
        <level value="debug" />
        <appender-ref ref="STDOUT" />
    </root>
</log4j:configuration>

③日志的级别

FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)

从左到右打印的内容越来越详细

④STDOUT

是standard output的缩写,意思是标准输出。对于Java程序来说,打印到标准输出就是打印到控制台。

⑤打印效果

DEBUG 05-24 18:51:13,331 ==> Preparing: select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=? (BaseJdbcLogger.java:137) DEBUG 05-24 18:51:13,371 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) DEBUG 05-24 18:51:13,391 <== Total: 1 (BaseJdbcLogger.java:137) o = Employee{empId=1, empName='tom', empSalary=200.33}

2、关联外部属性文件

①需求

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

②做法

创建jdbc.properties配置文件

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=atguigu
    
wechat.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=atguigu
    
wechat.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、用上 Mapper 接口

Mybatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口即可,我们不需要提供实现类。

①思路

②调整junit代码

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

③完成Mapper接口

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

④最终的junit测试方法

@Test
public void testUsrMapperInterface() {
    
    // 1.根据EmployeeMapper接口的Class对象获取Mapper接口类型的对象
    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
        
    // 2.调用EmployeeMapper接口的方法完成对数据库的操作
    Emp emp = employeeMapper.selectEmployee(1L);
    
    // 3.打印查询结果
    System.out.println("emp = " + emp);
}

4、增删改操作

①insert

SQL语句

<insert id="insertEmployee">
    <!-- 现在在这条SQL语句中,#{}中的表达式需要被用来从Emp emp实体类中获取emp_name的值、emp_salary的值 -->
    <!-- 而我们从实体类中获取值通常都是调用getXxx()方法 -->
    <!-- 而getXxx()方法、setXxx()方法定义了实体类的属性 -->
    <!-- 定义属性的规则是:把get、set去掉,剩下部分首字母小写 -->
    <!-- 所以我们在#{}中使用getXxx()方法、setXxx()方法定义的属性名即可 -->
    insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
</insert>

Java代码中的Mapper接口:

public interface EmployeeMapper {
    
    Employee selectEmployee(Integer empId);
    
    int insertEmployee(Employee employee);
}

Java代码中的junit测试:

@Test
public void testSaveEmployee() {
    
    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
    
    // 创建要保存到数据库的对象
    Employee employee = new Employee();
    
    // 给实体类对象设置具体属性值
    employee.setEmpName("jerry");
    employee.setEmpSalary(5000.33);
    
    // 执行保存操作
    int result = employeeMapper.insertEmployee(employee);
    
    // 打印受影响的行数
    System.out.println("result = " + result);
}

②delete

SQL语句

    <delete id="deleteEmployee">
        delete from t_emp where emp_id=#{empId}
    </delete>

Java代码中的Mapper接口:

public interface EmployeeMapper {
    
    Employee selectEmployee(Integer empId);
    
    int insertEmployee(Employee employee);
    
    int deleteEmployee(Integer empId);
}

Java代码中的junit测试:

@Test
public void testRemoveEmployee() {
    
    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
    
    int result = employeeMapper.deleteEmployee(1);
    
    System.out.println("result = " + result);
}

③update

SQL语句:

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

Java代码中的Mapper接口:

public interface EmployeeMapper {
    
    Employee selectEmployee(Integer empId);
    
    int insertEmployee(Employee employee);
    
    int deleteEmployee(Integer empId);
    
    int updateEmployee(Employee employee);
}

Java代码中的junit测试:

@Test
public void testUpdateEmployee() {
    
    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
    
    Employee employee = new Employee(2, "AAAAAA", 6666.66);
    
    int result = employeeMapper.updateEmployee(employee);
    
    System.out.println("result = " + result);
}

第三节 给SQL语句传参

1、#{}方式

Mybatis会在运行过程中,把配置文件中的SQL语句里面的#{}转换为“?”占位符,发送给数据库执行。

配置文件中的SQL:

<delete id="deleteEmployeeById">
    delete from t_emp where emp_id=#{empId}
</delete>

实际执行的SQL:

delete from t_emp where emp_id=?

2、${}方式

将来会根据${}拼字符串

①SQL语句

<select id="selectEmployeeByName" resultType="com.atguigu.mybatis.entity.Employee">
    select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_name like '%${empName}%'
</select>

②Mapper接口

注意:由于Mapper接口中方法名是作为SQL语句标签的id,不能重复,所以Mapper接口中不能出现重名的方法,不允许重载!

public interface EmployeeMapper {
    
    Employee selectEmployee(Integer empId);
    
    Employee selectEmployeeByName(@Param("empName") String empName);
    
    int insertEmployee(Employee employee);
    
    int deleteEmployee(Integer empId);
    
    int updateEmployee(Employee employee);
}

③junit测试

@Test
public void testDollar() {
    
    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
    
    Employee employee = employeeMapper.selectEmployeeByName("r");
    
    System.out.println("employee = " + employee);
}

④实际打印的SQL

select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_name like '%r%'

⑤应用场景举例

在SQL语句中,数据库表的表名不确定,需要外部动态传入,此时不能使用#{},因为 SQL 语法不允许表名位置使用问号占位符,此时只能使用${}。

其他情况,只要能用#{}肯定不用${},避免SQL注入。

第四节 数据输入

1、Mybatis总体机制概括

2、概念说明

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

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

3、单个简单类型参数

①Mapper接口中抽象方法的声明

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>

4、实体类类型参数

①Mapper接口中抽象方法的声明

int insertEmployee(Employee employee);

②SQL语句

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

③对应关系

④结论

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

5、零散的简单类型数据

①Mapper接口中抽象方法的声明

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>

③对应关系

6、Map类型参数

①Mapper接口中抽象方法的声明

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

②SQL语句

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

③junit测试

@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);
    
    System.out.println("result = " + result);
}

④对应关系

{}中写Map中的key

⑤使用场景

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

第五节 数据输出

TIP

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

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

1、返回单个简单类型数据

①Mapper接口中的抽象方法

int selectEmpCount();

②SQL语句

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

③junit测试

    @Test
    public void testEmpCount() {
    
        EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
    
        int count = employeeMapper.selectEmpCount();
    
        System.out.println("count = " + count);
    }

TIP

Mybatis 内部给常用的数据类型设定了很多别名。 以 int 类型为例,可以写的名称有:int、integer、Integer、java.lang.Integer、Int、INT、INTEGER 等等。

2、返回实体类对象

①Mapper接口的抽象方法

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、返回Map类型

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

①Mapper接口的抽象方法

Map<String,Object> selectEmpNameAndMaxSalary();

②SQL语句

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

③junit测试

    @Test
    public void testQueryEmpNameAndSalary() {
    
        EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
    
        Map<String, Object> resultMap = employeeMapper.selectEmpNameAndMaxSalary();
    
        Set<Map.Entry<String, Object>> entrySet = resultMap.entrySet();
    
        for (Map.Entry<String, Object> entry : entrySet) {
            String key = entry.getKey();
            Object value = entry.getValue();
            System.out.println(key + "=" + value);
        }
    }

4、返回List类型

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

①Mapper接口中抽象方法

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 empSalary
        from t_emp
    </select>

③junit测试

    @Test
    public void testSelectAll() {
    
        EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
    
        List<Employee> employeeList = employeeMapper.selectAll();
    
        for (Employee employee : employeeList) {
            System.out.println("employee = " + employee);
        }
    
    }

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对象时,数据库表以自增方式产生的,需要特殊办法拿到这个自增的主键值。至于,为什么不能通过查询最大主键的方式解决这个问题,参考下图:

②在Mapper配置文件中设置方式

[1]Mapper接口中的抽象方法

int insertEmployee(Employee employee);

[2]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>

[3]junit测试

@Test
public void testSaveEmp() {
    
    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
    
    Employee employee = new Employee();
        
    employee.setEmpName("john");
    employee.setEmpSalary(666.66);
    
    employeeMapper.insertEmployee(employee);
    
    System.out.println("employee.getEmpId() = " + employee.getEmpId());
    
}

④注意

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>

6、数据库表字段和实体类属性对应关系

①别名

将字段的别名设置成和实体类属性一致。

<!-- 编写具体的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>

关于实体类属性的约定:

getXxx()方法、setXxx()方法把方法名中的get或set去掉,首字母小写。

②全局配置自动识别驼峰式命名规则

在Mybatis全局配置文件加入如下配置:

<!-- 使用settings对Mybatis全局进行设置 -->
<settings>
    <!-- 将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

SQL语句中可以不使用别名

<!-- Employee selectEmployee(Integer empId); -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
    select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>

③使用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>

猜你喜欢

转载自blog.csdn.net/weixin_52733693/article/details/127498693
今日推荐