Mybatis快速入门
2022-07-29 # 学习笔记 # Mybatis # Java

JDBC的缺点

硬编码

在注册驱动和获取连接的时候,url,name等用字符串存储,存在硬编码

查询所用的sql语句也是用String存储,存在硬编码

虽然可以通过封装JDBC工具类的方式,读取Properties文件来解决硬编码的问题,但还是较为繁琐。

操作繁琐

需要手动设置参数

需要手动封装结果集

Mybatis简化方式

在使用JDBC时,我们首先需要创建Dao层的接口,并写出相应的实现类。

而使用Mybatis后,我们只需要给出Dao层的接口(一般被称作Mapper),然后再对应的MapperXml文件中配置好sql语句。Mysql就会自动给我们生成好一个实现类。完成获取结果封装结果集等一系列操作

快速入门

结构

配置mybatis-config.xml

这是mybatis的核心配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>

dataSource标签里的内容为连接数据库的链接和用户名密码等。

mappers标签内配置所有mapper配置文件的位置,Mybatis会按照这里面的内容来逐个生成实现类。

获取SqlSession

要拿到Mybatis给我们生成的这个实现类对象,我们需要一个SqlSession对象。这一对象的构造采用了工厂的设计模式,因此我们要先拿到一个SqlSessionFactory对象。

1
2
3
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

然后再通过下面的方法,即可拿到Sqlsession对象

1
SqlSession session = sqlSessionFactory.openSession()

通常我们会将上面的代码封装成一个工具类,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MybatisUtils {

private static SqlSessionFactory sqlSessionFactory;
static{
try {
String resource = "com/shijivk/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}

这样便可以通过getSqlSession()方法便捷的获取到一个SqlSession对象

创建pojo类

创建pojo类,并提供get和set方法。以便mybatis帮我们自动的把数据封装到对象里

对于pojo类中的变量名,可以与sql查询结果的列名相同,也可以不同。若不同后续则需要配置ResultMap

配置mapper

对于mapper的配置主要分为三个部分

定义mapper接口

1
2
3
public interface UserMapper {
List<User> selectAll();
}

定义方法名和对应的返回值

配置对应的xml

我们在对应的位置建立一个UserMapper.xml,使得xml和接口在同一目录下

1
2
3
4
5
6
7
8
9
<?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.shijivk.mapper.UserMapper">
<select id="selectAll" resultType="com.shijivk.pojo.User">
select * from user;
</select>
</mapper>

注意,此处这种配置的方式,就是让Mybatis自动的帮我们把sql查询到的数据给封装到我们指定的pojo对象中。

Mybatis会将sql语句返回结果的列名和pojo对象的变量名一一对应,若二者名字相同则可以自动对应。

若不同,我们当然可以通过类似下面的方式修改sql语句来更改查询结果的别名以完成匹配,但每次都要修改过于繁琐。

1
select name as user_name from user;

Mybatis提供了一种ResultMap,可以帮我们很好的解决这一问题,配置示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<mapper namespace="com.shijivk.mapper.UserMapper">

<resultMap id="userResultMap" type="com.shijivk.pojo.User">
<result column="id" property="user_id"/>
<result column="name" property="user_name"/>
<result column="pwd" property="user_pwd"/>
</resultMap>


<select id="selectAll" resultMap="userResultMap">
select * from user;
</select>

</mapper>

在mapper标签中填写resultMap标签。其中id为唯一标识,type指定为要映射的pojo类

column为查询结果的列名,property为pojo类中的变量名

在下面的select标签中,指定resultMap属性来替代resultType属性,值指定为上面定义的map的id即可。

在mybatis-config.xml中配置mapper

1
2
3
<mappers>
<mapper resource="com/shijivk/mapper/UserMapper.xml"/>
</mappers>

使用

1
2
3
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectAll();

首先我们通过工具类拿到SqlSession

再通过sqlSession.getMapper()方法拿到mapper

最后直接调用mapper的方法即可。

条件查询

参数只有一个基本类型时

mapper配置

1
User selectById(int uid);

xml配置

1
2
3
<select id="selectById" resultType="com.shijivk.pojo.User">
select * from user where id = #{uid};
</select>

此时直接采用#{}作为占位符(后面再讨论占位符的种类)

此时Mybatis会直接把形参中唯一一个变量赋值到占位符的位置。

注意:此处并不是按照下图中两个位置的名字来进行匹配的。能匹配上仅仅是因为此时只有一个形参和一个占位符。

例如我们增加一个形参,并保持xml文件中的名字不变

1
User selectById(int uid,int num);

会发现此时并没有匹配上,而是报错。从报错内容中我们可以看出,Mybatis在用户没有指定形参的名字的时候,默认采用的是arg1,arg0之类的名字。我们把占位符处的值改为arg0,发现便可以正常匹配了。

1
select * from user where id = #{arg0};

但此种方式太难使用,因此我们采用注解的方式来给形参命名。

采用注解来命名形参

在mapper的形参处加上下面所示的注解

1
User selectById(@Param("uid")int id,@Param("num") int num);

此时便可以在#{}中用uid和num两个名称来传递这两个形参了

1
2
3
<select id="selectById" resultType="com.shijivk.pojo.User">
select * from user where id = #{uid};
</select>

采用对象或Map的方式封装参数

1
User selectById(User user);
1
2
3
<select id="selectById" resultType="com.shijivk.pojo.User">
select * from user where id = #{id};
</select>

我们可以直接传入一个对象参数,然后在占位符里面直接写对象内部的变量名,Mybatis就会自动的从传入的对象中取出相应的值。

当然上述传入的参数可以替换为Map,也是同样的直接使用其内部的变量名称取值即可。

占位符的区别

${}类似于jdbc中的statement,是直接使用拼串的操作来进行的。不能防sql注入。一般只有在查询的表的名称或者是列的名称不确定时使用,用它放在列名的位置上用于占位

#{}类似于jdbc中的PreparedStatement,先用?作为占位符,然后再set参数,安全性略高。

动态查询

对于一些多条件筛选的情况。例如我们的页面上有公司的位置和公司的类型两个选项,用户可以通过选择这两个选项中的一个或是全都选择,来从数据库中查询一些公司。那么如果我们的sql语句是静态的,我们就需要对两个选项是否选择,共计三种情况,分别写出对应的sql语句,这样的操作过于繁琐。

Mybatis提供了一些标签。可以帮助我们实现动态sql

if标签

1
2
3
4
5
6
7
8
9
10
11
<select id="selectUser" resultType="com.shijivk.pojo.User">
select *
from user
where
<if test="id != 0">
id = #{id}
</if>
<if test="name != null and name != ''">
and name like #{name}
</if>
</select>

在if标签中的test属性里的判别式成立时,标签中的内容才会被加到sql语句中

但此时存在着一个问题,如果没有id,那么where关键词会和and关键词直接相连。

这种情况下有两个解决方式,一个是在where的后面加上 1 = 1 之类的恒等式,然后在id前面加and,但不够美观。

第二种方式便是采用Mybatis提供的where标签

where标签

where标签会自动检测里面的逻辑连接符是否多余,如果多余会自动将其去掉,同时如果所有条件均没有,那么where关键字也会被去掉。

1
2
3
4
5
6
7
8
9
10
11
12
<select id="selectUser" resultType="com.shijivk.pojo.User">
select *
from user
<where>
<if test="id != 0">
id = #{id}
</if>
<if test="name != null and name != ''">
and name like #{name}
</if>
</where>
</select>

如果条件是n选1的话,还可以采用choose等标签来解决。

Mybatis事务

Mybatis默认关闭了自动提交。因此如果进行增删改操作,完成后需要用下面的方法来提交事务

1
sqlSession.commit();

除此之外我们也可以在创建session的时候开启自动提交

1
sqlSessionFactory.openSession(true);

向openSession方法中传入参数true,可以开启自动提交

增加&修改

增加

1
void add(User user);
1
2
3
4
<insert id="add">
insert into user (name,pwd)
values (#{name},#{pwd});
</insert>

添加方法返回值为void即可,出现错误直接抛出异常。

完成后commit事务即可。

主键返回

在添加完对象后,想获取其在数据库中的主键值,就需要使用主键返回。

例如上面的案例,在数据库中,id这一项被设置为了auto_increament,因此我们在insert的时候不需要输入id,但我们在添加完后可能有其他的操作需要用到id,这个时候,我们需要在insert标签上添加两个属性

1
<insert useGeneratedkKeys = "true" keyProperty = "id">

这样便可以开启主键返回,在调用完add方法后,mybatis会自动将数据库中返回的id值,赋值给传入add方法的那个user对象,我们只需要用getter拿到值即可。

修改

修改和增加的操作基本一致,只是将insert标签换成update标签,然后在其中写update的sql语句即可

返回值也是void

如果要修改动态字段,那么需要借助if标签和set标签(和where标签类似)等。

删除

将标签换位delete即可,返回值为void

要实现批量删除的话需要传入一个数组,然后通过foreach标签遍历即可

注解开发

对于一些比较简单的sql语句,我们可以直接通过注解的方式开发

1
2
@Select("select * from user where id = #{id}")
User selectById(int id);

Mybatis提供了@Select,@Insert,@Update,@Delete四种注解,只需要按照和xml中相同的方式在Mapper的方法定义上方写上sql语句即可。