Mybatis实验

基础

入门

平时我们都用JDBC访问数据库,除了需要自己写SQL之外,还必须操作Connection, Statement, ResultSet 这些其实只是手段的辅助类。 不仅如此,访问不同的表,还会写很多雷同的代码,显得繁琐和枯燥。

那么用了Mybatis之后,只需要自己提供SQL语句,其他的工作,诸如建立连接,Statement, JDBC相关异常处理等等都交给Mybatis去做了,那些重复性的工作Mybatis也给做掉了,我们只需要关注在增删改查等操作层面上,而把技术细节都封装在了我们看不见的地方。

创建数据库

1
create database how2java

创建表

1
2
3
4
5
6
7
USE how2java;

CREATE TABLE category_ (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(32) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

导入数据

1
2
3
USE how2java;
INSERT INTO category_ VALUES (null,'category1');
INSERT INTO category_ VALUES (null,'category2');

上面数据库的三步完成后截图:

image-20220417225356691

创建项目

e:\project\mybatis

导包

C:\Users\hahaha\Desktop\计算机\spring\mybatis_experiment_HOW2J\lib

创建实体类

准备实体类Category,用于映射表category_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.how2java.pojo;

public class Category {
private int id;
private String name;
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;
}

}

配置文件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.how2java.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/how2java?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/how2java/pojo/Category.xml"/>
</mappers>
</configuration>

解释:

在src目录下创建mybatis的主配置文件mybatis-config.xml (相当于hibernate.cfg.xml,如果没有hibernate基础请忽略本句)。
其作用主要是提供连接数据库用的驱动,数据库名称,编码方式,账号密码

1
2
3
4
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/how2java?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>

以及别名,自动扫描com.how2java.pojo下的类型,使得在后续配置文件Category.xml中使用resultType的时候,可以直接使用Category,而不必写全com.how2java.pojo.Category

1
2
3
<typeAliases>
<package name="com.how2java.pojo"/>
</typeAliases>

映射Category.xml

1
2
3
<mappers>
<mapper resource="com/how2java/pojo/Category.xml"/>
</mappers>

配置文件Category.xml

在包com.how2java.pojo下,新建文件Category.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.how2java.pojo">
<select id="listCategory" resultType="Category">
select * from category_
</select>
</mapper>

解释:

表示命名空间是com.how2java.pojo,在后续调用sql语句的时候,会用到它

1
namespace="com.how2java.pojo"

里面定义了一条sql语句

这条sql语句用id: listCategory 进行标示以供后续代码调用。resultType=”Category” 表示返回的是一个Category类型的对象,里面就有在数据库里面查询出来的值,这里本应该使用的是 com.how2java.pojo.Category, 但是因为上一步配置了别名(自动扫描com.how2java.pojo下面的类型),所以直接使用Category就行了

1
2
3
<select id="listCategory" resultType="Category">
select * from category_
</select>

测试类TestMybatis

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
package com.how2java;

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

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 com.how2java.pojo.Category;

public class TestMybatis {

public static void main(String[] args) throws IOException {
// 根据配置文件mybatis-config.xml得到sqlSessionFactory(会话工厂)
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 然后再根据sqlSessionFactory 打开会话得到session
SqlSession session=sqlSessionFactory.openSession();

// 最后通过session的selectList方法,调用sql语句listCategory。
// listCategory这个就是在配置文件Category.xml中那条sql语句设置的id
// 相当于执行了select * from category_,因为listCategory是他的别名
// 执行完毕之后,得到一个Category集合(集合里面的每个元素,就是一条数据,也就是一个Category对象),遍历即可看到数据
List<Category> cs=session.selectList("listCategory");
for (Category c : cs) {
System.out.println(c.getName());
}

}
}

基本原理图

  1. 应用程序找Mybatis要数据

  2. mybatis从数据库中找来数据
    2.1 通过mybatis-config.xml 定位哪个数据库
    2.2 通过Category.xml执行对应的select语句
    2.3 基于Category.xml把返回的数据库记录封装在Category对象中
    2.4 把多个Category对象装在一个Category集合中

  3. 返回一个Category集合

image-20220416205104364

CRUD(增删改查)

配置文件Category.xml

首先一次性修改配置文件Category.xml,提供CRUD对应的sql语句。
每个SQL如何使用在后续对应操作里一一讲解。

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.how2java.pojo">
<insert id="addCategory" parameterType="Category" >
insert into category_ ( name ) values (#{name})
</insert>

<delete id="deleteCategory" parameterType="Category" >
delete from category_ where id= #{id}
</delete>

<select id="getCategory" parameterType="_int" resultType="Category">
select * from category_ where id= #{id}
</select>

<update id="updateCategory" parameterType="Category" >
update category_ set name=#{name} where id=#{id}
</update>
<select id="listCategory" resultType="Category">
select * from category_
</select>
</mapper>

增加

addCategory对应的插入sql语句,#{name}会自动获取c对象的name属性值

1
2
3
4
5
<mapper namespace="com.how2java.pojo">
<insert id="addCategory" parameterType="Category" >
insert into category_ ( name ) values (#{name})
</insert>
</mapper>

通过session.insert调用addCategory对应的SQL语句

1
2
3
Category c = new Category();
c.setName("新增加的Category");
session.insert("addCategory",c);

测试类,增加后再遍历

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
package com.how2java;

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

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 com.how2java.pojo.Category;

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 session = sqlSessionFactory.openSession();

Category c = new Category();
c.setName("新增加的Category");
session.insert("addCategory",c);

listAll(session);

session.commit();
session.close();

}

private static void listAll(SqlSession session) {
List<Category> cs = session.selectList("listCategory");
for (Category c : cs) {
System.out.println(c.getName());
}
}
}

结果:增加了一条数据进去

image-20220417235245255

删除

删除id=6的对象

1
2
3
Category c = new Category();
c.setId(6);
session.delete("deleteCategory",c);

deleteCategory对应删除的sql语句,#{id}会自动获取c对象的id属性值

1
2
3
<delete id="deleteCategory" parameterType="Category" >
delete from category_ where id= #{id}
</delete>

结果:

image-20220417235629587

获取

通过session.selectOne获取id=3的记录

1
Category c= session.selectOne("getCategory",3);

getCategory对应的sql语句:

1
2
3
<select id="getCategory" parameterType="_int" resultType="Category">
select * from category_ where id= #{id}
</select>

修改

通过session.update进行修改

1
2
3
Category c= session.selectOne("getCategory",3);
c.setName("修改了的Category名稱");
session.update("updateCategory",c);

updateCategory对应的sql语句:

1
2
3
<update id="updateCategory" parameterType="Category" >
update category_ set name=#{name} where id=#{id}
</update>

查询所有

session.selectList执行查询语句

1
2
3
4
5
6
private static void listAll(SqlSession session) {
List<Category> cs = session.selectList("listCategory");
for (Category c : cs) {
System.out.println(c.getName());
}
}

listCategory对应的sql语句

1
2
3
<select id="listCategory" resultType="Category">
select * from category_
</select>
  1. MyBatis的传入参数parameterType类型分两种

    1. 基本数据类型:int,string,long,Date;
    2. 复杂数据类型:类和Map
  2. 如何获取参数中的值:

    2.1 基本数据类型:#{value}或${value} 获取参数中的值

    2.2 复杂数据类型:#{属性名}或${属性名} ,map中则是#{key}或${key}

resultType:

1、基本类型 :resultType=基本类型

2、List类型: resultType=List中元素的类型

3、Map类型: 单条记录:resultType =map

​ 多条记录:resultType =Map中value的类型

parameterType 指定输入参数类型,mybatis 通过 ognl 从输入对象中获取参数值拼接在 sql 中。

resultType 指定输出结果类型,mybatis 将 sql 查询结果的一行记录数据映射为 resultType 指定类型的对象。如果有多条数据,则分别进行映射,并把对象放到容器List中。

selectOne 查询一条记录,如果使用 selectOne 查询多条记录则抛出异常。

selectList 可以查询一条或多条记录,返回一个 List 集合。

更多查询

模糊查询

修改Category.xml,提供listCategoryByName查询语句

1
2
3
<select id="listCategoryByName"  parameterType="string" resultType="Category">
select * from category_ where name like concat('%',#{0},'%')
</select>

测试类由List<Category> cs = session.selectList("listCategory");

变为List<Category> cs = session.selectList("listCategoryByName","cat");

解释:

SQL语句中:

CONCAT 关键字:把字符串拼接到一起

LIKE 关联字:模糊匹配 。”%” 可用于定义通配符(模式中缺少的字母)。

参数解释:
like concat(’%/’,#{datePath,jdbcType=VARCHAR},’/%’)

    concat函数:
       第一个参数 ,就是'%',其中%后边可以加上一些常量字符比如  / 。
       第二参数,基本上就是传递过来的参数 。
       第三个参数,是结尾的 '%', %前边可以加一定的常量字符比如  / 。

出现了cat的字符串都输出了:

image-20220418084145071

多条件查询

结合前面的模糊查询,多一个id>多少的条件
Category.xml 准备sql语句

1
2
3
<select id="listCategoryByIdAndName"  parameterType="map" resultType="Category">
select * from category_ where id> #{id} and name like concat('%',#{name},'%')
</select>

测试代码
因为是多个参数,而selectList方法又只接受一个参数对象,所以需要把多个参数放在Map里,然后把这个Map对象作为参数传递进去

1
2
3
4
5
6
7
8
Map<String,Object> params = new HashMap<>();
params.put("id", 3);
params.put("name", "cat");

List<Category> cs = session.selectList("listCategoryByIdAndName",params);
for (Category c : cs) {
System.out.println(c.getName());
}

解释模糊查询的逻辑:session(会话)

Test.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
String resource = "mybatis-config.xml";
//将mybatis-config配置文件,作为输入流。这个文件里面有数据库连接,category.xml映射。
InputStream inputStream = Resources.getResourceAsStream(resource);
//根据输入流的配置文件,创建一个sqlsession工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//根据工厂,创建一个用来操作数据库的示例对象session
SqlSession session = sqlSessionFactory.openSession();

//创建集合cs,用来存放session对象获取的集合数据。通过“listCategoryByName”锁定Category.xml中的sql语句,将“cat”传值进去,用来进行模糊查询。selectlist()方法用来将查询的单个或者多个结果放入一个list集合中,并返回给新建的集合cs
List<Category> cs = session.selectList("listCategoryByName","cat");
//将获得的集合遍历输出
for (Category c : cs) {
System.out.println(c.getName());
}
//提交数据,用来更新数据库。(增加 修改)
session.commit();
//关闭会话
session.close();

一对多

表结构

分类表不变化,新增加产品表

1
2
3
4
5
6
7
8
use how2java;
create table product_(
id int NOT NULL AUTO_INCREMENT,
name varchar(30) DEFAULT NULL,
price float DEFAULT 0,
cid int ,
PRIMARY KEY (id)
)AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

准备数据

清空category_ 和 product_ 表
新增2条分类数据,id分别是1,2
新增6条产品数据,分别关联上述2条分类数据

1
2
3
4
5
6
7
8
9
10
11
use how2java;
delete from category_;
INSERT INTO category_ VALUES (1,'category1');
INSERT INTO category_ VALUES (2,'category2');
delete from product_;
INSERT INTO product_ VALUES (1,'product a', 88.88, 1);
INSERT INTO product_ VALUES (2,'product b', 88.88, 1);
INSERT INTO product_ VALUES (3,'product c', 88.88, 1);
INSERT INTO product_ VALUES (4,'product x', 88.88, 2);
INSERT INTO product_ VALUES (5,'product y', 88.88, 2);
INSERT INTO product_ VALUES (6,'product z', 88.88, 2);

image-20220418085539997

image-20220418085549874

Product实体类

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
package com.how2java.pojo;

public class Product {
private int id;
private String name;
private float price;
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 float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public String toString() {
return "Product [id=" + id + ", name=" + name + ", price=" + price + "]";
}

}

修改Category实体类

修改Category实体类,提供products的集合

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
package com.how2java.pojo;

import java.util.List;

public class Category {
private int id;
private String name;
List<Product> products;
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<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = products;
}
@Override
public String toString() {
return "Category [id=" + id + ", name=" + name + "]";
}

}

暂时无需 Product.xml

本例演示通过分类对产品的一对多,暂时无需 Product.xml

修改Category.xml

通过left join关联查询,对Category和Product表进行关联查询。
与前面学习的有所区别,这里不是用的resultType, 而是resultMap,通过resultMap把数据取出来放在对应的 对象属性里
: Category的id 字段 和Product的id字段同名,Mybatis不知道谁是谁的,所以需要通过取别名cid,pid来区分。
name字段同理。

我的理解:

resultMap是用来给返回的结果起别名的,其映射是property和column的映射,

比如说<id column="cid" property="id" />的意思就是,key是属性id,值是数据库中取出来的cid,cid就是数据库中c.id的别名

<result column="price" property="price" />因为属性price和数据库中的列price名字一样,所以没有起别名,直接把属性price作为key,取出value,也就是数据库中的price

这里也可以看出取别名的过程,将category_表取名为c,将product__表取名为p。将c.id列取名为cid,将p.id列取名为pid

select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid

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
<mapper namespace="com.how2java.pojo">
<!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
<!-- id:设置ResultMap的id -->
<resultMap type="Category" id="categoryBean">
<!-- 配置主键,是关联products的唯一标识 -->
<!-- property:主键在pojo中的属性名 -->
<!-- column:主键在数据库中的列名或者别名 -->
<id column="cid" property="id" />

<!-- 定义普通属性 -->
<result column="cname" property="name" />

<!-- 一对多的关系 -->
<!-- property: 指的是集合属性的值, ofType:指的是集合中元素的类型 -->
<collection property="products" ofType="Product">
<!-- 配置主键,是关联categoryBean的唯一标识 -->
<id column="pid" property="id" />
<result column="pname" property="name" />
<result column="price" property="price" />
</collection>
</resultMap>

<!-- 关联查询分类和产品表 -->
<select id="listCategory" resultMap="categoryBean">
<!-- 这里其实是c.id as 'cid',省略了as,也就是起别名 -->
select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
</select>
</mapper>

测试运行TestMybatis

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

结果:

image-20220418093013886

一对多

修改Product.java

为Product增加category属性

1
2
3
4
5
6
7
8
private Category category;

public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}

Product.xml

提供Product.xml,通过listProduct配置关联查询的sql语句。
然后通过resultMap ,进行字段和属性的对应。
使用association 进行多对一关系关联,指定表字段名称与对象属性名称的一一对应关系
注: Category的id 字段 和Product的id字段同名,Mybatis不知道谁是谁的,所以需要通过取别名cid,pid来区分。
name字段同理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<mapper namespace="com.how2java.pojo">
<resultMap type="Product" id="productBean">
<id column="pid" property="id" />
<result column="pname" property="name" />
<result column="price" property="price" />

<!-- 多对一的关系 -->
<!-- property: 指的是属性名称, javaType:指的是属性的类型 -->
<association property="category" javaType="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</association>
</resultMap>

<!-- 根据id查询Product, 关联将Orders查询出来 -->
<select id="listProduct" resultMap="productBean">
select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
</select>
</mapper>

mybatis-config.xml

在mybatis-config.xml中增加对于Product.xml的映射

1
2
3
4
<mappers>
<mapper resource="com/how2java/pojo/Category.xml"/>
<mapper resource="com/how2java/pojo/Product.xml"/>
</mappers>

TestMybatis

1
2
3
4
List<Product> ps = session.selectList("listProduct");
for (Product p : ps) {
System.out.println(p+" 对应的分类是 \t "+ p.getCategory());
}

结果:

image-20220418094305520

多对多

定义多对多关系

本知识点是基于多对一的基础上进行。
在学习之前首先要理清楚多对多的关系,这里以订单Order和产品Product为例:
一张订单里 可以包含多种产品
一种产品 可以出现在多张订单里
这就是多对多关系
为了维系多对多关系,必须要一个中间表。 在这里我们使用订单项(OrderItem)表来作为中间表

表结构

1
2
3
4
5
6
7
8
9
10
11
12
13
create table order_ (
id int(11) NOT NULL AUTO_INCREMENT,
code varchar(32) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

create table order_item_(
id int(11) NOT NULL AUTO_INCREMENT,
oid int ,
pid int ,
number int ,
PRIMARY KEY(id)
)AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

导入数据

一对多关系中准备的数据里已经有Product数据里,这里就只准备订单数据和订单项数据:

  1. 插入两个订单
  2. 插入6条订单项数据,建立如下关系
    2.1 订单1对应产品 1,2,3
    2.2 订单2对应产品 2,3,4
1
2
3
4
5
6
7
8
9
INSERT INTO order_ VALUES (1,'code000A');
INSERT INTO order_ VALUES (2,'code000B');

INSERT INTO order_item_ VALUES (null, 1, 1, 100);
INSERT INTO order_item_ VALUES (null, 1, 2, 100);
INSERT INTO order_item_ VALUES (null, 1, 3, 100);
INSERT INTO order_item_ VALUES (null, 2, 2, 100);
INSERT INTO order_item_ VALUES (null, 2, 3, 100);
INSERT INTO order_item_ VALUES (null, 2, 4, 100);

实体类

实体类Order和OrderItem

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
package com.how2java.pojo;

public class OrderItem {
private int id;
private int number;
private Order order;
private Product product;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}

}
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
package com.how2java.pojo;

import java.util.List;

public class Order {
private int id;
private String code;

List<OrderItem> orderItems;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public List<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}

}

映射文件Order.xml

后续会解释

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
<?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.how2java.pojo">
<resultMap type="Order" id="orderBean">
<id column="oid" property="id" />
<result column="code" property="code" />

<collection property="orderItems" ofType="OrderItem">
<id column="oiid" property="id" />
<result column="number" property="number" />
<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.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
from order_ o
left join order_item_ oi on o.id =oi.oid
left join product_ p on p.id = oi.pid
</select>

<select id="getOrder" resultMap="orderBean">
select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
from order_ o
left join order_item_ oi on o.id =oi.oid
left join product_ p on p.id = oi.pid
where o.id = #{id}
</select>
</mapper>

映射文件Product.xml

后续会解释

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
<?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.how2java.pojo">
<resultMap type="Product" id="productBean">
<id column="pid" property="id" />
<result column="pname" property="name" />
<result column="price" property="price" />

<!-- 多对一的关系 -->
<!-- property: 指的是属性名称, javaType:指的是属性的类型 -->
<association property="category" javaType="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</association>
</resultMap>

<select id="listProduct" resultMap="productBean">
select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname'
from category_ c
left join product_ p on c.id = p.cid
</select>
<select id="getProduct" resultMap="productBean">
select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname'
from category_ c
left join product_ p on c.id = p.cid
where p.id = #{id}
</select>
</mapper>

映射文件OrderItem.xml

后续会解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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.how2java.pojo">

<insert id="addOrderItem" parameterType="OrderItem">
insert into order_item_
values(null,#{order.id},#{product.id},#{number})
</insert>
<insert id="deleteOrderItem" parameterType="OrderItem">
delete from order_item_
where oid = #{order.id} and pid = #{product.id}
</insert>

</mapper>

修改mybatis-config.xml

添加对于Order.xml和OrderItem的映射

1
2
3
4
5
6
<mappers>
<mapper resource="com/how2java/pojo/Category.xml"/>
<mapper resource="com/how2java/pojo/Product.xml"/>
<mapper resource="com/how2java/pojo/Order.xml"/>
<mapper resource="com/how2java/pojo/OrderItem.xml"/>
</mappers>

查询操作

image-20220418095310028

如图所示,查询出所有的订单,然后遍历每个订单下的多条订单项,以及订单项对应的产品名称,价格,购买数量

通过Order.xml的listOrder对应的sql语句进行查询:

1
2
3
4
5
6
<select id="listOrder" resultMap="orderBean">
select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
from order_ o
left join order_item_ oi on o.id =oi.oid
left join product_ p on p.id = oi.pid
</select>

联合order_ , order_item_, product_ 三张表进行查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<resultMap type="Order" id="orderBean">
<id column="oid" property="id" />
<result column="code" property="code" />

<collection property="orderItems" ofType="OrderItem">
<id column="oiid" property="id" />
<result column="number" property="number" />
<association property="product" javaType="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
</association>
</collection>
</resultMap>

建立关系

如图所示,建立了让订单000A和产品z建立了关系
首先通过id分别获取Order对象和Product对象,然后创建一个新的OrderItem对象,接着设置Order,设置Product,设置数量,最后调用”addOrderItem” 对应的sql语句插入数据。

1
2
3
4
5
6
7
Order o1 = session.selectOne("getOrder", 1);
Product p6 = session.selectOne("getProduct", 6);
OrderItem oi = new OrderItem();
oi.setProduct(p6);
oi.setOrder(o1);
oi.setNumber(200);
session.insert("addOrderItem", oi);

addOrderItem调用insert into 语句插入一条OrderItem记录

1
2
3
4
<insert id="addOrderItem" parameterType="OrderItem">
insert into order_item_
values(null,#{order.id},#{product.id},#{number})
</insert>
image-20220418100026482

删除关系

如图所示,删除了订单00A和产品z的关系,再次查询,就看不到产品z了。
删除关系的时候,通过订单id(1)和产品id(6)进行删除。
其实所谓的删除关系,就是删除掉OrderItem记录。

1
2
3
4
5
6
Order o1 = session.selectOne("getOrder",1);
Product p6 = session.selectOne("getProduct",6);
OrderItem oi = new OrderItem();
oi.setProduct(p6);
oi.setOrder(o1);
session.delete("deleteOrderItem", oi);

修改关系

多对多不存在修改关系的做法,就是删除旧的,然后新增一条即达到修改的效果。

注解

CRUD

Mapper接口

新增加接口CategoryMapper ,并在接口中声明的方法上,加上注解
对比配置文件Category.xml,其实就是把SQL语句从XML挪到了注解上来

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
package com.how2java.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import com.how2java.pojo.Category;

public interface CategoryMapper {

@Insert(" insert into category_ ( name ) values (#{name}) ")
public int add(Category category);

@Delete(" delete from category_ where id= #{id} ")
public void delete(int id);

@Select("select * from category_ where id= #{id} ")
public Category get(int id);

@Update("update category_ set name=#{name} where id=#{id} ")
public int update(Category category);

@Select(" select * from category_ ")
public List<Category> list();
}

mybatis-config.xml

1
2
3
<mappers>
<mapper class="com.how2java.mapper.CategoryMapper"/>
</mappers>

测试类

进行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
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 session = sqlSessionFactory.openSession();
CategoryMapper mapper = session.getMapper(CategoryMapper.class);

// add(mapper);
// delete(mapper);
// get(mapper);
// update(mapper);
listAll(mapper);

session.commit();
session.close();

}

private static void update(CategoryMapper mapper) {
Category c= mapper.get(8);
c.setName("修改了的Category名称");
mapper.update(c);
listAll(mapper);
}

private static void get(CategoryMapper mapper) {
Category c= mapper.get(8);
System.out.println(c.getName());
}

private static void delete(CategoryMapper mapper) {
mapper.delete(2);
listAll(mapper);
}

private static void add(CategoryMapper mapper) {
Category c = new Category();
c.setName("新增加的Category");
mapper.add(c);
listAll(mapper);
}

private static void listAll(CategoryMapper mapper) {
List<Category> cs = mapper.list();
for (Category c : cs) {
System.out.println(c.getName());
}
}
}

一对多

CategoryMapper

新增加CategoryMapper接口,查询所有Category
@Select注解获取Category类本身

1
@Select(" select * from category_ ")

@Results 通过@Result和@Many中调用ProductMapper.listByCategory()方法相结合,来获取一对多关系

1
2
@Results({@Result(property = "products", javaType = List.class, column = "id",  
many = @Many(select = "com.how2java.mapper.ProductMapper.listByCategory"))})
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
public interface CategoryMapper {
/*首先,默认情况下,一个column(数据库字段)只映射到 “自身”JavaBean的一个property,即属性。
在一对多的时候,
@Result(property="products", javaType=List.class, column="id",
many=@Many(select="com.how2java.mapper.ProductMapper.listByCategory")),
这个语句已经通过listByCategory方法把 category表的id字段映射到了products属性,就不会再自动映射到自身的id属性了,所以查询出来自身的id为0;
所以需要加@Result(property="id", column="id") 来将id字段映射到id属性
而在多对一的时候,
@Result(property="category", column="cid", one=@One(select="com.how2java.mapper.CategoryMapper.get"))
注意:这里引用的字段是cid,不是id!而cid本来就是用于映射到category属性的。所以不存在单字段多属性映射的情况。*/

// @Select注解获取Category类本身
@Select(" select * from category_ ")
// @Results 通过@Result和@Many中调用ProductMapper.listByCategory()方法相结合,来获取一对多关系
@Results({
@Result(property = "id", column = "id"),
// property:指定将关联查询的结果封装到Category对象的List<Product> products属性上
// javaType返回值类型是一个List
// column:指定在执行@Many注解中定义的select语句时,把category表的id字段当做参数传入
// many:指定通过@Many注解定义关联查询的语句是ProductMapper中的listByCategory方法
@Result(property = "products", javaType = List.class, column = "id", many = @Many(select = "com.how2java.mapper.ProductMapper.listByCategory") )
})
public List<Category> list();

}

ProductMapper

新增接口ProductMapper
注解@Select用于根据分类id获取产品集合
@Select(“ select * from product_ where cid = #{cid}”)

1
2
3
4
public interface ProductMapper {
@Select(" select * from product_ where cid = #{cid}")
public List<Product> listByCategory(int cid);
}

mybatis-config.xml

添加ProductMapper和CategoryMapper的映射

1
2
3
4
<mappers>
<mapper class="com.how2java.mapper.CategoryMapper"/>
<mapper class="com.how2java.mapper.ProductMapper"/>
</mappers>

测试

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
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 session = sqlSessionFactory.openSession();
CategoryMapper mapper = session.getMapper(CategoryMapper.class);

listAll(mapper);

session.commit();
session.close();

}

private static void listAll(CategoryMapper mapper) {
List<Category> cs = mapper.list();
for (Category c : cs) {
System.out.println(c.getName());
List<Product> ps = c.getProducts();
for (Product p : ps) {
System.out.println("\t"+p.getName());
}
}
}
}

多对一

CategoryMapper

新增CategoryMapper接口,提供get方法

1
2
3
4
public interface CategoryMapper {
@Select(" select * from category_ where id = #{id}")
public Category get(int id);
}

ProductMapper

新增ProductMapper接口,提供list方法

1
2
3
4
5
6
7
public interface ProductMapper {
@Select(" select * from product_ ")
@Results({
@Result(property="category",column="cid",one=@One(select="com.how2java.mapper.CategoryMapper.get"))
})
public List<Product> list();
}

测试类

1
2
3
4
List<Product> ps= mapper.list();
for (Product p : ps) {
System.out.println(p + "\t对应的分类是:\t" + p.getCategory().getName());
}

多对多

ProductMapper

新增一个ProductMapper接口,提供 get方法

1
2
3
4
public interface ProductMapper {
@Select("select * from product_ where id = #{id}")
public Product get(int id);
}

OrderItemMapper

新增OrderItemMapper,提供listByOrder方法。
这里会与Product建立多对一关系

1
2
3
4
5
6
7
public interface OrderItemMapper {
@Select(" select * from order_item_ where oid = #{oid}")
@Results({
@Result(property="product",column="pid",one=@One(select="com.how2java.mapper.ProductMapper.get"))
})
public List<OrderItem> listByOrder(int oid);
}

OrderMapper

新增OrderMapper,提供list方法,这里会与OrderItem建立一对多关系

1
2
3
4
5
6
7
8
9
public interface OrderMapper {
@Select("select * from order_")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "orderItems", javaType = List.class, column = "id",
many = @Many(select = "com.how2java.mapper.OrderItemMapper.listByOrder"))
})
public List<Order> list();
}

修改mybatis-config.xml

新增Mapper类的映射

1
2
3
4
5
6
<mappers>
<mapper resource="com/how2java/pojo/OrderItem.xml"/>
<mapper class="com.how2java.mapper.OrderItemMapper"/>
<mapper class="com.how2java.mapper.OrderMapper"/>
<mapper class="com.how2java.mapper.ProductMapper"/>
</mappers>

查询测试

如图所示,运行测试遍历所有的订单,再通过一对多和多对一,间接地多对多遍历出产品。

image-20220418104934353
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void listOrder(SqlSession session) {
OrderMapper mapper =session.getMapper(OrderMapper.class);
List<Order> os = mapper.list();
for (Order o : os) {
System.out.println(o.getCode());
List<OrderItem> ois= o.getOrderItems();
if(null!=ois){
for (OrderItem oi : ois) {
System.out.format("\t%s\t%f\t%d%n", oi.getProduct().getName(),oi.getProduct().getPrice(),oi.getNumber());
}
}

}
}
-------------本文结束感谢您的阅读-------------