三、MyBatis实践:提高持久层数据处理效率
目录
一、Mybatis简介 1.1 简介 https://mybatis.org/mybatis-3/zh/index.html
MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
社区会持续更新开源项目,版本会不断变化,我们不必每个小版本都追,关注重大更新的大版本升级即可。
本课程使用:3.5.11版本
1.2 持久层框架对比
JDBC
SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
代码冗长,开发效率低
Hibernate 和 JPA
操作简便,开发效率高
程序中的长难复杂 SQL 需要绕过框架
内部自动生成的 SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
反射操作太多,导致数据库性能下降
MyBatis
轻量级,性能出色
SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
开发效率稍逊于 Hibernate,但是完全能够接收
开发效率:Hibernate>Mybatis>JDBC
运行效率:JDBC>Mybatis>Hibernate
1.3 快速入门(基于Mybatis3方式)
准备数据模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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. 依赖导入pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.11</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.25</version > </dependency > <dependency > <groupId > org.junit.jupiter</groupId > <artifactId > junit-jupiter-api</artifactId > <version > 5.3.1</version > </dependency > </dependencies >
3. 实体类准备
1 2 3 4 5 6 7 8 9 10 public class Employee { private Integer empId; private String empName; private Double empSalary; }
准备Mapper接口和MapperXML文件MyBatis 框架下,SQL语句编写位置发生改变,从原来的Java类,改成XML 或者注解定义!
推荐在XML文件中编写SQL语句,让用户能更专注于 SQL 代码,不用关注其他的JDBC代码。
如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码!!
一般编写SQL语句的文件命名:XxxMapper.xml Xxx一般取表名!!
Mybatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口即可,我们不需要提供实现类,具体的SQL写到对应的Mapper文件,该用法的思路如下图所示:
1. 定义mapper接口包:com.atguigu.mapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.atguigu.mapper;import com.atguigu.pojo.Employee;public interface EmployeeMapper { Employee selectEmployee (Integer empId) ; }
2. 定义mapper xml位置: resources/mappers/EmployeeMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?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" > <mapper namespace ="com.atguigu.mapper.EmployeeMapper" > <select id ="selectEmployee" resultType ="com.atguigu.pojo.Employee" > select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_id = #{empId} </select > </mapper >
注意:
* 方法名和SQL的id一致
* 方法返回值和resultType一致
* 方法的参数和SQL的参数一致
* 接口的全类名和映射配置文件的名称空间一致
准备MyBatis配置文件
mybatis框架配置文件: 数据库连接信息,性能配置,mapper.xml配置等!
习惯上命名为 mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合 Spring 之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <?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 default ="development" > <environment id ="development" > <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 ="root" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="mappers/EmployeeMapper.xml" /> </mappers > </configuration >
运行和测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class MyBatisTest { @Test public void testSelectEmployee () throws IOException { String mybatisConfigFilePath = "mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(mybatisConfigFilePath); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder ().build(inputStream); SqlSession session = sessionFactory.openSession(); EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class); Employee employee = employeeMapper.selectEmployee(1 ); System.out.println("employee = " + employee); session.commit(); session.close(); } }
说明:
- SqlSession:代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的会话)
- SqlSessionFactory:是“生产”SqlSession的“工厂”。
- 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。
SqlSession和HttpSession区别
HttpSession:工作在Web服务器上,属于表述层。
SqlSession:不依赖Web服务器,属于持久化层。
代表Java程序和数据库之间的会话。
二、MyBatis基本使用 2.1 向SQL语句传参 2.1.1 mybatis日志输出配置 mybatis配置文件设计标签和顶层结构如下:
我们可以在mybatis的配置文件使用settings标签 设置,输出运过程SQL日志!
通过查看日志,我们可以判定#{} 和 ${}的输出效果!
settings设置项:
| logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 | | — | — | — | — |
日志配置:
1 2 3 4 5 <settings > <setting name ="logImpl" value ="SLF4J" /> </settings >
2.1.2 #{}形式 Mybatis会将SQL语句中的#{}转换为问号占位符。
2.1.3 $****{}形式 ${}形式传参,底层Mybatis做的是字符串拼接操作。
通常不会采用${}的方式传值。一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用
结论:实际开发中,能用#{}实现的,肯定不用${}。
特殊情况: 动态的不是值,是列名或者关键字,需要使用${}拼接
1 2 3 4 @Select("select * from user where ${column} = #{value}") User findByColumn (@Param("column") String column, @Param("value") String value) ;
2.2 数据输入 2.2.1 Mybatis总体机制概括
2.2.2 概念说明 这里数据输入具体是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。
简单类型:只包含一个值的数据类型
基本数据类型:int、byte、short、double、……
基本数据类型的包装类型:Integer、Character、Double、……
字符串类型:String
复杂类型:包含多个值的数据类型
实体类类型:Employee、Department、……
集合类型:List、Set、Map、……
数组类型:int[]、String[]、……
复合类型:List、实体类中包含集合……
2.2.3 单个简单类型参数 Mapper接口中抽象方法的声明
1 Employee selectEmployee (Integer empId) ;
SQL语句
1 2 3 4 <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 >
单个简单类型参数,在#{}中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名。
2.2.4 实体类类型参数 Mapper接口中抽象方法的声明
1 int insertEmployee (Employee employee) ;
SQL语句
1 2 3 4 <insert id ="insertEmployee" > insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary}) </insert >
对应关系
结论
Mybatis会根据#{}中传入的数据,加工成getXxx()方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}解析后的问号占位符这个位置。
2.2.5 零散的简单类型数据 零散的多个简单类型参数,如果没有特殊处理,那么Mybatis无法识别自定义名称:
Mapper接口中抽象方法的声明
1 int updateEmployee (@Param("empId") Integer empId,@Param("empSalary") Double empSalary) ;
SQL语句
1 2 3 4 <update id ="updateEmployee" > update t_emp set emp_salary=#{empSalary} where emp_id=#{empId} </update >
对应关系
2.2.6 Map类型参数 Mapper接口中抽象方法的声明
#{}写map的key值
1 int updateEmployeeByMap (Map<String, Object> paramMap) ;
SQL语句
1 2 3 4 5 6 <update id ="updateEmployeeByMap" > update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey} </update >
junit测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private SqlSession session;@BeforeEach public void init () throws IOException { session = new SqlSessionFactoryBuilder () .build( Resources.getResourceAsStream("mybatis-config.xml" )) .openSession(); } @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); } @AfterEach public void clear () { session.commit(); session.close(); }
对应关系
#{}中写Map中的key
使用场景
有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。
2.3数据输出 2.3.1 输出概述 数据输出总体上有两种形式:
增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
查询操作的查询结果
我们需要做的是,指定查询的输出数据类型即可!
并且插入场景下,实现主键数据回显示!
2.3.2 单个简单类型 Mapper接口中的抽象方法
SQL语句
1 2 3 4 <select id ="selectEmpCount" resultType ="int" > select count(*) from t_emp </select >
Mybatis 内部给常用的数据类型设定了很多别名。 以 int 类型为例,可以写的名称有:int、integer、Integer、java.lang.Integer、Int、INT、INTEGER 等等。
junit测试
1 2 3 4 5 6 7 8 9 10 11 @Test public void testEmpCount () { EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class); int count = employeeMapper.selectEmpCount(); log.info("count = " + count); }
细节解释:
select标签,通过resultType指定查询返回值类型!
resultType = “全限定符 | 别名 | 如果是返回集合类型,写范型类型即可”
别名问题:
https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
1 2 3 4 5 <typeAliases > <typeAlias alias ="Author" type ="domain.blog.Author" /> <typeAlias alias ="Blog" type ="domain.blog.Blog" /> </typeAliases >
当这样配置时,Blog
可以用在任何使用 domain.blog.Blog
的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
1 2 <typeAliases > <package name ="domain.blog" /> </typeAliases >
每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 author
;若有注解,则别名为其注解值。见下面的例子:
1 2 3 4 @Alias("author") public class Author { ... }
下面是Mybatis为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名
映射的类型
_byte
byte
_char (since 3.5.10)
char
_character (since 3.5.10)
char
_long
long
_short
short
_int
int
_integer
int
_double
double
_float
float
_boolean
boolean
string
String
byte
Byte
char (since 3.5.10)
Character
character (since 3.5.10)
Character
long
Long
short
Short
int
Integer
integer
Integer
double
Double
float
Float
boolean
Boolean
date
Date
decimal
BigDecimal
bigdecimal
BigDecimal
biginteger
BigInteger
object
Object
object[]
Object[]
map
Map
hashmap
HashMap
list
List
arraylist
ArrayList
collection
Collection
2.3.3 返回实体类对象 Mapper接口的抽象方法
1 2 Employee selectEmployee (Integer empId) ;
SQL语句
1 2 3 4 5 6 7 8 9 10 <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=#{maomi} </select >
通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来。
增加全局配置自动识别对应关系
在 Mybatis 全局配置文件中,做了下面的配置,select语句中可以不给字段设置别名
1 2 3 4 5 6 7 8 9 10 11 12 <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
2.3.4 返回Map类型 适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。
Mapper接口的抽象方法
1 Map<String,Object> selectEmpNameAndMaxSalary () ;
SQL语句
1 2 3 4 5 6 7 8 9 10 11 12 <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测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @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(); log.info(key + "=" + value); } }
2.3.5 返回List类型 查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。
Mapper接口中抽象方法
1 List<Employee> selectAll () ;
SQL语句
1 2 3 4 5 6 <select id ="selectAll" resultType ="com.atguigu.mybatis.entity.Employee" > select emp_id empId,emp_name empName,emp_salary empSalary from t_emp </select >
junit测试
1 2 3 4 5 6 7 8 @Test public void testSelectAll () { EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class); List<Employee> employeeList = employeeMapper.selectAll(); for (Employee employee : employeeList) { log.info("employee = " + employee); } }
2.3.6 返回主键值
自增长类型主键
Mapper接口中的抽象方法
1 int insertEmployee (Employee employee) ;
SQL语句
1 2 3 4 5 6 7 8 9 <insert id ="insertEmployee" useGeneratedKeys ="true" keyColumn ="emp_id" keyProperty ="empId" > insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary}) </insert >
junit测试
1 2 3 4 5 6 7 8 9 @Test public void testSaveEmp () { EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class); Employee employee = new Employee (); employee.setEmpName("john" ); employee.setEmpSalary(666.66 ); employeeMapper.insertEmployee(employee); log.info("employee.getEmpId() = " + employee.getEmpId()); }
注意Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回。
非自增长类型主键
而对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用!使用 selectKey
帮助插入UUID作为字符串类型主键示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <insert id ="insertUser" parameterType ="User" > <selectKey keyProperty ="id" resultType ="java.lang.String" order ="BEFORE" > SELECT UUID() as id </selectKey > INSERT INTO user (id, username, password) VALUES ( #{id}, #{username}, #{password} ) </insert >
在上例中,我们定义了一个 insertUser
的插入语句来将 User
对象插入到 user
表中。我们使用 selectKey
来查询 UUID 并设置到 id
字段中。
通过 keyProperty
属性来指定查询到的 UUID 赋值给对象中的 id
属性,而 resultType
属性指定了 UUID 的类型为 java.lang.String
。
需要注意的是,我们将 selectKey
放在了插入语句的前面,这是因为 MySQL 在 insert
语句中只支持一个 select
子句,而 selectKey
中查询 UUID 的语句就是一个 select
子句,因此我们需要将其放在前面。
最后,在将 User
对象插入到 user
表中时,我们直接使用对象中的 id
属性来插入主键值。
使用这种方式,我们可以方便地插入 UUID 作为字符串类型主键。当然,还有其他插入方式可以使用,如使用Java代码生成UUID并在类中显式设置值等。需要根据具体应用场景和需求选择合适的插入方式。
2.3.7 实体类属性和数据库字段对应关系
别名对应将字段的别名设置成和实体类属性一致。
1 2 3 4 5 6 7 8 9 10 <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=#{maomi} </select >
关于实体类属性的约定: getXxx()方法、setXxx()方法把方法名中的get或set去掉,首字母小写。
全局配置自动识别驼峰式命名规则在Mybatis全局配置文件加入如下配置:
1 2 3 4 5 6 7 8 <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
SQL语句中可以不使用别名
1 2 3 4 5 6 7 <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语句中引用这个对应关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <resultMap id ="selectEmployeeByRMResultMap" type ="com.atguigu.mybatis.entity.Employee" > <id column ="emp_id" property ="empId" /> <result column ="emp_name" property ="empName" /> <result column ="emp_salary" property ="empSalary" /> </resultMap > <select id ="selectEmployeeByRM" resultMap ="selectEmployeeByRMResultMap" > select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId} </select >
2.4 CRUD强化练习
准备数据库数据首先,我们需要准备一张名为 user
的表。该表包含字段 id(主键)、username、password。创建SQL如下:
1 2 3 4 5 6 7 CREATE TABLE `user ` ( `id` INT (11 ) NOT NULL AUTO_INCREMENT, `username` VARCHAR (50 ) NOT NULL , `password` VARCHAR (50 ) NOT NULL , PRIMARY KEY (`id`) ) ENGINE= INNODB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8;
实体类准备
接下来,我们需要定义一个实体类 User
,来对应 user 表的一行数据。
1 2 3 4 5 6 @Data / / lombokpublic class User { private Integer id; private String username; private String password; }
Mapper接口定义
定义一个 Mapper 接口 UserMapper
,并在其中添加 user 表的增、删、改、查方法。
1 2 3 4 5 6 7 8 9 10 11 12 public interface UserMapper { int insert (User user) ; int update (User user) ; int delete (Integer id) ; User selectById (Integer id) ; List<User> selectAll () ; }
MapperXML编写
在 resources /mappers目录下创建一个名为 UserMapper.xml
的 XML 文件,包含与 Mapper 接口中相同的五个 SQL 语句,并在其中,将查询结果映射到 User
实体中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?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" > <mapper namespace ="com.atguigu.mapper.UserMapper" > <insert id ="insert" useGeneratedKeys ="true" keyProperty ="id" > INSERT INTO user(username, password) VALUES(#{username}, #{password}) </insert > <update id ="update" > UPDATE user SET username=#{username}, password=#{password} WHERE id=#{id} </update > <delete id ="delete" > DELETE FROM user WHERE id=#{id} </delete > <select id ="selectById" resultType ="user" > SELECT id, username, password FROM user WHERE id=#{id} </select > <select id ="selectAll" resultType ="user" > SELECT id, username, password FROM user </select > </mapper >
MyBatis配置文件
位置:resources: mybatis-config.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <?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 > <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> <setting name ="logImpl" value ="SLF4J" /> </settings > <typeAliases > <package name ="com.atguigu.pojo" /> </typeAliases > <environments default ="development" > <environment id ="development" > <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 ="root" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="mappers/UserMapper.xml" /> </mappers > </configuration >
效果测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 package com.atguigu.test;import com.atguigu.mapper.UserMapper;import com.atguigu.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.jupiter.api.AfterEach;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import java.io.IOException;import java.util.List;public class MyBatisTest { private SqlSession session; @BeforeEach public void init () throws IOException { session = new SqlSessionFactoryBuilder () .build( Resources.getResourceAsStream("mybatis-config.xml" )) .openSession(); } @Test public void createTest () { User user = new User (); user.setUsername("admin" ); user.setPassword("123456" ); UserMapper userMapper = session.getMapper(UserMapper.class); userMapper.insert(user); System.out.println(user); } @Test public void updateTest () { UserMapper userMapper = session.getMapper(UserMapper.class); User user = userMapper.selectById(1 ); user.setUsername("root" ); user.setPassword("111111" ); userMapper.update(user); user = userMapper.selectById(1 ); System.out.println(user); } @Test public void deleteTest () { UserMapper userMapper = session.getMapper(UserMapper.class); userMapper.delete(1 ); User user = userMapper.selectById(1 ); System.out.println("user = " + user); } @Test public void selectByIdTest () { UserMapper userMapper = session.getMapper(UserMapper.class); User user = userMapper.selectById(1 ); System.out.println("user = " + user); } @Test public void selectAllTest () { UserMapper userMapper = session.getMapper(UserMapper.class); List<User> userList = userMapper.selectAll(); System.out.println("userList = " + userList); } @AfterEach public void clear () { session.commit(); session.close(); } }
2.5 mapperXML标签总结 MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
insert
– 映射插入语句。
update
– 映射更新语句。
delete
– 映射删除语句。
select
– 映射查询语句。
select标签:
MyBatis 在查询和结果映射做了相当多的改进。一个简单查询的 select 元素是非常简单:
1 2 3 <select id ="selectPerson" resultType ="hashmap" resultMap ="自定义结构" > SELECT * FROM PERSON WHERE ID = #{id} </select >
这个语句名为 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。
注意参数符号:#{id} ${key}
MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:
1 2 3 4 String selectPerson = "SELECT * FROM PERSON WHERE ID=?" ;PreparedStatement ps = conn.prepareStatement(selectPerson);ps.setInt(1 ,id);
select 元素允许你配置很多属性来配置每条语句的行为细节:
属性
描述
id
在命名空间中唯一的标识符,可以被用来引用这条语句。
resultType
期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap
对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
timeout
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
insert, update 和 delete标签:
数据变更语句 insert,update 和 delete 的实现非常接近:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <insert id ="insertAuthor" statementType ="PREPARED" keyProperty ="" keyColumn ="" useGeneratedKeys ="" timeout ="20" > <update id ="updateAuthor" statementType ="PREPARED" timeout ="20" > <delete id ="deleteAuthor" statementType ="PREPARED" timeout ="20" >
属性
描述
id
在命名空间中唯一的标识符,可以被用来引用这条语句。
timeout
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys
(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty
(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset
)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn
(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。