什么是MyBatis?
- MyBatis是一款优秀的持久层框架,用于简化JDBC开发。
- MyBatis 本来是 Apache 的一个开眼项目iBatis,2010年这个项目由 apache software foundation 迁移到 google code ,并且改名为MyBatis,2013年11月迁移到 GitHub
-
中文网:MyBatis中文网
什么是持久层?
- 持久层主要负责将数据保存到数据库的那一层代码。
- JavaEE有三层架构,分别是表现层、业务层、持久层
什么是框架?
- 框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
- 在框架的基础之上构建软件更加高效、规范、通用、可扩展
为什么要使用MyBatis?
使用MyBatils可以简化JDBC开发。
JDBC缺点
因为传统的JDBC开发效率低、操作繁琐,并且有些需要硬编码,JDBC缺点如下:
1、硬编码
- 需要注册驱动,获取连接
- 编写SQL语句
2、操作繁琐
- 手动设置参数
- 手动封装结果集
MyBatis 简化 JDBC
MyBatis 简化 JDBC,通过MyBatis可以将硬编码通过配置文件的方式处理,然后一些操作比较繁琐的代码由MyBatis自动完成,如下图所示:
MyBatis快速入门
需求:通过MyBatis查询user表中的所有数据,并且封装到结果集中
实现步骤:
1、创建user表,并添加数据(通过执行sql语句来完成)
tb_user.sql 文件内容如下:
create database mybatis;
use mybatis;
drop table if exists tb_user;
create table tb_user(
id int primary key auto_increment,
username varchar(20),
password varchar(20),
gender char(1),
addr varchar(30)
);
INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京');
INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津');
INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');
通过navicat 执行以上sql文件,执行结果如下:
2、创建模块,配置MyBatis相关的Mvean依赖
archettype 选择 maven-archetype-quickstart
配置相关依赖
<!-- 添加mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!-- 添加mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 添加slf4j日志api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- 添加logback-classic依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 添加logback-core依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
添加 logback 所需的配置文件 logbacl.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%level] %cyan([%thread]) %boldGreen(%logger{15}) - %msg %n</pattern>
</encoder>
</appender>
<logger name="com.itheima" level="DEBUG" additivity="false">
<appender-ref ref="Console"/>
</logger>
<!--
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
, 默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
-->
<root level="DEBUG">
<appender-ref ref="Console"/>
</root>
</configuration>
3、编写MyBatis核心配置文件 mybatis-config.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.itheima.pojo"/>
</typeAliases>
<!--
environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
-->
<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:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载sql映射文件-->
<mapper resource="com/itheima/mapper/UserMapper.xml"/>
</mappers>
</configuration>
4、编写SQL映射文件。
编写该文件主要是为了统一管理SQL语句,解决硬编码问题
UserMapper.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">
<!--
namespace:名称空间
-->
<mapper namespace="com.itheima.mapper.UserMapper">
<!--statement-->
<!--
resultType="user" 表示返回的结构类型,user表示返回User类对象
-->
<select id="selectAll" resultType="user">
select *
from tb_user;
</select>
<!-- <select id="selectById" resultType="user">
select *
from tb_user where id = #{id};
</select>-->
<select id="select" resultType="user">
select *
from tb_user
where
username = #{arg0}
and password = #{param2}
</select>
</mapper>
UserMapper.class
package com.itheima.mapper;
import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public interface UserMapper {
List<User> selectAll();
@Select("select * from tb_user where id = #{id}")
User selectById(int id);
/*
MyBatis 参数封装:
* 单个参数:
1. POJO类型:直接使用,属性名 和 参数占位符名称 一致
2. Map集合:直接使用,键名 和 参数占位符名称 一致
3. Collection:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
map.put("arg0",collection集合);
map.put("collection",collection集合);
4. List:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
map.put("arg0",list集合);
map.put("collection",list集合);
map.put("list",list集合);
5. Array:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
map.put("arg0",数组);
map.put("array",数组);
6. 其他类型:直接使用
* 多个参数:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
map.put("arg0",参数值1)
map.put("param1",参数值1)
map.put("param2",参数值2)
map.put("agr1",参数值2)
---------------@Param("username")
map.put("username",参数值1)
map.put("param1",参数值1)
map.put("param2",参数值2)
map.put("agr1",参数值2)
*/
User select(@Param("username") String username,String password);
User select(Collection collection);
}
5、定义POJO类
该类对应的就是user表
User.class文件内容如下
package com.itheima.pojo;
// alt + 鼠标左键 整列编辑
public class User {
private Integer id;
private String username;
private String password;
private String gender;
private String addr;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", gender='" + gender + '\'' +
", addr='" + addr + '\'' +
'}';
}
}
6、加载核心配置文件,获取SqlSessionFactory对象
7、获取SqlSession对象,执行SQL语句
8、释放资源
package com.itheima;
import com.itheima.pojo.User;
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;
/**
* Mybatis 快速入门代码
*/
public class MyBatisDemo {
public static void main(String[] args) throws IOException {
//1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 执行sql
List<User> users = sqlSession.selectList("test.selectAll");
System.out.println(users);
//4. 释放资源
sqlSession.close();
}
}
效果图:
示例代码参见:https://download.csdn.net/download/qq_35366269/87610565
Mapper代理开发
Mapper代理是为了让Mybatis开发更简单。
Mapper代理开发的目的主要是解决原声方式中的硬编码,简化后期执行SQL,如下截图:
使用Mapper代理的方式的具体步骤:
1、首先定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下
注意:在resources目录下创建文件夹,例如:com.zhbe.mybatis ,需要用斜杠 / 作为分隔符
2、设置SQL映射文件的namespace属性为Mapper接口的全路径名称
3、在Mapper接口中定义方法,方法名就是SQL映射文件中SQL语句的id,并保持参数类型和返回值类型一致
4、在mybatis-config.xml文件中设置mapper路径
5、然后通过 SqlSession 中的getMapper方法获取 Mapper 接口的代理对象,然后调用自定义的方法完成sql的执行
MyBatis核心配置文件
MyBatis核心配置文件指的就是mybatis-config.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>
<!--
注意:配置各个标签时,需要遵守前后顺序!!!
顺序参见:https://mybatis.org/mybatis-3/zh/configuration.html
-->
<!--
typeAliases 别名
在AreaMapper.xml文件中,getAreaById查询中,resultType(查询返回值)指定的是Area类路径,如果不设置别名,那么类路径为绝对路径(com.itheima.pojo.Area)
如果在此处package标签中设置了name属性为为返回值的父路径(com.itheima.pojo),那么resultType就只需要设置相对路径即可,也就是area
-->
<typeAliases>
<package name="com.itheima.pojo"/>
</typeAliases>
<!--
environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
default="development" 表示默认为开发环境
-->
<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:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</dataSource>
</environment>
<!-- 测试环境相关配置 -->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载sql映射文件-->
<!-- <mapper resource="com/itheima/mapper/UserMapper.xml"/>-->
<!--Mapper代理方式-->
<package name="com.itheima.mapper"/>
</mappers>
</configuration>
官网核心配置文件介绍:mybatis – MyBatis 3 | 配置
environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment,default="development" 表示默认为开发环境,如下图所示:
typeAliases:typeAliases 表示别名。 在AreaMapper.xml文件中,getAreaById查询中,resultType(查询返回值)指定的是Area类路径,如果不设置别名,那么类路径为绝对路径(com.itheima.pojo.Area) 如果在此处package标签中设置了name属性为为返回值的父路径(com.itheima.pojo),那么resultType就只需要设置相对路径即可,也就是Area
注意:配置各个标签时,需要遵守前后顺序!!!
通过配置文件(Mapper.xml文件)完成增删改查
1、查询
1.1、查询所有数据
具体步骤如下图所示:
核心的点在于:
1、需要根据业务的不同分析出SQL语句该怎么写?
2、完成这个功能需不需要参数?
3、完成之后返回什么结果?
例如:操作区域表 area
area表如下:
AreaMapper.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">
<!--
namespace:名称空间
-->
<mapper namespace="com.itheima.mapper.AreaMapper">
<!-- 添加单个区域 -->
<insert id="add">
INSERT INTO tb_area (areaName,areaCode) VALUES (#{areaName},#{areaCode})
</insert>
<!-- 根据id删除区域 -->
<delete id="deleteAreaById">
DELETE FROM tb_area WHERE id = #{id}
</delete>
<!-- 删除所有数据 -->
<delete id="deleteAll">
DELETE FROM tb_area
</delete>
<!-- 修改 -->
<update id="update">
UPDATE tb_area
<set>
<if test="areaName != null and areaName != ''">
areaName = #{areaName},
</if>
<if test="areaCode != null and areaCode != ''">
areaName = #{areaCode}
</if>
</set>
WHERE id = #{id}
</update>
<!-- 查询所有区域数据 -->
<select id="selectAll" resultType="area">
SELECT * FROM tb_area
</select>
<!--
其中 #{id} 表示要传递的数据
-->
<select id="getAreaById" resultType="com.itheima.pojo.Area">
SELECT * FROM tb_area WHERE id = #{id}
</select>
</mapper>
AreaMapper.java文件如下:
package com.itheima.mapper;
import com.itheima.pojo.Area;
import com.itheima.pojo.Brand;
import java.util.List;
/**
* @program: mybatis-demo
* @author: 郭宝
* @create: 2023-04-26 07:37
* @description: 区域maper
**/
public interface AreaMapper {
// 添加单个区域
void add(Area area);
// 根据id删除区域
void deleteAreaById(int id);
// 删除所有数据
void deleteAll();
// 修改
int update(Area area);
// 查询所有区域
List<Area> selectAll();
// 根据id查询区域数据
Area getAreaById(int id);
}
测试代码如下:
package com.itheima.test;
import com.itheima.mapper.AreaMapper;
import com.itheima.pojo.Area;
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;
/**
* @program: mybatis-demo
* @author: 郭宝
* @create: 2023-04-26 07:44
* @description:
**/
public class AreaBatisTest {
/**
* 测试获取所有
* @throws IOException
*/
@Test
public void testSelectAll() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
AreaMapper areaMapper = sqlSession.getMapper(AreaMapper.class);
List<Area> areas = areaMapper.selectAll();
System.out.println(areas);
sqlSession.close();
}
/**
* 测试根据id获取区域
* @throws IOException
*/
@Test
public void testGetAreaById() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
AreaMapper areaMapper = sqlSession.getMapper(AreaMapper.class);
// 根据id获取区域数据
Area area = areaMapper.getAreaById(1);
System.out.println(area);
sqlSession.close();
}
/**
* 测试添加
* @throws IOException
*/
@Test
public void testAdd() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
AreaMapper areaMapper = sqlSession.getMapper(AreaMapper.class);
// 根据id获取区域数据
Area area = new Area();
area.setAreaName("夷陵区");
area.setAreaCode("003");
areaMapper.add(area);
System.out.println(area);
// 提交事务 注意:只有提交了事务才能将数据添加到数据库中
sqlSession.commit();
sqlSession.close();
}
@Test
public void testDeleteAreaById() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
AreaMapper areaMapper = sqlSession.getMapper(AreaMapper.class);
areaMapper.deleteAreaById(1);
// 提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
}
@Test
public void testDeleteAll() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
AreaMapper areaMapper = sqlSession.getMapper(AreaMapper.class);
areaMapper.deleteAll();
// 提交事务
sqlSession.commit();
sqlSession.close();
}
/**
* 测试修改
* @throws IOException
*/
@Test
public void testUpdate() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
AreaMapper areaMapper = sqlSession.getMapper(AreaMapper.class);
Area area = new Area();
area.setId(9);
area.setAreaName("猇亭区");
areaMapper.update(area);
// 提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
}
}
结果映射
在开发过程中,表中设计的字段名称为area_name ,java类中的属性名称为areaName,数据库中表的字段名称和实体类的属性名称不一致,则不能自动封装数据。
方案一:
对SQL语句的查询字段名称设置一个别名,让别名与实体类的属性名一样,也就是通过AS关键字来实现
SELECT
字段名 AS 别名
FROM 数据表;
示例代码如下:
<!--
查询所有区域数据
area_name AS areaName 设置别名。其中area_name为字段名,areaName为别名
-->
<select id="selectAll" resultType="area">
SELECT id, area_name AS areaName,area_code AS areaCode FROM tb_area
</select>
结果图:
通过别名的方式可以解决该问题,但是缺点是每次查询都要定义一次别名,操作繁琐
那么我们可以通过sql片段标签将相同的语句进行抽取封装,来简化代码
<!-- sql 片段 -->
<sql id="area_column">
id, area_name AS areaName,area_code
</sql>
<!--
查询所有区域数据
area_name AS areaName 设置别名。其中area_name为字段名,areaName为别名
-->
<select id="selectAll" resultType="area">
SELECT
<include refid="area_column"></include>
FROM tb_area
</select>
效果图:
方案二:
通过 resultMap 属性和标签可以解决列名不匹配的问题。
select 标签中的resultMap属性是对外部resultMap标签的命名引用。
注意:resultType和resultMap属性之间只能同时使用一个。
AreaMapper.xml 文件
<resultMap id="areaResultMap" type="area">
<id property="id" column="id"></id>
<result property="areaName" column="area_name"></result>
<result property="areaCode" column="area_code"></result>
</resultMap>
<select id="selectAll2" resultMap="areaResultMap">
SELECT * FROM tb_area
</select>
结果图:
1.2、查询-查看详情
参数占位符:
定义:
1、#{ } :会将其替换为 ? ,为了防止SQL注入。
2、${ } :拼SQL,会存在SQL注入问题。
使用场景:
参数传递的时候使用 #{ }
表名或者列名不固定的情况下,使用 ${ } 进行SQL拼接
#{ } 示例代码如下:
<!--
其中 #{id} 表示要传递的数据
-->
<select id="getAreaById" resultType="com.itheima.pojo.Area">
SELECT * FROM tb_area WHERE id = #{id}
</select>
${ } 示例代码如下
<select id="selectAll3" resultMap="areaResultMap">
SELECT ${tableName} FROM tb_area
</select>
parameterType: 用于设置参数类型,该参数可以省略。
SQL语句中特殊字符处理:
例如:WHERE 后面的条件是小于号时,代码就会报错,如下报错代码:
<select id="selectAll4" resultMap="areaResultMap">
SELECT * FROM tb_area WHERE tb_area.area_code < 100
</select>
解决办法:
1、通过转移字符来处理该问题
通过转义字符处理如下代码
<select id="selectAll4" resultMap="areaResultMap">
SELECT * FROM tb_area WHERE tb_area.area_code < 100
</select>
常见转移字符如下:
转义字符 | 符号 | 说明 |
---|---|---|
< ; | < | 小于号 |
> ; | > | 大于号 |
< ;> ; | <> | 不等号 |
2、通过 CDATA区 来处理。输入大写CD,然后回车,然后输入条件
<select id="selectAll4" resultMap="areaResultMap">
SELECT * FROM tb_area WHERE tb_area.area_code
<![CDATA[
这里输入条件,例如:<
]]>
100
</select>
1.3、查询-多条件查询
需求:根据当前状态、企业名称、品牌名称进行条件查询,如下图所示:
分析:
在上述列表条件查询需求中, 首选需要分析出
- 条件表达式是什么?
- 如何连接?是使用AND关键字来进行连接,还是通过OR关键字来进行连接?
具体分析结果如下图所示:
具体编写步骤:
方式一:
参数接收为散装参数,如果方法中有多个参数,需要使用@Param("SQL参数占位符名
1、编写Mapper接口方法,参数为查询条件,结果为List<Brand>称")
/**
* 方式一:
* Param中的 status 对应的就是select语句中的 #{status} 占位符
*/
List<Brand> select(@Param("status") int status,@Param("companyName") String companyName,@Param("brandName") String brandName);
2、编写SQL语句,设置参数占位符
<!--
id:唯一标识
type:映射的类型,支持别名
-->
<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="select" resultMap="brandResultMap">
SELECT * FROM tb_brand WHERE status = #{status} AND company_name LIKE #{companyName} AND brand_name LIKE #{brandName}
</select>
3、执行方法进行测试
@Test
public void testBrandList() throws IOException {
// 模拟查询条件
int status = 0;
String companyName = "松鼠";
String brandName = "松鼠";
// 处理模糊查询
companyName = "%"+companyName+"%";
brandName = "%"+brandName+"%";
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
List<Brand> select = brandMapper.select(status, companyName, brandName);
System.out.println(select.toString());
sqlSession.close();
}
tb_brand表如下图所示:
执行结果如下图所示:
方式二:
参数为对象参数,对象的属性名称要和参数占位符名称一致。
1、编写Mapper接口方法,参数为对象,结果为List<Brand>
/**
* 方式二:
* 如果这三个参数都是Brand类中的属性,那么就可以使用Brand对象传递的方式,#{status} 占位符会自动在Brand对象中查找getStatus()方法,然后设置值
* @param brand
* @return
*/
List<Brand> select(Brand brand);
2、编写SQL语句,设置参数占位符
<!--
id:唯一标识
type:映射的类型,支持别名
-->
<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="select" resultMap="brandResultMap">
SELECT * FROM tb_brand WHERE status = #{status} AND company_name LIKE #{companyName} AND brand_name LIKE #{brandName}
</select>
3、执行方法进行测试
/**
* 通过对象参数来查询
* @throws IOException
*/
@Test
public void testBrandList2() throws IOException {
// 模拟查询条件
int status = 0;
String companyName = "松鼠";
String brandName = "松鼠";
// 处理模糊查询
companyName = "%"+companyName+"%";
brandName = "%"+brandName+"%";
// 将参数封装成对象
Brand brand = new Brand();
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setStatus(status);
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
List<Brand> select = brandMapper.select(brand);
System.out.println(select.toString());
sqlSession.close();
}
执行结果如下图所示:
方式三:
map中键的名称需要和SQL语句中的参数占位符相同。
1、编写Mapper接口方法,参数为对象,结果为List<Brand>
/**
* 方式三:
* 通过map集合参数查询
* @param map
* @return
*/
List<Brand> select(Map map);
2、编写SQL语句,设置参数占位符
<!--
id:唯一标识
type:映射的类型,支持别名
-->
<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="select" resultMap="brandResultMap">
SELECT * FROM tb_brand WHERE status = #{status} AND company_name LIKE #{companyName} AND brand_name LIKE #{brandName}
</select>
3、执行方法测试
/**
* 通过对象参数来查询
* @throws IOException
*/
@Test
public void testBrandList3() throws IOException {
// 模拟查询条件
int status = 0;
String companyName = "松鼠";
String brandName = "松鼠";
// 处理模糊查询
companyName = "%"+companyName+"%";
brandName = "%"+brandName+"%";
// 将参数封装成map
HashMap hashMap = new HashMap();
hashMap.put("status",status);
hashMap.put("companyName",companyName);
hashMap.put("brandName",brandName);
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
List<Brand> select = brandMapper.select(hashMap);
System.out.println(select.toString());
sqlSession.close();
}
运行结果:
总结:
SQL语句设置多个参数有几种方式?
- 散装参数:需要使用@param("SQL中的参数占位符名称")
- 实体类封装参数:只需要保证SQL中的参数名与实体类中的属性名一致即可
- map集合:只需要保证SQL中的参数名与map集合中的键名称一致即可
1.4、查询-多条件-动态条件查询
动态条件查询指的是SQL语句会随着用户的输入或外部条件的变化而变化,我们称之为动态SQL。
在上述条件查询中,会出现只输入品牌名称来进行查询的情况,那么通过固定的SQL语句来进行查询就无法满足需求了,那么这个时候就需要用到动态SQL来进行查询
MyBatis中的提供动态SQL的标签如下:
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
固定SQL代码如下:
<!-- 多条件查询 -->
<select id="select" resultMap="brandResultMap">
SELECT *
FROM tb_brand
WHERE status = #{status}
AND company_name LIKE #{companyName}
AND brand_name LIKE #{brandName}
</select>
动态SQL 代码如下:
<!--
动态条件查询
* if: 条件判断
* test:逻辑表达式
* 如何解决当status未传入该参数,后边出现SQL语法的问题(SELECT * FROM tb_brand WHERE AND company_name LIKE ?),也就是WHERE后面跟的是AND:
1、通过恒等式( where 1 = 1)来过度一下,最终拼接的SQL语句为:SELECT * FROM tb_brand WHERE 1=1 AND company_name LIKE ?
2、通过 <where>标签来替换 where 关键字,在最终拼接SQL语句时,如果出现上述问题,该标签会自动将AND关键字去除,最终得出的SQL语句为SELECT * FROM tb_brand WHERE company_name LIKE ?
推荐方式二
-->
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
/* where 1 = 1*/
<where>
<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>
</where>
</select>
总结:
if 用于判断参数是否有值,使用test属性来进行条件判断,但是存在一个问题,就是第一个条件不需要逻辑运算符,解决方案如下两种:
- 使用恒等式让所有条件格式都一样
- 使用<where>标签替换where关键字
1.5、查询-单条件-动态条件查询
单条件动态查询指的是从多个条件中选择一个,例如根据不同的状态来查询数据
MyBais提供了 choose (when, otherwise) 标签,类似于Java中的switch语句,其中choose就类似于switch关键字,when就类似于case关键字,otherwise就类似于default关键字,示例如下:
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from tb_brand
where
<choose><!--相当于switch-->
<when test="status != null"><!--相当于case-->
status = #{status}
</when>
<when test="companyName != null and companyName != '' "><!--相当于case-->
company_name like #{companyName}
</when>
<when test="brandName != null and brandName != ''"><!--相当于case-->
brand_name like #{brandName}
</when>
<otherwise>
1 = 1
</otherwise>
</choose>
</select>
通过where标签再次优化代码,使用where标签的可以去除相关的语法错误,例如: SELECT * FROM tb_brand WHERE
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from tb_brand
<where>
<choose><!--相当于switch-->
<when test="status != null"><!--相当于case-->
status = #{status}
</when>
<when test="companyName != null and companyName != '' "><!--相当于case-->
company_name like #{companyName}
</when>
<when test="brandName != null and brandName != ''"><!--相当于case-->
brand_name like #{brandName}
</when>
</choose>
</where>
</select>
2、添加
2.1、表单添加
具体步骤:
1、编写Mapper接口方法:
/**
* 添加
*/
void add(Brand brand);
2、编写SQL语句
<!--
添加SQL语句
-->
<insert id="add" >
insert into tb_brand (brand_name, company_name, ordered, description, status)
values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>
3、执行方法测试
注意:SqlSessionFactory中的 openSession()函数默认是开启事务的,进行增删改查操作后需要使用 sqlSession.commit(); 手动提交事务。也可以设置为自动提交事务(关闭事务),单代码为 openSession(true);
@Test
public void testAdd() throws IOException {
//接收参数
int status = 1;
String companyName = "波导手机";
String brandName = "波导";
String description = "手机中的战斗机";
int ordered = 100;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
//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 = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.add(brand);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
2.2、表单添加-主键返回
主键返回指的是在数据添加完成以后,需要获取当前数据的主键id
其实只需要在insert标签中添加如下属性即可
useGeneratedKeys="true" keyProperty="id"
useGeneratedKeys="true" 表示使用生成的秘钥
keyProperty="id" 表示主键的名称
<!--
添加SQL语句
-->
<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>
3、修改
3.1、修改全部字段
具体步骤:
① 编写Mapper接口
/**
* 修改
*/
int update(Brand brand);
② 编写SQL语句
<!-- 更新 -->
<update id="update">
update tb_brand
set brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
description = #{description},
status = #{status}
where id = #{id};
</update>
③ 执行方法进行测试
@Test
public void testUpdate() throws IOException {
//接收参数
int status = 0;
String companyName = "波导手机";
String brandName = "波导";
String description = "波导手机,手机中的战斗机";
int ordered = 200;
int id = 6;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
brand.setId(id);
//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 = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
int count = brandMapper.update(brand);
System.out.println(count);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
3.2、修改动态字段
也就是修改部分字段
① 编写Mapper 接口
/**
* 修改
*/
int update(Brand brand);
② 编写SQL语句
在编写动态SQL语句时,可以使用<set>标签来代替set关键字,防止出现SET后面没有SQL语句,或者SQL语句最后面有一个多余的逗号等等问题
<update id="update">
update tb_brand
<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>
③ 执行方法,进行测试
@Test
public void testUpdate() throws IOException {
//接收参数
int status = 0;
String companyName = "波导手机";
String brandName = "波导";
String description = "波导手机,手机中的战斗机";
int ordered = 200;
int id = 6;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
// brand.setCompanyName(companyName);
// brand.setBrandName(brandName);
// brand.setDescription(description);
// brand.setOrdered(ordered);
brand.setId(id);
//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 = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
int count = brandMapper.update(brand);
System.out.println(count);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
4、删除
4.1、删除一个
具体步骤:
1、编写Mapper接口
/**
* 根据id删除
*/
void deleteById(int id);
2、编写SQL语句
<delete id="deleteById">
delete
from tb_brand
where id = #{id};
</delete>
3、执行测试代码
@Test
public void testDeleteById() throws IOException {
//接收参数
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 = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.deleteById(id);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
4.2、批量删除
具体步骤:
① 编写Mapper接口
/**
* 批量删除
*/
void deleteByIds(int[] ids);
② 编写SQL 语句
<!--
mybatis会将数组参数,封装为一个Map集合。
* 默认:array = 数组
* 使用@Param注解改变map集合的默认key的名称
-->
<delete id="deleteByIds">
delete from tb_brand where id
in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
;
</delete>
<foreach> 标签可以完成数组的遍历
collection="array" 其中collection属性指定的是需要遍历哪个数组/集合。mybatis会将数组参数封装为一个Map集合,该集合是通过key和value来存储的,默认key的名称为array,value就是对应的数组参数。如果想改变默认key的名称,可以使用@Param注解来实现
/**
* 批量删除
*/
void deleteByIds(@Param("ids") int[] ids);
<!--
mybatis会将数组参数,封装为一个Map集合。
* 默认:array = 数组
* 使用@Param注解改变map集合的默认key的名称
-->
<delete id="deleteByIds">
delete from tb_brand where id
in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
;
</delete>
item="id" item属性指定的是遍历出来的每个元素
separator="," 表示设置分隔符为逗号,主要是为拼接SQL,例如:DELETE FROM tb_brand WHERE id IN (1,2,3);
open="(" 表示开始遍历之前添加的数据,主要是为了组拼SQL
close=")" 表示遍历完成以后要添加的数据,主要是为了组拼SQL
③ 执行方法进行测试
@Test
public void testDeleteByIds() throws IOException {
//接收参数
int[] ids = {5,7,8};
//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 = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.deleteByIds(ids);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
MyBatis参数传递
MyBatis 接口方法中可以接收各种各样的参数,MyBatis底层对于这些参数进行不同的封装处理方式:
单个参数:
- POJO类型,直接使用,实体类属性名和参数占位符名称一致
- Map集合,直接使用,键名和参数占位符名称一致
- Collection
- List
- Array
- 其它类型
多个参数:
- 通过@Param注解来传递
建议:将来都使用@Param注解来修改Map集合中默认的键名,并使用修改后的名称来获取值,这样可读性更高!
通过在Mapper类中的函数注解完成增删改查
使用注解开发会比配置文件开发更加方便,例如根据id获取用户数据如下代码所示:
/**
* 根据id获取Brand
* @param id
* @return
*/
@Select("SELECT * FROM tb_brand WHERE id = #{id}}")
Brand getBrandById(int id);
- 查询:@Select
- 添加:@Insert
- 修改:@Update
- 删除:@Detele
通过注解完成简单功能,通过配置文件的方式完成复杂功能
其它:
MyBatisX 插件
MyBatisX是一款基于IDEA的快速开发插件,为效率而生。
主要的功能:
1、XML 和 接口方法 项目跳转
2、根据接口方法生成 statement