一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情。
Mybatis 快速入门(二)
写在前面👀
审核大大,球球你了,俺今天稿子只完成了三分之一,我可以今天交吗?我保证明天12点之前补完剩下的内容,审核大大最帅(最漂亮)😘
今天主要讲讲MyBatis使用xml映射文件进行查询所有数据和查询单行数据的操作
一、环境准备
1️⃣数据库准备
-- 创建海贼王人物表
CREATE TABLE tb_op (
id INT PRIMARY KEY AUTO_INCREMENT, # 主键
name VARCHAR(20), # 姓名
gender VARCHAR(5), # 性别
devil_fruit VARCHAR(30), # 果实能力
reward BIGINT, # 悬赏金,单位:贝利
identity VARCHAR(10) # 人物身份
);
-- 添加人物数据
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('路飞','男','人人果实-幻兽种-尼卡形态',1500000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('索隆','男',null,320000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('黄猿','男','闪闪果实',null,'海军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('亚尔丽塔','女','滑滑果实',5000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('伊万科夫','人妖','荷尔蒙果实',null,'革命军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('萨博','男','烧烧果实',602000000,'革命军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('小冯','人妖','模仿果实',32000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('艾斯','男','烧烧果实',550000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('达斯琪','女',null,null,'海军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('小萨蒂','女',null,null,'海军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('战国','男','人人果实-幻兽种-大佛形态',null,'海军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('熊','男','肉球果实',296000000,'革命军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('红发','男','面子果实',4028900000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('罗宾','女','花花果实',130000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('Joker','男','线线果实',340000000,'天龙人');
2️⃣创建Op实体类
- 在
com.bighorn.pojo包下创建 Op 实体类。
public class Op {
private Integer id;
private String name;
private String gender;
private String devilFruit;
private Long reward;
private String identity;
/* 省略getter和setter方法 */
}
3️⃣创建MyBatis工具类
/**
* MyBatis工具类
*/
public class MyBatisUtils {
//创建全局唯一的SqlSessionFactory对象
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
//1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
throw new ExceptionInInitializerError(e);
}
}
/**
* 获取SqlSession对象
* @return SqlSession
*/
public static SqlSession openSession() {
return sqlSessionFactory.openSession();
}
/**
* 关闭SqlSession,释放资源
* @param sqlSession
*/
public static void closeSession(SqlSession sqlSession) {
if (sqlSession != null) {
sqlSession.close();
}
}
/**
* 提交事务
* @param sqlSession
*/
public static void commitSession(SqlSession sqlSession){
if (sqlSession != null) {
sqlSession.commit();
}
}
/**
* 回滚事务
* @param sqlSession
*/
public static void rollbackSession(SqlSession sqlSession) {
if (sqlSession != null) {
sqlSession.rollback();
}
}
}
4️⃣编写测试类
- 先测试下数据库连接是否正常
public class testMyBatis {
//获取sqlSession对象
private SqlSession sqlSession = MyBatisUtils.openSession();
//获取OpMapper接口的代理对象
private OpMapper opMapper = sqlSession.getMapper(OpMapper.class);
@Test
/**
* 0.测试数据库连接
*/
public void testSession() throws IOException {
//1.打印sqlSession对象
System.out.println(sqlSession);
//2.释放资源
sqlSession.close();
}
}
- 结果如下,没问题👇
5️⃣创建Dao接口和映射文件
- 接口和映射文件放在同一包下:
com.bighorm.mapper - 接口和映射文件名称要一样:
OpMapper
- 创建OpMapper接口
public interface OpMapper {}
- 创建OpMapper.xml映射文件
<?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元素: 绑定DAO接口,因此 namespace 的命名必须要跟接口同名。-->
<mapper namespace="com.bighorn.mapper.OpMapper">
</mapper>
- mapper映射文件常见元素表👇
| 元素名称 | 描述 | 备注 |
|---|---|---|
mapper | 映射文件的根节点,只有namescape 一个属性 | namescape用于区分不同的 mapper,全局唯一绑定DAO接口,即面向接口编程。因此 namescape 的命名必须要跟接口同名。 |
select | 查询语句,最常用、最复杂的元素之一 | 可以自定义参数,返回结果集等 |
insert | 插入语句 | 执行后返回一个整数,代表插入的条数 |
update | 更新语句 | 执行后返回一个整数,代表更新的条数 |
delete | 删除语句 | 执行后返回一个整数,代表删除的条数 |
sql | sql语句片段 | 可以在其他标签被引用 |
resultMap | 用来描述数据库结果集与对象的对应关系,它是最复杂、最强大的元素 | 提供映射规则 |
| cache | 配置给定命名空间的缓存 | - |
| cache-ref | 其它命名空间缓存配置的引用 | - |
6️⃣安装MyBatisX插件
- 安装方法
- 按Ctrl+Alt+S打开设置-->Plugins-->在插件市场Marketplace搜索MyBatisX-->Install下载--->重启IDE!
- 主要功能:
- XML映射配置文件和接口方法间相互跳转,点这只小鸟就能跳转!
- 根据接口方法生成 statement
- XML映射配置文件和接口方法间相互跳转,点这只小鸟就能跳转!
二、一些偷懒的方法
- 在
mybatis-config.xmlMybatis核心配置文件里,在typeAliases标签写入实体类所在包名,这样就不用每次都书写类的全限定名称了;在mappers标签里写入映射文件所在包名,这样就不用写每个mapper文件的相对路径了
<!--起别名操作-->
<typeAliases>
<!--name属性的值是实体类所在包-->
<package name="com.bighorn.pojo"/>
</typeAliases>
<!--加载sql映射文件-->
<mappers>
<package name="com.bighorn.mapper"/>
</mappers>
三、select标签常用属性
| 属性名称 | 描 述 | 备注 |
|---|---|---|
id | 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 | 如果命名空间+id不唯一,那么 MyBatis 抛出异常 |
parameterType | 表示传入 SQL 语句传入参数类型的全限定名或别名。它是一个可选属性,MyBatis 能推断出具体传入语句的参数 | 支持基本数据类型和 JavaBean、Map 等复杂数据类型 |
resultType | SQL 语句执行后返回的类型(全限定名或者别名)。如果是集合类型,返回的是集合元素的类型,返回时可以使用 resultType 或 resultMap 之一 | - |
resultMap | 它是映射集的引用,与 元素一起使用,返回时可以使用 resultType 或 resultMap 之一 | 是 MyBatis 最复杂的元素,可以配置映射规则、级联、typeHandler 等 |
flushCache | 用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存 | 默认值为 false,如果设置为 true,则任何时候只要 SQL 语句被调用都将清空本地缓存和二级缓存 |
useCache | 启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中 | - |
timeout | 用于设置超时参数,单位是秒(s),超时将抛出异常 | - |
fetchSize | 获取记录的总条数设定 | 默认值是数据库厂商提供的 JDBC 驱动所设置的条数 |
statementType | 告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,取值为 STATEMENT(Statement)、 PREPARED(PreparedStatement)、CALLABLE(CallableStatement) | - |
resultSetType | 这是针对 JDBC 的 ResultSet 接口而言,其值可设置为 FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新) | - |
四、查询所有数据
1️⃣编写接口方法
/**
* 1.查询所有人物的数据,返回一个JavaBean对象的List集合
* @return List<Op>
*/
List<Op> selectAll();
2️⃣编写映射文件
<select id="selectAll" resultType="Op">
select *
from tb_op;
</select>
3️⃣编写测试方法
@Test
/**
* 1.查询所有数据
*/
public void testSelectAll() {
//1.1执行方法
List<Op> opList = opMapper.selectAll();
for (Op op : opList
) {System.out.println(op);}
System.out.println(opList);
//1.2释放资源
sqlSession.close();
}
- 有个小问题:tb_op表的
devil_fruit字段名与实体类Op的属性名devilFruit不匹配,查询该字段时,不能自动封装,显示为null,如下图👇
4️⃣优化方案
数据库表的字段名称和实体类属性名称不一样,则不能自动封装,解决方案有如下三种
1.给字段起别名
- 用 as 关键字给字段起和实体类属性名一样的别名
<select id="selectAll" resultType="Op">
select
id, name,gender,devil_fruit as devilFruit,reward,identity
from tb_op;
</select>
2.使用sql片段
- 缺点:不同的操作都要重新定义一次别名,比较繁琐
<sql id="op_column">
id, name,gender,devil_fruit as devilFruit,reward,identity
</sql>
<select id="selectAll" resultType="Op">
select
<include refid="op_column"></include>
from tb_op;
</select>
3.使用resultMap
- 推荐使用这种方法!
- 定义resultMap标签
- 在select标签中使用
resultMap属性
<!--
id:唯一标识
type:映射类型
-->
<resultMap id="OpResultMap" type="Op">
<!--
id:完成主键字段的映射
result:完成一般字段的映射
column:表字段名称
property:类属性名称
-->
<result column="devil_fruit" property="devilFruit"></result>
</resultMap>
<select id="selectAll" resultMap="OpResultMap">
select *
from tb_op;
</select>
4.优化后的测试结果
五、查询单行数据
1️⃣编写接口方法
/**
* 2.根据id查询单条数据,返回一个Op对象
* @param id
* @return Op
*/
Op searchById(int id);
2️⃣编写映射文件
parameterType:指定传入参数的类型。缺省属性,因为MyBatis 能推断出具体传入语句的参数。
<!-- 根据id查询单条记录-->
<select id="searchById" parameterType="int" resultMap="OpResultMap">
select *
from tb_op
where id = #{id};
</select>
3️⃣编写测试方法
@Test
/**
* 2.根据id查询单行数据
*/
public void testSearchById(){
//查询id为1的数据
int id=1;
//2.1执行方法
Op op = opMapper.searchById(id);
System.out.println(op.toString());
//2.2释放资源
sqlSession.close();
}
- 结果如下👇
4️⃣参数占位符
- mybatis提供了两种参数占位符
- #{} :执行SQL时,会将 #{} 占位符替换为?,从下图可以看出使用底层使用的是
PreparedStatement,可以防止sql注入
- #{} :执行SQL时,会将 #{} 占位符替换为?,从下图可以看出使用底层使用的是
- ${} :拼接SQL。从下图可以看出底层使用的是
Statement,会存在SQL注入问题。
写在后面🍻
感谢观看啦✨
有什么不足,欢迎指出哦💖
掘金的运营同学审核辛苦了💗