mybatis 1-3
核心概念:<sql> 与 <include> 到底是什么?1. 本质作用<sql> 是 MyBatis 提供的「SQL 片段定义标签」,用于把重复出现的 SQL 片段(如列列表、固定条件、排序规则) 抽离成独立模块;<include> 是「引用标签」,用于在主 SQL 中复用这些片段。2. 核心价值减少重复代码:比如 10 个查询都需要查 id, username, email, created_
6.可重用 SQL 片
核心概念:<sql> 与 <include> 到底是什么?
1. 本质作用
<sql> 是 MyBatis 提供的「SQL 片段定义标签」,用于把重复出现的 SQL 片段(如列列表、固定条件、排序规则) 抽离成独立模块;<include> 是「引用标签」,用于在主 SQL 中复用这些片段。
2. 核心价值
减少重复代码:比如 10 个查询都需要查 id, username, email, created_at,无需重复写 10 次,抽成 <sql> 后只需 <include> 引用;
统一维护:如果需要新增查询列(如 phone),只需修改 <sql> 片段,所有引用处自动生效,避免漏改 / 错改;
提升可读性:主 SQL 只保留核心逻辑,通用片段抽离后更易理解。
三种主要方式:
(1)复用列的列表
Java类如下1-1图:
package com.lo.mybatis.model;
public class Books {
private int id;
private String name;
private String author;
private double price;
private String publish;
private Students students;
//setget省略
}
1-1
xml配置如下图1-2:
<?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.lo.mybatis.dao.BooksDao">
<!-- 定义sql片段-->
<sql id="com-lon-bookslie">
id,
name,
author,
price,
publish
</sql>
<select id="finddBooks" resultType="com.lo.mybatis.model.Books" parameterType="java.lang.Integer">
select
<include refid="com-lon-bookslie"/>
from books where id=#{id}
</select>
</mapper>
1-2
数据库字段如下1-3:

1-3
运行代码如下
public class T {
private static final String MYBATIS_CONFIG_PATH = "mybatis-config.xml";
public static void main(String[] args) {
try (InputStream configInputStream = Resources.getResourceAsStream(MYBATIS_CONFIG_PATH);
SqlSession sqlSession = new SqlSessionFactoryBuilder()
.build(configInputStream)
.openSession()) {
BooksDao booksDao = sqlSession.getMapper(BooksDao.class);
Books book=booksDao.finddBooks(1);
System.out.println(book);
} catch (IOException e) {
// 5. 异常捕获与提示,便于排查问题
System.err.println("加载 MyBatis 配置文件失败:" + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
System.err.println("查询书籍数据失败:" + e.getMessage());
e.printStackTrace();
}
}
}
结果如下:
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@47eaca72]
==> Preparing: select id, name, author, price, publish from books where id=?
==> Parameters: 1(Integer)
<== Columns: id, name, author, price, publish
<== Row: 1, book_124, author_50, 27, publisher_6
<== Total: 1
Books{id=1, Myname='book_124', author='author_50', price=27.0, publish='publisher_6', students=null}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@47eaca72]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@47eaca72]
Returned connection 1206569586 to pool.
可以看出完全能查询出结果。
(2).复用固定 WHERE 条件(静态条件)
类和上面是相同的:
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">
<mapper namespace="com.lo.mybatis.dao.BooksDao">
<!-- 定义sql片段-->
<sql id="com-lon-bookslie">
id,
name,
author,
price,
publish
</sql>
<!-- where的sql片段-->
<sql id="com-lon-where">
AND students_id =#{Id}
</sql>
<select id="selectById" parameterType="java.lang.Integer" resultType="com.lo.mybatis.model.Books">
select
<include refid="com-lon-bookslie"></include>
from books
<where>
<if test="book !=null and book != ''">
AND name LIKE CONCAT('%',#{book},'%')
</if>
<include refid="com-lon-where"></include>
</where>
</select>
</mapper>
一般 AND students_id =#{Id}是一个具体的值但是我的数据表没有使用students_id来进行对比。
<where> 标签的必要性:
自动清理前缀:无论条件块开头是 AND 还是 OR,都会被移除,避免生成 WHERE AND is_deleted = 0 这种语法错误的 SQL;
条件为空时不生成 WHERE:避免生成 FROM users WHERE 这种无意义的语法错误;
兼容多条件组合:无论哪个条件先满足,都能保证 WHERE 子句语法正确。
Java接口代码如下:
public interface BooksDao {
// where sql用法
public List<Books> selectById(@Param("Id") int id,@Param("book") String bookname);
}
运行代码如下:
public class T {
private static final String MYBATIS_CONFIG_PATH = "mybatis-config.xml";
public static void main(String[] args) {
try (InputStream configInputStream = Resources.getResourceAsStream(MYBATIS_CONFIG_PATH);
SqlSession sqlSession = new SqlSessionFactoryBuilder()
.build(configInputStream)
.openSession()) {
BooksDao booksDao = sqlSession.getMapper(BooksDao.class);
List<Books> book = booksDao.selectById(6, "book");
book.forEach(System.out::println);
} catch (IOException e) {
// 5. 异常捕获与提示,便于排查问题
System.err.println("加载 MyBatis 配置文件失败:" + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
System.err.println("查询书籍数据失败:" + e.getMessage());
e.printStackTrace();
}
}
}
数据库如下:

有50条数据。
执行后查询结果如下:
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4c402120]
==> Preparing: select id, name, author, price, publish, students_id AS studentsId from books WHERE name LIKE CONCAT('%',?,'%') AND students_id =?
==> Parameters: book(String), 6(Integer)
<== Columns: id, name, author, price, publish, studentsId
<== Row: 2, book_706, author_11, 55, publisher_17, 6
<== Row: 5, book_159, author_12, 23, publisher_6, 6
<== Row: 16, book_27, author_32, 65, publisher_14, 6
<== Row: 45, book_488, author_28, 107, publisher_0, 6
<== Total: 4
Books{id=2, name='book_706', author='author_11', price=55.0, publish='publisher_17', students=null, studentsId=6}
Books{id=5, name='book_159', author='author_12', price=23.0, publish='publisher_6', students=null, studentsId=6}
Books{id=16, name='book_27', author='author_32', price=65.0, publish='publisher_14', students=null, studentsId=6}
Books{id=45, name='book_488', author='author_28', price=107.0, publish='publisher_0', students=null, studentsId=6}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4c402120]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4c402120]
Returned connection 1279271200 to pool.
可以看出执行成功。
(3).参数作用域与动态片段
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">
<mapper namespace="com.lo.mybatis.dao.BooksDao">
<resultMap id="book_students" type="com.lo.mybatis.model.Books">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="author" property="author"/>
<result column="price" property="price"/>
<result column="publish" property="publish"/>
<result column="studentsId" property="studentsId"/>
</resultMap>
<!-- 定义sql片段-->
<sql id="com-lon-bookslie">
id,
name,
author,
price,
publish,
students_id AS studentsId
</sql>
<!-- where的sql片段-->
<sql id="com-lon-where">
AND students_id =#{Id}
</sql>
<!-- 使用${}-->
<sql id="fragment_user_orderBy">
ORDER BY
<choose>
<when test="sortColumn !=null and sortColumn in ('id','name','price','publish','studentsId')">
${sortColumn}
</when>
<otherwise>id</otherwise>
</choose>
<choose>
<when test="sortDirection !=null and sortDirection in('ASC','DESC')">
${sortDirection}
</when>
<otherwise>ASC</otherwise>
</choose>
</sql>
<select id="selectALLL" parameterType="java.lang.String" resultMap="book_students">
select
<include refid="com-lon-bookslie"></include>
from books
<include refid="fragment_user_orderBy"></include>
</select>
</mapper>
Java类如下:
public class Books {
private int id;
private String name;
private String author;
private double price;
private String publish;
private Students students;
private int studentsId;
}
java接口如下:
public List<Books> selectALLL(
@Param("sortColumn")String a1,@Param("sortDirection")String a2
);
测试代码:
public class T {
private static final String MYBATIS_CONFIG_PATH = "mybatis-config.xml";
public static void main(String[] args) {
try (InputStream configInputStream = Resources.getResourceAsStream(MYBATIS_CONFIG_PATH);
SqlSession sqlSession = new SqlSessionFactoryBuilder()
.build(configInputStream)
.openSession()) {
BooksDao booksDao = sqlSession.getMapper(BooksDao.class);
List<Books> book = booksDao.selectALLL("studentsId","ASC");
book.forEach(System.out::println);
} catch (IOException e) {
// 5. 异常捕获与提示,便于排查问题
System.err.println("加载 MyBatis 配置文件失败:" + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
System.err.println("查询书籍数据失败:" + e.getMessage());
e.printStackTrace();
}
}
}
数据库图片:
执行结果:
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6a78afa0]
==> Preparing: select id, name, author, price, publish, students_id AS studentsId from books ORDER BY studentsId ASC
==> Parameters:
<== Columns: id, name, author, price, publish, studentsId
<== Row: 22, book_13, author_35, 84, publisher_13, 2
<== Row: 14, book_763, author_12, 41, publisher_3, 4
<== Row: 39, book_971, author_74, 93, publisher_18, 4
<== Row: 11, book_14, author_13, 74, publisher_16, 5
<== Row: 2, book_706, author_11, 55, publisher_17, 6
<== Row: 5, book_159, author_12, 23, publisher_6, 6
Books{id=22, name='book_13', author='author_35', price=84.0, publish='publisher_13', students=null, studentsId=2}
Books{id=14, name='book_763', author='author_12', price=41.0, publish='publisher_3', students=null, studentsId=4}
Books{id=39, name='book_971', author='author_74', price=93.0, publish='publisher_18', students=null, studentsId=4}
Books{id=11, name='book_14', author='author_13', price=74.0, publish='publisher_16', students=null, studentsId=5}
Books{id=2, name='book_706', author='author_11', price=55.0, publish='publisher_17', students=null, studentsId=6}
Books{id=5, name='book_159', author='author_12', price=23.0, publish='publisher_6', students=null, studentsId=6}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6a78afa0]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6a78afa0]
Returned connection 1786294176 to pool.
Process finished with exit code 0
可以看出执行成功了且和数据库一致。
注:sql语句执行我简单记忆为静(<sql>、<resultMap>、<mapper>)->参数->动态(<include>、<if>、<choose>、<foreach>、<where>),select等标签是混合态的;MyBatis 中 resultMap 的 column 匹配的是SQL 执行后返回的结果集列名。
片段职责区分:
纯静态(列列表、固定 join、固定 where)放 <sql>;与参数相关或带 <if> 的动态逻辑尽量放在主语句内,用 <where>/<if>/<choose> 控制。
7. 参数处理方式
其他的参数处理前面都有涉及现在介绍MyBatis 中 TypeHandler 处理枚举类型转换。
TypeHandler 处理枚举类型转换
数据库结构下图1-1:

1
1-1
接口如下图1-2:
public interface TestUserMapper {
@Select("SELECT id, username, password, nickname, phone, email, user_status as enue " +
"FROM testuser WHERE id = #{id}")
TestUser getTestUserById(@Param("id") Integer id);
}
1-2
以上的是注解的方式没有xml文件
再总xml中添加下图1-3:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置该语句可以看到计算机执行的语句和顺序-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 枚举-->
<typeHandlers>
<typeHandler handler="com.lo.mybatis.enue.UserEnueTypeHandler"/>
</typeHandlers>
<!--配置数据库的链接信息-->
<environments default="abc">
<environment id="abc">
<!-- 配置事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 配置数据库链接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_yc?serverTimezone=UTC&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 注册Mapper接口 -->
<mappers>
<mapper class="com.lo.mybatis.dao.TestUserMapper"/>
</mappers>
</configuration>
1-3
JSON类(实体类)下图1-4:
public class TestUser {
private int id;
private String username;
private String password;
private String nickname;
private String phone;
private String email;
private UserEnue enue;
@Override
public String toString() {
return "TestUser{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", nickname='" + nickname + '\'' +
", phone='" + phone + '\'' +
", email='" + email + '\'' +
", enue=" + enue.getResult() +enue.getCode()+
'}';
}
//setget省略
}
1-4
定义枚举类如下图1-5:
public enum UserEnue {
// 数据库到 Java字段(展示或是)
ZHENGCHANG(1,"正常"),
JINYONG(2,"禁用"),
SHANCHU(3,"删除");
//枚举私有属性
private final int code;
private final String result;
private UserEnue(int code, String result) {
this.code = code;
this.result = result;
}
public int getCode() {
return code;
}
public String getResult() {
return result;
}
// 查询并返回相应的值
public static UserEnue getJavaValue(int code){
for (UserEnue userEnue:
values()) {
if(userEnue.getCode()==code){
return userEnue;
}
}
throw new IllegalArgumentException("无效的用户状态码:" + code + ",仅支持1、2、3");
}
}
1-5
作用:例如数据库字段user_status字段存储的值是1查询时查询到的值也是1但是有枚举类和枚举转换器后会将对应的值赋给对应的属性,如 UserEnue enue的值不是一而是“禁用或其他”。
枚举类的处理器下图1-6:
@MappedTypes(UserEnue.class)
@MappedJdbcTypes(JdbcType.INTEGER)
public class UserEnueTypeHandler extends BaseTypeHandler<UserEnue> {
@Override
public void setParameter(PreparedStatement ps, int i, UserEnue parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i,parameter.getCode());
}
@Override
public UserEnue getResult(ResultSet rs, String columnName) throws SQLException {
int code= rs.getInt(columnName);
return code != 0 ? UserEnue.getJavaValue(code) :null;
}
@Override
public UserEnue getResult(ResultSet rs, int columnIndex) throws SQLException {
int code= rs.getInt(columnIndex);
return UserEnue.getJavaValue(code);
}
@Override
public UserEnue getResult(CallableStatement cs, int columnIndex) throws SQLException {
int code= cs.getInt(columnIndex);
return UserEnue.getJavaValue(code);
}
}
1-6
处除了第一个时从Java-->数据库,其余三个都是数据库-->Java。
执行代码下图1-7:
public class T {
private static final String MYBATIS_CONFIG_PATH = "mybatis-config.xml";
public static void main(String[] args) {
try (InputStream configInputStream = Resources.getResourceAsStream(MYBATIS_CONFIG_PATH);
SqlSession sqlSession = new SqlSessionFactoryBuilder()
.build(configInputStream)
.openSession()) {
TestUserMapper testUserMapper=sqlSession.getMapper(TestUserMapper.class);
TestUser testUserById = testUserMapper.getTestUserById(1);
System.out.println(testUserById);
} catch (IOException e) {
// 5. 异常捕获与提示,便于排查问题
System.err.println("加载 MyBatis 配置文件失败:" + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
System.err.println("查询数据失败:" + e.getMessage());
e.printStackTrace();
}
}
}
1-7
执行流程:加载配置文件-->调用getTestUserById(1)触发SQL执行-->Mybatis解析结果集,触发TypeHandler转换(Mybatis发现enue属性是UserEnue类型,并且配置了枚举类型处理器,Mybatis会调用这个处理器;Mybatis从结果集中取出enue(即数据库的 user_status))字段的值 1,作为 code 参数,调用 getJavaValue(1),最终赋值给enue属性;最后Mybatis封装后返回结果。
执行结果如下图1-8:
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2e005c4b]
==> Preparing: SELECT id, username, password, nickname, phone, email, user_status as enue FROM testuser WHERE id = ?
==> Parameters: 1(Integer)
<== Columns: id, username, password, nickname, phone, email, enue
<== Row: 1, zhangsan, e10adc3949ba59abbe56e057f20f883e, 张三, 13800138000, zhangsan@test.com, 1
<== Total: 1
TestUser{id=1, username='zhangsan', password='e10adc3949ba59abbe56e057f20f883e', nickname='张三', phone='13800138000', email='zhangsan@test.com', enue=正常1}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2e005c4b]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2e005c4b]
Returned connection 771775563 to pool.
1-8
动态sql前面有提及,这里就不作解释了。
更多推荐

所有评论(0)