前言
为什么会有这个东西?
在前面我们学习MySQL数据库时,都是利用图形化客户端工具(如:idea),来操作数据库的
我们做为后端程序开发人员,通常会使用Java程序来完成对数据库的操作。Java程序操作数据库,现在主流的方式是:Mybatis。
MyBatis是一款优秀的 持久层框架,用于简化JDBC的开发。
入门
Mybatis操作数据库的步骤:
-
准备工作(创建springboot工程、数据库表user、实体类User)
-
引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)
-
编写SQL语句(注解/XML)
@Mapper注解:表示是mybatis中的Mapper接口
-
程序运行时:框架会自动生成接口的实现类对象(代理对象),并给交Spring的IOC容器管理
@Mapper
public interface UserMapper {
//查询所有用户数据
@Select("select id, name, age, gender, phone from user")
public List<User> list();
}
JDBC
JDBC: ( Java DataBase Connectivity ),就是使用Java语言操作关系型数据库的一套API。
Mybatis框架,就是对原始的JDBC程序的封装。
数据库连接池
在前面我们所讲解的mybatis中,使用了数据库连接池技术,避免频繁的创建连接、销毁连接而带来的资源浪费。
数据库连接池的好处:
-
资源重用
-
提升系统响应速度
-
避免数据库连接遗漏
常见的数据库连接池:
-
C3P0
-
DBCP
-
Druid 德鲁伊 阿里巴巴的
-
Hikari (springboot默认) 追光者
lombok
Lombok是一个实用的Java类库,可以通过简单的注解来简化和消除一些必须有但显得很臃肿的Java代码。
重点使用后面三个注解
Mybatis基础
预编译SQL
有两个优势:
-
性能更高
-
更安全(防止SQL注入)
SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
日志输入
在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果。具体操作如下:
-
打开application.properties文件
-
开启mybatis的日志,并指定输出到控制台
#指定mybatis输出日志的位置, 输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
参数占位符
在Mybatis中提供的参数占位符有两种:${...} 、#{...}
-
#{...}
-
执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值
-
使用时机:参数传递,都使用#{…}
-
-
${...}
-
拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题
-
使用时机:如果对表名、列表进行动态设置时使用
-
注意事项:在项目开发中,建议使用#{...},生成预编译SQL,防止SQL注入安全。
删除
@Mapper
public interface EmpMapper {
@Delete("delete from emp where id = #{id}")//使用#{key}方式获取方法中的参数值
public void delete(Integer id);
}
新增
@Mapper
public interface EmpMapper {
//会自动将生成的主键值,赋值给emp对象的id属性
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
public void insert(Emp emp);
}
更新
@Mapper
public interface EmpMapper {
@Update("update emp set username=#{username}, name=#{name}, gender=#{gender}, image=#{image}, job=#{job}, entrydate=#{entrydate}, dept_id=#{deptId}, update_time=#{updateTime} where id=#{id}")
public void update(Emp emp);
}
查询
@Mapper
public interface EmpMapper {
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where id=#{id}")
public Emp getById(Integer id);
}
开启驼峰命名
# 在application.properties中添加:
mybatis.configuration.map-underscore-to-camel-case=true
#{%张%}不能出现在' '内,解决方法:
-
使用MySQL提供的字符串拼接函数:concat('%' , '关键字' , '%')
where name like concat('%',#{name},'%')
XML配置文件
为什么要使用xml文件?
使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。
在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:
-
XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
-
XML映射文件的namespace属性为Mapper接口全限定名一致
-
XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。
映射代码
<?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">
<mapper namespace="">
</mapper>
完整实例:
<?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">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--查询操作-->
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
where name like concat('%',#{name},'%')
and gender = #{gender}
and entrydate between #{begin} and #{end}
order by update_time desc
</select>
</mapper>
MyBatisX
作用: 可以通过MybatisX快速定位
动态SQL
动态 SQL 是 MyBatis 的强大特性之一
为什么要用动态SQL?
在页面原型中,列表上方的条件是动态的,是可以不传递的,也可以只传递其中的1个或者2个或者全部。要灵活,有输入就有改变,没输入就不要变
是什么:SQL语句会随着用户的输入或外部条件的变化而变化,我们称为:动态SQL
SQL-if
<where></where> :只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR
<set></set>:动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中)
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
<where>
<!-- if做为where标签的子元素 -->
<if test="name != null and name!=''">
and 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>
为什么name要判断两个东西?
1.处理 null 值:
如果字符串name为null,会抛出 NullPointerException。
2.处理空字符串 ' ':
即使 name 不为 null,也可能是一个空字符串 ""。这种情况下,虽然不会抛出异常,但逻辑上可能不符合预期。
<update id="update">
update dish
<set>
<if test="name != null">name = #{name},</if>
<if test="categoryId != null">category_id = #{categoryId},</if>
<if test="price != null">price = #{price},</if>
<if test="image != null">image = #{image},</if>
<if test="description != null">description = #{description},</if>
<if test="status != null">status = #{status},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="updateUser != null">update_user = #{updateUser},</if>
</set>
where id = #{id}
</update>
SQL-foreach
-
使用
<foreach>
遍历deleteByIds方法中传递的参数ids集合
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--删除操作-->
<delete id="deleteByIds">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
</mapper>
SQL-sql&include
为什么用这个?
-
在xml映射文件中配置的SQL,有时可能会存在很多重复的片段,此时就会存在很多冗余的代码
-
<sql>
:定义可重用的SQL片段
<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>
Mybatis-Plus跳转