全文转自how2j官网。

转发目的:好记性不如烂笔头。

入门

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

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

必读: 基于框架的程序要成功运行,对于JAR包的版本,配置文件的正确性有着苛刻的要求,任何一个地方出错了,都会导致框架程序运行失败。 如果你是第一次学习本框架,务必严格按照教程的指导,完全模仿操作,直到成功看到运行效果。 第一次成功之后,信心,思路都会有较好的铺垫,然后再根据自己的疑惑,在“成功”的代码上做原本想做的改动和调整,这样可以大大节约学习的时间,提高效率,切勿一来就擅自改动,给自己的学习制造障碍

  1. 创建数据库
CREATE DATEBASE test;
  1. 创建表
USE test;
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. 插入测试数据
INSERT INTO category_ VALUES (null,'category1');
INSERT INTO category_ VALUES (null,'category2');
  1. 添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.happycoder</groupId>
    <artifactId>MyBatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.17</version>
        </dependency>
        <!-- druid数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>0.1.18</version>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.1</version>
        </dependency>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>

        <!-- spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.3.RELEASE</version>
        </dependency>
    </dependencies>
    
</project>
  1. 创建实体类

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

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

  1. 配置文件mybatis-config.xml

在resources目录下创建mybatis的主配置文件mybatis-config.xml

其作用主要是提供连接数据库用的驱动,数据库名称,编码方式,账号密码

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

映射Category.xml

<?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.happycoder.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/test?characterEncoding=UTF-8" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="Category.xml" />
    </mappers>
</configuration>
  1. 配置文件Category.xml

在resources目录下,新建文件Category.xml

表示命名空间是com.how2java.pojo,在后续调用sql语句的时候,会用到它
里面定义了一条sql语句

这条sql语句用id: listCategory 进行标示以供后续代码调用。resultType=“Category” 表示返回的数据和Category关联起来,这里本应该使用的是 com.happycoder.pojo.Category, 但是因为上一步配置了别名,所以直接使用Category就行了

<?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.happycoder.pojo">
    <select id="selectAll" resultType="Category">
        select * from category_
    </select>
</mapper>
  1. 编写测试类
package com.happycoder.test;

import com.happycoder.pojo.Category;
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 org.junit.Test;


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

public class TestMybatis {
    @Test
    public void test() throws IOException{
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();

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

运行并观察到如图所示的结果。
根据配置文件mybatis-config.xml得到sqlSessionFactory 。

然后再根据sqlSessionFactory 得到session

最后通过session的selectList方法,调用sql语句listCategory。listCategory这个就是在配置文件Category.xml中那条sql语句设置的id。
执行完毕之后,得到一个Category集合,遍历即可看到数据。

原理图

  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集合

练习

参考Category,做一个Product类的mybatis查询。 Product类有三个属性:
int id
String name
float price

CRUD

  1. 配置文件Category.xml

首先一次性修改配置文件Category.xml,提供CRUD对应的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="com.happycoder.pojo">
    <insert id="add" parameterType="Category" >
        insert int category_ (name) values (#{name})
    </insert>
    <delete id="delect" parameterType="Category">
        delete from category_ where id = #{id}
    </delete>
    <select id="selectOne" resultType="Category" parameterType="_int">
        select * from category_ where id = #{id}
    </select>
    <update id="update" parameterType="Category">
        update category_ set name = #{name} where id = #{id}
    </update>
    <select id="selectAll" resultType="Category">
        select * from category_
    </select>
</mapper>
  1. 测试类
  • 增加
 @Test
    public void test() 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("add", c);

        all(session);

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

    public void all(SqlSession session){
        List<Category> cs = session.selectList("selectAll");
        for(Category c : cs){
            System.out.println(c.getName());
        }
    }
  • 删除
 @Test
    public void test() 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.setId(2);
        session.delete("delect",c.getId());

        all(session);

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

   
  • 更新
@Test
    public void test() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();

        Category c = session.selectOne("selectOne",3);
        c.setName("新增加的第二个category");
        session.update("update",c);

        all(session);

        session.commit();
        session.close();
    }
  • 查找
@Test
    public void test() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();

        Category c = session.selectOne("selectOne",3);
        System.out.println(c.getName());

        //all(session);

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

更多查找

模糊查询

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

concat(’%’,#{0},’%’) 这是mysql的写法
如果是oracle,写法是

select * from category_ where name like ‘%’||#{0}||’%’

  1. 运行测试
<!-- 新增语句 -->
<select id="selectByName" parameterType="string" resultType="Category">
        select * from   category_  where name like concat('%',#{0},'%')
    </select>
@Test
    public void test() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();

        List<Category> cs = session.selectList("selectByName","cat");
        for(Category c :cs ){
            System.out.println(c.getName());
        }
        session.commit();
        session.close();
    }

多条件查询

结合前面的模糊查询,多一个id>多少的条件

  1. Category.xml准备sql语句
  1. 测试代码

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

Map<String,Object> params = new HashMap<>();
        params.put("id", 3);
        params.put("name", "cat");
        List<Category> cs = session.selectList("listCategoryByIdAndName",params);
        session.commit();
        session.close();

一对多

  1. 创建数据
use test;
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;
delete from category_;
INSERT INTO category_ VALUES (1,'category1');
INSERT INTO category_ VALUES (2,'category2');
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);
  1. Product实体类
package com.happycoder.pojo;

public class Product {
    private int id;
    private String name;
    private float price;
	//get//set//toString
}
  1. 修改Category实体类

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

package com.happycoder.pojo;

import java.util.List;

public class Category {
    private int id;
    private String name;
    List<Product> products;
 	//get//set/toString   
}
  1. 修改Category.xml

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

<mapper namespace="com.happycoder.pojo">
    <resultMap id="categoryBean" type="Category">
        <id column="cid" property="id" />
        <result column="cname" property="name" />

    <!-- 一对多的关系 -->
    <!-- property: 指的是集合属性的值, 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.*, 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>
  1. 运行测试类
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);
            }
        }
        session.commit();
        session.close();

多对一

  1. 修改Product.java

为Product增加category属性

...
private Category category;
//get//set
...
  1. Product.xml

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

<mapper namespace="com.happycoder.pojo">
    <resultMap id="productBean" type="Product">
        <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-confing.xml中增加product映射文件

<mapper resource="Product.xml" />
  1. 运行测试类
...
List<Product> ps = session.selectList("listProduct");
        for(Product p : ps){
            System.out.println(p + "对应的分类是\t" + p.getCategory());
        }
        session.commit();
        session.close(); 
...

练习-修改多对一关系

眼下的多对一关系是
product(id=5) 对应 category(id=2)
结合CRUD ,通过Mybatis,做到 product(id=5) 对应category(id=1)

多对多

定义多对多关系

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

本知识点讲解如何查询多对多关系,建立多对多关系,删除多对多关系

创建数据

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;
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);
  1. 插入两个订单
  2. 插入6条订单项数据,建立如下关系
    2.1 订单1对应产品 1,2,3
    2.2 订单2对应产品 2,3,4

实体类

package com.happycoder.pojo;

public class OrderItem {
    private int id;
    private int number;
    private Order order;
    private Product product;
    //get//set
}
package com.happycoder.pojo;

import java.util.List;

public class Order {
    private int id;
    private String code;
    List<OrderItem> orderItems;
    //get//set
}

映射文件

Order.xml

<mapper namespace="com.happycoder.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

<mapper namespace="com.happycoder.pojo">
    <resultMap id="productBean" type="Product">
        <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

<mapper namespace="com.happycoder.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.xml的映射

<mapper resource="Order.xml"/>
        <mapper resource="OrderItem.xml" />

查询操作

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

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

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

查询结果 id和code字段放在Order对象里, 然后通过一对多的标签把oiid和number放在OrderItem对象里,最后把pid,pname,price放进Product对象里。

//测试类
@Test
    public void test() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();

        listOrder(session);
        session.commit();
        session.close();
    }
public void listOrder(SqlSession session){
        List<Order> os = session.selectList("listOrder");
        for(Order o : os){
            System.out.println(o.getCode());
            List<OrderItem> ois = o.getOrderItems();
            for(OrderItem oi : ois){
                System.out.format("\t%s\t%f\t%d\n",
                        oi.getProduct().getName(),oi.getProduct().getPrice(),oi.getNumber());
            }
        }
    }

建立关系

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

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

//测试类
@Test
    public void test() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();

        addOrderItem(session);
        listOrder(session);
        session.commit();
        session.close();
    }
public void addOrderItem(SqlSession session){
        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);
    }

删除关系

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

//测试类@Test    public void test() throws IOException {        String resource = "mybatis-config.xml";        InputStream inputStream = Resources.getResourceAsStream(resource);        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        SqlSession session = sqlSessionFactory.openSession();        deleteOrderItem(session);        listOrder(session);        session.commit();        session.close();    }public void deleteOrderItem(SqlSession session){        Order o1 = session.selectOne("getOrder",1);        Product p6 = session.selectOne("getProduct",6);        OrderItem oi = new OrderItem();        oi.
10-07 16:03