全文转自how2j官网。
转发目的:好记性不如烂笔头。
入门
平时我们都用JDBC访问数据库,除了需要自己写SQL之外,还必须操作Connection, Statment, ResultSet 这些其实只是手段的辅助类。 不仅如此,访问不同的表,还会写很多雷同的代码,显得繁琐和枯燥。
那么用了Mybatis之后,只需要自己提供SQL语句,其他的工作,诸如建立连接,Statement, JDBC相关异常处理等等都交给Mybatis去做了,那些重复性的工作Mybatis也给做掉了,我们只需要关注在增删改查等操作层面上,而把技术细节都封装在了我们看不见的地方。
必读: 基于框架的程序要成功运行,对于JAR包的版本,配置文件的正确性有着苛刻的要求,任何一个地方出错了,都会导致框架程序运行失败。 如果你是第一次学习本框架,务必严格按照教程的指导,完全模仿操作,直到成功看到运行效果。 第一次成功之后,信心,思路都会有较好的铺垫,然后再根据自己的疑惑,在“成功”的代码上做原本想做的改动和调整,这样可以大大节约学习的时间,提高效率,切勿一来就擅自改动,给自己的学习制造障碍
- 创建数据库
CREATE DATEBASE test;
- 创建表
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;
- 插入测试数据
INSERT INTO category_ VALUES (null,'category1');
INSERT INTO category_ VALUES (null,'category2');
- 添加依赖
<?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>
- 创建实体类
准备实体类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;
}
}
- 配置文件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>
- 配置文件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>
- 编写测试类
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集合,遍历即可看到数据。
原理图
- 应用程序找Mybatis要数据
- mybatis从数据库中找来数据
2.1 通过mybatis-config.xml 定位哪个数据库
2.2 通过Category.xml执行对应的select语句
2.3 基于Category.xml把返回的数据库记录封装在Category对象中
2.4 把多个Category对象装在一个Category集合中 - 返回一个Category集合
练习
参考Category,做一个Product类的mybatis查询。 Product类有三个属性:
int id
String name
float price
CRUD
- 配置文件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>
- 测试类
- 增加
@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();
}
更多查找
模糊查询
- 修改Category.xml,提供listCateogryByName查询语句
concat(’%’,#{0},’%’) 这是mysql的写法
如果是oracle,写法是
select * from category_ where name like ‘%’||#{0}||’%’
- 运行测试
<!-- 新增语句 -->
<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>多少的条件
- Category.xml准备sql语句
- 测试代码
因为是多个参数,而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();
一对多
- 创建数据
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);
- Product实体类
package com.happycoder.pojo;
public class Product {
private int id;
private String name;
private float price;
//get//set//toString
}
- 修改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
}
- 修改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>
- 运行测试类
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();
多对一
- 修改Product.java
为Product增加category属性
...
private Category category;
//get//set
...
- 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" />
- 运行测试类
...
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);
- 插入两个订单
- 插入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.