什么是Mybatis?
- Mybatis是一个简化JDBC的持久层框架,MyBatis是一个半自动化框架,是因为它在SQL执行过程中只提供了基本的SQL执行功能,而没有像Hibernate那样将所有的ORM操作都自动化了。在MyBatis中,需要手动编写SQL语句,但是它提供了很多便捷的操作,例如参数映射、结果集映射、缓存机制等等,使得开发者能够更加灵活地操作SQL。此外,MyBatis还提供了许多插件机制,可以对SQL执行流程进行拦截和修改,以满足各种不同的需求。这也是MyBatis相对于其他ORM框架的一个优点。
- 持久层:负责将数据保存到数据库的那一层代码
- JavaEE三层架构:表现层,业务层,持久层
- 框架:写好部分代码的半成品软件
JDBC的缺点:
- 硬编码:
- 注册驱动,获取连接的那部分代码不灵活,改变的时候会很麻烦
- 写死的SQL语句没有那么灵活
- 操作繁琐
- 需要手动设置参数(prepareStatement)
- 手动封装结果集(resultSet)
Mybatis的优点
- 硬编码–》利用**配置文件(xml)**可以减少硬编码的编写
- 操作繁琐–》底层自动完成参数的设置以及结果集的封装
- Mybatis 免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作
Mybatis快速入门(mybatis-demo)
- 非maven项目要使用 MyBatis, 只需将 mybatis-x.x.x.jar文件置于类路径(classpath)中即可。
- 如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中
Mapper代理开发:
使用Mapper代理方式,必须满足以下要求:
- 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下(紧对于使用包扫描方式映射xml文件)。如下图:
注意:这里的UserMapper.xml里面的内容可以到mybatis官网上面粘贴进来的,然后加以修改一下就能成为我们自己的代码了
<?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.knife.mapper.BrandMapper"><!-- 只有这里namespace的路径是需要修改的 -->
</mapper>
- 设置SQL映射文件的namespace属性为Mapper接口全限定名(即对应mapper接口的路径)
- 在 Mapper 接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
- 注意:
- 如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则
可以使用包扫描的方式
简化SQL映射文件的加载(即在mybatis-config.xml文件下配置)。也就是将核心配置文件的加载映射配置文件的配置修改为
- 如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则
<mappers>
<!--加载sql映射文件-->
<!-- <mapper resource="com/itheima/mapper/UserMapper.xml"/>-->
<!--Mapper代理方式-->
<package name="com.itheima.mapper"/>
</mappers>
Mybatis的核心配置(mybatis-config.xml)
环境配置
- 在核心配置文件的 environments 标签中其实是可以配置多个 environment ,使用 id 给每段环境起名,在environments 中使用 default=‘环境id’ 来指定使用哪儿段配置。我们一般就配置一个 environment 即可。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimeZone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimeZone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
- 类型别名
在映射配置文件中的 resultType 属性需要配置数据封装的类型(类的全限定名(即类的路径))。而每次这样写是特别麻烦的,Mybatis提供了 类型别名 (typeAliases) 可以简化这部分的书写。
首先需要现在核心配置文件中配置类型别名,也就意味着给pojo包下所有的类起了别名(默认的别名就是:实体类名),不区分大小写。
内容如下:
<typeAliases>
<!--name属性的值是实体类所在包-->
<package name="com.itheima.pojo"/>
</typeAliases>
通过上述的配置,我们就可以简化映射配置文件中 resultType 属性值的编写
<mapper namespace="com.itheima.mapper.UserMapper">
<select id="selectAll" resultType="user">
select * from tb_user;
</select>
</mapper>
配置各个标签需要遵循前后顺序(即上面图片的那个顺序)
一、查询
1、查询所有
实体类属性名和数据库表的列名不一致,则idea不能自动封装数据
解决方法:
* 方法一: 起别名 :对不一样的字段名起别名,让别名和实体类的属性名一样
- 缺点:每次查询都要定义一次别名
<select id="selectAll" resultType="brand">
select id , brand_name as brandName, company_name as companyName, ordered,description,status 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"></include> from tb_brand;
</select>
* 缺点:sql片段不灵活
-解决:方法二
* 方法二:resultMap:
1. 在<mapper namespace="com.itheima.mapper.BrandMapper"></mapper>标签中定义resultMap标签
2. 在<select>标签中使用resultMap属性替换resultType属性
<!--
id: 唯一标识
type:映射的类型(支持别名)
-->
<resultMap id="brandResultMap" type="brand">
<!--
id:完成主键字段的映射
result:完成一般字段的映射
column: 该字段在数据库中的列名
property: 该字段在实体类pojo的属性名
<id column="brand_name" property="brandName"/>//主键字段的映射
-->
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
2、Id查询
<!--
*参数占位符:
1. #{};会替换为 ?, 为了防止SQl注入
2. ${}; 拼sql语句,会存在sql注入问题
3. 使用时机:
* 参数传递的时候,使用#{}
* 数据库表名或者列名不固定的时候,使用${}
* 参数类型:
parameterType="int";可以省略
<select id="selectById" parameterType="int" resultMap="brandResultMap">
select * from tb_brand where id = ${id};
</select>
* 特殊字符处理:
1. 转移字符:
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id < ${id};
</select>
2. CDATA区://其实就相当于文本框;<![CDATA[ 这里就是填数据的地方 ]]>
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id <![CDATA[ < ]]> ${id};
</select>
-->
3、多条件查询
// 多条件查询
/**
* * 参数接收:
* 1. 散装参数:如果方法中有!!!多个参数!!!,需要使用@Param("sql参数占位符名称")
* 2. 对象参数: 对象的属性名称要和参数占位符名称一致
* 3. map集合: 只需保证sql中的参数名和map集合的键的名称对应上
* @param status
* @param companyName
* @param brandName
* @return
*/
// 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);
一、动态查询
- 动态sql:sql语句会随着用户的输入或者外部条件的变化而变化
<!--动态条件查询
* if : 条件查询
* test : 逻辑表达式
* 产生的问题:第一个条件不需要逻辑运算符
<if test="companyName !=null and companyName !='' ">
and company_name like #{companyName}
</if>
*解决1: 多写一个恒等式: 1=1
<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
-->
<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>
</select>
//上面的一段代码单单用一个if标签是不能满足需求的),存在的问题是where后面的第一个条件不需要and,所以很难满足
//使用动态sql就可以解决
<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>
一、动态查询——单条件查询
<select id="selectByConditionSingle" resultMap="brandResultMap">
select * from tb_brand
where
<choose> <!--相当于swith-->
<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> <!--相当于default-->
1=1
</otherwise>
</choose>
</select>
//查询单个的
<select id="selectByConditionSingle" resultMap="brandResultMap">
select * from tb_brand
where
<choose> <!--相当于swith-->
<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> <!--相当于defaut-->
1=1
</otherwise>
</choose>
</select>
//使用where标签的改进
<select id="selectByConditionSingle" resultMap="brandResultMap">
select * from tb_brand
<where>
<choose> <!--相当于swith-->
<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>
二、添加数据
添加
public void testAdd() throws IOException {
// 1.接收参数
int status=1;
String companyName="华为";
String brandName ="波导";
String description="手机中的战斗机";
int ordered=100;
// 封装参数
Brand brand = new Brand();
brand.setStatus(status);
brand.setBrandName(brandName);
brand.setCompanyName(companyName);
brand.setDescription(description);
brand.setOrdered(ordered);
// Map集合
// Map map = new HashMap();
// map.put("status",status);
// map.put("companyName",companyName);
// map.put("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(); //默认开启事务,进行增删改查需要使用sqlSession.commit();手动提交事务
SqlSession sqlSession = sqlSessionFactory.openSession(true);//设置自动提交事务
// 3. 获取Mapper对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
brandMapper.add(brand);
// 手动提交事务
sqlSession.commit();
// 5. 释放资源
sqlSession.close();
}
添加-----主键返回
- 默认没有主键返回的(即id)
<insert id="add">
insert into tb_brand(brand_name, company_name,ordered,description,status)
values (#{companyName},#{brandName},#{ordered},#{description},#{status});
</insert>
//其实当我们指向这个sql语句的时候,id值就已经存在的了,只是没有直接绑定到该对象里面,我们没有去获取而已,下面的代码就是获取id值(这里id是主键)
- 有主键返回 :
//获取到主键值后,将来就可以通过该实体类对象对应的get方法得到
<!--keyProperty指向主键名-->
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into tb_brand(brand_name, company_name,ordered,description,status)
values (#{companyName},#{brandName},#{ordered},#{description},#{status});
</insert>
三、修改
三、1、修改全部字段
<!-- 修改全部字段-->
<update id="update">
update tb_brand
set brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
description = #{description},
status = #{status}
where id =#{id};
</update>
三、2、修改动态字段(将来修改哪个字段是不固定的)
- 原来的:
<update id="update">
update tb_brand
set brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
description = #{description},
status = #{status}
where id =#{id};
</update>
- 使用
动态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>
四、删除
四、1、删除一条记录
<!-- 根据id进行 删除-->
<delete id="deleteById">
delete from tb_brand where id = #{id};
</delete>
四、2、批量删除
- 使用数组进行作为参数
<!-- 批量删除-->
<!-- mybatis会将数组参数封装到一个Map集合
* 默认key名字:array
* 可以用 @Param("ids")注解改变map集合的默认key(map里面的键)名称
* 1.collection:要遍历的目标集合
* open:循环开始时的字符;close:循环结束时的字符
* item:遍历出的集合成员,自己定义的变量
* separator:结合成员之间的分隔符
* #{item}:注意与item="item"中的的值相同(即变量名要相同)
-->
<delete id="deleteByIds">
delete from tb_brand where id
in (
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
);
</delete>
Mybatis参数传递
Mybatis接口方法中可以接收各种各样的参数,Mybatis底层对于这些参数进行不同的封装处理方式
1.单个参数的封装:
-参数是 POJO类型:直接使用,实体类属性名和参数占位符名称一致
- 参数是 Map集合:直接使用,键名 和参数占位符一致
- 参数是 Collection:封装为Map集合
//两种方法
map.put("collection",collection集合);
map.put("arg0",collection集合);
- 参数是 List:封装为Map集合
map.put("collection",list集合);
map.put("list",list集合);
map.put("arg0",list集合);
- 参数是 Array:封装为Map集合
map.put("array",数组);
map.put("arg0",数组);
- 参数是 其它类型:直接使用
2.多个参数:
多个参数会被封装为map集合,可以使用@param()注解,来替换map集合中默认的arg键名
//两种
map.put("arg0",参数值1);
map.put("arg1",参数值2);
map.put("param1",参数值1);
map.put("param2",参数值2);
- 建议:将来都使用@Param注解来修改Map集合(多个参数,系统会默认把参数封装成集合)中的默认键名,并使用修改后的名称来获取值,这样可读性更高
- 首先明确这个@Param注解是为SQL语句中参数赋值而服务的。
@Param的作用就是给参数命名,比如在mapper里面某方法A(int id),当添加注解后A(@Param(“userId”) int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。将参数值传如SQL语句中,通过#{userId}进行取值给SQL的参数赋值。
- 首先明确这个@Param注解是为SQL语句中参数赋值而服务的。
实例一:@Param注解基本类型的参数
-
- mapper中的方法:
public User selectUser(@Param("userName") String name,@Param("password") String pwd);
映射到xml中的标签
<select id="selectUser" resultMap="User">
select * from user where user_name = #{userName} and user_password=#{password}
</select>
- 其中where user_name = #{userName} and user_password = #{password}中的userName和password都是从注解@Param()里面取出来的,取出来的值就是方法中形式参数 String name 和 String pwd的值。
当使用了@Param注解来声明参数的时候,SQL语句取值使用#{}, ${}取值都可以。
当不使用@Param注解声明参数的时候,必须使用的是#{}来取参数。使用${}方式取值会报错。
注解开发
- 使用注解开发会比配置文件开发更加简单方便
- 查询:@Select
- 添加:@Insert
- 修改:@Update
- 删除:@Delete
提示:注解开发完成简单功能,配置文件开发完成复杂功能
- xml开发,需要配置xml文件中设置
<select id="selectById" resultType="user">
select * from tb_user where id =#{id};
</select>
注解开发:
- 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
- 选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。