1. 为什么要使用Mybatis?

之前使用的都是JDBC,需要自己写Connection,Statement,ResultSet,这些其实只是一些辅助类。另一方面,如果需要访问不同的表,需要写对应的DAO,其中包含很多重复的代码,十分繁琐和枯燥。

使用Mybatis之后,只需要自己提供SQL语句,其他工作如建立连接,创建Statement以及异常处理都交给Mybatis去做。我们只需要关注在增查改删的操作层面上即可,而把技术细节封装在看不见的地方。

简单来说,使用Mybatis可以更简单、高效地操作数据库👀。

2. Hello Mybatis

2.1 新建mybatis-config.xml

使用IDEA创建普通java项目(quickstart模板)。

项目结构如下:

- src
    - main
        - java
    - test

这里的java是代码的根目录,将mybatis-config.xml新建在这里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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>
<typeAliases>
<package name="com.ssl.bean"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/cart?characterEncoding=UTF-8&amp;serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/ssl/bean/Order.xml"/>
</mappers>
</configuration>

在配置文件中需要提供ip、端口号、所连接的数据库、用户名、密码,在property标签中设置好,MySQL驱动也通过配置文件设置好。

typeAliases标签中的是起的别名,方便一会儿设置实体类的xml不用写完整路径,自动扫描com.ssl.bean下的类型,使得在后续配置文件Order.xml中使用resultType的时候,可以直接使用Order,而不必写全com.ssl.bean.Order

这里数据库order_和对应的实体类都需要提前新建好哇✔,我就不赘述了。

2.2 创建Order.xml

我这里查询的表是order_,对应的实体类为Order,所以新建的配置文件也应是Order.xml,名字要相同😶。路径和实体类位置一样,即com/ssl/bean/Order.xml

1
2
3
4
5
6
7
8
9
10
<?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.ssl.bean">
<select id="listOrder" resultType="Order">
select * from order_
</select>
</mapper>

这里resultType直接填Order就🆗。

id中的名字一会儿调用的时候会用到。

这个配置文件就相当于JDBC中的DAO,需要其它方法,如增改删,在mapper标签中添加对应的SQL语句即可。

2.3 创建启动类

  1. 加载配置文件
  2. 根据配置文件创建SqlSessionFactory
  3. 打开会话
  4. 使用会话执行查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestMybatis {

public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<Order> orders = sqlSession.selectList("listOrder");
for (Order o : orders) {
System.out.println("id: " + o.getId() + " uid: " + o.getUid());
}
}

}

流程

  1. 应用程序找Mybatis要数据
  2. Mybatis根据mybatis-config.xml找到数据库
  3. 通过Order.xml执行SQL语句
  4. 将Order封装在集合中
  5. 返回Order集合

2.4 几个小问题

2.4.1 maven依赖

MySQL依赖

1
2
3
4
5
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>

Mybatis依赖

1
2
3
4
5
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>

2.4.2 xml中&符号的使用

在mybatis-config.xml文件中,如果连接数据库的url要提交多个参数需要用到&,而在xml文件中直接使用&是不允许的,需要转换成实体进行使用。

&lt; < 小于号
&gt; > 大于号
&amp; & 和
&apos; ' 单引号
&quot; " 双引号

2.4.3 IDEA编译问题

起初使用IDEA将上面都配置完之后,发现程序跑不起来,报错是找不到mybatis-config.xml文件。打开target的文件夹,发现编译完成的文件中没有xml文件。

这是因为IDEA默认对xml文件不进行编译,所以需要在pom.xml文件中的build标签添加下面代码

1
2
3
4
5
6
7
8
9
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>

即可将java文件夹下面的所有xml文件编译到target文件夹中。

3. Mybatis CRUD

在Order.xml中添加CRUD的SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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.ssl.bean">
<insert id="addOrder" parameterType="Order">
insert into order_ values (null, #{uid})
</insert>
<select id="listOrder" resultType="Order">
select * from order_
</select>
<select id="getOrder" parameterType="_int" resultType="Order">
select * from order_ where id=#{id}
</select>
<update id="updateOrder" parameterType="Order">
update order_ set uid=#{uid} where id=#{id}
</update>
<delete id="deleteOrder" parameterType="Order">
delete from order_ where id=#{id}
</delete>
</mapper>

在TestMybatis类中实现CRUD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.ssl;

import com.ssl.bean.Order;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class TestMybatis {

public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
listAll(sqlSession);
System.out.println("------------------------------");
// 增
Order order = new Order();
order.setUid(10);
sqlSession.insert("addOrder", order);
System.out.println("新增一条记录");
listAll(sqlSession);
System.out.println("-------------------------------");
// 查
Order order1 = sqlSession.selectOne("getOrder", 3);
System.out.println("id: " + order1.getId() + " uid: " + order1.getUid());
System.out.println("-------------------------------");
// 改
Order order2 = new Order();
order2.setId(2);
order2.setUid(100);
sqlSession.update("updateOrder", order2);
System.out.println("修改一条记录");
listAll(sqlSession);
System.out.println("-------------------------------");
// 删
sqlSession.delete("deleteOrder", order2);
System.out.println("删除一条记录");
listAll(sqlSession);

sqlSession.commit();
sqlSession.close();
}

public static void listAll(SqlSession sqlSession) {
List<Order> orders = sqlSession.selectList("listOrder");
for (Order o : orders) {
System.out.println("id: " + o.getId() + " uid: " + o.getUid());
}
}

}

注意:

  1. 获取数据库中一条数据的方法是selectOne!返回的是一个Order对象。
  2. 最后一定要加commit方法,否则添加的数据不会在数据库当中显示(我也不知道为啥。。😂记得加~)Mybatis不会自动提交,查询可以不提交,只显示内容。
  3. _int对应java中的基本类型int,int对应Integer类。

4. Mybatis 更多查询

4.1 模糊查询

在Order.xml中添加

1
2
3
<select id="listOrderByUid" parameterType="string" resultType="Order">
select * from order_ where uid like concat('%', '#{name}', '%')
</select>

concat是MySQL中的字符串拼接函数;%是通配符,适配任意字符。

TestMybatis中这样写

1
List<Order> orders = sqlSession.selectList("listOrderByUid", "s");

这里我的表可能查不出结果。。会用就可以啦。

关于占位符的问题

  1. 如果是bean类型,Mybatis会自动调用其getter获取值,替换到相应的位置;
  2. 如果是其它类型,如string,_int,则会顺序填入,无所谓#{}中的字段是什么。

4.2 多条件查询

SQL语句中使用逻辑关联词连接条件就可以啦😜

使用的时候参数需要装在Map中,把这个Map作为参数传递进去。

1
2
3
4
Map<String,Object> params = new HashMap<>();
params.put("id", 3);
params.put("uid", "c");
List<Category> cs = session.selectList("listCategoryByIdAndName",params);

5. Mybatis 表关系

5.1 一对多

例如分类和产品的关系,一个分类下对应多个产品,这就是一对多的关系。

新建category表和product

category_

product_

因为是展示分类与产品一对多的关系,新建Category.xml(实体类Category这里就不多说了[其中有一个属性为List<Product> products],同样地,Product实体类也需要新建好),利用连接查询两表数据并返回List(使用collection标签把对应列的数据放在product对应的属性中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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.ssl.bean">
<resultMap id="categoryBean" type="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>

<!-- 一对多关系 -->
<!-- property: 这个集合放在Category的products属性中;ofType: 集合中元素的类型 -->
<collection property="products" ofType="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
</collection>
</resultMap>
<select id="listCategory" resultMap="categoryBean">
select c.id as cid, c.name as cname, p.id as pid, p.name as pname, p.price from category_ as c left join product_ as p on c.id=p.cid;
</select>
</mapper>

在mybatis-config.xml中添加映射

1
2
3
<mappers>
<mapper resource="com/ssl/bean/Category.xml"/>
</mappers>

在启动类使用如下代码查询

1
2
3
4
5
6
7
List<Category> listCategory = sqlSession.selectList("listCategory");
for (Category c : listCategory) {
System.out.println(c);
for (Product p : c.getProducts()) {
System.out.println("\t" + p);
}
}

结果如图

5.2 多对一

对应的,产品与分类的关系则是多对一。

在Product实体类中添加Category category属性,新建Product.xml(使用association标签将对应列的数据放在bean类当中),同样使用连接查询两表的数据(我喜欢把要查询的表作为左表( ̄▽ ̄)”好理解一些)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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.ssl.bean">
<resultMap id="productBean" type="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>

<!-- 多对一关系 -->
<!-- 使用association为category属性添加属性,类似于collection -->
<association property="category" javaType="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</association>
</resultMap>
<select id="listProduct" resultMap="productBean">
select p.id as pid, p.name as pname, p.price, c.id as cid, c.name as cname from product_ as p left join category_ as c on p.cid=c.id;
</select>
<select id="getProduct" parameterType="_int" resultMap="productBean">
select p.id as pid, p.name as pname, p.price, c.id as cid, c.name as cname from product_ as p left join category_ as c on p.cid=c.id where p.id=#{id};
</select>
</mapper>

添加映射,最后运行结果如下

5.3 多对多

如订单和产品的关系,一个订单可以包含多个产品,而一个产品也可以对应多个订单,它们两个是多对多的关系。

这里借助订单项这个表来建立它们之间的联系,每一条订单项对应一个订单中的一条数据。

order_

orderItem_(oid对应所属order的id,pid对应product的id)

新建Order.xml,连接三表进行查询(注意collection和association的嵌套)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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.ssl.bean">
<resultMap id="orderBean" type="Order">
<id column="id" property="id"/>
<result column="code" property="code"/>

<collection property="orderItems" ofType="OrderItem">
<id column="oiid" property="id"/>
<result column="num" property="num"/>
<association property="product" javaType="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
</association>
</collection>
</resultMap>
<select id="listOrder" resultMap="orderBean">
select o.id, o.code, oi.id as oiid, oi.num, p.id as pid, p.name as pname, p.price from order_ as o left join orderItem_ as oi on o.id=oi.oid left join product_ as p on oi.pid=p.id;
</select>
<select id="getOrder" parameterType="_int" resultMap="orderBean">
select o.id, o.code, oi.id as oiid, oi.num, p.id as pid, p.name as pname, p.price from order_ as o left join orderItem_ as oi on o.id=oi.oid left join product_ as p on oi.pid=p.id where o.id=#{id};
</select>
</mapper>

添加映射,运行结果如下

订单和产品的关系是通过订单项联系起来的,所以建立关系和删除关系对应的就是创建orderItem_数据和删除其数据。

新建OrderItem.xml,添加增添数据的SQL

1
2
3
4
5
6
7
8
9
10
<?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.ssl.bean">
<insert id="addOrderItem" parameterType="OrderItem">
insert into orderItem_ values (null, #{order.id}, #{product.id}, #{num});
</insert>
</mapper>

添加映射,在启动类的步骤如下:

  1. 查询一个Order
  2. 查询一个Product
  3. 新建OrderItem,设置Order、Product和num
  4. 将OrderItem插入到orderItem_表中
    二者关系建立完成。
1
2
3
4
5
6
7
Order order = sqlSession.selectOne("getOrder", 1);
Product product = sqlSession.selectOne("getProduct", 6);
OrderItem oi = new OrderItem();
oi.setOrder(order);
oi.setProduct(product);
oi.setNum(300);
sqlSession.insert("addOrderItem", oi);

结果如下

删除关系则删除orderItem_的对应数据即可,在OrderItem.xml文件中设置SQL。

6. 总结

使用Mybatis实现增查改删,可以让人更专注于SQL的编写,而不是考虑编写冗余的JDBC代码来操作数据库。

Mybatis其它高阶的使用方法,如c3p0连接池、缓存等等技术需要用到再进行学习。

7. 参考