Mybatis的学习入门操作
此文章根据尚硅谷的ssm视频学习所作笔记,有不对的地方大家多多指出。
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
提示:以下是本篇文章正文内容,下面案例可供参考
1、Mybatis简介
1.1 Mybatis是什么?
- Mybatis是支持定制SQL,存储过程以及高级映射的优秀持久层框架,
- Mybatis是对JDBC的封装,避免了繁琐的数据库连接等操作,不需要手动操作数据库连接,也可以避免了数据库操作的一些硬代码,
- Mybatis可以通过xml将接口映射到数据库中
- SQL和Java的编码分开,使得功能更加清晰,Java专注于业务的功能实现,而Myabtis则封装sql语句专注于数据。
1.2 Mybatis官网
学习Mybatis可以通过官网了解Mybatis的安装以及使用方法。
2、搭建Mybatis入门
2.1 开发环境
1. 使用工具
IDE: idea
构建工具:maven
MysSQL版本: (注意事项)
- 如果是Mysql 8.0版本的jdbc驱动:
com.mysql.cj.java.Driver
- 如果是Mysql 5.0版本的jdbc驱动:
com.mysql.java.Driver
2.2 创建Maven工程
1. 创建Maven工程以及导入相关依赖
默认 了解一定的maven基础
打开idea-file-new-module
Mybatis的使用需要导入jar包,根据Mybatis官网(开篇有链接)有两种方式
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。(此处需自行下载Myabtis.jar包)
如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
<!-- version是版本号-->
</dependency>
在pom.xml文件中导入相关依赖:
maven依赖相关导入坐标可以通过网址搜查:https://mvnrepository.com/
<dependencies>
<!-- mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- 数据库驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- 测试类依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
-
此处 mysql 8.0驱动用的是
8.0
-
如果是mysql 5.0 则用
5.0
2. 创建Mybatis核心文件
1.创表以及实体类pojo的准备
在创建核心文件之前,需要先创数据库表–创建相关实体类pojo.java
简单设计一个表,方便学习
创建pojo实体类: (基本的构造方法,get set toString)
保存路径为: src/main/java/com.xxx.pojo.User
2.配置核心文件
习惯上将mybatis核心文件命名为:
mybatis-config.xml
创建相关核心文件可以通过 mybatis 官网直接复制黏贴
将配置文件放在 src/main/resources
<?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">
<!-- ${driver} 修改为:
相关版本的数据库链接驱动
Mysql 5.0: com.mysql.java.Driver
Mysql 8.0: com.mysql.cj.java.Driver
-->
<property name="driver" value="${driver}"/>
<!-- ${url} 修改为:
jdbc:mysql://localhost:3306/数据库名(按需设置编码集)
-->
<property name="url" value="${url}"/>
<!--${username} ${password} 根据自己的数据库设置用户名和密码 -->
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- <mappers> 引入mybatis的映射文件 -->
<mappers>
<!-- resource文件路径 需要作修改-->
<mapper resource=""/>
</mappers>
</configuration>
配置后的environments:
3. 创建mapper接口
Mybatis当中的mapper接口与dao层类似,在区别在于mapper接口仅仅只是接口
创建UserMapper接口实现相应的方法: src/main/java/com/xxx/mapper.UserMapper
编写方法类 例如: int insertUser();
4. 配置映射文件
mapper接口和映射文件要保证两个一致:
-
mapper接口的全类名与映射文件的namespace一致
-
mapper接口当中的方法名与映射文件中的sql语句的id一致
同样在Mybatis官网当中复制黏贴相关映射文件代码
<?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">
<!-- namespace :是mapper接口的全类名 并且是mybatis的唯一标识
--> com.xxx.mapper.UserMapper
-->
<mapper namespace="">
<!--
id 与接口当中需要操作的方法名对应 即上述创建的 int insertUser();
--> insertUser
resultType的值设置为pojo实体类全类名
--> com.xxx.pojo.User
-->
<select id="" resultType="">
select * from Blog where id = #{id}
</select>
</mapper>
根据创建的接口方法: int insertUser();在UserMapper.xml映射文件封装sql
(此处先作学习因此把代码写死)
注意: 配置好映射文件之后,需要将上述在mybatis-config.xml 的 < mapper
标签设置为对应的UserMapper.xml映射文件的路径:即在resources下的路径
5.入门案例测试Test(insert功能)
前面通过maven已将test jar包导入
创建个test包,并创建test类测试
从mybatis官网当中复制黏贴代码–>构建 SqlSessionFactory -->通过UserMapper代理实现类的方式
即: (此处不需要死记硬背,可以直接从官网中直接拿来用)
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
实现如下:
将resource文件:赋值为 mybatis-config.xml的路径
public class UserMapperTest {
@Test
public void insertUserTest(){
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理实现类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.insertUser();
System.out.println("插入了"+i+"数据");
sqlSession.close();
}
}
*注意: 在启动insertUserTest时,会发现数据并没有写入数据,是因为SqlSession默认的事务提交为false,不会自动提交 ::
两种方式提交事务
–>SqlSession sqlSession = sqlSessionFactory.openSession(true);
–>sqlSession.commit();
6.加入日志功能
- 导入日志 logj4 jar包 —> 通过maven导入日志依赖:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
-
在resources下创建log4j.xml文件
-
log4j.xml文件下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration>
<!-- 定义默认目的地 -->
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p [%c-%L] - %m%n" />
</layout>
</appender>
<!--定义文件目的地
<appender name="File" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="/log.log" />
<param name="MaxFileSize" value="1024KB" />
<param name="MaxBackupIndex" value="5" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p [%c-%L] - %m%n" />
</layout>
</appender>-->
<!-- 配置日志记录器 -->
<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
打印的内容从左到右越来越详细
7.其他功能实现
与上述类似:
步骤:
- mapper接口中创建方法
- 根据将创建好的mapper接口中的方法的方法名复制到映射文件(UserMapper.xml文件中)
- 增删改查的标签各不相同
查询(select) < select id =”方法名“ > select * from … < /select>
增加(insert) < insert id = “方法名”> update table set coulmn = ’ ’ </ insert>
…与此类推
值得注意的是: 查询< select > 在返回结果映射的时候如果没有设置 resultType 或者 resultMap会报错
因此操作: 在映射文件中
< select id = ’ 方法名’ resultType = ’ pojp类全类名’>
resultType将数据转化为java类型
3、Mybatis核心配置文件标签
3.1 核心配置文件标签顺序:
标签的顺序需要按照Mybatis核心配置文件规定的顺序配置:
properties?,settings?,typeAliases?,typeHandlers?,
objectFactory?,objectWrapperFactory?,reflectorFactory?,
plugins?,environments?,databaseIdProvider?,mappers?
如果标签顺序没有按照上图所示,则会报错
3.2 environments标签
<!-- 这个环境为开发环境 即:development
id:是唯一表示 ,在开发过程当中不能使用多个标识
因此:
<environments default="development">
default:就是默认使用环境的id
-->
<environments default="development">
<!--
environemnt:设置连接数据库的环境
属性: id 不能重复的id标识
-->
<environment id="development">
<!--
transactionManager: 设置事物管理器
属性type:设置事物管理的方式 :JDBC/MANAGED
-->
<transactionManager type="JDBC"/>
<!--
dataSource:数据源
属性type: 设置数据源类型 : POOLED / UNPOOLED / JNDI
POOLED:使用数据库连接池
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/tb"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
3.3 properties标签
properties:用来引用properties文件,例如 jdbc.properties 连接池
第一步:创建jdbc.properties
第二步: 配置连接池
第三步: 在核心文件当中引入properties文件
第四步: 在environments附属标签中的property标签 修改为对应的 ${key} 形式
1.创建properties文件 : jdbc
2.配置jdbc连接池文件
3.引入properties 的resource文件
4.修改与resource文件中对应的${key}
3.4 typeAliases标签
< typerAliases>< /typerAliases>标签 是将一个具体的类型设置为一个别名
在映射文件当中的< select>标签要设置resultType
> 可以在核心配置文件 mybatis-config.xml文件中部署typerAliases标签
将映射文件当中的selec标签下的resultType可以直接设置为类名
-----------此时默认以com.zyh.pojo下的类名为默认别名,并且不分大小写--------
测试结果:
上述有当中的< typeAlias type=" ">存在缺陷,当开发项目过程当中,会接触更多的pojo实体类,
此时对于一一针对的实体类都要部署一边,这样大大降低了效率,并且代码大量且复杂,
因此可以使用< package>
标签将包引入
3.5 mapper标签
mapper标签 : 引入mybatis映射文件
一个表对应一个实体类,一个实体类对应这一个映射文件
当存在多个表,多个实体类的情况时候 ,一个一个引入会很麻烦
可以通过< package> 标签来引入
但是前提是条件是:
mapper映射文件与mapper接口所在的包必须一致:
mapper接口的名字与映射文件的必须一致 ( 即类名 )
在resources下创建: New Directory
4、Mybatis核心配置文件以及映射文件模板(IDEA)
为了避免重复多次在官网中复制黏贴配置文件内容,可以利用IDEA的功能将mybatis-config.xml 以及映射文件设置成模板,创建模板即可使用
File->Settings->Edit->File and Code Templates
设置完成之后:
映射文件的模板设置和核心配置文件的模板设置方式一致
5、Mybatis获取参数情况:
5.1mybatis获取参数的两种方式: #{ } 和 ${ }
二者的区别:
#{ }本质上是占位符: ? 自动添加单引号
$ { }本质上是字符串拼接 , 不过$ { }需要手动加 单引号’ ’ ,否则mybatis会认为这是一个columns
5.2 单个字面量类型的参数
创建一个返回User对象的接口,并且传递一个参数,然后配置对应的映射文件.
配置映射文件:
方式一:#{ }
方式二: $ { } 但是要注意的是需要加单引号
单个字面量类型的参数的sql的情况,#{ }或者$ { }传递的参数名称可以任意的名称来获取 : 但是建议设置直接明了的参数内容
5.3 多个字面量类型的参数
1. select查询多个条件情况
同样创建mapper接口方法,并且接收多个参数值
执行的结果如下: 报错
原因在于多个参数的时候,Myabtis会将参数的存放在map集合当中,此时直接使用传递的参数内容不正确
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]
如果mapper接口方法的参数是多个的时候,此时Myabtis会将参数放在map集合当中,并且存储方式有两种:
1.比arg0,arg1…为键,参数为值
2.param1,param2…为键,参数为值
修改为如下:
2. insert 插入
- 创建mapper接口方法insertUser,传递User对象参数
- 配置映射文件中的< insert >
- 测试结果
值得注意的是, 在映射文件当中,#{ }当中的参数的内容直接引用了User的属性名,但与成员变量无关,
5.4 @param注解
@param注解
以@param注解的内容为属性值的键,以参数为值
- 创建接口方法,并且设置@param( " ")注解
- 配置映射文件
6、Mybatis的各种查询功能
6.1、查询一个实体类对象
/**
mapper接口方法
* 根据id查找
* @param id
*/
User selectById(@Param("id") Integer id);
<!-- User selectById(@Param("id") int id)-->
<select id="selectById" resultType="User">
select * from user where id = #{id};
</select>
6.2、查询一个List集合
/**
* 利用集合的方式查询所有对象
* @return
*/
List<User> getUserList();
<!-- List<User> getUserList();-->
<select id="getUserList" resultType="User">
select * from user;
</select>
6.3、查询数据的数量
/**
* 查找表数据中的个数
* @return
*/
Integer getUserCount();
因为返回值为Integer,所以在mapper.xml映射文件当中resutlType应该返回Integer
还可以写成 int , Integer, integer 因为这是mybatis默认规定:详情可查阅mybatis官网
6.4、查询一条数据为map集合
/**
* 通过id查询的数据存放在map,通过map返回
* @return
*/
Map<String,Object> getUserByMap(@Param("id") Integer id);
<select id="getUserByMap" resultType="java.util.Map">
select * from user where id = '${id}';
</select>
6.5、查询多条数据为map集合
- 创建方法
- 配置映射文件
- 测试:
此处为错误写法,报错内容如下:
TooManyResultsException
因为查询的数据是多条map集合,而返回值是一条map集合,所以map集合是获取不到的:而mybatis里调用的方法就是获取一条map集合的方法:
6.5.1、解决方式一:
将mapper接口方法放在一个泛型是map的list集合中
查询结果:
[{password=123456, sex=男, id=3, username=李四},
{password=12345, sex=男, id=5, username=张三},
{password=123654, sex=男, id=6, username=王五}]
6.5.2、解决方式二:
可以将每条数据转化的map集合存放在一个大的map集合中,须通过注解@MapKey(" ")注解
将查询的某个字段的值作为大的map的键
查询的结果:id即为键,数据为值
{
3={password=123456, sex=男, id=3, username=李四},
5={password=12345, sex=男, id=5, username=张三},
6={password=123654, sex=男, id=6, username=王五}
}
7、特殊SQL的执行
7.1、模糊查询
模糊查询的映射文件配置有三种方式:@param(“mohu”)–>参数内容名
- ‘%${mohu}%’
- concat(‘%’,#{mohu},‘%’)
- “%”#{mohu}“%”
错误写法:
7.2、批量删除
假设需要删除数据库表中的id为8,9的数据
传递的参数为String类型 --> 需要用${}不自动添加单引号的方法
如果使用#{}会报错 因为这个#{}会自动0添加 ’ ’
编译解析为:delete from user where id in ('8,9')
;在mysql中这种是错误写法
如下:
7.3、动态设置表名
此处需要使用 ${} 不能用#{},因为#{}编译解析为含单引号
7.4、添加功能获取自增的主键
userGeneratedKeys:表示当前添加功能使用自增的主键
keyProperty:将添加的数据的自增主键为实体类类型的参数的属性赋值
mapper.
测试:
此处id插入为null
执行结果:
8、自定义映射resultMap
8.1、创建两个表t_emp&t_dept
- 表t_emp
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for t_emp
-- ----------------------------
DROP TABLE IF EXISTS `t_emp`;
CREATE TABLE `t_emp` (
`emp_id` int NOT NULL AUTO_INCREMENT,
`emp_name` varchar(25) DEFAULT NULL,
`age` int DEFAULT NULL,
`gender` char(1) DEFAULT NULL,
`dept_id` int DEFAULT NULL,
PRIMARY KEY (`emp_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of t_emp
-- ----------------------------
INSERT INTO `t_emp` VALUES ('1', '张三', '20', '男', '1');
INSERT INTO `t_emp` VALUES ('2', '李四', '21', '女', '2');
INSERT INTO `t_emp` VALUES ('3', '王五', '22', '女', '3');
INSERT INTO `t_emp` VALUES ('4', '刘六', '23', '男', '4');
INSERT INTO `t_emp` VALUES ('5', '林七', '24', '男', '1');
INSERT INTO `t_emp` VALUES ('6', '吕八', '25', '男', '2');
- 表t_dept
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for t_dept
-- ----------------------------
DROP TABLE IF EXISTS `t_dept`;
CREATE TABLE `t_dept` (
`dept_id` int NOT NULL AUTO_INCREMENT,
`dept_name` varchar(25) DEFAULT NULL,
PRIMARY KEY (`dept_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of t_dept
-- ----------------------------
INSERT INTO `t_dept` VALUES ('1', 'A');
INSERT INTO `t_dept` VALUES ('2', 'B');
INSERT INTO `t_dept` VALUES ('3', 'C');
INSERT INTO `t_dept` VALUES ('4', 'D');
8.2、属性名和字段名不一致的映射以及解决
- Emp实体类的属性
private Integer empId;
private String empName;
private Integer age;
private String gender;
- 表字段名:
- 映射文件配置:
<!-- Emp getEmpById(@Param("emp_id") Integer emp_id);-->
<select id="getEmpById" resultType="Emp">
select * from t_emp where emp_id = #{emp_id}
</select>
- 执行结果发现empId=null,empName=null
原因:数据库表字段名中的emp_id,emp_name与实体类的属性名empId,empName不一致,由数据库不能映射到实体类属性对应的属性名
8.2.1解决方式:
1. 方式一 字段名设置别名
在映射文件当中的sql 字段名一 一设置别名 与属性名一致:
2. 方式二 下划线映射为驼峰
当字段名符合MySql的要求使用下划线_ , 而属性符合JAVA要求使用驼峰
此时可以在Mybatis的核心配置文件当中设置一个全局配置,可以自动将下划线映射为驼峰
但是前提格式如下:
字段名: emp_id ->empId
emp_name->empName
3. 方式三 自定义映射resultMap
resultMap: 设置自定义的映射关系
id: 唯一标识–>resultMap=" "
type: 处理映射关系的实体类的类型
标签:
id: 处理主键和实体类中属性的映射关系
result: 处理普通字段和实体类中属性的映射关系
column: 设置映射关系中的字段名,必须是sql中的某字段
property: 设置映射关系中的属性的属性名,必须为实体类中的属性名
8.3、多对一的映射处理方式(对象)
处理Emp(员工表)对Dept(部门表的)多对一的映射
Dept类结构 : 根据表创建对应属性
8.3.1 resultMap级联方式处理
- 首先在Emp实体类中,定义Dept类型的属性:
- 设置映射文件:
代码:
<resultMap id="empAndEmptResultMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<result column="dept_id" property="dept.deptId"></result>
<result column="dept_name" property="dept.deptName"></result>
</resultMap>
<select id="getEmpAndDeptByEmpId" resultMap="empAndEmptResultMap">
select t_emp.*,t_dept.*
from t_emp
left join t_dept
on t_emp.dept_id = t_dept.dept_id
where t_emp.emp_id = #{emp_id}
</select>
报错处理:
1.Dept类即关联的那个实体类需要设置无参构造器
Cannot set value of property ‘dept.deptId’ because ‘dept.deptId’ is null…
2.getter和setter方法出现错误写错的情况:
8.3.2 通过association标签定义
其他均不变,只需要在映射文件中修改< reslutMap >
<resultMap id="empAndEmptResultMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<association property="dept" javaType="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
association: 处理多对一的映射关系(主要处理实体类类型的属性比如Dept)
javaType:设置需要处理的属性的类型
property:设置需要处理的映射关系的属性的属性名
8.3.2 分步查询方式
一个表对应一个映射文件,需要另外创建一个DeptMapper接口
分步查询,通过先查询t_emp表中的dept_id,再通过dept_id从t_dept表中找到相对应的dept_id
- 创建DeptMapper接口以及创建DeptMapper.xml映射文件(此处操作省略)
- 创建Emp方法以及配置映射文件
映射问文件
3.测试:
不难发现,测试当中只调用了EmpMapper的方法,而DeptMapper的方法没手动直接调用,而是通过调用了getEmpAndDeptByEmpIdOne方法,随后通过关联的< association 中的select中间接调用了
property:设置需要处理映射关系的属性的属性名
select:设置分步查询的sql的唯一标识
column:将查询出的某个字段作为分步查询的sql条件
8.3.3 分步查询优点(延迟加载)
分布查询的延迟加载,可以减少内存的负担
- 在全局配置中设置标签属性名为: lazyLoadingEnabled(延迟加载)
<settings>
<!-- 开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
lazyLoadingEnabled: 当需要在分布查询当中,只需要查询一条员工信息的sql语句,而其他sql语句不做查询,则修改为true
- 设置与lazyLoadingEnabled配x套的标签属性名为aggressiveLazyLoading(按需加载)
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--按需加载默认为false
如果你只做查询员工信息,其他sql语句也会加载
-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
aggressiveLazyLoading:默认值为false,如果为ture则全局加载,即在分布查询当只做员工的sql语句,其他的分布查询的sql语句也会加载在内存.
- fetchType的作用
在开启延迟加载的环境中,通过设置该属性设置当前的分布查询是否使用延迟加载
它有两个值:
eager:立即加载
lazy:延迟加载
在映射文件当中,做分布查询的sql
8.4、一对多的映射处理(集合)
8.4.1 方式一:collection标签
处理Dept(部门)对Emp(员工)的一对多的映射关系
- 因为处理一对多的关系,所以需要创建集合类型的属性
- 创建接口方法
- 配置映射文件
collection:处理一对多的映射关系(集合)
property: 集合类型的属性变量名
opType: 集合所存储的数据的类型: List<?> --> ?
注: 与多对一的处理方式不同: association javaType: 实体类的java类型
- 测试
结果:
8.4.2 方式二: 分步查询
- 创建接口方法以及配置映射文件
首先Dept:
Emp:
9、动态SQL
9.1、动态sql概述
Mybatis框架的动态sql技术是一种根据特定条件拼接sql语句的功能,为了解决拼接sql语句字符串时的问题
比如在页面中的复选框,文本框,单选框等等是否作查询或者选择,浏览器在传递给服务器时,有可能的值为null或者" "…此时在做sql语句拼接的时候就需要处理
如果姓名文本框中没有数据则传递的是null,在传递数据时的sql语句就需要拼接字符串,此时姓名的if name = … 则未被拼接在sql语句当中
9.2、if标签
mapper接口方法:
映射文件配置
<!-- List<Emp> getEmpByCondition(Emp emp); -->
<select id="getEmpByCondition" resultType="Emp">
select * from
t_emp where
<if test="empName != null and empName != ''">
emp_name = #{empName}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
<if test="gender !=null and gender != ''">
and gender = #{gender}
</if>
</select>
if,通过test属性中的表达式判断标签中的内容是否有效,是否将标签中的内容拼接到sql语句当中,如果不为null 或者 " " 则拼接
test里作条件判断的属性可以直接用当前处理的实体类中的所对应的属性名
在做if的sql语句判断中不能使用&& 或者 || 会报错 可以使用英文的and 或者 or
测试类
@Test
public void getEmpByConditionTest(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class);
Emp emp = new Emp(null,"李四",20,"男");
List<Emp> emps = mapper.getEmpByCondition(emp);
System.out.println(emps);
sqlSession.close();
}
测试结果如果没有查询出任何结果说明该条件所查询的数据不存在
存在则:
9.3、where标签
9.3.1 where标签当中存在的问题
上述的if标签示例中,存在一个问题:当第一个empName如果为空的时候,程序会报错: where 后面跟着的是and
9.3.2 解决方式一: 恒等方式
只需要在where后面加个条件 1 = 1或者其他满足左值=右值的条件
但是需要在第一个条件判断也加上and
9.3.3 解决方式二: where标签
where标签:
1.如果where标签中有条件成立,会自动生成where关键字
2.会自动将where标签中的内容多余的and去掉,但是如果and在内容后面的多余的and无法去掉
3.如果where标签中没有任何一个条件成立,则where没有任何功能
上一张图片当中存在多个and,自动去掉了前面多余的and
当查询的值全部为null时:where没有任何功能
9.4、trim标签
如果gender为null 而且and放在 内容后面:
执行报错:后面多了个and
解决方式:
trim标签下的属性:
prefix、suffix:在标签中内容前面或后面添加指定内容
prefixOverrides、suffixOverrides:在标签中内容前面或后面去掉指定内容
select * from
t_emp
<!--因为trim标签不能自动生成where
所以需要通过prefix标签在if前面添加where
因为and在内容后面,所以通过suffixOverrides来去掉多余的and
以免报错
-->
<trim prefix="where" suffixOverrides="and">
<if test="empName != null and empName != ''">
emp_name = #{empName} and
</if>
<if test="age != null and age != ''">
age = #{age} and
</if>
<if test="gender !=null and gender != ''">
gender = #{gender}
</if>
</trim>
9.5、choose、when、otherwise
choose、when、otherwise相当于java中的if…else if…else语句
也就是说当一个when满足条件时,其余的when即其后面的when将不起作用
when至少设置一个,otherwise至多设置一个
when下有test属性与if下的test属性相同,用作判断条件
/**
* 通过Choose标签来查询员工信息,mapper接口方法
* @param emp
* @return
*/
List<Emp> getEmpByChoose(Emp emp);
<!-- List<Emp> getEmpByChoose(Emp emp);-->
<select id="getEmpByChoose" resultType="com.zyh.pojo.Emp">
select *
from t_emp
<where>
<choose>
<when test="empName!=null and empName!=''">
emp_name=#{empName}
</when>
<when test="age!=null and age!=''">
age=#{age}
</when>
<when test="gender!=null and gender!=''">
gender = #{gender}
</when>
</choose>
</where>
</select>
测试例子:
执行结果:
9.6、foreach标签
foreach在mybatis操作数据库中常常用作批量修改的操作
collection: 设置要循环的数组或集合,建议使用@Param注解设置的属性名
item:用一个字符串表示数组或集合中的每一个数据
separator:设置每次循环的数据之间的分隔符号
open:循环的所有内容以什么开始,例:左括号(
close:循环的所有内容以什么结束,例:右括号)
9.6.1、批量添加
- 创捷mapper接口方法
/**
* 批量添加Emp数据
* @param emps
*/
void insertEmpByForeach(@Param("emps") List<Emp> emps);
- 配置insert标签的映射文件
<!--void insertEmpByForeach(@Param("emps") List<Emp> emps);-->
<insert id="insertEmpByForeach">
insert into t_emp values
<foreach collection="emps" item="emp" separator=",">
(null,#{emp.empName},#{emp.age},#{emp.gender},null)
</foreach>
</insert>
- 测试与结果
最好写@Param注解,因为List集合在mybatis中默认存入的时Map中,它的存储方式是以list为键,list的值为值,如果没有设置@Param注解,不能直接使用emps,它默认的是List:
不能将 ( ),写在< foreach >外面,因为它是以一个循环一个(value)值,即在mysql的标准是:insert into 表名 values(),(),()
因为使用的是list集合存储,并非一个实体类,所以插入的数据不能直接使用实体类对象的属性名,错误:#{empName},应该使用每个对象的属性名#{emp.empName}
9.6.2、批量删除
- 创建mapper删除方法
/**
* 批量删除emp员工信息
* 通过Integer[]数组保存需要删除的id
* @param empIds
*/
void deletEmpByForeach(@Param("empIds") Integer[] empIds);
- 配置映射文件sql
<!-- void deletEmpByForeach(@Param("empIds") Integer[] empIds);-->
<delete id="deletEmpByForeach">
delete from t_emp where emp_id in
(
<foreach collection="empIds" item="empId" separator=",">
#{empId}
</foreach>
)
</delete>
此时括号写在< foreach >外面,根据mysql标准: delete from t_emp where emp_id in (? ,? ,?..)
还有例外一种写法:利用open和close属性:
结果:
数组和集合在mybatis中均存放在Map集合当中,如果没有设置@Param注解表名名称,分别以默认Array为键,Array的值为值,List为键,List的值为值
9.7、sql标签
sql标签(sql片段),在有需要的地方就使用include标签中的refid引用id属性进行引用。比如:查询中的 * 换成sql表中的每个字段名
10、Mybatis的缓存
10.1、一级缓存
Mybatis的一级缓存是Sqlsession级别的,即通过同一个Sqlsession查询的数据将会被缓存,再次使用同一个Sqlsession查询的同一条数据,会从缓存中获取,而并非再一次执行sql语句。一级缓存是默认开启的
@Test
public void getEmpByEmpIdTest(){
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
//此处用的是同一个Sqlsession
Emp emp1 = mapper1.getEmpByEmpId(1);//第一条emp1
System.out.println(emp1);
Emp emp2 = mapper1.getEmpByEmpId(1);//第二条emp2
System.out.println(emp2);
}
10.2、一级缓存失效情况
- 同一条Sqlsession级别下的查询不同的数据的情况
- 不同Sqlsession级别的下查询相同或不同数据的情况
@Test
public void getEmpByEmpIdTest(){
//第一个Sqlsession
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEmpId(1);
System.out.println(emp1);
Emp emp2 = mapper1.getEmpByEmpId(1);
System.out.println(emp2);
//第二个Sqlsession
SqlSession sqlSession2 = SqlSessionUtil.getSqlSession();
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
Emp emp3 = mapper2.getEmpByEmpId(1);
System.out.println(emp3);
}
- 同一条Sqlsession级别下,两次查询相同数据期间执行了任何的增删改的操作(mybatis自动清除缓存)
@Test
public void getEmpByEmpIdTest(){
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEmpId(1);
System.out.println(emp1);
//同一个Sqlsession,两次查询之间做插入员工的操作
mapper1.insertEmp(new Emp(null,"小天",25,"男"));
Emp emp2 = mapper1.getEmpByEmpId(1);
System.out.println(emp2);
}
因为增删改操作可能会影响到缓存中的数据,所以mybatis会自动清理缓存,重新再从数据库查询
- 同一条Sqlsession级别下,两次查询期间手动清理缓存
@Test
public void getEmpByEmpIdTest(){
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEmpId(1);
System.out.println(emp1);
//同一个Sqlsession,两次查询之间做插入员工的操作
//mapper1.insertEmp(new Emp(null,"小天",25,"男"));
//手动清空缓存,因为是Sqlsession级别,所以直接用Sqlsession级别的方法
sqlSession1.clearCache();//清理缓存
Emp emp2 = mapper1.getEmpByEmpId(1);
System.out.println(emp2);
10.3、二级缓存
二级缓存是SqlsessionFactory级别,通过同一个SqlsessionFactory创建Sqlsession查询的结果会被缓存;之后,如果再次执行相同的查询语句,结果就会从缓存中获取
因为需要进行二级缓存,需要使用SqlsessionFactory级别,所以不能再使用SqlsessionFactoryUtils工具类,因为需要创建由同一个SqlsessionFactory创建的Sqlsession
开启二级缓存的条件:
- 在核心配置文件中设置全局配置属性cacheEnabled=“true”,默认为true,故不需要设置
- 在映射文件当中设置标签< cache/>
- 二级缓存必须在Sqlsession关闭之后或者提交之后有效
- 查询的数据所转换的实体类类型必须实现序列化的接口
@Test
public void getEmpBySqlsessionFactoryTest() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEmpId(1);
System.out.println(emp1);
sqlSession1.clearCache();
sqlSession1.close();
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
Emp emp2 = mapper2.getEmpByEmpId(1);
System.out.println(emp2);
}
- 为什么要关闭Sqlsession才能将数据进行二级缓存,因为数据在Sqlsession第一次执行sql查询语句时,默认缓存,此时需要手动关闭或者提交才能将数据进行二级缓存。
- Sqlsession的缓存进行手动清空时,不会影响二级缓存,但是二级缓存必须关闭或者提交Sqlsession
- 当执行的结果中,缓存命中率不为0,说明缓存数据成功,查询的数据从缓存中获取 Cache Hit Ratio [com.zyh.mapper.CacheMapper]: 0.5
- 再执行相同查询期间,执行任意的增删改操作都会使得一级缓存和二级缓存失效。
11、逆向工程
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成:
- java实体类
- mapper接口
- 映射文件
逆向工程中的方法只适用于单表的操作,对于多表的操作还需要自己通过设置映射来操作处理
10.1、创建逆向工程一: MyBatis3Simple
生成基本的CRUD
1、 导入相关依赖的jar包和插件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 逆向工程依赖-->
<groupId>org.example</groupId>
<artifactId>mybatis-mbg</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<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>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
mybatis的逆向工程的插件mybatis-generator-maven-plugin也需要mybatis的核心依赖的数据库驱动
- 创建Mybatis的核心配置文件mybatis-config.xml
- 创建逆向工程配置文件
文件名必须为: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>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD
MyBatis3: 生成带条件的CRUD
-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/tb?serverTimezone=UTC"
userId="root"
password="abc123">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.zyh.pojo" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.zyh.mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.zyh.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
- 执行mbg插件
插件执行结果:
10.2、创建逆向工程二: MyBatis3
带条件的CRUD
- 只需在上面的基础上将
generatorConfig.xml
中将targetRuntime=“Mybatis3”
- 启动插件
10.2.1、生成的mapper接口方法
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table t_emp
*
* @mbggenerated Tue Aug 30 21:01:14 CST 2022
*/
int countByExample(EmpExample example);
此方法为根据条件从数据库中,查询并记录多少条记录,这里是EmpExample类,是mybatis逆向工程生成,还有很多方法这里不一一介绍。
10.2.2、测试mapper接口的方法
- 例子一:
@Test
public void MyabtisMbgTest(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//根据条件查询,当为null时,则没有条件,查询所有数据
List<Emp> emps = mapper.selectByExample(null);
emps.forEach(System.out::println);
}
2. 例子二:
//根据主键查询,只能通过主键的方法
Emp emp = mapper.selectByPrimaryKey(1);
System.out.println(emp);
3. EmpExample
//根据EmpExample的对象为条件查询对应的数据
EmpExample example = new EmpExample();
example.createCriteria().andEmpNameEqualTo("李四");
List<Emp> emps = mapper.selectByExample(example);
emps.forEach(System.out::println);
通过创建 EmpExample实体类对象,在通过对象调用createCriteria()方法
example.createCriteria().andEmpNameEqualTo("李四");
eample.createCriteria(). 全都以and开头的方法
当然可以用or方法
10.2.3、创建逆向工程时的问题
通过maven插件快速创建mybatis的pojo类中,会发现实体类名与数据库表中的字段名不一致,导致程序报错: 以及pojo多出了Emp.java1的文件
解决方法:
在generatorConfig.xml中的connectionURL数据库连接中添加nullCatalogMeansCurrent=true
connectionURL="jdbc:mysql://localhost:3306/tb?serverTimezone=UTC&nullCatalogMeansCurrent=true"
12、分页插件
12.1、导入分页插件的依赖
- 在pom.xml文件中分页插件的依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
- 在mybatis-config.xml的核心文件中设置插件
位置放在environment标签之前的位置
<!--设置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"> </plugin>
</plugins>
12.2、分页插件的使用
- 启动分页插件:
@Test
public void LimitPageTest(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//pageNum:表示第一页
//pageSize:表示每一页展示多少条数据
PageHelper.startPage(1,4);//启动插件
//为null表示没有查询的条件,显示所有数据
List<Emp> emps = mapper.selectByExample(null);
emps.forEach(System.out::println);
}
2. PageInfo类
PageInfo<Emp> pageInfo = new PageInfo<>();
此传递的参数可以有三种:
无参
List
(List,导航分页页码数)
@Test
public void LimitPageTest(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//pageNum:表示第一页
//pageSize:表示每一页展示多少条数据
PageHelper.startPage(1,4);
//为null表示没有查询的条件,显示所有数据
List<Emp> emps = mapper.selectByExample(null);
PageInfo<Emp> pageInfo = new PageInfo<>(emps,2);
//emps.forEach(System.out::println);
System.out.println(pageInfo);
}
分页相关数据:
此处截取尚硅谷ssm视频资料