本文速览
- 使用映射配置文件实现CRUD操作
- 使用注解实现CRUD操作
环境准备
- 创建对应数据库
USE mybatis
-- 删除tb_brand表
DROP TABLE IF EXISTS tb_brand;
-- 创建tb_brand表
CREATE TABLE tb_brand
(
-- id 主键
id INT PRIMARY KEY AUTO_INCREMENT,
-- 品牌名称
brand_name VARCHAR(20),
-- 企业名称
company_name VARCHAR(20),
-- 排序字段
ordered INT,
-- 描述信息
description VARCHAR(100),
-- 状态:0:禁用 1:启用
STATUS INT
);
-- 添加数据
INSERT INTO tb_brand (brand_name, company_name, ordered, description, STATUS)
VALUES ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1);
- 在
com.xzxhappy.pojo
下创建实体类Brand
public class Brand {
// id 主键
private Integer id;
// 品牌名称
private String brandName;
// 企业名称
private String companyName;
// 排序字段
private Integer ordered;
// 描述信息
private String description;
// 状态:0:禁用 1:启用
private Integer status;
//省略 setter and getter 和 toString方法。自己写时要补全这部分代码
}
- 在
test/java
目录下面建 com.xzxhappy.test.MyBatisTestDemo测试类
package com.xzxhappy.test;
import com.xzxhappy.mapper.BrandMapper;
import com.xzxhappy.pojo.Brand;
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 MyBatisTest {
@Test
public void testSelectAll() throws Exception {
// 1. 获取sqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3. 获取接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 4. 执行对应方法 (其他不用动,如果要测试其他增删改方法就改下面两行代码就行)
List<Brand> brands = brandMapper.selectAll();
System.out.println(brands);
// 5. 关闭资源
sqlSession.close();
}
}
查询所有
1. 编写 Mapper 接口的对应方法
在 com.xzxhappy.mapper
包写创建名为 BrandMapper
的接口。并在该接口中定义 List<Brand> selectAll()
方法。(显然查询所有不需要参数)
public interface BrandMapper {
/**
* 查询所有
*/
List<Brand> selectAll();
}
2. 编写xml文件中的对应sql语句
在 resources
下创建 com/xzxhappy/mapper
目录结构(注意resources目录下面不能建包,只能目录,所以建立的语法得用目录的 /
),并在该目录下创建名为 BrandMapper.xml
的映射配置文件
<?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.xzxhappy.mapper.BrandMapper">
<select id="selectAll" resultType="brand">
select *
from tb_brand;
</select>
</mapper>
<!-- 由于在实体类中属性名是 `brandName` 和 `companyName` ,
而表中的字段名为 `brand_name` 和 `company_name`,
所以会导致这两个字段名的内容查出来是null的情况。
那么如何解决呢
下面有三种方法 -->
<!--
解决方法把上述<mapper>标签中的代码改成下面这些即可
* 1. 起别名
* 2. sql片段
* 3. 使用resultMap标签
-->
<!-- 方法一,起别名 -->
<select id="selectAll" resultType="Brand">
select id, brand_name brandName, company_name companyName, ordered, description, status
from tb_brand;
</select>
<!-- 方法二,sql片段 -->
<sql id="brand_column">
id, brand_name brandName, company_name companyName, ordered, description, status
</sql>
<select id="selectAll" resultType="Brand">
select <include refid="brand_column"/>
from tb_brand;
</select>
<!-- 方法三,使用resultMap标签,这种方法也是最常用的,使用这种方法那么原本 select 标签中的 resultType 属性要替换成 resultMap 属性-->
<resultMap id="brandResultMap" type="brand">
<!--
id:完成主键字段的映射
column:表的列名
property:实体类的属性名
result:完成一般字段的映射
column:表的列名
property:实体类的属性名
-->
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select id, brand_name , company_name , ordered, description, status
from tb_brand;
</select>
3. 编写测试代码
@Test
public void testSelectAll() throws IOException {
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
List<Brand> brands = brandMapper.selectAll();
System.out.println(brands);
//5. 释放资源
sqlSession.close();
}
根据id查询
1. 编写 Mapper 接口的对应方法
BrandMapper
接口中定义根据id查询数据的方法
/**
* 查看详情:根据Id查询
*/
Brand selectById(int id);
2. 编写xml文件中的对应sql语句
<!--
mybatis提供了两种参数占位符:
* #{} :执行SQL时,会将 #{} 占位符替换为?,将来自动设置参数值。从上述例子可以看出使用#{} 底层使用的是 `PreparedStatement`
* ${} :拼接SQL。底层使用的是 `Statement`,会存在SQL注入问题。
-->
<select id="selectById" resultMap="brandResultMap">
select *
from tb_brand where id = #{id};
</select>
3. 编写测试代码
@Test
public void testSelectById() throws Exception {
// 假装是传过来的参数
int id = 1;
// 1. 获取sqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3. 获取接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 4. 执行对应方法
Brand brand = brandMapper.selectById(id);
System.out.println(brand);
// 5. 关闭资源
sqlSession.close();
}
4. sql语句中特殊字符处理
SQL语句中写一下特殊字符,比如 <
号
- 使用xml的转义字符
例如:
原句:
<select id="selectById" resultMap="brandResultMap">
select *
from tb_brand
where id < #{id};
</select>
转义:
<select id="selectById" resultMap="brandResultMap">
select *
from tb_brand
where id < #{id};
</select>
- 使用CDATA区(该区域内均被解析为正常文本)
例如:
原句:
<select id="selectById" resultMap="brandResultMap">
select *
from tb_brand
where id < #{id};
</select>
转义:
<select id="selectById" resultMap="brandResultMap">
select *
from tb_brand
where id
<![CDATA[
<
]]>
#{id};
</select>
条件查询
多条件查询
-
使用
@Param("参数名称")
标记每一个参数,在映射配置文件中就需要使用#{参数名称}
进行占位 -
将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用
#{内容}
时,里面的内容必须和实体类属性名保持一致。 -
将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用
#{内容}
时,里面的内容必须和map集合中键的名称一致。 -
代码示例:
1. 编写 Mapper 接口的对应方法
BrandMapper.java
/**
* 条件查询
*/
/*<1> 散装参数*/
// List<Brand> selectByCondition(@Param("status") int status,@Param("companyName") String companyName,@Param("brandName") String brandName);
/*<2> brand对象*/
// List<Brand> selectByCondition(Brand brand);
/*<3> map对象*/
List<Brand> selectByCondition(Map map);
2. 编写xml文件中的对应sql语句
BrandMapper.xml
(此文件在三种条件下不变)
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
3. 编写测试代码
MyBatisTest.java
/*
条件查询
*/
@Test
public void testSelectByCondition() throws Exception {
// 假装是传过来的参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
// 处理参数
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
// 1. 获取sqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3. 获取接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 4. 执行对应方法
// <1>散装参数
// List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName);
// <2> brand对象
// <2>封装brand对象
// Brand brand = new Brand();
// brand.setStatus(status);
// brand.setCompanyName(companyName);
// brand.setBrandName(brandName);
// List<Brand> brands = brandMapper.selectByCondition(brand);
// <3> map对象
// <3>封装map对象
Map map = new HashMap();
map.put("status",status);
map.put("companyName",companyName);
map.put("brandName",brandName);
List<Brand> brands = brandMapper.selectByCondition(map);
System.out.println(brands);
// 5. 关闭资源
sqlSession.close();
}
动态SQL多条件查询
1. 编写 Mapper 接口的对应方法
/**
* 动态sql
*/
List<Brand> selectByCondition(Map map);
2. 编写xml文件中的对应sql语句
<!-- 情况一,但是这种情况下,如果条件一不存在那么sql语句会报错,因为where后面直接跟了and... -->
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where
<!--
if 标签:条件判断
test 属性:逻辑表达式
-->
<if test="status != null">
status = #{status}
</if>
<if test="companyName != null and companyName != '' ">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != '' ">
and brand_name like #{brandName}
</if>
</select>
<!-- 情况二,在where后面拼接一个恒等式,思想值得学习,但不是最常用的-->
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where 1 = 1
<if test="status != null">
and status = #{status}
</if>
<if test="companyName != null and companyName != '' ">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != '' ">
and brand_name like #{brandName}
</if>
</select>
<!-- 情况三,使用where标签,如果条件一不存在那么where标签会自动检测,把where后面跟的and删去,where是一个神奇的标签-->
<!--
where 标签作用:
1. 替换where关键字
2. 会动态的去掉第一个条件前的 and
3. 如果所有的参数没有值则不加where关键字
-->
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
<where>
<if test="status != null">
status = #{status}
</if>
<if test="companyName != null and companyName != '' ">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != '' ">
and brand_name like #{brandName}
</if>
</where>
</select>
3. 编写测试代码
// 模仿用户,故意少了第一个参数
Map map = new HashMap();
// map.put("status" , status);
map.put("companyName", companyName);
map.put("brandName" , brandName);
List<Brand> brands = brandMapper.selectByCondition(map);
System.out.println(brands);
动态SQL单条件查询
1. 编写 Mapper 接口的对应方法
/**
* 单条件动态sql
* @param brand
* @return
*/
List<Brand> selectByConditionSingle(Brand brand);
2. 编写xml文件中的对应sql语句
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from tb_brand
<!-- 考虑到这个条件3选1,若用户都不选,用where标签,到时候sql语句里直接没where -->
<where>
<!-- choose相当于switch -->
<choose>
<!-- when相当于case -->
<when test="status != null">
status = #{status}
</when>
<when test="companyName != null and companyName != '' ">
company_name like #{companyName}
</when>
<when test="brandName != null and brandName != '' ">
brand_name like #{brandName}
</when>
<!-- otherwise相当于default -->
<otherwise>
1 = 1
</otherwise>
</choose>
</where>
</select>
3. 编写测试代码
// 4. 执行对应方法
// <2> brand对象
// <2>封装brand对象
Brand brand = new Brand();
//brand.setStatus(status);
//brand.setCompanyName(companyName);
//brand.setBrandName(brandName);
List<Brand> brands = brandMapper.selectByConditionSingle(brand);
添加数据
正常添加
1. 编写 Mapper 接口的对应方法
/**
* 添加数据
*/
void add(Brand brand);
2. 编写xml文件中的对应sql语句
<insert id="add">
insert into tb_brand (brand_name, company_name, ordered, description, status)
values (#{brandName},#{companyName},#{ordered},#{description},#{status})
</insert>
3. 编写测试代码
/**
* 添加数据
*/
@Test
public void testAdd() throws Exception {
// 假装是传过来的参数
int status = 1;
String companyName = "波导";
String brandName = "波导";
int ordered = 0;
String description = "波导手机6666";
// 1. 获取sqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取sqlSession对象
// SqlSession sqlSession = sqlSessionFactory.openSession();
//获取sqlSession时,设置自动提交事务,这种情况不需要手动提交事务了
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 3. 获取接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 4. 执行对应方法
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setOrdered(ordered);
brand.setDescription(description);
brandMapper.add(brand);
// 默认开启事务,即需要手动提交事务
//sqlSession.commit();
// 5. 关闭资源
sqlSession.close();
}
添加后返回主键
在数据添加成功后,有时候需要获取插入数据库数据的主键,只需要把BrandMapper.xml
中的<insert>
标签添加两个属性就行
- 在 insert 标签上添加如下属性:
- useGeneratedKeys:是够获取自动增长的主键值。true表示获取
- keyProperty :指定将获取到的主键值封装到哪个属性里
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into tb_brand (brand_name, company_name, ordered, description, status)
values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>
在 MyBatisTest.java
里面就能直接获取到id
brandMapper.add(brand);
Integer id = brand.getId();
System.out.println(id);
修改数据
动态修改
1. 编写 Mapper 接口的对应方法
/**
* 修改数据
*/
// 返回值可以是void,也可以是int,如果是int则返回受影响的行数
int update(Brand brand);
2. 编写xml文件中的对应sql语句
<!-- 传入了什么参数就修改什么参数,其他的保持不变 -->
<update id="update" >
update tb_brand
<!-- 类似于where标签,set标签可以智能的识别sql语句,让其符合语法规范 -->
<set>
<if test="brandName != null and brandName != ''">
brand_name = #{brandName},
</if>
<if test="companyName != null and companyName != ''">
company_name = #{companyName},
</if>
<if test="ordered != null ">
ordered = #{ordered},
</if>
<if test="description != null and description != ''">
description = #{description},
</if>
<if test="status != null ">
status = #{status}
</if>
</set>
where id = #{id};
</update>
3. 编写测试代码
/**
* 修改数据
*/
@Test
public void testUpdate() throws Exception {
// 假装是传过来的参数
int status = 1;
String companyName = "波导";
String brandName = "波导";
int ordered = 0;
String description = "波导手机666655555";
int id = 5;
// 1. 获取sqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取sqlSession对象
// SqlSession sqlSession = sqlSessionFactory.openSession();
//获取sqlSession时,设置自动提交事务,这种情况不需要手动提交事务了
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 3. 获取接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 4. 执行对应方法
Brand brand = new Brand();
//brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setOrdered(ordered);
brand.setDescription(description);
brand.setId(id);
int count = brandMapper.update(brand);
System.out.println(count);
// 默认开启事务,即需要手动提交事务
//sqlSession.commit();
// 5. 关闭资源
sqlSession.close();
}
删除数据
删除一个
1. 编写 Mapper 接口的对应方法
/**
* 删除一个
*/
void deleteById(int id);
2. 编写xml文件中的对应sql语句
<delete id="deleteById">
delete from tb_brand
where id = #{id};
</delete>
3. 编写测试代码
/**
* 删除一行数据
*/
@Test
public void testDeleteById() throws Exception {
// 假装是传过来的参数
int id = 6;
// 1. 获取sqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取sqlSession对象
// SqlSession sqlSession = sqlSessionFactory.openSession();
//获取sqlSession时,设置自动提交事务,这种情况不需要手动提交事务了
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 3. 获取接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 4. 执行对应方法
brandMapper.deleteById(id);
// 默认开启事务,即需要手动提交事务
//sqlSession.commit();
// 5. 关闭资源
sqlSession.close();
}
批量删除
1. 编写 Mapper 接口的对应方法
/**
* 批量删除
* 使用注解那么这个参数会替换xml文件中foreach标签里面的“ids”
*/
void deleteByIds(@Param("ids") int [] ids);
2. 编写xml文件中的对应sql语句
<delete id="deleteByIds">
delete from tb_brand
where id in
<!--
collection:要遍历的集合
item:遍历出来的项的名称
separator:项之间用什么分隔开
open:给foreach标签加上开始符号,close同理
-->
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
;
</delete>
3. 编写测试代码
/**
* 批量删除
*/
@Test
public void testDeleteByIds() throws Exception {
// 假装是传过来的参数
int [] ids = {5,7};
// 1. 获取sqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取sqlSession对象
// SqlSession sqlSession = sqlSessionFactory.openSession();
//获取sqlSession时,设置自动提交事务,这种情况不需要手动提交事务了
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 3. 获取接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 4. 执行对应方法
brandMapper.deleteByIds(ids);
// 默认开启事务,即需要手动提交事务
//sqlSession.commit();
// 5. 关闭资源
sqlSession.close();
}
参数传递问题
在 BrandMapper.java
文件的方法中传递参数,我们建议使用注解传参。
例如:
User select(@Param("username") String username,@Param("password") String password);
我们在接口方法中定义多个参数,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param
注解时有以下命名规则:
-
以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:
map.put("arg0",参数值1);
map.put("arg1",参数值2);
-
以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:
map.put("param1",参数值1);
map.put("param2",参数值2);
举例:
- 接口方法
User select(@Param("username") String username, String password);
- xml配置文件
<select id="select" resultType="user">
select *
from tb_user
where
username=#{username}
and password=#{param2}
</select>
总结
-
POJO 类型
直接使用。要求属性名
和参数占位符名称
一致 -
Map 集合类型
直接使用。要求map集合的键名
和参数占位符名称
一致 -
Collection 集合类型
Mybatis 会将集合封装到 map 集合中,如下:map.put(“arg0”,collection集合);
map.put(“collection”,collection集合);
可以使用@Param
注解替换map集合中默认的 arg 键名。 -
List 集合类型
Mybatis 会将集合封装到 map 集合中,如下:map.put(“arg0”,list集合);
map.put(“collection”,list集合);
map.put(“list”,list集合);
可以使用@Param
注解替换map集合中默认的 arg 键名。 -
Array 类型
Mybatis 会将集合封装到 map 集合中,如下:map.put(“arg0”,数组);
map.put(“array”,数组);
可以使用@Param
注解替换map集合中默认的 arg 键名。 -
其他类型
比如int类型,参数占位符名称
叫什么都可以。尽量做到见名知意
注解实现CRUD
使用注解开发会比配置文件开发更加方便。如下就是使用注解进行开发
@Select(value = "select * from tb_user where id = #{id}")
public User select(int id);
注:注解是用来替换映射配置文件方式配置的,所以使用了注解,就不需要在xml配置文件中书写对应的sql代码了。
Mybatis 针对 CURD 操作都提供了对应的注解,已经做到见名知意。如下:
- 查询 :@Select
- 添加 :@Insert
- 修改 :@Update
- 删除 :@Delete
请注意:注解方式完成简单功能,xml配置文件完成复杂功能。
Q.E.D.