MyBatis详解
MyBatis
MyBatis是持久层框架(持久层: 和数据库进行交互的代码;框架: 半成品软件模型)。简化了JDBC开发(使用Java提供的标准Api来操作和访问数据库)。
JDBC缺点
JDBC开发的流程:
- 注册驱动(Class.forName(“com.mysql.jdbc.Driver”))
- 建立连接
- 编写sql
- 执行sql,获取数据
- 解析数据,封装对象
对应下面的代码:
// 1. 注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");// 2. 建立Connection连接
String url = "jdbc:mysql://db1?useSSL=false";
String uname = "root";
String pwd = "1234";
Connection conn = DriverManager.getConnection(url, uname, pwd);// 3. 设置性别参数为男
String gender = "男";// 4. 定义SQL查询语句
String sql = "select * from tb_user where gender = ?";// 5. 创建预处理语句对象
PreparedStatement pstmt = conn.prepareStatement(sql);// 6. 设置参数值
pstmt.setString(1, gender);// 7. 执行SQL查询
ResultSet rs = pstmt.executeQuery();// 8. 创建用户对象和用户列表
User user = null;
ArrayList<User> users = new ArrayList<>();// 9. 遍历结果集
while (rs.next()) {// 从结果集中获取数据int id = rs.getInt("id");String username = rs.getString("username");String password = rs.getString("password");// 创建User对象并设置属性user = new User();user.setId(id);user.setUsername(username);user.setPassword(password);user.setGender(gender);// 将用户对象添加到列表中users.add(user);
}
硬编码: 配置信息和sql语句写到了代码里,之后如果改动需要改动代码。
操作繁琐: 手动设置sql语句的参数、手动解析结果并封装到对象中。
MyBatis如何简化?
xml配置文件: 对于硬编码问题,MyBatis使用将配置信息和sql语句都写到xml配置文件中来简化的。
配置信息:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><environments default="dev"><environment id="dev"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test"/><property name="username" value="root"/><property name="password" value="password"/></dataSource></environment></environments><mappers><mapper resource="mapper/UserMapper.xml"/></mappers>
</configuration>
sql语句:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.example.mapper.UserMapper"><select id="findUserById" parameterType="int" resultType="com.example.model.User">SELECT id, name, age FROM user WHERE id = #{id}</select></mapper>
对于操作繁琐问题,mybatis只需要一行代码就能把参数设置和封装结果解决完,让这两个操作实现了自动完成。
// 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(statement: "test.selectAll");
System.out.println(users);// 4. 释放资源
sqlSession.close();
SqlSession
MyBatis中的SqlSession类似于JDBC中的Connection,但是提供了更高级别的抽象和封装。
通过xml配置文件获取SqlSessionFactory,然后获取SqlSession,也就类似于JDBC中的Connection
Mapper代理开发
上文使用SqlSession来执行sql,还是会存在硬编码的问题,就是我们要知道sql配置文件中对应的sql语句的id叫什么。写起来还是不太方便。所以还可以使用Mapper代理开发,更加方便。
具体细节就不赘述。简单讲一讲实现。
创建一个mapper接口,用来配合mapper.xml文件,这个接口和mapper.xml要放在同一个路径下(spring可以通过更改扫描路径)。mapper.xml中的namespace要是接口的全限定名,并且mapper接口要有一个与mapper.xml对应sql的id相同名的方法,返回值也要和mapper.xml中对应sql的返回值相同。
然后通过SqlSession获取mapper接口的代理对象,使用代理对象直接调用接口的方法就能直接执行sql语句并且获取到对应的返回值了。
数据库表列名和实体属性名不一致
起别名: sql语句查询的时候可以通过as给不一致的列表起与实体属性名一致的别名。
resultMap: 定义完成不一致的属性名和列名的映射,然后使用resultMap替换 标签中的resultType。
#{}和${}的区别和使用
mapper接口中带参数的方法需要在sql的select语句中搭配一个占位符,而占位符有两种,分别是${}和#{},他们两个是不同的。
${}: 会把参数替换占位符,拼接上原来的sql语句,然后组成一个完整的sql语句去进行sql查询,类似jdbc的Statement做的一样,不会预编译。
#{}: 会进行sql语句的预编译,然后会把参数替换掉预编译的占位符上,可以有效防止sql注入的问题。类似PreparedStatement。
常见标签
1. <select>
用于定义查询语句,执行数据库的SELECT
操作,返回结果集。
常用于查询数据,支持动态SQL和映射。
2. @Param
这是MyBatis的注解,用于给Mapper接口方法的参数命名。
当接口方法有多个参数时,使用@Param("name")
可以在SQL中通过#{name}
引用对应参数。
3. <set>
用于UPDATE
语句中,动态拼接SET
子句。
会自动处理多余的逗号,避免SQL语法错误。
示例:
xml复制编辑<set><if test="name != null">name = #{name},</if><if test="age != null">age = #{age},</if>
</set>
4. <foreach>
用于遍历集合(List、数组、Map等),动态生成SQL片段,常用于批量操作和IN
条件。
示例:
xml复制编辑WHERE id IN
<foreach item="id" collection="idList" open="(" separator="," close=")">#{id}
</foreach>
5. <where>
用于动态生成WHERE
子句,会智能处理多余的AND
或OR
。
如果内部没有条件,则不会生成WHERE
关键字。
6. <insert>
定义插入语句,对应数据库的INSERT
操作。
可以支持动态字段插入。
7. <delete>
定义删除语句,对应数据库的DELETE
操作。
用于删除符合条件的数据。
8. <update>
定义更新语句,对应数据库的UPDATE
操作。
常配合<set>
标签使用,动态更新字段。
9. <choose>
类似Java中的switch-case
,用于多个条件分支中选择一个执行。
包含<when>
和<otherwise>
子标签。
示例:
xml复制编辑<choose><when test="type == 'A'">...</when><when test="type == 'B'">...</when><otherwise>...</otherwise>
</choose>
参数传递
Mapper接口的方法中可以接收各种参数,可以接收
单个参数:
- POJO类型
属性名和参数占位符名称一致。
- Map集合
Map集合键名要和参数占位符一致。
- Collection
封装为map集合,map.put(“collection”,Collection集合),map.put(“arg0”,Collection集合)
- List
封装为map集合,map.put(“arg0”,List集合),map.put(“collection”,List集合),map.put(“list”,List集合)
- Array
封装为map集合,map.put(“arg0”,Array数组), map.put(“array”, Array数组)
- 其它类型
多个参数:
使用多个参数的时候,要使用@Param注解来定义参数的名称,这个名称要和sql语句的参数占位符一致,才能将这个参数替换到占位符上去。
ParamNameResolver封装参数: mapper接口方法如果有多个参数,那么这些参数就会被封装到一个Map集合中,key就是使用@Param定义的名称(没有@Param注解的话,默认就是arg0、arg1…和param0、param1…作为键值,使用@Param注解的话,替换的键名是argx的,paramx还存在),然后值就是参数传递过来的。