一、原始JDBC开发存在的问题
package com.qf.java2107.test;
import org.junit.Test;
import java.math.BigDecimal;
import java.sql.*;
/**
* @author ghy
* @version 1.0
* @date 2021-12-22
**/
public class JdbcTest {
/**
* 存在的问题:
* 1. 需要频繁的手动获取连接
* 2. 需要手动封装查询结果集
* 3. 需要手动释放资源
* 4. SQL硬编码
* 有自己封装的工具类,也有DBUtils等工具类API。但是都没有从根本上解决上面的问题
*/
@Test
public void testJdbc(){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1.反射加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2106_hotel", "root", "root");
//3.编写SQL
String sql = "SELECT * FROM t_user WHERE user_id = ?";
//4.获取执行SQL的载体对象,预编译SQL
preparedStatement = connection.prepareStatement(sql);
//填充占位符
preparedStatement.setLong(1, 3);
//5.执行SQL
resultSet = preparedStatement.executeQuery();
//6.处理结果
if (resultSet.next()) {
//user_id : 结果集的列名
long userId = resultSet.getLong("user_id");
String username = resultSet.getString("username");
BigDecimal balance = resultSet.getBigDecimal("balance");
System.out.println(userId + "," + username + "," + balance );
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//7.释放资源
//先开后关
try {
if(null != resultSet) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(null != preparedStatement) {
preparedStatement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(null != connection) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
-
存在的问题
-
- 需要频繁的手动获取连接
-
2. 需要手动封装查询结果集
-
3. 需要手动释放资源
-
4. SQL硬编码
-
二、ORM框架
- 通过ORM框架解决JDBC存在的问题
1、ORM
- Object Relation Mapping:对象关系映射
Java对象 | 数据库表 |
---|---|
类名 | 表名 |
属性名 | 列名【字段名】 |
对象 | 行【记录】 |
2、框架
-
概述:就是一个半成品软件
- 需要使用框架帮我们完成JavaEE应用的开发
-
作用
- 能够帮我们快速有效的开发JavaEE应用
- 有自己对应用场景的完整解决方案
三、Mybatis
1、概述
-
是一款开源的、优秀的、支持定制SQL的ORM框架
-
官网:https://mybatis.org/mybatis-3/zh/index.html
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2、快速入门
- 参考官网案例
3.2.1 实现步骤
- 创建数据库表
- 导入依赖
- 实体类
- Mapper接口【Dao接口】
- SQL映射文件
- 全局配置文件
- 测试
3.2.2 具体实现
- 创建数据库表
CREATE TABLE `t_user` (
`user_id` bigint(100) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(20) DEFAULT NULL COMMENT '用户名',
`password` varchar(32) DEFAULT NULL COMMENT '密码',
`nick_name` varchar(20) DEFAULT NULL COMMENT '昵称',
`is_admin` tinyint(4) DEFAULT NULL COMMENT '是否管理员 0:否 1:是',
`phone` varchar(11) DEFAULT NULL COMMENT '手机',
`gender` tinyint(4) DEFAULT NULL COMMENT '性别 0:保密 1:男 2:女',
`birth` date DEFAULT NULL COMMENT '生日',
`user_status` tinyint(4) DEFAULT NULL COMMENT '状态(是否激活) 0:否 1:是',
`user_create_time` datetime DEFAULT NULL COMMENT '创建时间',
`user_update_time` datetime DEFAULT NULL COMMENT '更新时间',
`is_delete` tinyint(4) DEFAULT NULL COMMENT '是否删除 0:否 1:是',
`is_member` tinyint(4) DEFAULT NULL COMMENT '是否会员 0:否 1:是',
`balance` decimal(20,2) DEFAULT NULL COMMENT '账户余额',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='这是一个用户表';
insert into `t_user`(`user_id`,`username`,`password`,`nick_name`,`is_admin`,`phone`,`gender`,`birth`,`user_status`,`user_create_time`,`user_update_time`,`is_delete`,`is_member`,`balance`) values
(1,'aa','e10adc3949ba59abbe56e057f20f883e','666',1,'13566778899',1,'1990-07-18',0,'2021-10-15 10:56:40','2021-10-22 10:56:43',0,1,10000.00),
(2,'bb','bb','666',1,'111111',1,'1990-07-18',0,'2021-11-02 14:20:25','2021-11-03 09:25:58',0,1,1111.00),
(3,'cc','mark123','666',1,'13512341234',0,'1990-07-18',0,'2021-11-03 10:05:37','2021-11-03 10:05:37',0,1,10000.00),
(5,'dd','BB','666',1,'13512341234',0,'1990-07-18',0,'2021-11-03 10:15:00','2021-11-03 10:15:00',0,1,10000.00),
(6,'ee','CC','666',1,'13512341234',2,'1990-07-18',0,'2021-11-03 10:40:01','2021-11-03 10:40:01',0,1,10000.00),
(7,'ff','mark123','666',0,'13512341234',0,'1990-07-18',0,'2021-11-05 11:23:45','2021-11-05 11:23:45',0,1,10000.00);
- 导入依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- lombok : 能够快速帮我们生成实体的getter/setter方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
</dependencies>
- 实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long userId;
private String username; //成员变量
private String password;
private String nickName;
private Integer isAdmin;
private String phone;
private Integer gender;
private Date birth;
private Integer userStatus;
private Date userCreateTime;
private Date userUpdateTime;
private Integer isDelete;
private Integer isMember;
private BigDecimal balance;
}
- Mapper接口
public interface IUserMapper {
/**
* 查询所有
* @return
*/
List<User> findAll();
}
- SQL【Mapper】映射文件
<?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">
<!--
mybatis代理接口开发的要求:
1.mapper映射文件的namespace 写 mapper接口的全类名
2.mapper映射文件的statementId 写 mapper接口的对应方法名
3.mapper映射文件的resultType 写 mapper接口的对应方法返回值类型。如果是集合,写泛型
4.mapper映射文件的parameterType 写 mapper接口的对应方法形参类型。高版本mybatis可以不写,但是不推荐
-->
<mapper namespace="com.qf.java2107.mapper.IUserMapper">
<select id="findAll" resultType="com.qf.java2107.pojo.User">
<!-- 原生SQL -->
SELECT
user_id userId,
username,
password,
nick_name,
is_admin,
phone,
gender,
birth,
user_status,
user_create_time,
user_update_time,
is_delete,
is_member,
balance
FROM
t_user
</select>
</mapper>
- 全局配置文件
<?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.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/java2107"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/qf/java2107/mapper/IUserMapper.xml"/>
</mappers>
</configuration>
- 测试代码
/**
* mapper接口代理测试,掌握
**/
@Test
public void test02() {
SqlSession session = null;
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession();
//获取mapper代理
IUserMapper userMapper = session.getMapper(IUserMapper.class);
List<User> users = userMapper.findAll();
//mybatis能够帮我们自动完成查询结果集和实体间的映射,前提结果集列名和实体属性名相同
for (User user : users) {
System.out.println(user);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(null != session) {
session.close();
}
}
}
四、CURD操作
- 增删改操作涉及事务,务必要提交
1、增加
- Mapper接口
/**
* 增加
* @param user
*/
void save(User user);
- Mapper映射文件
<!-- void save(User user);-->
<insert id="save" parameterType="com.qf.java2107.pojo.User">
INSERT INTO t_user (
username,password,nick_name,is_admin,phone,gender,birth,user_status,
user_create_time,user_update_time,is_delete,is_member,balance)
VALUES
(#{username}, #{password}, #{nickName}, #{isAdmin}, #{phone}, #{gender}, #{birth}, #{userStatus},
#{userCreateTime}, #{userUpdateTime}, #{isDelete}, #{isMember}, #{balance})
</insert>
- 测试类
/**
* 增加
**/
@Test
public void test03() throws Exception {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = factory.openSession();
IUserMapper userMapper = sqlSession.getMapper(IUserMapper.class);
User user = new User();
user.setUsername("lucy");
user.setPassword("lucy123");
user.setNickName("露西");
user.setIsAdmin(0);
user.setPhone("13566778899");
user.setGender(0);
user.setBirth(java.sql.Date.valueOf("2005-10-30"));
user.setUserStatus(1);
user.setUserCreateTime(new Date());
user.setUserUpdateTime(new Date());
user.setIsDelete(0);
user.setIsMember(1);
user.setBalance(new BigDecimal(2000));
userMapper.save(user);
//提交事务
sqlSession.commit();
if(null != sqlSession) {
sqlSession.close();
}
}
1.1 主键回填
-
主键自增
- 方式一【Mapper映射文件】
<!--void saveReturnPrimaryKey(User user);--> <insert id="saveReturnPrimaryKey" parameterType="com.qf.java2107.pojo.User"> <!-- selectKey : 是指要执行的相关的SQL order: selectKey中SQL的执行顺序,after代表之后,before代表之前 resultType : selectKey中SQL的返回值类型 keyProperty : selectKey中SQL的返回值要赋值给哪个JavaBean的属性 keyColumn : selectKey中SQL的返回值对应的数据库表的列【这个值可以不写,会自动映射】 --> <selectKey order="AFTER" resultType="java.lang.Long" keyProperty="userId" keyColumn="user_id"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO t_user (username,password,nick_name,is_admin,phone,gender,birth,user_status, user_create_time,user_update_time,is_delete,is_member,balance) VALUES (#{username}, #{password}, #{nickName}, #{isAdmin}, #{phone}, #{gender}, #{birth}, #{userStatus},#{userCreateTime}, #{userUpdateTime}, #{isDelete}, #{isMember}, #{balance}) </insert>
- 方式二
<!-- void saveReturnPrimaryKey2(User user); --> <!-- useGeneratedKeys : 是否使用主键生成策略 true:是 这种方式可以获取批量插入的主键值 --> <insert id="saveReturnPrimaryKey2" parameterType="com.qf.java2107.pojo.User" useGeneratedKeys="true" keyProperty="userId" keyColumn="user_id"> INSERT INTO t_user (username,password,nick_name,is_admin,phone,gender,birth,user_status, user_create_time,user_update_time,is_delete,is_member,balance) VALUES (#{username}, #{password}, #{nickName}, #{isAdmin}, #{phone}, #{gender}, #{birth}, #{userStatus}, #{userCreateTime}, #{userUpdateTime}, #{isDelete}, #{isMember}, #{balance}) </insert>
2、修改
<!--int update(User user);-->
<update id="update">
UPDATE t_user
SET PASSWORD = #{password}, nick_name = #{nickName}, user_update_time = #{userUpdateTime}
WHERE user_id = #{userId}
</update>
3、删除
<!--boolean deleteById(Long userId);-->
<delete id="deleteById" parameterType="long">
DELETE FROM t_user WHERE user_id = #{userId}
</delete>
4、主键是字符串实现主键回填
表 |
---|
<img src=“pic/image-20211223144131489.png” alt=”image-202112231441![]() |
31489″ style=“zoom:80%;” /> |
- 实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author ghy
* @version 1.0
* @date
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Stu {
private String stuId;
private String stuName;
}
- Mapper接口和映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.java2107.mapper.IStuMapper">
<!-- int saveReturnPrimaryKey(Stu stu); -->
<insert id="saveReturnPrimaryKey" parameterType="com.qf.java2107.pojo.Stu">
<selectKey order="BEFORE" keyProperty="stuId" resultType="string" keyColumn="stu_id">
SELECT REPLACE(UUID(), '-', '')
</selectKey>
insert into t_stu (stu_id, stu_name) values (#{stuId},#{stuName})
</insert>
</mapper>
五、日志
1、日志体系
-
Slf4j:接口【门面】
- Log4j
- Logback
- commons-logging
2、日志作用
- 没日志:很难判断问题来源
-
有日志:一般都可以判断问题来源。
- 可以通过日志把问题错误信息给记录下来
3、使用
- 引入依赖
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 日志配置文件【log4j.properties】
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=ssm.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=debug, stdout, file
- 日志级别
级别 | 描述 |
---|---|
ALL LEVEL | 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。 |
DEBUG |
输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。 【开发使用】 |
INFO | 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。【线上使用】 |
WARN | 输出警告信息;表明会出现潜在错误的情形。 |
ERROR | 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。 |
FATAL | 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。 |
OFF LEVEL | 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。 |
- 直接使用API即可
package com.qf.java2107.test;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author ghy
* @version 1.0
* @date 2021-12-23
**/
public class LoggerTest {
Logger logger = LoggerFactory.getLogger(LoggerTest.class);
/**
*
**/
@Test
public void logTest() throws Exception {
logger.debug("debug---->{}", "debug level");
logger.info("info--->{}---{}", "aa", "bb");
logger.warn("warn----->");
logger.error("error---->");
}
}
4、常用插件
| 常用插件 |
——————————————–![]() |
---|
| <img src=”pic/image-202112
六、Mapper接口参数绑定
1、单个简单类型参数
基本数据类型及其包装类,String
Mapper映射文件获取参数值时,名称可以随意。但是强烈建议参数名跟形参名一致 |
---|
![]() |
2、实体类型参数
JavaBean
Mapper映射文件获取参数值时,使用#{},{}中写JavaBean的属性名 |
---|
![]() |
3、Map入参
Mapper映射文件获取参数值时,使用#{},{}中写Map中key的名称 |
---|
![]() |
4、多个参数
mybatis会把参数封装成一个Map,第一个参数的key为
arg0
【
param1
】,第二参数的key为
arg1
【
param2
】,以此类推。以上方式不推荐使用,可读性太差。
Mapper映射文件获取参数值时,使用@Param来为Mapper接口方法指定入参的参数名,使用#{}取值 |
---|
![]() |
七、ORM映射
把查询结果集跟JavaBean进行映射绑定
1、映射规则
-
当查询结果集的列名跟JavaBean属性名相同时,自动映射
-
当查询结果集的列名跟JavaBean属性名不相同时,需要手动映射
2、不满足映射规则
-
如果满足驼峰
- 使用别名进行映射【但是SQL太长】
<!-- User findByIdMapping1(Long userId); --> <select id="findByIdMapping1" parameterType="java.lang.Long" resultType="com.qf.java2107.pojo.User"> SELECT user_id userId, username, password, nick_name nickName, is_admin isAdmin, phone, gender, birth, user_status userStatus, user_create_time userCreateTime, user_update_time userUpdateTime, is_delete isDelete, is_member isMember, balance FROM t_user WHERE user_id = #{userId} </select>
- 全局配置文件【mybatis-config.xml】开启驼峰映射
<settings> <!-- 开启驼峰映射,编写的SQL就不用使用别名了 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
<!-- User findByIdMapping2(Long userId); --> <select id="findByIdMapping2" parameterType="java.lang.Long" resultType="com.qf.java2107.pojo.User"> SELECT user_id, username, password, nick_name, is_admin, phone, gender, birth, user_status, user_create_time, user_update_time, is_delete, is_member, balance FROM t_user WHERE user_id = #{userId} </select>
3、ResultMap
-
自定义结果集映射
-
mybatis内部已经集成了结果集映射,就是Mapper映射文件的
resultType
属性。
resultType
底层使用其实也是
ResultMap
-
resultType和resultMap最好只使用一个
- 如果满足驼峰并且不使用别名的情况下,可以使用resultType
- resultMap可以在查询结果集封装中自定义使用。连表查询只能使用resultMap
-
mybatis内部已经集成了结果集映射,就是Mapper映射文件的
<!--
resultMap: 自定义结果集
id : resultMap的名称,是一个唯一标识,用于被select的resultMap属性所引用
type : 查询的结果集要映射到的实体类型
-->
<resultMap id="myResultMap" type="com.qf.java2107.pojo.User">
<!--
id : 映射主键列,只是一个标识作用,也可以用result
column : 查询的结果集的列名
property : JavaBean的属性名
-->
<id column="uid" property="userId"/>
<!-- result : 映射普通列 -->
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="name" property="nickName"/>
<result column="is_admin" property="isAdmin"/>
<result column="phone" property="phone"/>
<result column="sex" property="gender"/>
<result column="birth" property="birth"/>
<result column="user_status" property="userStatus"/>
<result column="ctime" property="userCreateTime"/>
<result column="user_update_time" property="userUpdateTime"/>
<result column="is_delete" property="isDelete"/>
<result column="is_member" property="isMember"/>
<result column="balance" property="balance"/>
</resultMap>
<!-- User findByIdUseResultMap(Long userId); -->
<select id="findByIdUseResultMap" parameterType="java.lang.Long" resultMap="myResultMap">
SELECT
user_id uid, username, password, nick_name name, is_admin, phone, gender sex,
birth, user_status, user_create_time ctime, user_update_time, is_delete, is_member, balance
FROM
t_user
WHERE user_id = #{userId}
</select>
resultMap的使用 |
---|
![]() |
八、全局配置文件【了解】
- 现在:会
- 以后:配置文件不见了,放到Spring中
全局配置文件支持的标签 |
---|
![]() |
1、properties
<!--
properties : 加载外部properties文件
resource : properties文件基于classpath的路径
-->
<properties resource="jdbc.properties" />
2、settings
<!-- 全局设置 -->
<settings>
<!-- 开启驼峰映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3、typeAliases
内置别名【推荐使用】 |
---|
![]() |
| <img src=”pic/image-2021122409542759
- 自定义别名
<!-- 别名设置 -->
<typeAliases>
<!-- 单个取别名 -->
<!--<typeAlias type="com.qf.java2107.pojo.User" alias="user"></typeAlias>
<typeAlias type="com.qf.java2107.pojo.Student" alias="student"></typeAlias>-->
<!-- 批量取别名,默认别名是类名,别名不区分大小写 -->
<package name="com.qf.java2107.pojo"/>
</typeAliases>
4、plugins
4.1 分页插件概述
- 可以屏蔽底层数据库的差异,实现同一API实现不同数据库的分页功能
- https://gitee.com/free/Mybatis_PageHelper
4.2 使用步骤
- 导入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
- 全局配置文件
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 配置方言,不配置,则使用数据库连接来判断 -->
<property name="helperDialect" value="mysql"/>
<!-- 合理化参数 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
-
使用
- Mapper映射文件
<!-- List<User> findAll(); --> <select id="findAll" resultType="User"> select * from t_user </select>
- 测试代码
/** * 分页 **/ @Test public void Test() throws Exception { IUserMapper userMapper = sqlSession.getMapper(IUserMapper.class); //分页设置跟查询之间不要出现其他操作 //参数一:页码 //参数二:显示条数 PageHelper.startPage(3, 5); List<User> list = userMapper.findAll(); PageInfo<User> pageInfo = new PageInfo<>(list); System.out.println("当前页:" + pageInfo.getPageNum()); System.out.println("当前页集合:" + pageInfo.getList()); System.out.println("总页数:" + pageInfo.getPages()); System.out.println("总条数:" + pageInfo.getTotal()); System.out.println("显示条数:" + pageInfo.getPageSize()); //显示条数 System.out.println("实际显示条数:" + pageInfo.getSize()); //实际显示条数 }
5、environments
- 环境配置
<!-- 环境配置 -->
<!--
default 默认使用哪个环境,这个值是指environments下的某个environment子标签的id属性
-->
<environments default="mysqldb">
<!--
mysql环境
id :就是当前环境的唯一标识,可能被environments的default引用
-->
<environment id="mysqldb">
<!--
transactionManager : 事务管理器
type : JDBC
-->
<transactionManager type="JDBC"></transactionManager>
<!--
dataSource : 数据源
type : POOLED 池化
-->
<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>
<!-- oracle环境 -->
<!--<environment id="oracledb">
<transactionManager type="JDBC"></transactionManager>
<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>
6、mappers
- 用来加载Mapper映射文件
<!-- 加载mapper映射文件 -->
<mappers>
<!--
mapper : 加载单个mapper映射文件
resource :基于classpath路径下的mapper映射文件
class : 基于mapper接口,mybatis注解开发方式。写mapper接口全类名
-->
<!--<mapper resource="com/qf/java2107/mapper/IUserMapper.xml" ></mapper>-->
<!--
批量加载mapper映射文件
要求:mapper映射文件跟mapper接口在编译后必须在同一个路径下
-->
<package name="com.qf.java2107.mapper"/>
</mappers>
九、连表查询
1、表与表之间的关系
-
数据库层面
- 一对多:部门对员工、公司对部门
- 多对多:项目跟程序员、学生跟老师
- 多对一:学生对班级、员工对部门
- 一对一:人跟身份证、旅客跟护照
-
mybatis层面
- 一对多【多对多】
- 一对一【多对一】
2、部门表跟员工表为例
- 一对多:查询部门关联查询员工
- 一对一:查询员工关联查询部门
2.1 准备工作
- 数据库表
CREATE TABLE `t_department` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`dept_name` VARCHAR(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `t_department`(`id`,`dept_name`) VALUES
(1,'研发部'),
(2,'市场部'),
(3,'财务部'),
(4,'测试部');
CREATE TABLE `t_employee` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`emp_name` VARCHAR(20) NOT NULL,
`gender` INT(1) DEFAULT NULL COMMENT '1:男 0:女',
`birthday` DATE DEFAULT NULL,
`hire_date` DATETIME DEFAULT NULL,
`salary` INT(11) DEFAULT NULL,
`address` VARCHAR(200) DEFAULT NULL,
`dept_id` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;
INSERT INTO `t_employee`(`id`,`emp_name`,`gender`,`birthday`,`hire_date`,`salary`,`address`,`dept_id`) VALUES
(1,'宝宝',0,'2000-10-31','2021-10-01 09:00:00',8000,'杭州江干',2),
(2,'李四',1,'1985-11-10','2006-12-12 18:10:10',9000,'李家村',1),
(3,'王五',0,'1991-10-02','2009-12-02 00:00:00',1500,'王家村',2),
(5,'测试更新2',1,'2021-10-04','2021-11-11 00:00:00',15000,'杭州',2),
(7,'大CC',1,'2019-05-12','2021-11-07 00:00:00',6000,'杭州',2),
(8,'DD',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(9,'九妹',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',NULL),
(10,'萧炎',1,'1999-12-12','2021-02-03 00:00:00',5000,'牛田村',1),
(11,'萧媚',1,'1985-11-11','2006-12-12 00:00:00',9000,'李家村',1),
(12,'小医仙',0,'1991-10-02','2009-12-02 00:00:00',1500,'王家村',2),
(13,'林动',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(14,'凌青竹',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(15,'纪宁',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',2),
(16,'北冥',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(17,'叶伏天',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',NULL),
(18,'余生',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',2),
(19,'花解语',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',1),
(100,'张三',1,'1999-12-12','2021-02-03 00:00:00',5000,'牛田村',1);
部门表跟员工表 |
---|
![]() |
- 实体
部门 |
---|
![]() |
员工 |
---|
![]() |
2.2 一对多
- 根据ID查询部门信息且关联员工信息
2.2.1 修改部门实体
修改部门实体 |
---|
![]() |
2.2.2 Mapper接口和Mapper映射文件
Mapper接口和Mapper映射文件 |
---|
![]() |
- Mapper接口
public interface IDepartmentMapper {
Department findByIdAndEmps(Integer id);
}
- Mapper映射文件
<resultMap id="DeptAndEmpsResultMap" type="com.qf.java2107.pojo.Department" extends="BaseResultMap">
<!-- 映射一对多【集合】 -->
<!--
collection: 映射一对多【集合】
property: 集合名
ofType : 集合中的元素类型
-->
<collection property="employees" ofType="com.qf.java2107.pojo.Employee">
<!-- 映射单个员工 -->
<id column="emp_id" property="id"/>
<result column="emp_name" property="empName"/>
<result column="gender" property="gender"/>
<result column="birthday" property="birthday"/>
<result column="hire_date" property="hireDate"/>
<result column="salary" property="salary"/>
<result column="address" property="address"/>
<result column="dept_id" property="deptId"/>
</collection>
</resultMap>
<resultMap id="BaseResultMap" type="com.qf.java2107.pojo.Department">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
</resultMap>
<!-- Department findByIdAndEmps(Integer id); -->
<select id="findByIdAndEmps" parameterType="int" resultMap="DeptAndEmpsResultMap">
SELECT
d.id,
d.dept_name,
e.id emp_id,
e.emp_name,
e.gender,
e.birthday,
e.hire_date,
e.salary,
e.address,
e.dept_id
FROM
t_department d, t_employee e
WHERE d.id = e.dept_id
AND d.id = #{id}
</select>
2.3 一对一
- 根据ID查询员工信息且关联部门信息
2.3.1 修改员工实体
修改员工实体 |
---|
![]() |
2.3.2 Mapper接口和Mapper映射文件
Mapper接口和Mapper映射文件 |
---|
![]() |
- Mapper接口
public interface IEmployeeMapper {
Employee findByIdAndDept(Integer id);
}
- Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.java2107.mapper.IEmployeeMapper">
<resultMap id="BaseResultMap" type="com.qf.java2107.pojo.Employee">
<id column="id" property="id"/>
<result column="emp_name" property="empName"/>
<result column="gender" property="gender"/>
<result column="birthday" property="birthday"/>
<result column="hire_date" property="hireDate"/>
<result column="salary" property="salary"/>
<result column="address" property="address"/>
<result column="dept_id" property="deptId"/>
</resultMap>
<resultMap id="EmpAndDeptResultMap" type="com.qf.java2107.pojo.Employee" extends="BaseResultMap">
<!--
association : 映射实体
property : 实体属性名
javaType : 实体属性全类名
-->
<association property="department" javaType="com.qf.java2107.pojo.Department">
<id column="did" property="id"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
<!-- Employee findByIdAndDept(Integer id); -->
<select id="findByIdAndDept" parameterType="int" resultMap="EmpAndDeptResultMap">
SELECT
e.id id,
e.emp_name,
e.gender,
e.birthday,
e.hire_date,
e.salary,
e.address,
e.dept_id,
d.id did,
d.dept_name
FROM
t_employee e JOIN t_department d
ON e.dept_id = d.id
WHERE e.id = #{id}
</select>
</mapper>
十、分步查询、延迟加载
1、分步查询
-
是连表查询的另一种方式
- 把连表查询的SQL进行拆分出多个单表查询的SQL
2、一对多
- 查询部门信息关联查询员工信息
2.1 IDeparmentMapper映射文件
<!-- =================分步查询====================== -->
<resultMap id="DeptAndEmpStepQueryResultMap" type="com.qf.java2107.pojo.Department">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
<!-- 映射集合 -->
<collection property="employees" ofType="com.qf.java2107.pojo.Employee"
select="com.qf.java2107.mapper.IEmployeeMapper.findByDeptId" column="id">
</collection>
</resultMap>
<!--Department findByIdUseStepQuery(Integer id);-->
<select id="findByIdUseStepQuery" parameterType="int" resultMap="DeptAndEmpStepQueryResultMap">
SELECT id, dept_name FROM t_department WHERE id = #{id}
</select>
2.2 IEmployeeMapper映射文件
<!--==================分步查询相关======================== -->
<!--List<Employee> findByDeptId(Integer deptId);-->
<select id="findByDeptId" parameterType="int" resultMap="BaseResultMap">
SELECT * FROM t_employee WHERE dept_id = #{deptId}
</select>
2.3 执行流程
执行流程 |
---|
![]() |
3、延迟加载【面试题】
也叫懒加载,也叫按需加载
当需要使用到关联的数据时,才去执行查询操作
实现原理:Cglib动态代理【基于继承】
延迟加载只会出现在分步查询中。一般延迟加载的数据都是大数据【如集合】
- 即时加载:先执行完所有的SQL,再打印数据
- 延迟加载,先执行需要获取数据的SQL,再打印数据。后面如果还需要获取数据,再执行SQL,再打印数据
3.1 具体实现
- 全局配置文件开启延迟加载的开头
全局配置文件开启延迟加载的开头 |
---|
![]() |
- DepartmentMapper接口和映射文件
DepartmentMapper接口和映射文件 |
---|
![]() |
- 测试
即时加载 |
---|
![]() |
延迟加载 |
---|
![]() |
十一、动态SQL
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
1、if
-
条件判断
- 成立,就拼接SQL
2、where
- 功能相当于数据库的where关键字
<!-- List<Employee> findWithIf(Employee employee); -->
<select id="findWithIf" parameterType="com.qf.java2107.pojo.Employee" resultType="com.qf.java2107.pojo.Employee">
SELECT * FROM t_employee
<!--WHERE 1=1-->
<!-- where 会忽略条件成立的最前面的多余的and或者or -->
<where>
<if test="empName != null and empName.trim() != ''">
AND emp_name LIKE #{empName}
</if>
<if test="salary != null and salary > 0">
AND salary = #{salary}
</if>
<if test="gender == 0 || gender == 1">
AND gender = #{gender}
</if>
<if test="deptId != null">
AND dept_id = #{deptId}
</if>
<!-- 除SQL之外的地方都是JavaBean的属性名 -->
</where>
</select>
3、trim【理解】
- 可以自定义的拼接或去掉SQL片段前后缀
<!-- List<Employee> findWithTrim(Employee employee); -->
<select id="findWithTrim" parameterType="com.qf.java2107.pojo.Employee" resultType="com.qf.java2107.pojo.Employee">
SELECT * FROM t_employee
<!--
prefix : 要加的前缀
prefixOverrides : 要去掉的前缀
suffix : 要加的后缀
suffixOverrides : 要去掉的后缀
-->
<!--<trim prefix="where" prefixOverrides="and | or" suffix="" suffixOverrides="">
<if test="empName != null and empName.trim() != ''">
AND emp_name LIKE #{empName}
</if>
<if test="salary != null and salary > 0">
AND salary = #{salary}
</if>
<if test="gender == 0 || gender == 1">
AND gender = #{gender}
</if>
<if test="deptId != null">
AND dept_id = #{deptId}
</if>
</trim>-->
<trim prefix="where" prefixOverrides="" suffix="" suffixOverrides="AND | OR">
<if test="empName != null and empName.trim() != ''">
emp_name LIKE #{empName} AND
</if>
<if test="salary != null and salary > 0">
salary = #{salary} AND
</if>
<if test="gender == 0 || gender == 1">
gender = #{gender} AND
</if>
<if test="deptId != null">
dept_id = #{deptId} AND
</if>
</trim>
</select>
4、forEach
<!-- List<Employee> findByIds(List<Integer> ids); -->
<select id="findByIds" parameterType="list" resultType="com.qf.java2107.pojo.Employee">
<!--SELECT * FROM t_employee WHERE id IN (1,25,3,62)-->
SELECT * FROM t_employee
<where>
<if test="list != null and list.size() > 0">
<!--id IN (1,25,3,62)-->
<!--
collection : 要遍历的集合,可以使用别名,但是如果使用了@Param指定入参key,那么就指定这个key
item : 正在迭代的元素名,自己起名
separator : 元素之间的分隔符
open : 要遍历的元素开始之前的SQL片段
close : 要遍历的元素结束之后的SQL片段
-->
<foreach collection="list" item="id" separator="," open="id IN (" close=")">
#{id}
</foreach>
</if>
</where>
</select>
5、choose…when…otherwise【了解】
<!-- List<Employee> findWithChoose(Employee employee); -->
<select id="findWithChoose" parameterType="com.qf.java2107.pojo.Employee" resultType="com.qf.java2107.pojo.Employee">
SELECT * FROM t_employee
<where>
<choose>
<when test="empName != null and empName.trim() != ''">
AND emp_name LIKE #{empName}
</when>
<when test="salary != null and salary > 0">
AND salary = #{salary}
</when>
<when test="gender == 0 or gender == 1">
AND gender = #{gender}
</when>
<otherwise>
dept_id = 1
</otherwise>
</choose>
</where>
</select>
6、sql…include
<!-- List<Employee> findByIds(List<Integer> ids); -->
<select id="findByIds" parameterType="list" resultType="com.qf.java2107.pojo.Employee">
<include refid="BaseSelect"></include>
<where>
<if test="list != null and list.size() > 0">
<foreach collection="list" item="id" separator="," open="id IN (" close=")">
#{id}
</foreach>
</if>
</where>
</select>
<sql id="BaseSelect">
SELECT
<include refid="BaseColumn"></include>
FROM t_employee
</sql>
<sql id="BaseColumn">
id,
emp_name,
gender,
birthday,
hire_date,
salary,
address,
dept_id
</sql>
7、set
- 仅用于更新,跟if配套使用
<!-- int updateWithSet(Employee employee); -->
<update id="updateWithSet" parameterType="Employee">
update t_employee
<set>
<if test="empName != null and empName.trim() != ''">
emp_name = #{empName},
</if>
<if test="salary != null and salary > 0">
salary = #{salary},
</if>
<if test="gender == 0 or gender == 1">
gender = #{gender},
</if>
</set>
where id = #{id}
</update>
十二、缓存机制【理解】
-
作用
- 提高查询效率
- 减轻数据库访问压力
1、分类
- 一级缓存
- 二级缓存
2、区别
3、Mybatis执行查询流程
Mybatis执行查询流程 |
---|
![]() |
4、一级缓存
-
一级缓存默认开启,我们无法关闭他
-
在执行两次相同的查询时,第一次会向数据库发送SQL,并且写入一份到一级缓存中,那么后面的查询操作就直接从缓存中获取数据。不会向数据库发送SQL
-
一级缓存失效的情况
- 不是同一个SqlSession
- 两次相同的查询中间执行增删改
- 两次相同的查询中间手动清空缓存
- 两次相同的查询中间手动提交事务
@Test
public void firstLevelCacheTest() throws Exception {
IEmployeeMapper employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
Employee employee1 = employeeMapper.findById(10);
System.out.println(employee1);
//1.两个SqlSession
//sqlSession = factory.openSession();
//employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
//2.执行增删改
//employeeMapper.deleteById(111111);
//3.手动清空缓存
//sqlSession.clearCache();
//4.手动提交事务
sqlSession.commit();
Employee employee2 = employeeMapper.findById(10);
System.out.println(employee2);
System.out.println(employee1 == employee2);
}
5、二级缓存
- 二级缓存默认开启,我们可以通过配置文件对其关闭
-
二级缓存
- 必须在SqlSession关闭之后,数据才会被写入到二级缓存中。
-
存储介质是磁盘,所以写入的字节数据,那么要被写入的对象所在的类必须实现
Serializable
接口
-
实现步骤
- 全局配置文件开启
-
在要使用二级缓存的namespace中配置一个
<cache/>
标签 - 关闭SqlSession
/**
* 二级缓存
**/
@Test
public void secondLevelCacheTest() throws Exception {
IEmployeeMapper employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
Employee employee1 = employeeMapper.findById(10);
System.out.println(employee1);
sqlSession.close();
sqlSession = factory.openSession();
employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
Employee employee2 = employeeMapper.findById(10);
System.out.println(employee2);
System.out.println(employee1 == employee2);
}
十三、#{}和${}的区别
-
#{}
-
在填充参数时,是通过
?
占位符的方式,能够避免SQL注入 - 只是获取跟数据库表列相关的值
-
在填充参数时,是通过
-
${}
- 在填充参数时,使用的是直接进行字符串拼接,会有SQL注入的风险
- 使用其可以操作非数据库表列的取值
优先选择#{}取值,如果不行,则使用${}
十四、注解开发【会用】
- 注解开发跟配置文件开发,选择一种
package com.qf.java2107.mapper;
import com.qf.java2107.pojo.Department;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* @author ghy
* @version 1.0
* @date 2021-12-27
**/
public interface IDepartmentMapper {
@Insert("insert into t_department (dept_name) values (#{deptName})")
@SelectKey(statement = "select last_insert_id()",
keyProperty = "id",
keyColumn = "dept_id",
before = false,
resultType = int.class)
int save(Department department);
/**
* @Results 等同于配置文件的resultMap标签
* id 等同于配置文件的resultMap标签中id属性
*
* @Result : 映射单个属性,用boolean来区分是否是主键映射
*/
@Results(
id = "BaseResultMap",
value = {
@Result(id = true, column = "id", property = "id"),
@Result(id = false, column = "dept_name", property = "deptName")
}
)
@Select("select id, dept_name from t_department")
List<Department> findAll();
@ResultMap("BaseResultMap") //引用其他已经定义好的ResultMap
@Select("select id, dept_name from t_department where id = #{id}")
Department findById(Integer id);
}
- 分步查询【IEmployeeAnnoMapper.class】
package com.qf.java2107.mapper;
import com.qf.java2107.pojo.Employee;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;
/**
* @author ghy
* @version 1.0
* @date 2021-12-27
**/
public interface IEmployeeAnnoMapper {
/**
* 根据ID查询员工关联部门
* @param id
* @return
*/
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(id = false, column = "emp_name", property = "empName"),
@Result(id = false, column = "gender", property = "gender"),
@Result(id = false, column = "birthday", property = "birthday"),
@Result(id = false, column = "hire_date", property = "hireDate"),
@Result(id = false, column = "salary", property = "salary"),
@Result(id = false, column = "address", property = "address"),
@Result(id = false, column = "dept_id", property = "deptId"),
@Result(id = false, property = "dept", column = "dept_id",
//one 一对一映射
one = @One(select = "com.qf.java2107.mapper.IDepartmentMapper.findById",
fetchType = FetchType.LAZY))
})
@Select("select * from t_employee where id = #{id}") //dept_id
Employee findByIdUseStep(Integer id);
}
十五、源码分析【听一遍】
1、查询
-
查询单个
- DefaultSqlSession的selectOne方法
-
DefaultSqlSession的selectList方法,得到结果后,进行集合数量判断,如果是1,直接返回。>1,否则就报错。0返回null
-
CachingExecutor的query方法
-
BaseExecutor的query方法
-
BaseExecutor的queryFromDatabase方法
-
BaseExecutor的doQuery方法
- 通过SimpleExecutor中去调用JDBC的execute()
-
BaseExecutor的doQuery方法
-
BaseExecutor的queryFromDatabase方法
-
BaseExecutor的query方法
-
CachingExecutor的query方法
-
查询集合
-
DefaultSqlSession的selectList方法,得到结果后,直接返回
-
CachingExecutor的query方法
-
BaseExecutor的query方法
-
BaseExecutor的queryFromDatabase方法
-
BaseExecutor的doQuery方法
- 通过SimpleExecutor中去调用JDBC的execute()
-
BaseExecutor的doQuery方法
-
BaseExecutor的queryFromDatabase方法
-
BaseExecutor的query方法
-
CachingExecutor的query方法
-
DefaultSqlSession的selectList方法,得到结果后,直接返回
2、增删改方法
-
修改
-
DefaultSqlSession的update方法
-
CachingExecutor的update方法:会清空缓存操作
-
BaseExecutor的doUpdate方法
- 通过SimpleExecutor中去调用JDBC的execute()
-
BaseExecutor的doUpdate方法
-
CachingExecutor的update方法:会清空缓存操作
-
DefaultSqlSession的update方法
-
增加、删除
-
先调用DefaultSqlSession的insert方法或者delete方法
- 接下来就会执行DefaultSqlSession的update方法
-
先调用DefaultSqlSession的insert方法或者delete方法
十六、面试题
1、Mybatis支持延迟加载吗?
2、#{}和${}的区别
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XEsxGmym-1654564113636)(C:\Users\李泽楷\AppData\Roaming\Typora\typora-user-images\1654067290282.png)]
3、Mybatis应用到的设计模式
- 构建者模式
- 工厂模式
- 代理模式
4、Mybatis的Mapper接口是否支持方法重载
-
不支持
- mapper接口的方法名就是mapper映射文件的statementId,是用来获取执行SQL的唯一标识
5、Mybatis的Mapper映射文件的标签,你知道哪些?
-
除之前讲的之外
- sql:抽取的SQL片段
- include:引用抽取的SQL片段
<!-- List<Employee> findByIds(List<Integer> ids); -->
<select id="findByIds" parameterType="list" resultType="com.qf.java2107.pojo.Employee">
<include refid="BaseSelect"></include>
<where>
<if test="list != null and list.size() > 0">
<foreach collection="list" item="id" separator="," open="id IN (" close=")">
#{id}
</foreach>
</if>
</where>
</select>
<sql id="BaseSelect">
SELECT
<include refid="BaseColumn"></include>
FROM t_employee
</sql>
<sql id="BaseColumn">
id,
emp_name,
gender,
birthday,
hire_date,
salary,
address,
dept_id
</sql>
十七、Mybatis Generator插件
-
一个业务实体,基本都有CRUD操作。
- 这些写多了就都是体力活。这些重复性的代码,写多了是对技术毫无帮助。
- 已经有了很多的代码生成器,能够帮我们生成这些,无需我们自己写
-
一个完整的项目
- 会改其中的配置,生成代码即可
-
代码
- 参考工程
-
学习
-
参考视频
-
DefaultSqlSession的selectList方法,得到结果后,直接返回
-
CachingExecutor的query方法
-
BaseExecutor的query方法
-
BaseExecutor的queryFromDatabase方法
-
BaseExecutor的doQuery方法
- 通过SimpleExecutor中去调用JDBC的execute()
-
BaseExecutor的doQuery方法
-
BaseExecutor的queryFromDatabase方法
-
BaseExecutor的query方法
-
CachingExecutor的query方法
-
2、增删改方法
-
修改
-
DefaultSqlSession的update方法
-
CachingExecutor的update方法:会清空缓存操作
-
BaseExecutor的doUpdate方法
- 通过SimpleExecutor中去调用JDBC的execute()
-
BaseExecutor的doUpdate方法
-
CachingExecutor的update方法:会清空缓存操作
-
DefaultSqlSession的update方法
-
增加、删除
-
先调用DefaultSqlSession的insert方法或者delete方法
- 接下来就会执行DefaultSqlSession的update方法
-
先调用DefaultSqlSession的insert方法或者delete方法
十六、面试题
1、Mybatis支持延迟加载吗?
2、#{}和${}的区别
[外链图片转存中…(img-XEsxGmym-1654564113636)]
3、Mybatis应用到的设计模式
- 构建者模式
- 工厂模式
- 代理模式
4、Mybatis的Mapper接口是否支持方法重载
-
不支持
- mapper接口的方法名就是mapper映射文件的statementId,是用来获取执行SQL的唯一标识
5、Mybatis的Mapper映射文件的标签,你知道哪些?
-
除之前讲的之外
- sql:抽取的SQL片段
- include:引用抽取的SQL片段
<!-- List<Employee> findByIds(List<Integer> ids); -->
<select id="findByIds" parameterType="list" resultType="com.qf.java2107.pojo.Employee">
<include refid="BaseSelect"></include>
<where>
<if test="list != null and list.size() > 0">
<foreach collection="list" item="id" separator="," open="id IN (" close=")">
#{id}
</foreach>
</if>
</where>
</select>
<sql id="BaseSelect">
SELECT
<include refid="BaseColumn"></include>
FROM t_employee
</sql>
<sql id="BaseColumn">
id,
emp_name,
gender,
birthday,
hire_date,
salary,
address,
dept_id
</sql>
十七、Mybatis Generator插件
-
一个业务实体,基本都有CRUD操作。
- 这些写多了就都是体力活。这些重复性的代码,写多了是对技术毫无帮助。
- 已经有了很多的代码生成器,能够帮我们生成这些,无需我们自己写
-
一个完整的项目
- 会改其中的配置,生成代码即可
-
代码
- 参考工程
-
学习
- 参考视频