【大家好,我是爱干饭的猿,本文重点介绍ORM 框架和Mybatis的关系、如何通过注解 或者 通过XML配置文件的使用Mybatis,以及参数占位符 #{} 和 ${} 的区别, 什么是sql 注入。
后续会继续分享其他重要知识点总结,如果喜欢这篇文章,点个赞👍,关注一下吧】
上一篇文章:《【SSM】Spring AOP 统一问题处理(重点:Spring AOP 实现原理)》
🤞目录🤞
🎁1.MyBatis 是什么?
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 去除了几乎所有的 JDBC 代码以及设置参数和获 取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
简单来说 MyBatis 是更简单完成程序和数据库交互的工具,也就是更简单的操作和读取数据库工具。
🎁2. 什么是ORM框架?
ORM 把数据库映射为对象:
- 数据库表(table)--> 类(class)
- 记录(record,行数据)--> 对象(object)
- 字段(field) --> 对象的属性(attribute)
一般的 ORM 框架,会将数据库模型的每张表都映射为一个 Java 类。 也就是说使用 MyBatis 可以像操作对象一样来操作数据库中的表,可以实现对象和数据库表之间的转换。
MyBatis 也是一个 ORM 框架,ORM(Object Relational Mapping),即对象关系映射。在面向对象编程语言中,将关系型数据库中的数据 与对象建立起映射关系,进而自动的完成数据与对象的互相转换:
1. 将输入数据(即传入对象)+SQL 映射成原生 SQL
2. 将结果集映射为返回对象,即输出对象
编辑
一些其他框架:
- Hibernate框架:偏向简化SQL的模式
- MyBatis框架:偏向ORM的模式
- Spring 内部提供的JdbcTemplate:偏向简化SQL的模式
- JPA :完全倒向了ORM的形式,建表的过程都被抽象,我们看到的只有类(我写了类,框架根据类建表)
🎁3. MyBatis 的使用
3.1 添加MyBatis框架支持
1. 新项目添加MyBatis框架
编辑
2. 旧项目添加MyBatis框架
a. 使用 EditStarters插件
编辑
编辑b. 手动添加
<!-- 把 DataSource对象注册到Spring 中--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <!-- 添加 mybatis 框架 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <!-- 添加 MySQL 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
3.2 配置连接字符串和MyBatis
此步骤需要进行两项设置,数据库连接字符串设置和 MyBatis 的 XML 文件配置。
1. 配置连接字符串
如果是 application.yml 添加如下内容:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/***?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456
2. 配置 MyBatis 中的 XML 路径
通过XML配置文件的形式使用Mybatis 时需要配置,通过注解使用Mybatis时,不需要配置
mybatis: mapper-locations: classpath:mapper/**.xml
3.3 通过注解使用Mybatis
定义Mapper 接口
@Repository // 消除报错 @Mapper // Mapper 注解 public interface UserMapper { }
1. 查询
// 1. 查询 // 1.1 通过uid 查询得到Map对象 @Select("select uid, username, password from users where uid = #{uid}") Map<String, Object> select1(@Param("uid") int uid); // 1.2 通过uid 查询得到User类对象 @Select("select uid, username, password from users where uid = #{uid}") UserDO select2(@Param("uid") int uid);
2. 插入
// 2. 插入 // 2.1 插入,返回插入成功的条数 @Insert("insert into users (username, password) values (#{username}, #{password})") int insert1(UserDO userDO); // 2.2 由于用不到这个返回值,所以写成 void 更常见 @Insert("insert into users (username, password) values (#{username}, #{password})") void insert2(UserDO userDO); // 2.3 插入后拿到自增id // 通过 @Options 注解,添加一些配置,得到自增主键,设置成 uid // keyProperty : 对象的属性名是 uid // keyColumn : 表的字段名的 uid @Insert("insert into users (username, password) values (#{username}, #{password})") @Options(useGeneratedKeys = true, keyProperty = "uid", keyColumn = "uid") void insert3(UserDO userDO);
3. 修改
// 3. 修改 @Update("update users set username = #{username}, password = #{password} where uid = #{uid}") int update(UserDO userDO);
4. 删除
// 4. 删除 @Delete("delete from users where uid = #{uid}") int delete(@Param("uid") int uid);
5. 总览(增删查改)
import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; import java.util.Map; @Repository // 消除报错 @Mapper // Mapper 注解 public interface UserMapper { // 1. 查询 // 1.1 通过uid 查询得到Map对象 @Select("select uid, username, password from users where uid = #{uid}") Map<String, Object> select1(@Param("uid") int uid); // 1.2 通过uid 查询得到User类对象 @Select("select uid, username, password from users where uid = #{uid}") UserDO select2(@Param("uid") int uid); // 2. 插入 // 2.1 插入,返回插入成功的条数 @Insert("insert into users (username, password) values (#{username}, #{password})") int insert1(UserDO userDO); // 2.2 由于用不到这个返回值,所以写成 void 更常见 @Insert("insert into users (username, password) values (#{username}, #{password})") void insert2(UserDO userDO); // 2.3 插入后拿到自增id // 通过 @Options 注解,添加一些配置,得到自增主键,设置成 uid // keyProperty : 对象的属性名是 uid // keyColumn : 表的字段名的 uid @Insert("insert into users (username, password) values (#{username}, #{password})") @Options(useGeneratedKeys = true, keyProperty = "uid", keyColumn = "uid") void insert3(UserDO userDO); // 3. 修改 @Update("update users set username = #{username}, password = #{password} where uid = #{uid}") int update(UserDO userDO); // 4. 删除 @Delete("delete from users where uid = #{uid}") int delete(@Param("uid") int uid); }
3.4 通过XML配置文件的形式使用Mybatis
先配置 MyBatis 中的 XML 路径,然后在mapper下 .xml 文件中写
编辑
定义Mapper 接口:
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.List; @Repository @Mapper public interface UserMapper { // 1. 查询 // 1.1 单个查询 User selectOneByUid(@Param("uid") int uid); // 1.2 多个查询 List<User> selectListByUidList(@Param("uidList") List<Integer> uidList); // 1.3 动态查询 User selectByUser(@Param("user") User user); // 2. 插入 // 2.1 单个插入 int insertOneUser(@Param("user") User user); // 2.1 批量插入 int insertBatch(@Param("userList") List<User> userList); // 3. 修改 void update(int uid , String username); // 4. 删除 void delete(int uid); }
0. 关于resultMap
使用场景:
- 字段名称和程序中的属性名不同的情况,可使用 resultMap 配置映射;
- 一对一和一对多关系可以使用 resultMap 映射并查询数据。
<!-- 返回字典映射--> <resultMap id="xxx" type="com.haomin.mybatis_xml.User"> <id property="uid" javaType="Integer" column="uid" jdbcType="INTEGER" /> <result property="username" javaType="String" column="username" jdbcType="VARCHAR" /> <result property="password" column="password" /> </resultMap>
编辑
1. 查询
<!-- 1.1 单个查询 --> <select id="selectOneByUid" resultType="com.haomin.mybatis_xml.User" parameterType="int"> select uid, username, password from users where uid = #{uid} </select> <!-- 1.2 多个查询 --> <select id="selectListByUidList" resultMap="xxx" parameterType="List"> select uid, username, password from users where uid in ( <foreach collection="uidList" item="id" separator=", "> #{id} </foreach> ) order by uid </select> <!-- 1.3 动态查询--> <select id="selectByUser" resultMap="xxx" parameterType="com.haomin.mybatis_xml.User"> select uid, username, password from users where <if test="user.uid != null"> uid = #{user.uid} </if> <if test="user.username != null"> and username = #{user.username} </if> <if test="user.password != null"> and password = #{user.password} </if> </select>
2. 插入
<!-- 2.1 单个插入--> <insert id="insertOneUser" useGeneratedKeys="true" keyProperty="uid" keyColumn="uid"> insert into users (username, password) values (#{user.username}, #{user.password}) </insert> <!-- 2.2 批量插入--> <insert id="insertBatch" useGeneratedKeys="true" keyProperty="uid" keyColumn="uid"> insert into users (username, password) values <foreach collection="userList" item="user" separator=", "> (#{user.username}, #{user.password}) </foreach> </insert>
3. 修改
<!-- 3. 修改--> <update id="update"> update users set username = #{username} where uid = #{uid} </update>
4. 删除
<!-- 4. 删除--> <delete id="delete"> delete from users where uid = #{uid} </delete>
5. 总览(增删查改)
<?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.haomin.mybatis_xml.UserMapper"> <!-- 返回字典映射--> <resultMap id="xxx" type="com.haomin.mybatis_xml.User"> <id property="uid" javaType="Integer" column="uid" jdbcType="INTEGER" /> <result property="username" javaType="String" column="username" jdbcType="VARCHAR" /> <result property="password" column="password" /> </resultMap> <!-- 1.1 单个查询 --> <select id="selectOneByUid" resultType="com.haomin.mybatis_xml.User" parameterType="int"> select uid, username, password from users where uid = #{uid} </select> <!-- 1.2 多个查询 --> <select id="selectListByUidList" resultMap="xxx" parameterType="List"> select uid, username, password from users where uid in ( <foreach collection="uidList" item="id" separator=", "> #{id} </foreach> ) order by uid </select> <!-- 1.3 动态查询--> <select id="selectByUser" resultMap="xxx" parameterType="com.haomin.mybatis_xml.User"> select uid, username, password from users where <if test="user.uid != null"> uid = #{user.uid} </if> <if test="user.username != null"> and username = #{user.username} </if> <if test="user.password != null"> and password = #{user.password} </if> </select> <!-- 2.1 单个插入--> <insert id="insertOneUser" useGeneratedKeys="true" keyProperty="uid" keyColumn="uid"> insert into users (username, password) values (#{user.username}, #{user.password}) </insert> <!-- 2.2 批量插入--> <insert id="insertBatch" useGeneratedKeys="true" keyProperty="uid" keyColumn="uid"> insert into users (username, password) values <foreach collection="userList" item="user" separator=", "> (#{user.username}, #{user.password}) </foreach> </insert> <!-- 3. 修改--> <update id="update"> update users set username = #{username} where uid = #{uid} </update> <!-- 4. 删除--> <delete id="delete"> delete from users where uid = #{uid} </delete> </mapper>
当然还有更多用法:可以参考 Mybatis 官方文档
3.5 参数占位符 #{} 和 ${} 和 sql 注入
#{}:预编译处理。
${}:字符直接替换。
#{}预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使用 PreparedStatement 的 set 方法来赋值,编译后会带上 ‘ ’。
${}直接替换:是 MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
结论:
- 用于查询的字段,尽量使用 #{} 预查询的方式,如果用${} 可能会出现sql注入问题。
- 使用 ${sort} 可以实现排序查询或者分页,而使用 #{sort} 就不能实现排序查询了,因为当使用 #{sort} 查询时,如果传递的值为 String 则会加单引号,就会导致 sql 错误。
sql 注入代码例子:
执行:
select * from userinfo where username = '${name}' and password = '${pwd}'
当sql 为 : ' or 1=1 时
执行语句:
select * from users where username = ' or 1=1 and password = ' or 1=1;
当然,Mybatis 还有更多用法:可以参考 Mybatis 官方文档
分享到此,感谢大家观看!!!
如果你喜欢这篇文章,请点赞加关注吧,或者如果你对文章有什么困惑,可以私信我。
🏓🏓🏓