一:MyBatis简介
1. MyBatis是一款优秀的持久层框架,用来简化JDBC开发
注:持久层就是将数据保存到数据库的那一层代码
框架就是一套可重用的,通用的,软件基础代码模型
2.MyBatis是Apache的一个开源项目iBatis,2010年由apache software foundation迁移到google codeb并且改名为MyBatis,2013年迁移到了Github
3.官网:https://mybatis.org/mybatis-3/zh/index.html
二:Maven+配置文件 开发(MyBatis快速入门)
一起编写一个入门案例来感受MyBatis这个持久层框架吧~
案例:查询tb_user表中所有的数据
操作步骤: 1.数据库表(tb_user)建表语句如下
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', '男', '西安');
2.创建Maven模块,导入如下依赖
<dependencies>
<!--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.46</version>
</dependency>
</dependencies>
3.根据实际业务编写实体类,本案例中的实体类User如下
public class User {
private Integer id;
private String username;
private String password;
private String gender;
private String addr;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", gender='" + gender + '\'' +
", addr='" + addr + '\'' +
'}';
}
//变量的get/set方法省略,自己利用构造器生成
}
4. 在com.example.mapper目录下定义Mapper接口UserMapper
public interface UserMapper {
List<User> selectAll();
}
注意:-1-在Mapper接口中定义方法,方法名就是SQL映射文件的sql语句的id, 方法返回类型就是SQL映射文件的sql语句的resultType,两者要一一对应
-2-默认在mapper接口中编写SQL语句是不识别的。如果想要配置识别可以在show context actions中选择参考语言为MySQL
-3-在mapper接口中编写SQL语句是不识别表信息的原因是Idea没有与数据库建立连接,可以在右侧Database面板中配置Mysql数据库连接
5. 在resources目录下编写与Mapper接口同名的SQL映射文件UserMapper.xml用来统一管理sql语句,解决硬编码问题
<?xml version="1.0" encoding="UTF-8" ?>
<!--xml映射文件中的dtd约束,直接从mybatis官网复制即可-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespace:名称空间, 设置SQL映射文件的namespace属性为Mapper接口全限定名称
-->
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectAll" resultType="com.example.domain.User">
select * from tb_user;
</select>
</mapper>
注意:要创建与mapper接口同名的包.例如,com/example/mapper
6.在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="com.example.domain"/>
</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://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</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://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载sql映射文件-->
<mapper resource="com/example/mapper/UserMapper.xml"/>
<!--Mapper代理方式-->
<package name="com.example.mapper.UserMapper"/>
</mappers>
</configuration>
7.编写启动类,加载MyBstis配置文件,获取SqlSessionFactory对象,执行sql语句,完成业务功能
public class MyBatisQuickstart {
public static void main(String[] args) throws IOException {
//1.加载MyBatis的核心配置文件,利用SqlSessionFactoryBuilder获取SqlSessionFactory对象
String resource ="mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.利用SqlSessionFactory获取SqlSession对象.用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.执行sql
List<User> users = sqlSession.selectList("selectAll");
System.out.println(users);
//4.释放资源
sqlSession.close();
}
}
到这里的话,利用配置文件进行查询所有的功能就实现了
其实这个案例还可以使用SpringBoot环境实现,开发起来会更加便捷,下面一起来看一看使用SpringBoot完成查询所有的功能吧
三:SpringBoot+注解 开发(MyBatis快速入门)
使用注解开发入门案例就不用编写MyBatis配置类mybatis-config.xml了而是使用properties文件配置数据库连接信息即可,会更加方便
操作步骤如下:
1.数据库表(tb_user)建表语句如下
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', '男', '西安');
2.创建spring项目,选择MyBatis和MySql驱动依赖
3. 编写application.yml文件,配置数据库连接信息以及端口
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306//mybatis?serverTimezone=UTC
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
server:
port: 80
4.根据实际业务编写实体类,本案例中的实体类User如下
public class User {
private Integer id;
private String username;
private String password;
private String gender;
private String addr;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", gender='" + gender + '\'' +
", addr='" + addr + '\'' +
'}';
}
//变量的get/set方法省略,自己利用构造器生成
}
5. 在com.example.mapper目录下定义Mapper接口UserMapper并编写sql语句
@Mapper
public interface UserMapper {
@Select("select * from tb_user")
List<User> selectAll();
}
6. 在src下的test目录下编写测试测试代码进行测试
@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectAll() {
List<User> users = userMapper.selectAll();
for (User user : users) {
System.out.println(user);
}
}
}
到这里的话,使用springboot编写 MyBatis入门案例就搞定了,跟用xml配置文件相比还是简单很多的吧~这种方式也是我们以后项目首选的方式
四:Mapper代理开发
1.使用Mapper代理开发的目的:
1.1解决原生方式中的硬代码
List<User> users = sqlSession.selectList("selectAll");
替换为获取mapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
1.2简化后期执行SQL
2.使用Mapper代理开发的步骤:
2.1.创建数据库,表
2.2.创建Maven模块
2.3.引入mybatis,mysql依赖
2.4.定义实体类
2. 5.定义mapper接口
2.6.在resources目录下定义与接口同名同包的SQL映射文件
2.7.定义mybatis-config配置文件
如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化SQL映射文件的加载---在配置文件<mappers>中开启包扫描
<?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.example.domain"/>
</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://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</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://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--
加载sql映射文件
<mapper resource="com/example/mapper/UserMapper.xml"/>
Mapper代理方式
<package name="com.example.mapper.UserMapper"/>
-->
<!--开启包扫描 -->
<package name="com.example.mapper.UserMapper"/>
</mappers>
</configuration>
8.启动类编码
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("selectAll");
//3.1获取UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
System.out.println(users);
//4. 释放资源
sqlSession.close();
}
}
五:MyBatis核心配置文件
MyBatis配置文件的configuratio标签的结构如下(编写时需遵循以下顺序)
例子见入门案例中的配置文件
六:SpringBoot+映射配置文件实现增删改查
public interface BrandMapper {
/**
* 查询所有
*/
List<Brand> selectAll();
/**
* 查看详情:根据Id查询
*/
Brand selectById(int id);
/**
* 条件查询
* * 参数接收
* 1. 散装参数:如果方法中有多个参数,需要使用@Param("SQL参数占位符名称")
* 2. 对象参数:对象的属性名称要和参数占位符名称一致
* 3. map集合参数
*
*/
//List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName);
//List<Brand> selectByCondition(Brand brand);
List<Brand> selectByCondition(Map map);
/**
* 单条件动态查询
* @param brand
* @return
*/
List<Brand> selectByConditionSingle(Brand brand);
/**
* 添加
*/
void add(Brand brand);
/**
* 修改
*/
int update(Brand brand);
/**
* 根据id删除
*/
void deleteById(int id);
/**
* 批量删除
*/
void deleteByIds(int[] ids);
}
<?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.BrandMapper">
<!--
数据库表的字段名称和实体类的属性名称 不一样,则不能自动封装数据的两种解决方式:
* 1.起别名:对不一样的列名起别名,让别名和实体类的属性名一样
* 缺点:每次查询都要定义一次别
* sql片段
* 缺点:不灵活
* 2.resultMap:
1. 定义<resultMap>标签
2. 在<select>标签中,使用resultMap属性替换 resultType属性
-->
<!--
resultMap
id:唯一标识
type:映射的类型,支持别名
-->
<resultMap id="brandResultMap" type="brand">
<!--
id:完成主键字段的映射 column:表的列名 property:实体类的属性名
<id column="SId" property="id"/>
result:完成一般字段的映射 column:表的列名 property:实体类的属性名
<result column="brand_name" property="brandName"/>
-->
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select *
from tb_brand;
</select>
<!--
sql片段
-->
<!--
<sql id="brand_column">
id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>
<select id="selectAll" resultType="brand">
select
<include refid="brand_column" />
from tb_brand;
</select>
-->
<!--<select id="selectAll" resultType="brand">
select *
from tb_brand;
</select>-->
<!--
* 参数占位符:
1. #{}:会将其替换为 ?,为了防止SQL注入
2. ${}:拼sql。会存在SQL注入问题
3. 使用时机:
* 参数传递的时候:#{}
* 表名或者列名不固定的情况下:${} 会存在SQL注入问题
* 参数类型:parameterType:可以省略
* 特殊字符处理:
1. 转义字符:
2. 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>
<!--
条件查询
-->
<!-- <select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>-->
<!--
动态条件查询
* if: 条件判断
* test:逻辑表达式
* 问题:
* 恒等式
* <where> 替换 where 关键字
-->
<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>
<!--<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>-->
<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>
<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>
<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 id="deleteById">
delete from tb_brand where id = #{id};
</delete>
<!--
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>
</mapper>
七:SpringBoot+注解实现增删改查
使用注解就是在mapper接口上添加注解,在注解上编写简单sql语句,这样的话就不用编写xml映射文件中对应的statement
Mybatis 针对 CURD 操作都提供了对应的注解 :
添加 :@Insert
删除 :@Delete
修改 :@Update
查询 :@Select
准备工作就不再做了(键数据库建表建Springboot项目建实体类编写controller,service,配置文件),这里只展示dao层
@Mapper
public interface BookDao {
@Insert("insert into tbl_book(type,name,description) values(#{type},#{name},#{description}) ")
public int save(Book book);
@Update("update tbl_book set type=#{type},name=#{name},description=#{description} where id = #{id}")
public int update(Book book);
@Delete("delete from tbl_book where id=#{id}")
public int delete(Integer id);
@Select("select * from tbl_book where id=#{id}")
public Book getById(Integer id);
@Select("select * from tbl_book")
public List<Book> getAll();
}
八:动态sql
1.什么是动态Sql
SQL语句会随着用户的输入或外部条件的变化而变化
在Mybatis中提供了很多实现动态SQL的标签,我们学习Mybatis中的动态SQL就是掌握这些动态SQL标签
2.<if> & <where> &<choose>& <when>&<set>
<if test="条件表达式">
要拼接的sql语句
</if>
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
<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>
<choose>:用于判断条件是否成立, 相当于switch
<when>:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL, 相当于case
choose标签和when标签一起用的话,就不用加and连接了
<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>
<set>:动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)
<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>
3.<foreach> 遍历方法中传递过来的参数集合
<delete id="deleteByIds">
delete from tb_brand where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
collection:集合名称 item:集合遍历出来的元素/项 separator:每一次遍历使用的分隔符
4.<sql> & <include>
<sql id="commonSelect">
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
</sql>
<include>:通过属性refid,引用指定包含的sql片段。
<select id="list" resultType="com.itheima.pojo.Emp">
<include refid="commonSelect"/>
<where>
<if test="name != null">
name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
5.<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 *
from tb_brand;
</select>