【笔记】MyBatis学习笔记

前言

MyBatis是一个Java持久化框架,它通过XML描述符或注解把对象与存储过程或SQL语句关联起来。
MyBatis是在Apache许可证 2.0下分发的自由软件,是iBATIS 3.0的分支版本。其维护团队也包含iBATIS的初创成员。(维基百科

引入依赖

  • 在Maven项目中引入MyBatis的jar包

  • 编辑pom.xml文件

1
2
3
4
5
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.1</version>
</dependency>

创建配置文件

  • src/main/resources目录下新建mybatis-config.xml全局配置文件
1
2
3
4
5
6
7
8
9
<?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">

<!-- MyBatis的全局配置文件 -->
<configuration>

</configuration>
  • src/main/resources目录下新建XxxMapper.xmlMapper配置文件
1
2
3
4
5
6
7
8
9
10
11
12
<?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值应该保证唯一
在程序中通过[ namespace + id ]定位到要执行哪一条SQL语句
-->
<mapper namespace="">

</mapper>
  • 向全局配置文件引入Mapper配置文件

  • 详细步骤传送门

MyBatis实现增删改查

  • 读取mybatis的核心配置文件(mybatis-config.xml)
  • 通过配置信息获取一个SqlSessionFactory工厂对象
  • 通过工厂获取一个SqlSession对象
1
2
3
4
5
6
7
8
9
10
11
12
SqlSession session;

@Before
public void testBefore() throws IOException {
// 读取mybatis的核心配置文件(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 通过配置信息获取一个SqlSessionFactory工厂对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory fac = sqlSessionFactoryBuilder.build( in );
// 通过工厂获取一个SqlSession对象
session = fac.openSession(true);
}

  • 在Mapper定义SQL语句
1
2
3
<insert id="ins">
INSERT INTO emp VALUES(null, "赵云", "保安", 6000.0)
</insert>
  • 返回int类型的值
1
2
3
4
5
@Test
public void testIns() {
int rol = session.insert("EmpMapper.ins");
System.out.println("影响的行数:"+rol);
}

占位符(多个)

  • 在Mapper定义SQL语句
1
2
3
<insert id="ins">
INSERT INTO emp VALUES(null, #{name}, #{job}, #{salary})
</insert>
通过POJO对象传参
  • 返回int类型的值
1
2
3
4
5
6
7
8
9
@Test
public void testIns() {
Emp emp = new Emp();
emp.setName("赵云");
emp.setJob("保安");
emp.setSalary(6000.0);
int rol = session.insert("EmpMapper.ins", emp);
System.out.println("影响的行数:"+rol);
}
通过map对象传参
  • 返回int类型的值
1
2
3
4
5
6
7
8
9
@Test
public void testIns() {
Map map = new HashMap();
map.put("name", "赵云");
map.put("job", "保安");
map.put("salary", 6000.0);
int rol = session.insert("EmpMapper.ins", map);
System.out.println("影响的行数:"+rol);
}

  • 在Mapper定义SQL语句
1
2
3
<delete id="del">
DELETE FROM emp
</delete>
  • 返回int类型的值
1
2
3
4
5
@Test
public void testDel() {
int rol = session.insert("EmpMapper.del");
System.out.println("影响的行数:"+rol);
}

占位符

  • 在Mapper定义SQL语句
1
2
3
<update id="upd">
UPDATE emp SET job='保镖', salary=20000.0 WHERE name='赵云'
</update>
  • 返回int类型的值
1
2
3
4
5
@Test
public void testUpd() {
int rol = session.insert("EmpMapper.upd");
System.out.println("影响的行数:"+rol);
}

占位符

查全部

  • 在Mapper定义SQL语句
1
2
3
<select id="findAll" resultType="com.pojo.Emp">
SELECT * FROM emp
</select>
  • 返回保存所有POJO对象的List对象
1
2
3
4
List<Emp> list = session.selectList("EmpMapper.findAll");
for(Emp e : list) {
System.out.println(e);
}

查指定行(占位符)

  • 在Mapper定义SQL语句
1
2
3
<select id="fin2" resultType="com.pojo.Emp">
SELECT * FROM emp WHERE name=#{name}
</select>
  • 如果只有一个参数,可以直接使用重载方法
  • 如果返回结果只有一条,可以直接使用selectOne只接收一条
1
2
3
4
5
@Test
public void testFin2() {
Emp emp = session.selectOne("EmpMapper.fin2", "赵云");
System.out.println(emp);
}

查指定列(占位符)

  • 在Mapper定义SQL语句
1
2
3
<select id="fin3" resultType="com.pojo.Emp">
SELECT ${cols} FROM emp
</select>
  • 返回保存所有POJO对象的List对象
1
2
3
4
5
6
7
8
9
@Test
public void testFin3() {
Map map = new HashMap();
map.put("cols", "name,job");
List<Emp> selectList = session.selectList("EmpMapper.fin3", map);
for (Emp emp:selectList) {
System.out.println(emp.getName()+"\t"+emp.getJob());
}
}

占位符

两种占位符示例(索引)

  • #{}占位符示例

传送门

  • ${}占位符示例

传送门

三种传参方式示例(索引)

  • 单个参数传参

传送门

  • map对象传参

传送门

  • POJO对象传参

传送门

两种占位符的区别

  • #{}在接收到数据后,如果是日期类型字符串类型,会在前后添加引号,适用于指定值
  • ${}在接收到数据后,会直接将数据拼接到SQL语句中,适用于指定SQL子句
  • ${}如果指定了POJO对象不存在的成员变量,只能用map方式传参

标签和参数

<select></select>:查询语句

id:与@Mapper所标注的类中方法名相对应
resultType:与@Mapper所标注的类中方法返回值类型(的别名)相对应
resultMap传送门

<insert></insert>:插入语句

parameterType:用于设置参数的类型,可以省略
useGeneratedKeys:值为true时表示使用主键
useGeneratedKeys:值为true时表示使用主键
keyProperty:指定主键名

<update></update>:修改语句
<delete></delete>:删除语句
<sql></sql>传送门

MyBatis动态SQL标签

choose…when

  • 只会选择一个分支执行
1
2
3
4
5
6
7
8
9
10
11
<choose>
<when test="username == 1">
...
</when>
<when test="username == 2">
...
</when>
<otherwise>
...
</otherwise>
</choose>

where

  • 有值时才会添加WHERE关键字
1
2
3
<where>
...
</where>

if

  • 满足条件时才会执行
1
2
3
<if test="username != null">
...
</if>

foreach

  • 遍历集合

ids:从外面传递的集合

1
2
3
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>

面向接口开发

条件

  1. 写一个接口,接口的全类名等于对应Mapper文件的namespace值
  2. 接口中的方法名要对应Mapper文件中SQL标签的id值
  3. 如果是查询,select标签上的resultType类型和接口中的方法的返回值类型保持一致(如果接口返回List<泛型>,resultType只需要指定集合中泛型即可)
  4. 接口中的方法的参数类型和SQL标签上的参数类型(可以不指定)保持一致

示例

  • 修改EmpMapper.xml<mapper></mapper>标签内namespace的值为对应接口的全局限定名
1
2
3
<mapper namespace="com.dao.EmpMapper">
...
</mapper>
  • 在dao包下创建EmpMapper接口
  • 接口内提供各种功能的抽象方法,根据SQL语句修改返回值类型

@Param(""):定义映射到数据表的字段名

1
2
3
public interface EmpMapper {
public List<Emp> findAll(@Param("name") String name);
}
  • 方法名需要和EmpMapper.xml内的对应标签的id值相同
1
2
3
<select id="findAll" resultType="com.pojo.Emp">
select * from emp
</select>
  • 在测试类中,使用session对象,通过反射,获取EmpMapper实现类(我们只需要定义接口,实现类由框架为我们提供)
  • 通过mapper对象调用对应方法实现增删改查
1
2
3
4
5
6
7
8
@Test
public void testFindAll() {
EmpMapper mapper = session.getMapper(EmpMapper.class);
List<Emp> findAll = mapper.findAll();
for (Emp emp : findAll) {
System.out.println(emp);
}
}

面向注解开发

示例

  • 在面向接口开发的基础上,可以删除EmpMapper.xml文件
  • 在接口定义的抽象方法上使用注解@Insert@Delete@Update@Select作增删改查操作
1
2
3
4
public interface EmpMapper {
@Select("select * from emp")
public List<Emp> findAll();
}
  • 注意:不允许重载方法,原因是面向注解开发不允许出现同名方法

在注解中使用标签

  • 在注解中定义的SQL,如果使用<script></script>包裹,那么可以在内部使用其他Mybatis动态SQL标签
1
2
3
4
public interface EmpMapper {
@Select("<script>select * from emp</script>")
public List<Emp> findAll();
}

Mapper.xml特殊符号处理方式

使用html转义字符

  • >->&gt;
  • <->&lt;

使用CDATA区存放特殊符号

1
2
3
<![CDATA[
>
]]>

MyBatis的xml实现sql代码的复用

  • 将重复语句写在<sql></sql>标签内,在其他语句通过id属性引用
1
2
3
4
5
6
7
<sql id="id1">
...
</sql>

<select id="id2">
<include refid="id1" />
</select>

jdbc注册信息写入properties文件

  • src/main/resources目录下新建jdbc.properties配置文件
1
2
3
4
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
  • mybatis-config.xml<configuration></configuration>标签哪引入
1
2
3
<configuration>
<properties resource="jdbc.properties"/>
</configuration>
  • mybatis-config.xml内的jdbc注册信息由常量改成变量
1
2
3
4
5
6
<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>

自定义映射

  • resultMap为框架中实现高级映射时采用的一种映射策略
  • 主要用于表中字段与类中属性不匹配、用于多表嵌套查询、用于多表关联查询

定义一个查询

1
2
3
<select id="" resultMap="">
...
</select>

一对一

resultMap->id:与查询语句的resultMap值相对应
resultMap->type:返回值实体类的全局限定名
<id></id>:主键字段映射
<result></result>:自定义映射
column:对应的查询到结果的数据表字段
property:对应的类的属性名

1
2
3
4
<resultMap id="" type="">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
</resultMap>

一对多

ofType:结果集中每个结果的类型
collection->select:嵌套查询。@Mapper所标注的类中方法的全局限定名
collection->columnresult->column:均为对应的查询到结果的数据表字段,如果相同可以省略<result></result>

1
2
3
4
5
<resultMap id="" type="">
<collection id="" column="" property="" ofType="" select="">
<result column=""></result>
</collection>
</resultMap>

多对一

1
2
3
4
<resultMap id="" type="">
<association id="" column="" property="" select="">
</association>
</resultMap>

List结果转换为Map结果

@MapKey("id"):指定每个值转换Map后的键

1
2
3
4
5
public interface UserMapper {
@MapKey("id")
@Select("SELECT * FROM user")
Map<Integer, User> method();
}

完成