MyBatis
什么是mybatis?
- mybatis是一款优秀的持久层框架用于简化JDBC开发
- 官网:https://mybatis.org/mybatis-3/zh/index.html
持久层
- 负责将数据保存到数据库的那一层代码
- javaEE三层架构:表现层,业务层,持久层
框架
- 框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
- 在框架的基础之上构建软件编写更加高效,规范,通用,可扩展
JDBC的缺点
- 硬编码
- 注册驱动,获取连接
- SQL语句
- 操作繁琐
- 手动设置参数
- 手动封装结果集
MyBatis快速入门
mybatis实现流程
1.创建tb_user表,添加数据
此处使用Navicat for MySQL
tb_user表建立在mybatis数据库下,建好的表如下:
2.创建模块,在pom.xml里的导入jar包
<dependency>
<!--junit 单元测试-->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--mybatis 依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
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>
<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="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载sql映射文件-->
<mapper resource="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"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespeace:命名空间
-->
<mapper namespace="test">
<select id="selectAll" resultType="com.itheima.pojo.User">
select * from tb_user;
</select>
</mapper>
5.编码
User类
package com.itheima.pojo;
public class User {
private Integer id;
private String username;
private String pward;
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 getPward() {
return pward;
}
public void setPward(String pward) {
this.pward = pward;
}
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 + '\'' +
", pward='" + pward + '\'' +
", gender='" + gender + '\'' +
", addr='" + addr + '\'' +
'}';
}
}
MyBatisDemo类
package com.itheima.pojo;
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 {
//快捷键psvm生成main方法
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();
}
}
解决SQL语句警告提示
idel连接数据库
-
打开idea数据库
-
添加数据源
-
添加MySql
-
连接数据库
点击测试连接,绿勾则连接成功
Mapper代理开发
Mapper代理开发规则
- 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录文件下
- 设置SQL映射文件的namespace属性为Mapper接口全限定名
- 在Mapper接口中定义方法,方法名就是SQL映射文件中SQL语句的id,并保持参数类型和返回值类型一致
步骤一
创建如图中的包结构结构,在com.itheima包下新建一个mapper包,在mapper包里建立一个与映射文件同名的接口,即UserMapper
可以在项目->右键->运行maven->complie,进行编译,点击编译后的路径,看两个同名的文件是否在同一路径下
步骤二
在映射文件UserMapper.xml里把namespace值改为接口路径,如上例中:
namespeace=“com.itheima.mapper.UserMapper”
步骤三
在接口中创建与映射文件中id同名的方法,例如:
package com.itheima.mapper;
import com.itheima.pojo.User;
import java.util.List;
public interface UserMapper {
// 查询单个:User selectAll();
List<User> selectAll();
}
步骤四
在MyBatisDemo类中,
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
替换了
List<User> users = sqlSession.selectList("test.selectAll");
源码如下:
package com.itheima.pojo;
import com.itheima.mapper.UserMapper;
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 MyBatisDemo2 {
//快捷键psvm生成main方法
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");
// 3.1 获取UserMapper接口的代理对象,即mapper代理
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
System.out.println(users);
// 4. 释放资源
sqlSession.close();
}
}
Mapper代理
当映射文件过多时,处理也比较麻烦,采用Mapper代理,直接导入mapper包中的所有Mapper文件
<mappers>
<!--加载sql映射文件-->
<!-- <mapper resource="com/itheima/mapper/UserMapper.xml"/>-->
<!--Mapper 代理-->
<package name="com.itheima.mapper"/>
</mappers>
MyBatis核心配置文件
MyBatis配置文件包含了会深深影响MyBatis的设置和属性信息,配置文件的顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mapper(映射器)
- environments: 配置数据库连接环境信息,可以配置多个environment,通过default属性切换不同的environment
- 类型别名
<typeAliases> <package name="com.itheima.pojo"/> </typeAliases>
配置文件完成增删改查
MyBatisX插件用于快速开发MyBatis
查询
查询所有数据
-
接口方法
List<Brand>selectAll();
-
SQL映射文件
<select id="selectAll" resultMap="brandResultMap"> select * from tb_brand; </select>
-
测试类
@Test public void testSelectAll() throws IOException { //获取对应的SqlsessionFectory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); // 执行方法 List<Brand> brands = brandMapper.selectAll(); System.out.println(brands); // 释放资源 sqlSession.close(); }
数据库表的字段名 和 实体类的属性名不一样,则不能自动封装数据
-
起别名:对不一样的列名起别名,让别名和实体类的属性名一样
<select id="selectAll" resultType="brand"> # 起别名 select id, brand_name as brandName, company_name as companyName, ordered, description, stat from tb_brand; </select>
- 缺点:每次查询都要定义一次别名
-
sql片段
<sql id="brand_colum"> id, brand_name as brandName, company_name as companyName, ordered, description, stat </sql> <select id="selectAll" resultType="brand"> select # 插入SQL片段 <include refid="brand_colum"></include> from tb_brand; </select>
- 缺点:不灵活
-
resultMap(用这个就对了!)
<resultMap id="brandResultMap" type="brand"> <!-- id : 完成主键字段的映射 cloum : 表的列名 property : 表的实体类名 result : 完成一般字段的映射 --> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap>
- 定义<resultMap>标签 - 在<select>标签中,使用resultMap标签替换resultType属性
-
查看详情
-
接口方法
``` Brand selectById(int id); ```
-
映射文件
``` <select id="selectById" resultMap="brandResultMap"> select * from tb_brand where id = #{id}; </select> ```
-
测试类
``` @Test public void testSelectById() throws IOException { //获取参数 int id = 1; //获取对应的SqlsessionFectory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); // 执行方法 Brand brand = brandMapper.selectById(id); System.out.println(brand); // 释放资源 sqlSession.close(); } ```
条件查询
-
接口方法
SQL语句设置多个参数的三种方法:
<!-- 参数占位符:#{}:会将其替换为?,为了防止sql注入 ${}: 拼sql,会存在sql注入问题 使用时机: - 参数传递的时候:#{} - 表名或列名不限定的情况下:${},会存在sql注入问题 *特殊字符的处理 1.转义字符:< : < 2.CDATA区:把特殊字符放入CDATA区 --> /** * 条件查询 * * 参数接收 * 1.散装参数,如果方法中有多个参数,需要使用@param("sql语句参数占位符") * 2.对象参数,对象的属性名称要和参数占位符名称一致 * 3.map集合参数,SQL中的参数名和map集合的键的名称对应上 * */ <!-- 散装参数 --> List<Brand> selectByCondition(@Param("stat") int stat, @Param("companyName") String companyName, @Param("brandName") String brandName);
List<Brand> selectByCondition(Brand,brand);
List<Brand> selectByCondition(Map,map);
-
映射文件
<!-- 条件查询--> <select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where stat = #{stat} and company_name like #{companyName} and brand_name like #{brandName} </select>
-
测试类
- 散装测试类
@Test public void testSelectByCondition() throws IOException { //获取参数 int stat =1; String companyName="华为"; String brandName="华为"; //参数处理,模糊化处理 companyName = "%"+companyName+"%"; brandName = "%"+brandName+"%"; //获取对应的SqlsessionFectory String resource = "mybatis-config.xml";1 InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(true); // 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); // 执行方法 List<Brand> brands = brandMapper.selectByCondition(stat,companyName,brandName); System.out.println(brands); // 释放资源 sqlSession.close();
- 对象测试类
<!-- 在散装测试类的“处理参数”下面加上“封装对象”操作 --> //封装对象 Brand brand = new Brand(); brand.setStat(stat); brand.setCompanyName(companyName); brand.setBrandName(brandName); //执行方法改为 List<Brand> brands = brandMapper.selectCondition(brand)
- Map测试类
<!-- 封装对象改为如下 --> Map map = new HashMap(); map.put("stat",stat); map.put("companyName",companyName); map.put("brandName",brandName); //执行方法 List<Brand> brands = brandMapper.selectByCondition(map);
动态条件查询–动态SQL
用户不一定会填写所有查询条件,当用户没有填写所有查询条件时,上述条件查询就会出错,用动态查询来解决这样的问题
1. if:条件判断
- test:逻辑表达式
- 问题:当情况变化时,SQL可能会出现错误,where后加入恒等式1=1可解决
1. mybatis提供的where标签,可以自动生成关键字,解决SQL语法问题
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
<where>
<if test="stat != null">
and stat = #{stat}
</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>
单条件的动态查询
- 接口方法
//单条件动态查询 List<Brand> selectByConditionSingle(Brand brand);
- 映射文件
<select id="selectByConditionSingle" resultMap="brandResultMap"> select * from tb_brand <where> <choose> <!--相当于switch--> <when test="stat != null"><!--相当于case--> stat = #{stat} </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>
- 测试类
@Test public void testSelectByConditionSingle() throws IOException { //获取参数 int stat =1; String companyName="华为"; String brandName="华为"; //参数处理 companyName = "%"+companyName+"%"; brandName = "%"+brandName+"%"; //封装对象 Brand brand = new Brand(); brand.setStat(stat); // brand.setCompanyName(companyName); // brand.setBrandName(brandName); //获取对应的SqlsessionFectory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); // 执行方法 List<Brand> brands=brandMapper.selectByConditionSingle(brand); System.out.println(brands); // 释放资源 sqlSession.close(); }
添加
简单添加
- 接口方法
//简单添加 void add(Brand brand);
- 映射文件
<!--简单添加--> <insert id="add"> insert into tb_brand(id, brand_name, company_name, ordered, description, stat) values (#{id},#{brandName},#{companyName},#{ordered},#{description},#{stat}); </insert>
- 测试类
@Test public void testAdd() throws IOException { //获取参数 int stat =1; String companyName="波导"; String brandName="波导战斗机"; String description="波导手机,手机中的战斗机"; int ordered=100; //封装对象 Brand brand = new Brand(); brand.setStat(stat); brand.setCompanyName(companyName); brand.setBrandName(brandName); brand.setDescription(description); brand.setOrdered(ordered); //获取对应的SqlsessionFectory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(true); // 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); // 执行方法 brandMapper.add(brand); // 释放资源 sqlSession.close(); }
主键返回
<insert id="add" useGeneratedKeys="true" keyProperty="id">
修改
修改全部字段
- 接口方法
//修改功能 int update(Brand brand);
- 映射文件
<!-- 修改--> <update id="update"> update tb_brand set brand_name = #{brandName}, company_name = #{companyName}, ordered = #{ordered}, description = #{description}, stat = #{stat} where id = #{id}; </update>
- 测试类
@Test public void testUpdate() throws IOException { //获取参数 int stat =1; String companyName="波导"; String brandName="波导战斗机"; String description="波导手机,手机中的战斗机"; int ordered=999; int id =2; //封装对象 Brand brand = new Brand(); brand.setStat(stat); brand.setCompanyName(companyName); brand.setBrandName(brandName); brand.setDescription(description); brand.setOrdered(ordered); brand.setId(id); //获取对应的SqlsessionFectory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(true); // 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); // 执行方法 int count = brandMapper.update(brand); System.out.println(count); // 释放资源 sqlSession.close(); }
修改动态字段
<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="stat != null ">
stat = #{stat}
</if>
</set>
where id = #{id};
</update>
删除
删除一个
- 接口方法
//根据id删除 void deleteById(int id);
- 映射文件
<!--删除--> <delete id="deleteById"> delete from tb_brand where id = #{id}; </delete>
- 测试类
@Test public void testDeleteById() throws IOException { //获取参数 int id =4; //获取对应的SqlsessionFectory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(true); // 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); // 执行方法 brandMapper.deleteById(id); // 释放资源 sqlSession.close(); }
批量删除
- 接口方法
//批量删除 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>
- 测试类
@Test public void testDeleteByIds() throws IOException { //获取参数 int[] ids={1,2}; //获取对应的SqlsessionFectory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(true); // 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); // 执行方法 brandMapper.deleteByIds(ids); // 释放资源 sqlSession.close(); }
参数传递
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("arg1",参数值2)
map.put("param2",参数值2)
建议将来都使用@param注解来修改Map集合中默认的键名,并使用修改后的名称来获取值,这样的可读性更高!
注解完成增删改查
对于简单的增删改查语句,注解更简单快捷,但面对复杂的语句,配置文件更方便
在方法上面加上对应的注解
@Select("select * from tb_brand where id=#{id}")
public User selectById(int id);
// 释放资源
sqlSession.close(); }
参数传递
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("arg1",参数值2)
map.put("param2",参数值2)
建议将来都使用@param注解来修改Map集合中默认的键名,并使用修改后的名称来获取值,这样的可读性更高!
# 注解完成增删改查
对于简单的增删改查语句,注解更简单快捷,但面对复杂的语句,配置文件更方便
在方法上面加上对应的注解
@Select("select * from tb_brand where id=#{id}")
public User selectById(int id);
此文章为笔者笔记,原视频链接
https://www.bilibili.com/video/BV1MT4y1k7wZ?p=1&vd_source=4d53dce2c75fc63ae64c5ac9efc9dc76