1. MyBatis的结构
2. MyBatis的入门案例
a. 创建java项目,并在其中导入相关开发包
b. 导入约束文件
c. 创建表,创建bean
i. 创建表
create database mydb;
use mydb;
create table user (
id int primary key auto_increment,
name varchar(255),
age int
);
insert into user values (null,'张三',19),(null,'李四',29),(null,'王五',39),(null,'赵六',22),(null,'陆七',33);
ii. 创建bean
package cn.tedu.domain;
public class User {
private int id;
private String name;
private int age;
public User() {
}
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
d. 编写配置文件sqlMapConfig.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>
<!-- 配置数据源 -->
< default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mydb" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
</configuration>
e.编写映射文件,描述bean和表sql的映射关系
<?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="cn.tedu.mapper.UserMapper">
<select id="sel01" resultType="cn.tedu.domain.User">
select * from user
</select>
</mapper>
f. 将映射文件配置到sqlMapConfig.xml中
<!-- 映射文件 -->
<mappers>
<mapper resource="userMapper.xml"/>
</mappers>
g. 测试类测试
/**
* MyBatis入门案例
* @throws Exception
*/
@Test
public void test01() throws Exception{
//1.生成SqlSessionFactory
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//2.创建sqlSession
SqlSession session = factory.openSession();
//3.通过SqlSession调用sql
List<User> list = session.selectList("cn.tedu.mapper.UserMapper.sel01");
//4.打印结果
System.out.println(list);
}
h. 调用过程
3. 值的传递 - Map传值
可以通过对象 获取 Map传递值,在配置文件中 通过 #{} 或 ${}进行应用
测试类:
4. 值的传递 - 对象传值
可以通过对象 获取 Map传递值,在配置文件中 通过 #{} 或 ${}进行应用在UserMapper.xml中配置:
测试类:
5. 值的传递 - 单值传值
如果程序中只有一个参数需要传递给sql,则不需要封装到bean或map中,可以直接传入。在sql中可以使用任意名称获取到这个参数,虽然名称可以任意,但通常仍然使用该属性的名称,以便于阅读。
测试类:
6. #{} 和 ${}区别
#{}在引用时,如果发现目标是一个字符串,则会将其值作为一个字符串拼接在sql上,即拼接时自动包裹引号
${}在引用时,即使发现目标是一个字符串,也不会作为字符串处理,拼接在sql时不会自动包裹引号
例如:
所以通常情况下,使用#{}
insert into user values (null,#{name},55); --> insert into user values (null,'fff',55);
insert into user values (null,${name},55); --> insert into user values (null,fff,55);//sql语句错误
而如果需要引用的是一个列名,使用${}
select * from user order by #{cname}; --> select * from user order by 'age';//sql语句错误
select * from user order by ${cname}; --> select * from user order by age;
7. update修改
update修改也可以使用之前的机制在配置文件中直接编写sql,但是update语句 的 set字句中 拼接哪些字段 是根据传入的值决定,此时可以通过MyBatis提供的标签 实现判断 动态拼接update语句:
测试类:
8. select查询
select查询也可以使用之前的机制在配置文件中直接编写sql,但是select语句 的 where字句中 拼接哪些查询字段 是根据传入的值决定,此时可以通过MyBatis提供的标签 实现判断 动态拼接select语句:
测试类:
9. insert操作
insert插入也可以使用之前的机制在配置文件中直接编写sql
但是insert语句 的参数和值的列表 拼接哪些字段 是根据传入的值决定,此时可以通过MyBatis提供的标签 实现判断 动态拼接insert语句:
测试类:
10. delete删除
delete删除也可以使用之前的机制在配置文件中直接编写sql,但是delete语句 的删除条件 拼接哪些字段 是根据传入的值决定,此时可以通过MyBatis提供的标签 实现判断 动态拼接delete语句:
测试类:
11. 手动映射结果集
MyBatis可以自动将查询结果封装到bean中,前提条件是bean的属性名和查询的结果列名相同, 就会依次对应存储。如果查询结果的列名和bean的属性名不一致,则需要手动映射结果集
测试类:
12. MyBatis中的一对一查询
创建表:
create table room(id int primary key,name varchar(255));
insert into room values (1,'梅花屋'),(2,'兰花屋'),(3,'桃花屋');
create table grade(id int primary key,name varchar(255), rid int);
insert into grade values (999,'向日葵班',2),(888,'玫瑰花班',3),(777,'菊花班',1);
在通过MyBatis实现一对一的查询时,需要通过resultMap指定如何将结果集中的列名对应到目标bean中,在一对一的bean中,如果包含了另一个表的对应对象,则可以在resultMap中通过association标签来声明映射方式:
配置方式:
<resultMap type="cn.tedu.domain.Grade" id="rm01">
<id column="gid" property="id"/>
<result column="gname" property="name"/>
<association property="room" javaType="cn.tedu.domain.Room">
<id column="rid" property="id"/>
<result column="rname" property="name"/>
</association>
</resultMap>
<select id="o2o1" resultMap="rm01" >
select grade.id as gid,grade.name as gname,room.id as rid,room.name as rname from grade inner join room on grade.rid = room.id;
</select>
测试类:
13. MyBatis中的一对多查询
创建表
create table dept(id int primary key,name varchar(255));
insert into dept values (1,'财务部'),(2,'行政部'),(3,'人事部'),(4,'销售部');
create table emp(id int primary key,name varchar(255), deptid int);
insert into emp values (999,'孙悟空',4),(888,'猪八戒',3),(777,'唐僧',1),(666,'白龙马',2),(555,'女儿国国王',3);
a. 方式一:以部门为角度,在部门bean中保存emp的集合
package cn.tedu.domain;
import java.util.List;
public class Dept {
private int id;
private String name;
private List<Emp> list;
public Dept() {
}
public Dept(int id, String name, List<Emp> list) {
this.id = id;
this.name = name;
this.list = list;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Emp> getList() {
return list;
}
public void setList(List<Emp> list) {
this.list = list;
}
@Override
public String toString() {
return "Dept [id=" + id + ", name=" + name + ", list=" + list + "]";
}
}
package cn.tedu.domain;
public class Emp {
private int id;
private String name;
private Dept dept;
public Emp() {
}
public Emp(int id, String name, Dept dept) {
this.id = id;
this.name = name;
this.dept = dept;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp [id=" + id + ", name=" + name + ", dept=" + dept + "]";
}
}
在通过MyBatis实现一对多的查询时,通过resultMap指定如何将结果集中的列名对应到目标bean中,在一对多的bean中,如果包含了另一个表的对应对象的集合,则可以在resultMap中通过collection标签来声明映射方式:
配置方式:
测试类:
b. 方式二:以员工为角度,在员工bean中存储部门bean
在通过MyBatis实现一对多的查询时,通过resultMap指定如何将结果集中的列名对应到目标bean中,在resultMap中通过association标签来声明映射方式:
配置方式:
测试类:
14. MyBatis中的多对多查询
创建表:
create table stu(id int primary key,name varchar(255));
insert into stu values (1,'小新'),(2,'小白'),(3,'美伢'),(4,'风间');
create table teacher(id int primary key,name varchar(255));
insert into teacher values (999,'孙悟空'),(888,'猪八戒'),(777,'萨达姆'),(666,'哈利波特');
create table stu_teacher (sid int,tid int);
insert into stu_teacher values (1,999),(1,888),(2,999),(2,777),(3,666),(4,888),(4,666);
在通过MyBatis实现多对多的查询时,需要通过resultMap指定如何将结果集中的列名对应到目标bean中,在多对多的bean中,如果包含了另一个表的对应对象的集合,则可以在resultMap中通过collection标签来声明映射方式
a. 方式一:以Teacher为角度,在Teacher bean中存储Stu bean的List配置方式:
测试类:
b. 方式二:以Stu为角度,在Stu bean中存储Teacher bean的List
配置方式:
测试类:
15. MyBatis中的其他细节
a. 别名标签
如果在映射文件中,大量使用类名比较长,可以在sqlMapConfig.xml声明别名,在映射文件中可以使用别名缩短配置,注意此配置要放在最前面
sqlMapConfig.xml中:
映射文件中:
b. sql的复用
如果某段sql语句的片段在映射文件中重复出现,可以将其单独配置为一个引用,从而在需要时直接引用,减少配置量
c. MyBatis的缓存机制
缓存机制可以减轻数据库的压力,原理是在第一查询时,将查询结果缓存起来,之后再查询同样的sql,不是真的去查询数据库,而是直接返回缓存中的结果。
缓存可以降低数据库的压力,但同时可能无法得到最新的结果数据。
i. 数据库缓存的实现:
通过第三方工具实现缓存:
Redis内存数据库 - 可以实现缓存
通过MyBatis提供的缓存机制来实现缓存:
一级缓存:
缓存只在一个事务中有效,即同一个事务中先后执行多次同一个查询,只在第一次真正去查库,并将结果缓存,之后的查询都直接获取缓存中的中数据。如果是不同的事务,则缓存是隔离的。
二级缓存:
缓存在全局有效,一个事务查询一个sql得到结果,会被缓存起来,之后只要缓存未被清除,则其他事务如果查询同一个sql,得到的将会是之前缓存的结果。二级缓存作用范围大,作用时间长,可能造成的危害也更大,所以在开发中一般很少启用Mybatis的二级缓存。
ii. MyBatis的一级缓存
MyBatis的一级缓存默认就是开启状态,且无法手动关闭。
iii. MyBatis的二级缓存
MyBatis的二级缓存默认是关闭的
可以配置选项开启二级缓存:
在sqlMapConfig.xml配置:
在映射文件中配置:
想要被二级缓存缓存的bean必须实现序列化接口:
测试类:
16. 接口的使用
为了简化MyBatis的使用,MyBatis提供了接口方式 自动化 生成调用过程,可以大大简化MyBatis的开发
a. 实现过程
开发映射文件:
开发接口:
映射文件中声明的名称空间应该为该映射文件对应的处理接口的全路径名称
接口中应该声明和映射文件中sql对应的id相同名称的方法
方法接收的参数应该和sql中接收的参数一致
方法的返回值应该和sql中声明的返回值类型一致
开发测试类:
真正的开发中,都是使用这种接口+配置文件方式,实现MyBatis的使用。
实现原理: