1.概念
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。
- 简化JDBC的开发
- 能够更好的完成ORM(Object Relational Mapping对象关系映射)
2.内部组件结构图
3.导入依赖
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version> </dependency>
4.核心配置文件
<!-- mybatis的核心配置文件 -->
<configuration>
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers><mapper resource="mappers/UserMapper.xml"/></mappers>
</configuration>
5.实现代码
//1,创建SqlSessionFactory对象,线程非安全,用来产生SqlSession
//2,创建SqlSession,用来执行sql
//3, 定位SQL: namespace的值+id的值,可以只传入id,但是,如果在mybatis的环境中有多个 相同id的映射名称,就会报错。
//4,解析结果并打印
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory session = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = session.openSession();
List<User> list = sqlSession.selectList("hello.get"); for (User u : list) {
System.out.println(u);
}
<mapper namespace="hello">
<select id="get" resultType="cn.tedu.pojo.User">
select * from user
</select>
</mapper>
6.ResultMap简单使用
当数据库的字段名和对象的属性名一致时,可以用简单属性resultType。
但是当数据库中的字段名称和对象中的属性名称不一致时,就需要resultMap属性。
例子:
<mapper namespace="cn.tedu.mybatis.pojo.PersonMapper">
<!-- 最强大对象resultMap,结果封装到哪个pojo对象,type就是谁 -->
<resultMap type="Person" id="personRM"> <!-- 映射主键 -->
<id column="id" property="id"/>
<!-- 映射其他列 -->
<!—单独处理属性名和字段名不一致的 -->
<result property="userName" column="user_name"/>
</resultMap>
<select id="find" resultMap="personRM">
SELECT id,user_name FROM person WHERE id=#{id}
</select> <mapper>
7.#{}与${}区别
#{}是预编译处理,${}是字符串替换。
Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;
Mybatis 在处理${}时,就是把${}替换成变量的值。
总结:
使用#{}进行预编译处理,可以有效的防止 SQL 注入攻击,提高系统安全性。
注意:order by后面的关键字必须用${}
传递表名时必须用${}
其他情况能使用#{}就不要使用${}
8.MyBatis与SpringBoot进行整合
(1)xml文件
#spring整合数据源 最快的数据源
spring:
datasource:
#使用高版本驱动时使用cj
#serverTimezone=GMT%2B8 东8区 %2B +号
#&useUnicode=true&characterEncoding=utf8 是否开启unicode编码/utf-8
#&autoReconnect=true 断线是否重连
#&allowMultiQueries=true 是否允许批量操作
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
username: root
#如果密码以数字0开头,则使用""号包裹 "0123456"
password: root#SpringBoot整合Mybatis配置
mybatis:
#设置别名包
type-aliases-package: com.jt.pojo
#加载映射文件
mapper-locations: classpath:/mappers/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
(2)为接口创建代理对象
@Mapper
@MapperScan("com.jt.mapper")
(3)实现数据查询时有2种Sql的写法
将所有的Sql语句都写到xml 映射文件中. (万能操作方式)
可以将Sql语句通过注解的方式标识在接口方法中.(只适用于简单操作)
@select @insert @update @delete
9.MyBatisPlus
使用MP主要完成单表的CURD操作简化开发
(1) 说明:
- POJO应该与数据库中的表完成映射
- POJO中的属性与表中的字段一一映射
注解:
- @TableName(“demo_user”) //实现对象与表名映射
- //设定主键自增 @TableId(type = IdType.AUTO)
- @TableField(“name”) 实现属性与字段映射(如果属性与字段的名称一致,则注解可以省略)
(2)Mapper继承公共的接口BaseMapper,添加泛型对象
(3)创建条件构造器,封装where条件(默认的关系链接符 and)
逻辑运算符 > gt, < lt, = eq, >= ge, <= le, != ne
like:leftlike,rightlike
关键字: order by 排序 默认规则: 升序 asc 降序 desc
QueryWrapper queryWrapper = new QueryWrapper();
(4)利用MP实现分页查询查询
PageResult
@Data
@Accessors(chain = true)
public class PageResult {
private String query; //查询的key
private Integer pageNum; //页数
private Integer pageSize; //条数
private Long total; //总数
private Object rows; //分页后的结果
}
UserController
/**
* 需求: 利用分页展现用户user列表数据
* URL: /user/list GET方式
* 请求参数: http://localhost:8091/user/list?query=查询关键字&pageNum=1&pageSize=10
* 返回值: SysResult对象(PageResult)
* */
@GetMapping("/list")
public SysResult getUserList(PageResult pageResult){
pageResult = userService.getUserList(pageResult);
return SysResult.success(pageResult);
}
UserServiceImpl
/**
* 利用MP的方式实现分页查询
* API说明:selectPage(arg1,arg2)
* arg1: MP中的分页对象 固定的
* arg2: MP分页中的条件构造器
* @param pageResult
* @return
*/
@Override
public PageResult getUserList(PageResult pageResult) {
//1.定义MP的分页对象 arg1:页数 arg2:行数
IPage iPage = new Page(pageResult.getPageNum(),pageResult.getPageSize());
//2.构建查询条件构造器
QueryWrapper queryWrapper = new QueryWrapper();
//判断用户数据是否有效 有效true 无效false
boolean flag = StringUtils.hasLength(pageResult.getQuery());
queryWrapper.eq(flag,"username",pageResult.getQuery());
//经过MP分页查询将所有的分页数据(total/结果/页面/条数)封装到iPage对象
iPage = userMapper.selectPage(iPage,queryWrapper);
//从分页对象中获取分页@Transactional后的总记录数/结果
return pageResult.setTotal(iPage.getTotal()).setRows(iPage.getRecords());
}
MP配置类
//命名规则:类似于配置文件 则把这个类称之为"配置类" 一般Config接我
@Configuration //标识是一个配置类(代替之前的xml文件)
public class MybatisPlusConfig {
//铺垫:xml中通过标签管理对象,将对象交给Spring容器管理 <Bean>
//配置类:将方法的返回值交给Spring容器管理 @Bean注解
/**
* 关于MP分页规则说明
* 规则:需要设定一个拦截器,将分页的sql进行动态的拼接
* sql规则:现在的sql都支持sql92标准
* */
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));
return interceptor;
}
}
(5)MP实现自动填充
//pojo基类,完成2个任务,2个日期,实现序列化
@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{
@TableField(fill = FieldFill.INSERT)
private Date created; //表示入库时需要赋值
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updated; //表示入库/更新时赋值.
}
MP对外暴露了一个自动填充的接口MetaObjectHandler ,用户只需要实现该接口,并且重写其中的方法。即可以实现自动填充的功能。
@Component //将对象交给Spring容器管理 不属于C/S/M
public class MyMetaObjectHandler implements MetaObjectHandler {
// 入库操作时调用created/updated
/**
* setFieldValByName(arg1,arg2,arg3)
* arg1 自动填充的字段名称
* arg2 自动填充的值
* arg3 metaObject(固定写法)
*/
@Override
public void insertFill(MetaObject metaObject) {
//设定时间变量
Date date = new Date();
this.setFieldValByName("created", date, metaObject);
this.setFieldValByName("updated", date, metaObject);
}
//更新操作时调用updated
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updated", new Date(), metaObject);
}
}
10.面试题:
(1)当实体类中的属性名和表中的字段名不一样 ,怎么办?
- 在Mapper.xml文件中使用resultMap来定义映射规则
- 写SQL语句时起别名
(2)模糊查询like怎么写?
- 在java代码中添加SQL通配符%(传参)
- 在SQL语句拼接通配符%,但是可能会引起SQL注入攻击
例子1:
string wildcardname = “%smi%”;
list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”>
select * from foo where bar like #{value}
</select>例子2
string wildcardname = “smi”;
list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”>
select * from foo where bar like "%"#{value}"%"
</select>
(3)Mybatis Dao层 接口的工作原理是什么?
Dao接口的工作原理是JDK动态代理,Mybatis运行时,使用JDK动态代理为Dao接口生成代理对象。
(4)Mybatis 的一级、二级缓存?
一级缓存:
Mybatis支持缓存,但在没有配置的情况下,默认情况下它只启用一级缓存。级别1缓存只对相同的SqlSession启用。因此,如果SQL参数一模一样,我们使用相同的SqlSession对象调用映射方法,通常只执行SQL一次,因为第一个查询使用SelSession MyBatis将把它放在缓存中,和将来查询,如果没有声明需要刷新,如果缓存中没有,SqlSession将获取当前缓存的数据,并且不会再次向数据库发送SQL。
二级缓存:
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。二级缓存与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置<cache/>。
(5)什么是 MyBatis 的接口绑定?有哪些实现方式?
接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。
接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@Select、@Update等注解,里面包含Sql语句来绑定;另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。
总结:当Sql语句比较简单时候,用注解绑定,当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。
(6)使用 MyBatis 的 mapper 接口调用时有哪些要求?
- Mapper接口方法名和mapper.xml中定义的每个sql的id相同;
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同;
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;
- Mapper.xml文件中的namespace即是mapper接口的类路径。
(7)JDBC和MyBatis的区别?
JDBC是java提供了一套专门用于和数据库对接的api,java.sql.*,其规范了如何和数据库进行对接,实现由各数据库厂商进行各自的实现和扩展。学习JDBC重点在学习如何使用其api。
MyBatis框架是轻量级封装了JDBC,我们已经看不到这些api,连接connection、语句preparedstatement、结果集ResultSet,而关注的是mybatis框架体系如何去使用,一旦写好,我们关注的是java对象。