简介 MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架
持久层框架对比
JDBC
SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
代码冗长,开发效率低
Hibernate 和 JPA
操作简便,开发效率高
程序中的长难复杂 SQL 需要绕过框架
内部自动生成的 SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
反射操作太多,导致数据库性能下降
MyBatis
轻量级,性能出色
SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
开发效率稍逊于 Hibernate,但是完全能够接收
开发效率:Hibernate>Mybatis>JDBC
运行效率:JDBC>Mybatis>Hibernate
快速入门 依赖导入 pom.xml
<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 >
准备Mapper接口和MapperXML文件 MyBatis 框架下,SQL语句编写位置发生改变,从原来的Java类,改成XML 或者注解定义!
推荐在XML文件中编写SQL语句,让用户能更专注于 SQL 代码,不用关注其他的JDBC代码。
如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码!!
一般编写SQL语句的文件命名:XxxMapper.xml Xxx一般取表名!!
Mybatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口即可,我们不需要提供实现类,具体的SQL写到对应的Mapper文件,该用法的思路如下图所示:
定义mapper接口
包:com.atguigu.mapper
package com.atguigu.mapper;import com.atguigu.pojo.Employee;public interface EmployeeMapper { Employee selectEmployee (Integer empId) ; }
定义mapper xml
位置: resources/mappers/EmployeeMapper.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" > <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 之后,这个配置文件可以省略。
<?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 >
运行和测试 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(); } }
说明:
基本使用 配置文件标签 mybatis配置文件设计标签和顶层结构如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<?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 > <properties resource ="jdbc.properties" > </properties > <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> <setting name ="lazyLoadingEnabled" value ="true" /> </settings > <typeAliases > <package name ="com.atguigu.mybatis.bean" /> </typeAliases > <environments default ="mysql_test" > <environment id ="mysql_test" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> </dataSource > </environment > </environments > <mappers > <package name ="com.atguigu.mybatis.mapper" /> </mappers > </configuration >
开启日志 日志配置:
<settings > <setting name ="logImpl" value ="SLF4J" /> </settings >
#{}与#{}形式
#{}:预编译,占位符? ,防止 SQL 注入,自动加引号。推荐用。
${}:字符串直接拼接,不预编译,有 SQL 注入风险 。
适用场景:表名、排序字段动态传入时才用 ${},其他一律#{}
<select id ="getUserByUsername" resultType ="User" > select * from t_user where username = #{username} </select > <select id ="getUserByUsername" resultType ="User" > select * from t_user where username = '${username}' </select >
数据输入
简单类型:只包含一个值的数据类型
基本数据类型:int、byte、short、double、……
基本数据类型的包装类型:Integer、Character、Double、……
字符串类型:String
复杂类型:包含多个值的数据类型
实体类类型:Employee、Department、……
集合类型:List、Set、Map、……
数组类型:int[]、String[]、……
复合类型:List、实体类中包含集合……
单个简单类型参数 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 >
单个简单类型参数,在#{}中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名。
实体类类型参数 Mapper接口中抽象方法的声明
int insertEmployee (Employee employee) ;
SQL语句
<insert id ="insertEmployee" > insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary}) </insert >
对应关系
结论
Mybatis会根据#{}中传入的数据,加工成getXxx()方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}解析后的问号占位符这个位置。
多个字面量类型的参数
若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中
以arg0,arg1…为键,以参数为值;
以param1,param2…为键,以参数为值;
因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号。
使用arg或者param都行,要注意的是,arg是从arg0开始的,param是从param1开始的
<select id ="checkLogin" resultType ="User" > select * from t_user where username = #{arg0} and password = #{arg1} </select >
<select id ="checkLogin" resultType ="User" > select * from t_user where username = '${param1}' and password = '${param2}' </select >
零散的简单类型数据 零散的多个简单类型参数,如果没有特殊处理,那么Mybatis无法识别自定义名称:
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 >
对应关系
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测试
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中。
数据输出 数据输出总体上有两种形式:
增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
查询操作的查询结果
我们需要做的是,指定查询的输出数据类型即可!
并且插入场景下,实现主键数据回显示!
单个简单类型 Mapper接口中的抽象方法
SQL语句
<select id ="selectEmpCount" resultType ="int" > select count(*) from t_emp </select >
Mybatis 内部给常用的数据类型设定了很多别名。 以 int 类型为例,可以写的名称有:int、integer、Integer、java.lang.Integer、Int、INT、INTEGER 等等。
junit测试
@Test public void testEmpCount () { EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class); int count = employeeMapper.selectEmpCount(); log.info("count = " + count); }
细节解释:
select标签,通过resultType指定查询返回值类型!
resultType = “全限定符 | 别名 | 如果是返回集合类型,写范型类型即可”
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases > <typeAlias alias ="Author" type ="domain.blog.Author" /> <typeAlias alias ="Blog" type ="domain.blog.Blog" /> </typeAliases >
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases > <package name ="domain.blog" /> </typeAliases >
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:
@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
返回实体类对象 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=#{maomi} </select >
通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来。
增加全局配置自动识别对应关系
在 Mybatis 全局配置文件中,做了下面的配置,select语句中可以不给字段设置别名
<settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
返回Map类型 适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。
Mapper接口的抽象方法
Map<String,Object> selectEmpNameAndMaxSalary () ;
SQL语句
<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(); log.info(key + "=" + value); } }
返回List类型 查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。
Mapper接口中抽象方法
List<Employee> selectAll () ;
SQL语句
<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) { log.info("employee = " + employee); } }
返回主键值 自增长类型主键
Mapper接口中的抽象方法
int insertEmployee (Employee employee) ;
SQL语句
<insert id ="insertEmployee" useGeneratedKeys ="true" keyProperty ="empId" > insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary}) </insert >
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); log.info("employee.getEmpId() = " + employee.getEmpId()); }
注意
Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回。
非自增长类型主键
而对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用!
使用 selectKey 帮助插入UUID作为字符串类型主键示例:
<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并在类中显式设置值等。需要根据具体应用场景和需求选择合适的插入方式。
实体类属性和数据库字段对应关系
别名对应
将字段的别名设置成和实体类属性一致。
<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全局配置文件加入如下配置:
<settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
SQL语句中可以不使用别名
<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 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 >
完整示例
准备数据库数据
首先,我们需要准备一张名为 user 的表。该表包含字段 id(主键)、username、password。创建SQL如下:
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 表的一行数据。
@Data / / lombokpublic class User { private Integer id; private String username; private String password; }
Mapper接口定义
定义一个 Mapper 接口 UserMapper,并在其中添加 user 表的增、删、改、查方法。
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 实体中。
<?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
<?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 >
效果测试
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(); } }
mapperXML标签总结 MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
insert – 映射插入语句。
update – 映射更新语句。
delete – 映射删除语句。
select – 映射查询语句。
select标签:
MyBatis 在查询和结果映射做了相当多的改进。一个简单查询的 select 元素是非常简单:
<select id ="selectPerson" resultType ="hashmap" resultMap ="自定义结构" > SELECT * FROM PERSON WHERE ID = #{id} </select >
这个语句名为 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。
注意参数符号:#{id} ${key}
MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:
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 的实现非常接近:
<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)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
特殊SQL的执行 模糊查询 List<User> getUserByLike (@Param("username") String username) ;
<select id ="getUserByLike" resultType ="User" > select * from t_user where username like "%"#{mohu}"%" </select >
其中select * from t_user where username like "%"#{mohu}"%"是最常用的
批量删除
只能使用${},如果使用#{},则解析后的sql语句为delete from t_user where id in ('1,2,3'),这样是将1,2,3看做是一个整体,只有id为1,2,3的数据会被删除。正确的语句应该是delete from t_user where id in (1,2,3),或者delete from t_user where id in ('1','2','3')
int deleteMore (@Param("ids") String ids) ;
<delete id ="deleteMore" > delete from t_user where id in (${ids}) </delete >
@Test public void deleteMore () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SQLMapper mapper = sqlSession.getMapper(SQLMapper.class); int result = mapper.deleteMore("1,2,3,8" ); System.out.println(result); }
动态设置表名
List<User> getUserByTable (@Param("tableName") String tableName) ;
<select id ="getUserByTable" resultType ="User" > select * from ${tableName} </select >
MyBatis多表映射 对一映射
需求说明
根据ID查询订单,以及订单关联的用户的信息!
OrderMapper接口
public interface OrderMapper { Order selectOrderWithCustomer (Integer orderId) ; }
OrderMapper.xml配置文件
<resultMap id ="selectOrderWithCustomerResultMap" type ="order" > <id column ="order_id" property ="orderId" /> <result column ="order_name" property ="orderName" /> <association property ="customer" javaType ="customer" > <id column ="customer_id" property ="customerId" /> <result column ="customer_name" property ="customerName" /> </association > </resultMap > <select id ="selectOrderWithCustomer" resultMap ="selectOrderWithCustomerResultMap" > SELECT order_id,order_name,c.customer_id,customer_name FROM t_order o LEFT JOIN t_customer c ON o.customer_id=c.customer_id WHERE o.order_id=#{orderId} </select >
对应关系可以参考下图:
Mybatis全局注册Mapper文件
<mappers > <mapper resource ="mappers/OrderMapper.xml" /> </mappers >
junit测试程序
@Slf4j 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 testRelationshipToOne () { OrderMapper orderMapper = session.getMapper(OrderMapper.class); Order order = orderMapper.selectOrderWithCustomer(2 ); log.info("order = " + order); } @AfterEach public void clear () { session.commit(); session.close(); } }
关键词
在“对一”关联关系中,我们的配置比较多,但是关键词就只有:association 和javaType
对多映射
需求说明
查询客户和客户关联的订单信息!
CustomerMapper接口
public interface CustomerMapper { Customer selectCustomerWithOrderList (Integer customerId) ; }
CustomerMapper.xml文件
<!-- 配置resultMap实现从Customer到OrderList的“对多”关联关系 --> <resultMap id="selectCustomerWithOrderListResultMap" type="customer" > <!-- 映射Customer本身的属性 --> <id column="customer_id" property="customerId" /> <result column="customer_name" property="customerName" /> <!-- collection标签:映射“对多”的关联关系 --> <!-- property属性:在Customer类中,关联“多”的一端的属性名 --> <!-- ofType属性:集合属性中元素的类型 --> <collection property="orderList" ofType="order" > <!-- 映射Order的属性 --> <id column="order_id" property="orderId" /> <result column="order_name" property="orderName" /> </collection> </resultMap> <!-- Customer selectCustomerWithOrderList (Integer customerId) ; --> <select id="selectCustomerWithOrderList" resultMap="selectCustomerWithOrderListResultMap" > SELECT c.customer_id,c.customer_name,o.order_id,o.order_name FROM t_customer c LEFT JOIN t_order o ON c.customer_id=o.customer_id WHERE c.customer_id=#{customerId} </select>
对应关系可以参考下图:
Mybatis全局注册Mapper文件
<mappers > <mapper resource ="mappers/OrderMapper.xml" /> <mapper resource ="mappers/CustomerMapper.xml" /> </mappers >
junit测试程序
@Test public void testRelationshipToMulti () { CustomerMapper customerMapper = session.getMapper(CustomerMapper.class); Customer customer = customerMapper.selectCustomerWithOrderList(1 ); log.info("customer.getCustomerId() = " + customer.getCustomerId()); log.info("customer.getCustomerName() = " + customer.getCustomerName()); List<Order> orderList = customer.getOrderList(); for (Order order : orderList) { log.info("order = " + order); } }
关键词
在“对多”关联关系中,同样有很多配置,但是提炼出来最关键的就是:“collection”和“ofType”
多表映射总结 多表映射优化
setting属性
属性含义
可选值
默认值
autoMappingBehavior
指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。
NONE, PARTIAL, FULL
PARTIAL
我们可以将autoMappingBehavior设置为full,进行多表resultMap映射的时候,可以省略符合列和属性命名映射规则(列名=属性名,或者开启驼峰映射也可以自定映射)的result标签!
修改mybati-sconfig.xml:
<setting name ="autoMappingBehavior" value ="FULL" />
修改teacherMapper.xml
<resultMap id ="teacherMap" type ="teacher" > <id property ="tId" column ="t_id" /> <collection property ="students" ofType ="student" > <id property ="sId" column ="s_id" /> </collection > </resultMap >
多表映射总结
关联关系
配置项关键词
所在配置文件和具体位置
对一
association标签/javaType属性/property属性
Mapper配置文件中的resultMap标签内
对多
collection标签/ofType属性/property属性
Mapper配置文件中的resultMap标签内
延迟加载
分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息:
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=”lazy(延迟加载)|eager(立即加载)”
<settings > <setting name ="lazyLoadingEnabled" value ="true" /> </settings >
@Test public void getEmpAndDeptByStepOne () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.getEmpAndDeptByStepOne(1 ); System.out.println(emp.getEmpName()); }
关闭延迟加载,两条SQL语句都运行了
开启延迟加载,只运行获取emp的SQL语句
@Test public void getEmpAndDeptByStepOne () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.getEmpAndDeptByStepOne(1 ); System.out.println(emp.getEmpName()); System.out.println("----------------" ); System.out.println(emp.getDept()); }
MyBatis动态语句 动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
if和where标签 使用动态 SQL 最常见情景是根据条件包含 where / if 子句的一部分。比如:
<select id ="selectEmployeeByCondition" resultType ="employee" > select emp_id,emp_name,emp_salary from t_emp <where > <if test ="empName != null" > or emp_name=#{empName} </if > <if test ="empSalary > 2000" > or emp_salary>#{empSalary} </if > </where > </select >
set标签 <update id ="updateEmployeeDynamic" > update t_emp <set > <if test ="empName != null" > emp_name=#{empName}, </if > <if test ="empSalary < 3000" > emp_salary=#{empSalary}, </if > </set > where emp_id=#{empId} </update >
trim标签 使用trim标签控制条件部分两端是否包含某些字符
prefix属性:指定要动态添加的前缀
suffix属性:指定要动态添加的后缀
prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
<select id ="selectEmployeeByConditionByTrim" resultType ="com.atguigu.mybatis.entity.Employee" > select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp <trim prefix ="where" suffixOverrides ="and|or" > <if test ="empName != null" > emp_name=#{empName} and </if > <if test ="empSalary > 3000" > emp_salary>#{empSalary} and </if > <if test ="empAge < = 20" > emp_age=#{empAge} or </if > <if test ="empGender=='male'" > emp_gender=#{empGender} </if > </trim > </select >
choose/when/otherwise标签 在多个分支条件中,仅执行一个。
从上到下依次执行条件判断
遇到的第一个满足条件的分支会被采纳
被采纳分支后面的分支都将不被考虑
如果所有的when分支都不满足,那么就执行otherwise分支
<select id ="selectEmployeeByConditionByChoose" resultType ="com.atguigu.mybatis.entity.Employee" > select emp_id,emp_name,emp_salary from t_emp where <choose > <when test ="empName != null" > emp_name=#{empName}</when > <when test ="empSalary < 3000" > emp_salary < 3000</when > <otherwise > 1=1</otherwise > </choose > </select >
foreach标签 基本用法
用批量插入举例
<foreach collection ="empList" item ="emp" separator ="," open ="values" index ="myIndex" > (#{emp.empName},#{myIndex},#{emp.empSalary},#{emp.empGender}) </foreach >
批量更新时需要注意
上面批量插入的例子本质上是一条SQL语句,而实现批量更新则需要多条SQL语句拼起来,用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置:
atguigu.dev.url =jdbc:mysql:///mybatis-example?allowMultiQueries=true
对应的foreach标签如下:
<update id ="updateEmployeeBatch" > <foreach collection ="empList" item ="emp" separator =";" > update t_emp set emp_name=#{emp.empName} where emp_id=#{emp.empId} </foreach > </update >
关于foreach标签的collection属性
如果没有给接口中List类型的参数使用@Param注解指定一个具体的名字,那么在collection属性中默认可以使用collection或list来引用这个list集合。这一点可以通过异常信息看出来:
Parameter 'empList' not found. Available parameters are [arg0, collection, list]
在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用@Param注解明确声明变量的名称,然后在foreach标签的collection属性中按照@Param注解指定的名称来引用传入的参数。
sql片段 抽取重复的SQL片段
<sql id ="mySelectSql" > select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp </sql >
引用已抽取的SQL片段
<include refid ="mySelectSql" />
MyBatis的缓存 MyBatis的一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
使一级缓存失效的四种情况:
不同的SqlSession对应不同的一级缓存
同一个SqlSession但是查询条件不同
同一个SqlSession两次查询期间执行了任何一次增删改操作
同一个SqlSession两次查询期间手动清空了缓存
MyBatis的二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件
在核心配置文件中,设置全局配置属性cacheEnabled=”true”,默认为true,不需要设置
在映射文件中设置标签
二级缓存必须在SqlSession关闭或提交之后有效
查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
二级缓存的相关配置
在mapper配置文件中添加的cache标签可以设置一些属性
eviction属性:缓存回收策略
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU
flushInterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新
size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false
MyBatis缓存查询的顺序
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存
整合第三方缓存EHCache(了解) 添加依赖 <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.2.1</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.2.3</version > </dependency >
各个jar包的功能
jar包名称
作用
mybatis-ehcache
Mybatis和EHCache的整合包
ehcache
EHCache核心包
slf4j-api
SLF4J日志门面包
logback-classic
支持SLF4J门面接口的一个具体实现
创建EHCache的配置文件ehcache.xml
<?xml version="1.0" encoding="utf-8" ?> <ehcache xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation ="../config/ehcache.xsd" > <diskStore path ="D:\atguigu\ehcache" /> <defaultCache maxElementsInMemory ="1000" maxElementsOnDisk ="10000000" eternal ="false" overflowToDisk ="true" timeToIdleSeconds ="120" timeToLiveSeconds ="120" diskExpiryThreadIntervalSeconds ="120" memoryStoreEvictionPolicy ="LRU" > </defaultCache > </ehcache >
设置二级缓存的类型
在xxxMapper.xml文件中设置二级缓存类型
<cache type ="org.mybatis.caches.ehcache.EhcacheCache" />
加入logback日志
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。创建logback的配置文件logback.xml,名字固定,不可改变
<?xml version="1.0" encoding="UTF-8" ?> <configuration debug ="true" > <appender name ="STDOUT" class ="ch.qos.logback.core.ConsoleAppender" > <encoder > <pattern > [%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern > </encoder > </appender > <root level ="DEBUG" > <appender-ref ref ="STDOUT" /> </root > <logger name ="com.atguigu.crowd.mapper" level ="DEBUG" /> </configuration >
EHCache配置文件说明
属性名
是否必须
作用
maxElementsInMemory
是
在内存中缓存的element的最大数目
maxElementsOnDisk
是
在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal
是
设定缓存的elements是否永远不过期。 如果为true,则缓存的数据始终有效, 如果为false那么还要根据timeToIdleSeconds、timeToLiveSeconds判断
overflowToDisk
是
设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
timeToIdleSeconds
否
当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时, 这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds
否
缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB
否
DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
diskPersistent
否
在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false
diskExpiryThreadIntervalSeconds
否
磁盘缓存的清理线程运行间隔,默认是120秒。每个120s, 相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy
否
当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。 默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出
扩展知识 Mapper批量映射优化
需求
Mapper 配置文件很多时,在全局配置文件中一个一个注册太麻烦,希望有一个办法能够一劳永逸。
配置方式
Mybatis 允许在指定 Mapper 映射文件时,只指定其所在的包:
<mappers > <package name ="com.atguigu.mapper" /> </mappers >
此时这个包下的所有 Mapper 配置文件将被自动加载、注册,比较方便。
3. 资源创建要求
插件机制 MyBatis 对插件进行了标准化的设计,并提供了一套可扩展的插件机制。插件可以在用于语句执行过程中进行拦截,并允许通过自定义处理程序来拦截和修改 SQL 语句、映射语句的结果等。
具体来说,MyBatis 的插件机制包括以下三个组件:
Interceptor(拦截器):定义一个拦截方法 intercept,该方法在执行 SQL 语句、执行查询、查询结果的映射时会被调用。
Invocation(调用):实际上是对被拦截的方法的封装,封装了 Object target、Method method 和 Object[] args 这三个字段。
InterceptorChain(拦截器链):对所有的拦截器进行管理,包括将所有的 Interceptor 链接成一条链,并在执行 SQL 语句时按顺序调用。
插件的开发非常简单,只需要实现 Interceptor 接口,并使用注解 @Intercepts 来标注需要拦截的对象和方法,然后在 MyBatis 的配置文件中添加插件即可。
PageHelper插件使用 Mybatis-PageHelper
pom.xml引入依赖
<dependency > <groupId > com.github.pagehelper</groupId > <artifactId > pagehelper</artifactId > <version > 5.1.11</version > </dependency >
mybatis-config.xml配置分页插件
在 MyBatis 的配置文件中添加 PageHelper 的插件:
<plugins > <plugin interceptor ="com.github.pagehelper.PageInterceptor" > <property name ="helperDialect" value ="mysql" /> </plugin > </plugins >
其中,com.github.pagehelper.PageInterceptor 是 PageHelper 插件的名称,dialect 属性用于指定数据库类型(支持多种数据库)
页插件使用
在查询方法中使用分页:
@Test public void testTeacherRelationshipToMulti () { TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class); PageHelper.startPage(1 ,2 ); List<Teacher> allTeachers = teacherMapper.findAllTeachers(); PageInfo<Teacher> pageInfo = new PageInfo <>(allTeachers); System.out.println("pageInfo = " + pageInfo); long total = pageInfo.getTotal(); System.out.println("total = " + total); int pages = pageInfo.getPages(); System.out.println("pages = " + pages); int pageNum = pageInfo.getPageNum(); System.out.println("pageNum = " + pageNum); int pageSize = pageInfo.getPageSize(); System.out.println("pageSize = " + pageSize); List<Teacher> teachers = pageInfo.getList(); System.out.println("teachers = " + teachers); teachers.forEach(System.out::println); }
分页相关数据 方法一:直接输出 @Test public void testPageHelper () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Page<Object> page = PageHelper.startPage(1 , 4 ); List<Emp> emps = mapper.selectByExample(null ); System.out.println(page); }
分页相关数据:
Page{count=true, pageNum=1, pageSize=4, startRow=0, endRow=4, total=8, pages=2, reasonable=false, pageSizeZero=false}[Emp{eid=1, empName='admin', age=22, sex='男', email='456@qq.com', did=3}, Emp{eid=2, empName='admin2', age=22, sex='男', email='456@qq.com', did=3}, Emp{eid=3, empName='王五', age=12, sex='女', email='123@qq.com', did=3}, Emp{eid=4, empName='赵六', age=32, sex='男', email='123@qq.com', did=1}]
方法二使用PageInfo
在查询获取list集合之后,使用PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages)获取分页相关数据
list:分页之后的数据
navigatePages:导航分页的页码数
@Test public void testPageHelper () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); PageHelper.startPage(1 , 4 ); List<Emp> emps = mapper.selectByExample(null ); PageInfo<Emp> page = new PageInfo <>(emps,5 ); System.out.println(page); }
常用数据:
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]
逆向工程
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
Java实体类
Mapper接口
Mapper映射文件
创建逆向工程的步骤 添加依赖和插件 <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.9</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.2</version > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.27</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-maven-plugin</artifactId > <version > 1.3.0</version > <dependencies > <dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-core</artifactId > <version > 1.3.2</version > </dependency > <dependency > <groupId > com.mchange</groupId > <artifactId > c3p0</artifactId > <version > 0.9.2</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.27</version > </dependency > </dependencies > </plugin > </plugins > </build >
创建MyBatis的核心配置文件 <?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 > <properties resource ="jdbc.properties" /> <typeAliases > <package name ="" /> </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> </dataSource > </environment > </environments > <mappers > <package name ="" /> </mappers > </configuration >
创建逆向工程的配置文件
文件名必须是:generatorConfig.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration > <context id ="DB2Tables" targetRuntime ="MyBatis3Simple" > <jdbcConnection driverClass ="com.mysql.cj.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/mybatis" userId ="root" password ="123456" > </jdbcConnection > <javaModelGenerator targetPackage ="com.atguigu.mybatis.pojo" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="com.atguigu.mybatis.mapper" targetProject =".\src\main\resources" > <property name ="enableSubPackages" value ="true" /> </sqlMapGenerator > <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.atguigu.mybatis.mapper" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> </javaClientGenerator > <table tableName ="t_emp" domainObjectName ="Emp" /> <table tableName ="t_dept" domainObjectName ="Dept" /> </context > </generatorConfiguration >
执行MBG插件的generate目标
如果出现报错:Exception getting JDBC Driver,可能是pom.xml中,数据库驱动配置错误
dependency中的驱动
mybatis-generator-maven-plugin插件中的驱动
两者的驱动版本应该相同
执行结果
QBC 查询
selectByExample:按条件查询,需要传入一个example对象或者null;如果传入一个null,则表示没有条件,也就是查询所有数据
example.createCriteria().xxx:创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系
example.or().xxx:将之前添加的条件通过or拼接其他条件
@Test public void testMBG () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); EmpExample example = new EmpExample (); example.createCriteria().andEmpNameEqualTo("张三" ).andAgeGreaterThanOrEqualTo(20 ); example.or().andDidIsNotNull(); List<Emp> emps = mapper.selectByExample(example); emps.forEach(System.out::println); }
增改
updateByPrimaryKey:通过主键进行数据修改,如果某一个值为null,也会将对应的字段改为null
mapper.updateByPrimaryKey(new Emp(1,"admin",22,null,"456@qq.com",3));
updateByPrimaryKeySelective():通过主键进行选择性数据修改,如果某个值为null,则不修改这个字段
mapper.updateByPrimaryKeySelective(new Emp(2,"admin2",22,null,"456@qq.com",3));
插件MyBatisX使用 MyBatisX 是一个 MyBatis 的代码生成插件,可以通过简单的配置和操作快速生成 MyBatis Mapper、pojo 类和 Mapper.xml 文件。下面是使用 MyBatisX 插件实现逆向工程的步骤:
安装插件:
在 IntelliJ IDEA 中打开插件市场,搜索 MyBatisX 并安装。
使用 IntelliJ IDEA连接数据库
查看生成结果
逆向工程案例使用
正常使用即可,自动生成单表的crud方法!
package com.atguigu.mapper;import com.atguigu.pojo.User;public interface UserMapper { int deleteByPrimaryKey (Long id) ; int insert (User record) ; int insertSelective (User record) ; User selectByPrimaryKey (Long id) ; int updateByPrimaryKeySelective (User record) ; int updateByPrimaryKey (User record) ; }
注意:逆向工程只能生成单表crud的操作,多表查询依然需要我们自己编写!