一、简介
MyBatis是一款优秀的持久层框架,用于简化JDBC开发。
- 持久层:负责将数据保存到数据库的那一层代码。(与数据库相关的都叫持久层)
- 框架:就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型。在框架的基础上编写软件更加高效、规范、通用、可扩展。
- JavaEE三层架构:表现层、业务层、持久层。
(一)JDBC使用缺点
- 硬编码:有字符串的地方都叫硬编码,硬编码太多修改麻烦。
- 操作繁琐:手动设置SQL等参数,JDBC使用数据库步骤太多。
解决办法:硬编码写到配置文件;操作繁琐利用自动完成
MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作
二、快速入门
- 创建Maven项目,在pom导入MyBatis的jar包(MyBatis中文网),坐标内容如下:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
另外附上格式化log日志格式的工具的坐标
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</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>
<!-- slf4j日志API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
- 在resources下新建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="cn.fn.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://127.0.0.1:3306/db1?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="000000"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载SQL映射文件,填写路径,映射文件在同目录下-->
<!-- <mapper resource="cn/fn/mapper"/>-->
<package name="cn.fn.mapper"/>
</mappers>
</configuration>
- 新建SQL映射文件
XXXMapper.xml
,约定是哪一个表就用表名代替XXX
来命名,如User表就是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:接口的全称
id:用于标识这一段的SQL语句
resultType:返回结果的类型,如查询结果返回String类型,就填String
-->
<mapper namespace="test">
<select id="selectAll" resultType="cn.fn.pojo.User">
select * from tb_user;
</select>
</mapper>
- 开始编码,获取
SqlSessionFactory
对象,用于加载核心配置文件。 - 使用
SqlSessionFactory
对象获取SqlSession
对象,SqlSession
对象用于执行SQL语句。 - 使用
SqlSession
对象执行SQL语句。 - 释放资源。
以上四条条示例如下
//1.加载核心配置文件
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语句,参数为namespace名.id名
List<User> users = sqlSession.selectList("test.selectAll");
System.out.println(users);
//4.释放资源
sqlSession.close();
最后整体结构如下:
logback.xml
内容如下
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%level] %blue(%d{HH:mm:ss.SSS}) %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>
附加
使用IDEA连接MySQL数据库
maper.xml没有提示解决办法,SQL Dialect改为MySQL
MyBatisX插件,主要用于XML和接口方法相互跳转;以及根据接口方法生成statement
三、Mapper代理开发(重点)
- 目的
- 解决原生方式中的硬编码
- 简化后期执行SQL
- 定义与SQL映射文件同名的Mapper接口,并将Mapper接口和SQL映射文件放在同一目录下
步骤:
A. 在java
目录下新建一个包和接口,接口名要和SQL映射文件同名,如cn.fn.mapper.UserMapper
。
B. 在resources目录右键New Directory
,name为cn/fn/mapper
(用/
分隔表示,后期Maven编译项目时候会将此目录下的文件编译到接口同目录下) - 设置SQL映射文件的
namespace
为Mapper接口的全限定名(即包名.接口名),如cn.fn.mapper.UserMapper
- 在Mapper接口中定义方法,方法命名规则:
A. 返回值为SQL映射文件查询后的返回值类型
B. 方法名为映射文件的id。 - 编写代码:
//3.执行sql语句
// List<User> users = sqlSession.selectList("test.selectAll"); //原 SQL执行语句
UserMapper mapper = sqlSession.getMapper(UserMapper.class); //新 Mapper代理执行SQL语句
List<User> users = mapper.selectAll();
- MyBatis核心配置文件修改
<mappers>
<!--加载SQL映射文件-->
<!--1.这种方式只指定一个映射文件 -->
<!-- <mapper resource="cn.fn.mapper/UserMapper.xml"/>-->
<!--2.这种方式指定此路径下的所有映射文件,路径名就是映射文件上面的包名 -->
<package name="cn.fn.mapper"/>
</mappers>
四、核心配置文件
别名:在<configuration>
的起始标签下添加
<!--别名-->
<typeAliases>
<package name="cn.fn.pojo"/>
</typeAliases>
那么SQL映射文件的resultType
值就可以只指定类名就行
<!--原resultType值:cn.fn.pojo.User(严格使用大小写)-->
<!--别名后resultType值:user(大小写都可)-->
<mapper namespace="cn.fn.mapper.UserMapper">
<select id="selectAll" resultType="user">
select * from tb_user;
</select>
</mapper>
五、配置文件完成增删改查
(一)查询(select)
实体类成员变量名和数据库表的列明不一致时,MyBatis不能自动封装数据,需要使用<resultMap>
标签手动调整,(在SQL映射文件中)如下:
(1)查询单条数据(占位符#{}、特殊字符处理)
- 参数占位符:
#{}
:防止SQL注入,会将SQL语句传参部分替换为?
${}
:拼接SQL语句,不能防止SQL注入
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id = #{id}
</select>
- 特殊字符处理(两种处理方式):
- 转义字符(以<为例):
<
- CDATA区:在需要处理的地方敲
CD
生成,然后将小于号写到里面。
- 转义字符(以<为例):
<select id="selectById" resultMap="brandResultMap">
select *
from tb_brand where id < #{id} <!--1.转义字符的方式-->
select *
from tb_brand where id <![CDATA[
<
]]> #{id} <!--1.CDATA区的方式-->
</select>
(2)条件查询
多条件查询
注:select的查询语句格式照写,变的是Mapper接口里的方法和执行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>
- Mapper接口(三种方式)
A. 方式1@Param(参数)
里的参数必须要和select
语句的查询条件一一对应(如#{status}
对应就必须写@Param("status")
)
B. 方式2参数接收brand对象
C. 方式3参数接收map集合
//BrandMapper.java接口
public interface BrandMapper {
//多条件查询一条数据 方式1,散装参数,@Param参数名称要和SQL语句占位符保持一致
List<Brand> selectByCondition(@Param("status") int status,
@Param("companyName") String companyName,
@Param("brandName") String brandName);
//多条件查询一条数据 方式2,对象参数
List<Brand> selectByCondition(Brand brand);
//多条件查询一条数据 方式3,map集合参数
List<Brand> selectByCondition(Map map);
}
- 测试用例,编码传参
注意:使用Map集合时,键的值必须和#{}内容一一对应。
//MyBatisTest.java测试用例
//接受参数
int status = 1;
String companyName = "谷歌";
String brandName = "谷歌";
//处理参数
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//方式1,散装参数
// List<Brand> brands = mapper.selectByCondition(status, companyName, brandName);
//方式2,对象参数,先new一个对象,传入需要查询的参数
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
// List<Brand> brands = mapper.selectByCondition(brand);
//方式3,map集合参数,先new一个集合,传入需要查询的参数键值对,其中键值必须和select中#{}里的内容一一对应
Map<Object, Object> hashMap = new HashMap<>();
hashMap.put("status", status);
hashMap.put("companyName", companyName);
hashMap.put("brandName", brandName);
List<Brand> brands = mapper.selectByCondition(hashMap);
动态条件查询
动态多条件查询
SQL语句随用户的输入或外部条件的变化而变化,称为动态SQL。(只更改select所在xml文件)
<where>SQL的WHERE语句后面内容</where>
(where标签替换原来的where,作用是自动识别and和or等运算符)<if test="条件判断">SQL语句</if>
<!-- 多条件查询之动态SQL-->
<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>
动态单条件查询
解决如下问题
注:全部在where后面写
<choose></choose>
标签相当于swith。
<when test="条件判断"></when>
标签相当于case。
<select id="selectByChoose" resultMap="brandResultMap">
select * from tb_brand
<where>
<choose>
<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>
</choose>
</where>
</select>
(二)添加(insert)
- SQL映射文件,使用添加数据标签
<insert id=""></insert>
<insert id="add">
insert into tb_brand(brand_name, company_name, ordered, description, status) values
(#{brandName},#{companyName},#{ordered},#{ordered},#{status})
</insert>
- Mapper接口文件,返回值为void(通过是否异常来判断有没有添加成功)
void add(Brand brand);
- 测试用例,可以传入一个实体类对象添加数据,特别注意:MyBatis默认关闭了提交事务,所以需要使用SqlSession的commit()方法手动提交事务
sqlSession.commit()
。或者在SqlSession sqlSession = sqlSessionFactory.openSession(true);
的时候传入true
参数,表示开启自动提交事务
@Test
public void testAdd() throws IOException {
//1.获取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语句
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
//接受参数
int status = 1;
String companyName = "波导手机";
String brandName = "波导";
int ordered = 60;
String description = "手机中的战斗机";
//方式2,对象参数,先new一个对象,传入需要查询的参数
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setOrdered(ordered);
brand.setDescription(description);
mapper.add(brand);
sqlSession.commit();
sqlSession.close();
}
主键返回
需求:在数据添加成功后,需要获取插入数据库数据的主键值。
- 在SQL映射文件中添加两个属性即可
<insert id="add" useGeneratedKeys="true" keyProperty="id">
省略...
</insert>
- 测试用例,在添加数据后,提交事务前获取ID
//方式2,对象参数,先new一个对象,传入需要查询的参数
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setOrdered(ordered);
brand.setDescription(description);
mapper.add(brand);
//下面是在提交事务前获取ID
Integer id = brand.getId();
System.out.println(id);
sqlSession.commit();
sqlSession.close();
(三)修改(update)
(1)修改全部字段(需要提交事务)
(2)修改动态字段(动态SQL)
使用场景:不修改一行里所有的数据,只修改一部分,动态去判断。
使用<set></set>
和<if test=""></if>
标签实现
<!-- 修改动态字段就是加入set和if标签 -->
<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>
(四)删除(delete)
批量删除
使用<foreach>
标签批量删除(在in里面使用)
接口里:
//void deleteByids(int[] ids);
//@Param名称要和SQL占位符名称保持一致
void deleteByids(@Param("ids") int[] ids);
xml里:
<!-- 批量删除 -->
<delete id="deleteByids">
delete from tb_brand
where id in (
<foreach collection="ids" item="id" separator=",">
<!-- 不起别名就是<foreach collection="array" item="id" separator=","> -->
#{id}
</foreach>
)
</delete>
六、MyBatis参数封装
-
单个参数
- POJO类型(实体类):直接传入,属性名和参数占位符名称一致即可(这样里面就可以装多个参数,然后再整体作为一个对象传入)
- Map集合:直接传入,键名和参数占位符名称一致
- Collection:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
- List:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
- Array:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名(如上面那个例子,传入的是int数组,就要起别名)
- 其他类型:直接使用
-
多个参数:会封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
- 默认:map.put(“arg0”,参数值1)
- map.put(“param1”,参数值1)
- map.put(“arg1”,参数值2)
- map.put(“param2”,参数值2)
- --------------------@Param(“username”)替换后
- map.put(“username”,参数值1)
- map.put(“param1”,参数值1)
- map.put(“arg1”,参数值2)
- map.put(“param2”,参数值2)
七、使用注解完成增删改查
注意:注解只适合完成简单功能,复杂功能还是要用xml配置文件完成