这里使用的是3.5.11版本
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1、持久层框架对比
- JDBC
- SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
- 维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
- 代码冗长,开发效率低
- Hibernate 和 JPA
- 操作简便,开发效率高
- 程序中的长难复杂 SQL 需要绕过框架
- 内部自动生成的 SQL,不容易做特殊优化
- 基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
- 反射操作太多,导致数据库性能下降
- MyBatis
- 轻量级,性能出色
- SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
- 开发效率稍逊于 Hibernate,但是完全能够接收
开发效率:Hibernate>Mybatis>JDBC
运行效率:JDBC>Mybatis>Hibernate
封装越多,运行效率越慢
2、快速入门(案例)
有一个mybitas数据库,使用Mybaits框架来使用id查询数据。
之前Dao时我们都是创建一个Dao接口,然后自己实现该接口,将sql语句等都写在类里。
使用Mybstis之后,我们只用写一个xxxMapper接口,之后编写xxxMapper.xml配置信息,在xml文件中编写sql语句,之后再编写一个mabatis-config.xml配置文件(配置数据库信息,以及我们mapper接口的位置),最后使用mybatisapi来进行数据库操作即可。
步骤①导入依赖
<dependencies>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency><!-- MySQL驱动 mybatis底层依赖jdbc驱动实现,本次不需要导入连接池,mybatis自带! -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency><!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
②.准备实体类
public class Employee {
private Integer empId;
private String empName;
private Double empSalary;
//getter | setter
}
③准备mapper接口以及mapperxml文件
MyBatis 框架下,SQL语句编写位置发生改变,从原来的Java类,改成**XML**或者注解定义!
推荐在XML文件中编写SQL语句,让用户能更专注于 SQL 代码,不用关注其他的JDBC代码。
如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码!!
一般编写SQL语句的文件命名:XxxMapper.xml Xxx一般取表名!!
Mybatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口即可,我们不需要提供实现类,具体的SQL写到对应的Mapper文件,该用法的思路如下图所示:
a.定义mapper接口
package com.cky.impl; import com.cky.pojo.Employee; public interface Mymapper { Employee queryByid(int id); }
b.定义mapperxml文件
位置resources/mapper/Mymapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace等于mapper接口类的全限定名,这样实现对应 --> <mapper namespace="com.cky.impl.Mymapper"> <!-- 查询使用 select标签 id = 方法名 resultType = 返回值类型 标签内编写SQL语句 --> <select id="queryByid" resultType="com.cky.pojo.Employee"> <!-- #{empId}代表动态传入的参数,并且进行赋值!后面详细讲解 --> select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_id = #{empId} </select> </mapper>
**注意:**
- 方法名和SQL的id一致
- 方法返回值和resultType一致
- 方法的参数和SQL的参数一致
- 接口的全类名和映射配置文件的名称空间一致
④准备Mybatis配置文件
在Resource文件下
mybatis框架配置文件: 数据库连接信息,性能配置,mapper.xml配置等!
习惯上命名为 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.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="cky"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <!-- Mapper注册:指定Mybatis映射文件的具体位置 --> <!-- mapper标签:配置一个具体的Mapper映射文件 --> <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 --> <!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 --> <mapper resource="mapper/Mymapper.xml"/> </mappers> </configuration>
⑤测试
import com.cky.impl.Mymapper; import com.cky.pojo.Employee; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; public class Mybatis_test { /** * projectName: com.atguigu.test * * description: 测试类 */ @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.根据EmployeeMapper接口的Class对象获取Mapper接口类型的对象(动态代理技术) //使用的是jdk动态代理,底层仍然调用的是ibtsa的对应的方法。 //动态代理将namespce与方法名连接,之后将参数整合到一起。 Mymapper employeeMapper = session.getMapper(Mymapper.class); // 4. 调用代理类方法既可以触发对应的SQL语句 Employee employee = employeeMapper.queryByid(1); System.out.println("employee = " + employee); // 4.关闭SqlSession session.commit(); //提交事务 [DQL不需要,其他需要] session.close(); //关闭会话 } }
注意:Mapper接口的抽象方法不能重载!!!
3、Mybatis基本使用
3.1 向sql语句传参
3.1.1 打开日志输出配置
通过日志输出配置,我们可以看到接下来我们要讲解的#{}和${}接收参数的区别。
mybatis配置文件中添加日志配置
日志配置:
<settings>
<!-- SLF4J 选择slf4j输出! -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
3.1.2 #{} 接收参数
Mybatis会将SQL语句中的#{}转换为问号占位符。占位符+赋值
3.1.3 ${} 接收参数
直接赋值,字符串直接拼接
注意:
通常不会采用${}的方式传值,为了防止注入攻击。一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用
结论:实际开发中,能用#{}实现的,肯定不用${}。
特殊情况: 动态的不是值,是列名或者关键字或者表名,需要使用${}拼接
3.2 传入参数类型
这里数据输入具体是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。
- 简单类型:只包含一个值的数据类型
- 基本数据类型:int、byte、short、double、……
- 基本数据类型的包装类型:Integer、Character、Double、……
- 字符串类型:String
- 复杂类型:包含多个值的数据类型
- 实体类类型:Employee、Department、……
- 集合类型:List、Set、Map、……
- 数组类型:int[]、String[]、……
- 复合类型:List<Employee>、实体类中包含集合……
根据不同的传入类型,我们有不同的规则
3.2.1 传入单个简单类型
mapper接口中抽象方法声明;
Employee selectEmployee(Integer empId);
mapperxml文件中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.2.2 传入一个实体参数类型
mapper接口抽象类定义
int insertEmployee(Employee employee);
mapperxmlSQL语句
<insert id="insertEmployee">
insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
</insert>
对于实体类,我们需要传入的参数要与实体类对象的变量名相同,不能乱写。
3.2.3多个简单数据类型
mapper接口抽象方法
Employee querybyidandname(Integer id,String name);
mapperxmlSQL编写
//不可以随便写 也不能按照抽象方法中的参数名来取 //方法1 mybatis默认机制 参数默认从arg0...argx 开始 //方法2 @Param注解 写在接口的抽象方法中 <select id="querybyidandname" resultType="com.cky.pojo.Employee"> select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_id = ${arg0} and emp_name=#{arg1} </select>此时类方法这样写
Employee querybyidandname(@Param("a") Integer id, @Param("b") String name);SQL语句这样写
<select id="querybyidandname" resultType="com.cky.pojo.Employee"> select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_id = ${a} and emp_name=#{b} </select>
3.2.4 Map参数数据类型
mapper接口方法
Employee querybyidandname(Map data);
Sql语句
<select id="querybyidandname" resultType="com.cky.pojo.Employee"> select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_id = ${emp_id} and emp_name=#{emp_name} </select>
传入的是Map时,sql语句中接收根据我们传入map的参数名的key就可以.
3.3 输出数据类型
对于增删改 一般都只返回影响行数 用int或者long 接收即可。
但是对于查询,返回的类型就多种多样,那么sql语句的ResultType如何编写呢?
3.3.1 单个简单输出类型
ResultType 写法
1、如果返回的是我们的实体类,直接写类的全限定符即可
2、别名简称
java为我们提供了72中基本数据类型的简称
基本上可以分为:
①基本数据类型 int double->_int _double
②包装数据类型 Integer Double ->int double
③基本组合数据类型 Map List->小写即可
3、自定义数据类型
如何为我们自己的类自定义别名呢?可以在Mybatis配置文件中配置
官网别名解释
①挨个类配置
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> <typeAlias alias="Comment" type="domain.blog.Comment"/> <typeAlias alias="Post" type="domain.blog.Post"/> <typeAlias alias="Section" type="domain.blog.Section"/> <typeAlias alias="Tag" type="domain.blog.Tag"/> </typeAliases>
②批量给包下的类给别名,别名就是类的首字母小写
<typeAliases> <package name="domain.blog"/> </typeAliases>
如果在批量给别名之后,我们仍想给某些类自己配置别名的话怎么办呢?
可以在类名上添加@Alias注解,注意该注解必须与批量给别名一起使用,否则不生效
@Alias("author") public class Author { ... }
3.3.2 单个实体类型输出
对于ResultType 我们可以①使用类的全限定符 ②起别名使用别名
注意:这里有个默认要求:
我们数据库的列名必须等于属性名 但是如果我们都配置别名的话,比如下方,就会比较麻烦。
<select id="querybyidandname" resultType="com.cky.pojo.Employee"> select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_id = ${emp_id} and emp_name=#{emp_name} </select>
这里,mybatis有个设置,开启该该驼峰式映射设置之后,框架会自动给我们起别名
但是规则是去掉数据库列名下划线,将下划线后的首字母变大写
比如 数据库列名 emp_id 会自动帮我们映射为 empId
如何开启驼峰式映射呢?
在settings中编写
<!-- 在全局范围内对Mybatis进行配置 -->
<settings><!-- 具体配置 -->
<!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 -->
<!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 -->
<!-- 规则要求数据库表字段命名方式:单词_单词 -->
<!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/></settings>
3.3.3 返回map类型
如果返回的是实体类型,则ResultType直接写相应的实体类全限定符或者我们定义过的简写就行。
但是如果返回的不是实体类型呢?
我们可以用Map接收。
mapper接口抽象类方法
//根据id来查找姓名和工资,返回的不是一个实体类,我们可以用map来接收 Map<String,Double> querybyid(Integer id);
mapperXML文件
<select id="querybyid" resultType="map"> select emp_name empName, emp_salary empSalary from t_emp where emp_id = ${emp_id} </select>
3.3.4 返回集合类型
有时候,我们查找返回的是多条信息,这时候我们返回值类型就是一个集合类型
mapper接口
//返回的是一个集合 List<Employee> queryByname(String name);
mapperXML文件
<select id="queryByname" resultType="com.cky.pojo.Employee"> select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_name = #{emp_name} </select>
这里需要注意:
我们resultType接收的是集合的泛型,而不需要写list
3.3.5 主键回显
a.对于自增长类型主键
int insertnew(Employee employee);
<insert id="insertnew" useGeneratedKeys="true" keyColumn="emp_id" keyProperty="empId"> insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary}) </insert> <!-- useGeneratedKeys属性字面意思就是“使用生成的主键” --> <!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 --> keyColumn 表示我们在数据库中的列名 、
如果我们不指定这三个值,之后我们便不会将id 赋给该实体的empId属性,我们就不到该值。
测试:
import com.cky.impl.Mymapper; import com.cky.pojo.Employee; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; public class Mybatis_test { /** * projectName: com.atguigu.test * * description: 测试类 */ @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); Employee employee1=new Employee(); employee1.setEmpName("cui"); employee1.setEmpSalary(20000.0); // 2.使用SqlSessionFactory对象开启一个会话 SqlSession session = sessionFactory.openSession(); // 3.根据EmployeeMapper接口的Class对象获取Mapper接口类型的对象(动态代理技术) Mymapper employeeMapper = session.getMapper(Mymapper.class); // 4. 调用代理类方法既可以触发对应的SQL语句 int rows = employeeMapper.insertnew(employee1); System.out.println(employee1.getEmpId()); System.out.println("rows = " +rows ); // 4.关闭SqlSession session.commit(); //提交事务 [DQL不需要,其他需要] session.close(); //关闭会话 } }结果:
如果没有配置那三个参数,我们将获得null
注意:我们也可以在 这句代码里自动开启提交事务,填写ture表示开启,默认为false
SqlSession session = sessionFactory.openSession(true);
b.对于非自增长主键
而对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用!
使用 `selectKey` 帮助插入UUID作为字符串类型主键示例:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace等于mapper接口类的全限定名,这样实现对应 --> <mapper namespace="com.cky.impl.TeacherMapper"> <insert id="insertTeacher"> <!--非自增长的主键,交给mybits帮助我们维护--> <selectKey order="BEFORE" resultType="string" keyProperty="tID">SELECT REPLACE(UUID(),'-','');</selectKey> insert into teacher(t_id,t_name) values(#{tID},#{tName}) </insert> </mapper>
通过 `keyProperty` 属性来指定查询到的 UUID 赋值给对象中的 `id` 属性,而 `resultType` 属性指定了 UUID 的类型为 `java.lang.String`。
order 有两个选值 BEFORE|AFTER
before 表示在insert语句执行之前执行 selectkey 反之表示在之后执行。这里生成id,并把id配置给Teacher 属性,肯定是在之前执行。
通过这种方式,我们不必在自己维护给Teacher对象添加id属性,而是通过mybitas来维护生成id。
使用这种方式,我们可以方便地插入 UUID 作为字符串类型主键。当然,还有其他插入方式可以使用,如使用Java代码生成UUID并在类中显式设置值等。需要根据具体应用场景和需求选择合适的插入方式。 Java代码自己生成UUID来显示设置如下:
//自己维护主键 // String id= UUID.randomUUID().toString().replaceAll("-",""); Teacher teacher=new Teacher(); tearucher.settName("hhh"); // teacher.settID(id);
3.3.6 当列名和属性名不一致时
(1)起别名
我们可以在sql语句中为 列名起别名 别名与哦我们的属性名一致即可
如
<select id="queryByid" resultType="com.cky.pojo.Employee"> <!-- #{empId}代表动态传入的参数,并且进行赋值!后面详细讲解 --> select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_id = ${empId} </select>
(2)开启驼峰式映射
<!-- 使用settings对Mybatis全局进行设置 -->
<settings><!-- 将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/></settings>
(3)使用resultMap自定义映射
resultType与resultMap 二选一
resultType按照规则自动映射,如果开启了驼峰式映射,会自动映射属性名和列名,但是只能单层映射。多表查询时就无法映射。
resultMap自定义映射关系,可以深层次可以单层次。
<!-- 专门声明一个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); -->
resultMap=“id标识”
<select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>