1 解决列名和属性名不匹配的问题
默认情况下, MyBatis会进行自动映射(Auto-Mapping), 指的是MyBatis能够自动将同名的列和属性进行映射
1.1 在 SQL 语句中给列名起别名
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="a.b.c">
<select id="selAll" resultType="com.wuyw.pojo.User">
select id, username, password, real_name realName,
age, birthday, reg_time regTime from tb_user
</select>
</mapper>
1.2 让MyBatis自动解决
在MyBatis核心配置文件中开启自动驼峰命名, 当MyBatis发现有带下划线的列名时, 会自动转换为对应的驼峰命名.
例如: reg_time --> regTime.
注意: settings标签要配置在environments标签的前面.
<!--配置系统设置-->
<settings>
<!--开启自动驼峰命名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
1.3 定义映射关系
映射关系可以在多处使用 可以处理数据库表格之间一对一 一对多 多对多的关系
<?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.wuyw.mapper.UserMapper">
<!--手动定义映射关系-->
<resultMap id="userMap" type="user">
<!--id标签对应主键列-->
<id column="id" property="id" />
<!--result标签对应非主键列-->
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="real_name" property="realName"/>
<result column="age" property="age"/>
<result column="birthday" property="birthday"/>
<result column="reg_time" property="regTime"/>
</resultMap>
<select id="selAll" resultMap="userMap">
select * from tb_user
</select>
</mapper>
2 查询常用的三种方法
三个常用的查询方法: selectList, selectOne和selectMap, 都允许在调用的时候传递参数, 参数的类型是Object, 个数只能传递一个. 如果需要传递多个参数, 需要处理成单个对象. 同时, SQL语句也需要相应的修改, 要使用占位符替代接收的参数.
2.1 selectList
用于查询多条数据, MyBatis会自动将查到的数据封装为List集合.
最常用, 查不到时返回空集合, 而不是null.
2.2 selectOne
用于查询单条数据, 结果是指定的某个对象或者某个值. 很常用. 底层调用了selectList, 如果查到一个值, 直接返回结果,
如果查到多个值, 抛出异常, 如果查不到, 返回null.
2.3 selectMap
用于将查询结果转换为Map集合, 使用时需要指定将结果中的哪个属性作为map的key, Map的value就是当前对象.
一般不用. 底层调用selectList.
3 占位符
占位符有两种, 分别是#{}, ${}.
#{}底层使用的是PreparedStatement, ${}底层使用的是Statement.
3.1 #{} 占位符
#{}作为SQL语句占位符时, MyBatis允许接收三种类型的参数:
1)简单类型的参数
简单类型指的是基本数据类型, 包装类型, String, java.sql.*...,
此时, #{}会忽略占位符的名称和个数, 将参数进行绑定. parameterType可以指定参数的类型, 如果省略, 表示参数类型为Object.
一旦指定, 传参时必须类型一致, 否则抛出类型转换异常.
<select id="selById" resultType="com.wuyw.pojo.User" parameterType="java.lang.String">
select * from tb_user where id=#{id} or username=#{suibian}
</select>
@Test
public void testSelById() {
SqlSession session = MyBatisUtil.getSession();
session.selectOne("selById", 3);
session.close();
}
2)Map类型的参数
多个参数传递时使用, 将多个参数封装为Map集合, #{}需要使用Map中对应的key去取值, 如果key不存在, 不会报错, 拿到的是null
<select id="sel4Login" resultType="com.wuyw.pojo.User">
select * from tb_user where username=#{uname} and password=#{upwd}
</select>
@Test
public void testSel4LoginByMap() {
SqlSession session = MyBatisUtil.getSession();
Map<String, Object> params = new HashMap<>();
params.put("uname", "lisi");
params.put("upwd", "123");
session.selectOne("sel4Login", params);
session.close();
}
3)POJO类型的参数
多个参数封装为对应的POJO类, 此时, #{}需要调用该POJO类中对应的 getter 方法取值, 如果没有对应的getter, 则抛出异常.
<select id="sel4Login" resultType="com.wuyw.pojo.User">
select * from tb_user where username=#{username} and password=#{password}
</select>
@Test
public void testSel4LoginByPojo() {
SqlSession session = MyBatisUtil.getSession();
User user = new User();
user.setUsername("wangwu");
user.setPassword("123");
session.selectOne("sel4Login", user);
session.close();
}
当SQL语句结构不确定时, 不能使用#{}占位符,
原因是#{}底层使用的是PreparedStatement, 会对SQL语句进行预编译, 导致SQL语句的结构无法被补全.
使用${}来进行操作.
3.2 ${}占位符
${}底层使用的是Statement实现, 做的是字符串的直接拼接.
SQL语句结构固定的情况下, 应该使用#{}占位符, 当SQL语句结构不确定是, 只能使用${}实现.
<select id="sel" resultType="com.wuyw.pojo.User">
select * from ${tbName} where ${cname}='${cvalue}' ${order}
</select>
@Test
public void test() {
SqlSession session = MyBatisUtil.getSession();
Map<String, Object> params = new HashMap<>();
params.put("tbName", "tb_user");
params.put("cname", "reg_time");
params.put("cvalue", "2019-12-24 15:18:35");
params.put("order", "order by id desc");
session.selectList("sel", params);
session.close();
}
4 实现增删改操作
增删改操作涉及到事务管理, MyBatis中, 默认将JDBC的自动管理事务机制关闭了. 要求所有增删改操作都必须进行手动的事务管理. 通过SqlSession来进行事务管理.
4.1 新增数据
- 标签: 使用<insert>标签
- 方法: 使用insert()
- useGeneratedKeys=”true”表示要获取自动增长的主键, keyProperty=”id”表示将获取到的主键值赋值给id属性, 会自动调用setId方法
<!--新增操作-->
<insert id="ins" useGeneratedKeys="true" keyProperty="id">
insert into tb_user values (
default, #{username}, #{password}, #{realName}, #{age}, #{birthday}, now()
)
</insert>
@Test
public void testInsert() {
SqlSession session = MyBatisUtil.getSession();
User user = new User(20, "zhaomin3", "123", "赵敏", new Date());
// 新增前打印user对象
System.out.println(user);
int rows = session.insert("ins", user);
if(rows == 1) {
// 提交事务
session.commit();
System.out.println("ok");
} else {
System.out.println("error");
}
// 新增后打印user对象
System.out.println(user);
session.close();
}
4.2 更新数据
- 标签: 使用<update>标签
- 方法: 使用update()
<!--更新操作-->
<update id="upd">
update tb_user set username=#{uname}, real_name=#{rname} where id=#{id}
</update>
@Test
public void testUpdate() {
SqlSession session = MyBatisUtil.getSession();
Map<String, Object> params = new HashMap<>();
params.put("uname", "xiexun");
params.put("rname", "谢逊");
params.put("id", 9);
try {
session.update("upd", params);
session.commit();
} catch (Exception e) {
session.rollback();
e.printStackTrace();
}
session.close();
}
4.3 删除数据
- 标签: 使用<delete>标签
- 方法: 使用delete()
<!--删除操作-->
<delete id="del">
delete from tb_user where id=#{id}
</delete>
@Test
public void testDelete() {
SqlSession session = MyBatisUtil.getSession();
session.delete("del", 8);
session.commit();
session.close();
}
5 类型别名
主要针对映射文件中定位类型时使用, 简化配置. 分为两种情况:
5.1 MyBatis内建别名
这是一些为常见的 Java 类型内建的相应的类型别名。它们都是不区分大小写的,注意对基本类型名称重复采取的特殊命名风格。
别名 | 映射的类型 | 别名 | 映射的类型 | 别名 | 映射的类型 |
---|---|---|---|---|---|
_byte | byte | string | String | decimal | BigDecimal |
_long | long | byte | Byte | bigdecimal | BigDecimal |
_short | short | long | Long | object | Object |
_int | int | short | Short | map | Map |
_integer | int | int | Integer | hashmap | HashMap |
_double | double | integer | Integer | list | List |
_float | float | double | Double | arraylist | ArrayList |
_boolean | boolean | float | Float | collection | Collection |
|
| boolean | Boolean | iterator | Iterator |
|
| date | Date |
|
|
5.2 自定义别名
<typeAliases>
<!--<typeAlias type="com.wuyw.pojo.User" />-->
<package name="com.wuyw.pojo"/>
</typeAliases>
6 动态SQL语句
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态SQL这一特性可以彻底摆脱这种痛苦。
6.1 <if>
用于条件判断, test属性表示判断结果, 要求是一个boolean值.
6.2 <where>
用于维护where子句, 通常配合<if>一起使用. 如下功能:
- 当没有条件时, 不会创建WHERE关键字;
- 当有条件时, 会自动生成WHERE关键字;
- 会自动去掉第一个条件的and/or关键字.
<mapper namespace="com.wuyw.mapper.UserMapper">
<select id="selByCondition" resultType="user">
select * from tb_user
<where>
<!-- test属性用于定义条件表达式 -->
<if test="username != null and username != ''">
and username = #{username}
</if>
<if test="age != null">
and age = #{age}
</if>
</where>
</select>
</mapper>
6.3 <choose><when><otherwise>
功能类似于java中的switch...case...default. 多分支判断, 只能成立一个条件.
6.4 <bind>
对数据进行加工, 通常用于处理模糊查询的数据.
6.5 <sql><include>
提取SQL片段和引用SQL片段
<mapper namespace="com.wuyw.mapper.UserMapper">
<!--定义SQL片段-->
<sql id="sel_sql">
select id, username, password, real_name,
age, birthday, reg_time from tb_user
</sql>
<select id="selByCondition" resultType="user">
<!-- 引用SQL片段 -->
<include refid="sel_sql" />
<where>
<!-- test属性用于定义条件表达式 -->
<if test="username != null and username != ''">
<!-- 对参数进行加工, 通常用于处理模糊查询的数据 -->
<bind name="username" value="'%' + username + '%'"/>
and username like #{username}
</if>
<if test="age != null">
and age > #{age}
</if>
<choose>
<when test="birthday != null and birthday != ''">
and birthday = #{birthday}
</when>
<otherwise>
and birthday is null
</otherwise>
</choose>
</where>
</select>
</mapper>
6.6 <set>
用于维护更新语句中的set子句, 类似于<where>的功能.
- 当条件不成立时, 不会创建SET关键字;
- 当条件成立时, 会自动生成SET关键字;
- 会自动去掉最后一个条件的逗号(,).
<update id="updUser">
update tb_user
<set>
id = #{id},
<if test="username != null and username != ''">
username=#{username},
</if>
<if test="password != null and password != ''">
password=#{password},
</if>
<if test="age != null">
age=#{age},
</if>
</set>
where id=#{id}
</update>
6.7 <foreach>
对数组, List, Set, Map集合进行遍历时使用, 通常用于in操作中. 一般用于实现批量新增或批量删除操作. 属性介绍:
- collection: 要遍历的集合
- item: 迭代项
- open: 以什么字符开头
- close: 以什么字符结束
- separator: 多个迭代项之间的分隔符
<delete id="delUser">
delete from tb_user where id in
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</delete>
@Test
public void testForeach() {
SqlSession session = MyBatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
// int[] ids = {1, 2, 3, 4, 5, 6};
// List<Integer> ids = new ArrayList<>();
// Set<Integer> ids = new HashSet<>();
// Collections.addAll(ids, 1, 3, 5, 7);
Map<Integer, Integer> ids = new HashMap<>();
ids.put(1, 11);
ids.put(2, 22);
ids.put(3, 33);
mapper.delUser(ids);
session.close();
}
6.8 <trim>
用于对数据进行处理, 可以在指定的字符串前后进行操作.
<insert id="insUser">
insert into tb_user values
<foreach collection="users" separator="," item="u">
<trim prefix="(" prefixOverrides="," suffix=")" suffixOverrides=",">
,default, #{u.username}, #{u.password}, #{u.realName},
#{u.age}, #{u.birthday}, now(),
</trim>
</foreach>
</insert>
6.9 <resultMap>标签的使用
默认情况下, MyBatis会进行自动映射(Auto-Mapping), 数据库表格的列名和对象的属性名如果同名, MyBatis会进行自动赋值. 当列名和属性名不同., MyBatis允许程序员自己定义列名和属性名的映射关系.
<resultMap>需要配置resultMap属性使用.
如果使用了resultType属性, 表示自动映射, 如果使用了resultMap属性, 表示手动映射.
- 同名可以省略不写;
- id标签要写在result标签的前面;
<?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.wuyw.mapper.UserMapper">
<!--手动定义映射关系-->
<resultMap id="userMap" type="user">
<!--id标签对应主键列-->
<id column="id" property="id" />
<!--result标签对应非主键列-->
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="real_name" property="realName"/>
<result column="age" property="age"/>
<result column="birthday" property="birthday"/>
<result column="reg_time" property="regTime"/>
</resultMap>
<select id="selAll" resultMap="userMap">
select * from tb_user
</select>
</mapper>
8 多表连接查询
在MyBatis中, 多表连接查询主要分为两种, 一种是多对一, 一种是一对多.
示例数据
create table tb_class (
id integer primary key auto_increment,
name varchar(10) not null,
room varchar(10)
);
# 创建学生表
create table tb_student (
id integer primary key auto_increment,
name varchar(20) not null,
gender char(1),
birthday date,
cid integer
);
# 添加数据
insert into tb_class values
(default, 'Java', '1601'),
(default, '计算机网络', '1602'),
(default, '数据结构', '1601');
insert into tb_student values
(default, '张三', '男', '1998-12-12', 1),
(default, '李四', '女', '1999-12-12', 2),
(default, '王五', '男', '1998-12-12', 2),
(default, '赵六', '男', '1998-12-12', 3),
(default, '孙七', '女', '1998-12-12', 3),
(default, '吴八', '女', '1998-12-12', 3);
8.1 多对一查询
多个学生对应一个班级, 查询所有学生信息, 同时查询每个学生所在班级的信息
8.1.1 实体类
在学生实体类中, 应该提供一个班级类型的对象, 用来表示班级信息.
public class Student implements Serializable {
private Integer id;
private String name, gender;
private Date birthday;
private Integer cid;
private Clazz clazz;
}
8.1.2 关联方式实现查询
指的是通过SQL语句关联查询的方式实现. 表1 joint 表2 on 关联条件.
在MyBatis中, 关联单个对象需要使用<association>标签进行.
<?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.wuyw.mapper.StudentMapper">
<resultMap id="smap" type="student" autoMapping="true">
<!--关联单个对象, 理解为小的resultMap即可-->
<association property="clazz" javaType="clazz" autoMapping="true">
<id column="cid" property="id" />
<result column="cname" property="name" />
</association>
</resultMap>
<select id="selAll" resultMap="smap">
select
s.*, c.name cname, c.room
from
tb_student s
join
tb_class c
on s.cid = c.id
</select>
</mapper>
8.1.3 N+1方式实现
指的是完成本次查询一共需要执行N+1条SQL语句. 每条SQL都是单表查询.
<mapper namespace="com.wuyw.mapper.StudentMapper">
<resultMap id="smap2" type="student">
<result column="cid" property="cid" />
<association
property="clazz"
javaType="clazz"
select="com.wuyw.mapper.ClazzMapper.selById"
column="cid"/>
</resultMap>
<select id="selAll2" resultMap="smap2">
select * from tb_student
</select>
</mapper>
8.2 一对多查询
指的是一个班级有多个学生, 查询班级时把当前班级的所有学生也查到.
8.2.1 实体类
public class Clazz implements Serializable {
private Integer id;
private String name, room;
private List<Student> list = new ArrayList<>();
}
8.2.2 关联方式实现
<mapper namespace="com.wuyw.mapper.ClazzMapper">
<resultMap id="cmap" type="clazz" autoMapping="true">
<!--关联集合对象-->
<collection property="students" javaType="list" ofType="student" autoMapping="true">
<id column="sid" property="id" />
<result column="sname" property="name" />
<result column="id" property="cid" />
</collection>
</resultMap>
<select id="selAll" resultMap="cmap">
select
c.*, s.id sid, s.name sname, s.gender, s.birthday
from
tb_class c
join
tb_student s
on
c.id=s.cid
</select>
</mapper>
8.2.3 N+1方式实现
<mapper namespace="com.wuyw.mapper.ClazzMapper">
<resultMap id="cmap2" type="clazz">
<id column="id" property="id" />
<collection
property="students"
javaType="list"
ofType="student"
select="com.wuyw.mapper.StudentMapper.selByCid"
column="id" />
</resultMap>
<select id="selAll2" resultMap="cmap2">
select * from tb_class
</select>
</mapper>
9 Mybatis的注解开发
MyBatis中常用的注解,使用MyBatis中的注解, 可以简化映射文件的配置. 注意, 动态SQL不能简化.
- @Select, 用于简化查询配置
- @Insert, 用于简化新增配置
- @Update, 用于简化更新配置
- @Delete, 用于简化删除配置
public interface DemoMapper {
@Select("select * from tb_user")
List<User> selAll();
@Delete("delete from tb_user where id=#{id}")
void delById(int id);
}
注解模式使用注意事项
1使用注解的接口可以同时存在mapper映射文件
2接口上使用了注解 在映射文件中就不能在定义对应的SQL语句标签
3使用了注解的接口 接口中并不是所有的方法都必须使用注解 个别方法使用映射文件OK
注解模式开发的好处
简化mapper映射文件
注解模式开发的缺点
将SQL语句转移到了代码中 修改代码需要重新编译项目 耦合度高
没有办法独立处理一对一 一对多 多对多的关联关系
什么情况下使用注解开发?
1业务逻辑简单
2业务逻辑稳定