Mybatis注解
Mybatis注解
开发流程
-
导入相关依赖
-
配置mybatis全局配置文件,创建实体类
-
创建mapper接口并使用注解在接口中定义sql操作
import org.apache.ibatis.annotations.*; import java.util.List; public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{id}") User getUserById(int id); @Select("SELECT * FROM users") List<User> getAllUsers(); @Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})") @Options(useGeneratedKeys = true, keyProperty = "id") void insertUser(User user); @Update("UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}") void updateUser(User user); @Delete("DELETE FROM users WHERE id = #{id}") void deleteUser(int id); } -
在全局配置文件中映射指定Mapper接口以及数据源信息
<configuration> <mappers> <mapper class="com.example.mapper.UserMapper"/> </mappers> </configuration> -
使用
SqlSessionFactory获取SqlSession,再获取 Mapper 接口实例- 可以纯注解,先不改了,流程不是重点
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class MyBatisExample { public static void main(String[] args) { SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(MyBatisExample.getClassLoader().getResourceAsStream("mybatis-config.xml")); try (SqlSession session = sqlSessionFactory.openSession()) { UserMapper mapper = session.getMapper(UserMapper.class); // 使用 Mapper 方法 User user = mapper.getUserById(1); System.out.println(user.getName()); } } }
SQL语句获取参数
-
使用
@Param注解-
@Param注解用于给方法参数命名,SQL 语句中可以通过#{参数名}的方式引用参数值@Update("UPDATE user SET username=#{username} WHERE id=#{id}") int updateUsernameById(@Param("id") int id, @Param("username") String username);
-
-
单个参数
-
如果方法只有一个参数,可以直接在 SQL 语句中通过
#{参数名}引用参数值,无需使用@Param注解@Select("SELECT * FROM user WHERE id = #{id}") User getUserById(int id);
-
-
默认参数名
-
如果方法参数没有使用
@Param注解,MyBatis 会使用默认的参数名-
单个参数:
_parameter。 -
多个参数:
param1,param2, ...@Select("SELECT * FROM user WHERE id = #{param1} AND username = #{param2}") User getUserByIdAndUsername(int id, String username);
-
-
-
使用 Map 传递参数
-
可以将参数封装到一个
Map中,SQL 语句中通过#{key}引用Map中的值@Update("UPDATE user SET username=#{username} WHERE id=#{id}") int updateUsernameById(Map<String, Object> params);
-
-
使用对象传递参数
-
可以将参数封装到一个 Java 对象中,SQL 语句中通过
#{属性名}引用对象的属性值public class User { private int id; private String username; // Getters and Setters } @Update("UPDATE user SET username=#{username} WHERE id=#{id}") int updateUsernameById(User user);
-
-
多个对象传递参数
-
通过
@Param注解为每个对象命名,SQL 语句中通过#{对象名.属性名}引用对象的属性值public class User { private int id; private String username; // Getters and Setters } public class Address { private String city; private String street; // Getters and Setters } @Update("UPDATE user SET username=#{user.username}, city=#{address.city} WHERE id=#{user.id}") int updateUserAndAddress(@Param("user") User user, @Param("address") Address address);
-
-
使用
#{}和${}的区别#{}:- 是预编译占位符,MyBatis 会将其替换为
?,并使用PreparedStatement设置参数。 - 可以防止 SQL 注入。
- 适用于传递参数值。
- 是预编译占位符,MyBatis 会将其替换为
${}:- 是字符串替换,MyBatis 会直接将其替换为参数值。
- 不能防止 SQL 注入。
- 适用于动态拼接 SQL 片段(如表名、列名)。
SQL语句设置返回值
-
基本返回值类型
-
基本返回值类型是指那些简单的、非结构化的数据类型,例如基本数据类型(
int、long等)、包装类型(Integer、Long等)、字符串类型(String)等。这些类型通常用于简单的查询或操作,比如统计查询、单值查询等。@Select("SELECT COUNT(*) FROM user") int countUsers(); @Select("SELECT age FROM user WHERE id = #{id}") int getAgeById(int id);
-
-
对象返回值类型
-
对象返回值类型是指将查询结果映射到一个 Java 对象(通常是实体类)。这种方式适用于查询结果包含多个字段,并且需要将这些字段映射到一个对象的场景。MyBatis 提供了多种方式来实现对象返回值类型的映射,包括自动映射和手动映射
-
自动映射
- MyBatis 支持自动将查询结果的列映射到实体对象的属性中。只要查询结果的列名与实体对象的属性名一致,MyBatis 就会自动完成映射
public class User { private int id; private String username; private String email; // Getters and Setters } @Select("SELECT id, username, email FROM user WHERE id = #{id}") User getUserById(int id); -
手动映射
- 如果查询结果的列名与实体对象的属性名不一致,可以使用
@Results和@Result注解手动指定映射关系
@Results({ @Result(property = "id", column = "user_id"), @Result(property = "username", column = "user_name"), @Result(property = "email", column = "user_email") }) @Select("SELECT user_id, user_name, user_email FROM user WHERE id = #{id}") User getUserByIdWithCustomMapping(int id); - 如果查询结果的列名与实体对象的属性名不一致,可以使用
-
嵌套对象映射
- 如果实体对象中包含嵌套对象(即对象属性是另一个对象),可以使用
@Result注解的columnPrefix属性来映射嵌套对象
public class Address { private String city; private String street; // Getters and Setters } public class User { private int id; private String username; private Address address; // Getters and Setters } @Results({ @Result(property = "id", column = "id"), @Result(property = "username", column = "username"), @Result(property = "address.city", column = "city"), @Result(property = "address.street", column = "street") }) @Select("SELECT id, username, city, street FROM user WHERE id = #{id}") User getUserWithAddressById(int id); - 如果实体对象中包含嵌套对象(即对象属性是另一个对象),可以使用
-
-
集合返回值类型
-
List集合
-
如果查询结果返回多条记录,并且每条记录需要映射为一个对象,可以直接将返回值类型设置为
List<实体类> -
自动映射
-
如果查询结果的列名与实体对象的属性名一致,MyBatis 会自动完成映射
-
public class User { private int id; private String username; private String email; // Getters and Setters } @Select("SELECT id, username, email FROM user") List<User> getAllUsers();
-
-
手动映射
-
如果查询结果的列名与实体对象的属性名不一致,可以使用
@Results和@Result注解手动指定映射关系 -
@Results({ @Result(property = "id", column = "user_id"), @Result(property = "username", column = "user_name"), @Result(property = "email", column = "user_email") }) @Select("SELECT user_id, user_name, user_email FROM user") List<User> getAllUsersWithCustomMapping();
-
-
-
Set集合
-
Set是一种不允许重复元素的集合,适用于需要去重的场景 -
@Select("SELECT DISTINCT username FROM user") Set<String> getAllUsernames();
-
-
Map集合
-
Map是一种键值对的集合,适用于将查询结果的某一列作为键,另一列作为值的场景 -
@Select("SELECT id, username FROM user") @MapKey("id") Map<Integer, User> getUserMap();@MapKey("id")注解指定将id列作为Map的键。- MyBatis 会将每条记录映射为
User对象,并将id和User对象放入Map中返回。 - 如果查询结果为空,返回一个空的
Map
-
-
-
嵌套集合
- 如果查询结果包含嵌套集合(即一个对象的属性是集合),可以使用
@Results和@Result注解手动映射
- 如果查询结果包含嵌套集合(即一个对象的属性是集合),可以使用
-
返回 Map 对象
-
如果查询结果需要以键值对的形式返回,可以将返回值类型设置为
Map -
MyBatis 会将查询结果的列名作为键,列值作为值,放入
Map中返回@Select("SELECT * FROM user WHERE id = #{id}") Map<String, Object> getUserByIdAsMap(int id);
-
嵌套查询
-
one的使用-
one属性用于处理 一对一 的关系,即一个对象中包含另一个对象 -
假设有两个表:
user表和address表,一个用户对应一个地址,根据用户表中的相关信息可以查询地址表 -
public class User { private int id; private String username; private Address address; // 一对一关系 // Getters and Setters } public class Address { private int id; private String city; private String street; // Getters and Setters } @Results({ @Result(property = "id", column = "id"), @Result(property = "username", column = "username"), @Result(property = "address", column = "address_id", one = @One(select = "getAddressById")) }) @Select("SELECT id, username, address_id FROM user WHERE id = #{id}") User getUserWithAddress(int id); @Select("SELECT id, city, street FROM address WHERE id = #{id}") Address getAddressById(int id); -
@Result(property = "address", column = "address_id", one = @One(select = "getAddressById")):property = "address": 指定User对象中的address属性。column = "address_id": 指定查询结果中的address_id列作为参数传递给嵌套查询。one = @One(select = "getAddressById"): 指定使用getAddressById方法查询Address对象。
-
getAddressById(int id): 根据address_id查询Address对象
-
-
many的使用-
many属性用于处理 一对多 的关系,即一个对象中包含一个集合 -
假设有两个表:
user表和order表,一个用户对应多个订单 -
public class User { private int id; private String username; private List<Order> orders; // 一对多关系 // Getters and Setters } public class Order { private int id; private String orderNo; // Getters and Setters } @Results({ @Result(property = "id", column = "id"), @Result(property = "username", column = "username"), @Result(property = "orders", column = "id", many = @Many(select = "getOrdersByUserId")) }) @Select("SELECT id, username FROM user WHERE id = #{id}") User getUserWithOrders(int id); @Select("SELECT id, order_no FROM orders WHERE user_id = #{userId}") List<Order> getOrdersByUserId(int userId); -
@Result(property = "orders", column = "id", many = @Many(select = "getOrdersByUserId")):property = "orders": 指定User对象中的orders属性。column = "id": 指定查询结果中的id列作为参数传递给嵌套查询。many = @Many(select = "getOrdersByUserId"): 指定使用getOrdersByUserId方法查询Order列表。
-
getOrdersByUserId(int userId): 根据user_id查询Order列表
-
动态SQL
-
MyBatis 注解本身不支持直接在注解中编写复杂的动态 SQL(如
<if>、<choose>等 XML 标签),但可以通过@SelectProvider、@UpdateProvider、@InsertProvider、@DeleteProvider等注解结合 Java 代码实现动态 SQL -
MyBatis 注解中实现动态 SQL 的主要方式是使用
@*Provider注解(如@SelectProvider、@UpdateProvider等),并编写一个 Java 类来生成动态 SQL -
常用的
@*Provider注解@SelectProvider: 动态生成查询 SQL。@UpdateProvider: 动态生成更新 SQL。@InsertProvider: 动态生成插入 SQL。@DeleteProvider: 动态生成删除 SQL。
-
MyBatis 提供了一个
SQL类,用于以编程方式构建 SQL 语句。SQL类的方法链式调用可以方便地生成动态 SQL -
参数传递: 当调用 Mapper 方法时,MyBatis 会将方法的参数封装到一个
Map中,键是参数名,值是参数值。 -
动态 SQL 生成: 在
Provider方法中,可以通过params.get("key")获取参数值,并根据参数值动态生成 SQL -
SQL类的常用方法SELECT(String): 添加SELECT子句。FROM(String): 添加FROM子句。WHERE(String): 添加WHERE子句。SET(String): 添加SET子句。INSERT_INTO(String): 添加INSERT INTO子句。VALUES(String, String): 添加VALUES子句。UPDATE(String): 添加UPDATE子句。DELETE_FROM(String): 添加DELETE FROM子句。AND(): 添加AND条件。OR(): 添加OR条件
动态查询
根据条件动态生成查询 SQL。
-
Java类
public class UserSqlProvider { public String findUserByCondition(Map<String, Object> params) { return new SQL() {{ SELECT("*"); FROM("user"); if (params.get("username") != null) { WHERE("username = #{username}"); } if (params.get("age") != null) { WHERE("age = #{age}"); } if (params.get("city") != null) { WHERE("city = #{city}"); } }}.toString(); } } -
Mapper接口
@SelectProvider(type = UserSqlProvider.class, method = "findUserByCondition") List<User> findUserByCondition(@Param("username") String username, @Param("age") Integer age, @Param("city") String city);
动态更新
根据条件动态生成更新 SQL
-
Java类
public class UserSqlProvider { public String updateUser(Map<String, Object> params) { return new SQL() {{ UPDATE("user"); if (params.get("username") != null) { SET("username = #{username}"); } if (params.get("age") != null) { SET("age = #{age}"); } if (params.get("city") != null) { SET("city = #{city}"); } WHERE("id = #{id}"); }}.toString(); } } -
Mapper 接口
@UpdateProvider(type = UserSqlProvider.class, method = "updateUser") int updateUser(@Param("id") int id, @Param("username") String username, @Param("age") Integer age, @Param("city") String city);
动态插入
根据条件动态生成插入 SQL
-
Java类
public class UserSqlProvider { public String insertUser(Map<String, Object> params) { return new SQL() {{ INSERT_INTO("user"); if (params.get("username") != null) { VALUES("username", "#{username}"); } if (params.get("age") != null) { VALUES("age", "#{age}"); } if (params.get("city") != null) { VALUES("city", "#{city}"); } }}.toString(); } } -
Mapper 接口
@InsertProvider(type = UserSqlProvider.class, method = "insertUser") int insertUser(@Param("username") String username, @Param("age") Integer age, @Param("city") String city);
动态删除
根据条件动态生成删除 SQL
-
Java类
public class UserSqlProvider { public String deleteUser(Map<String, Object> params) { return new SQL() {{ DELETE_FROM("user"); if (params.get("id") != null) { WHERE("id = #{id}"); } if (params.get("username") != null) { WHERE("username = #{username}"); } }}.toString(); } } -
Mapper接口
@DeleteProvider(type = UserSqlProvider.class, method = "deleteUser") int deleteUser(@Param("id") Integer id, @Param("username") String username);
分页查询-PageHelper
PageHelper是 MyBatis 的一个非常流行的开源分页插件,它可以极大地简化分页查询的实现。通过拦截 MyBatis 的 SQL 语句,PageHelper能够自动为查询添加分页逻辑,并返回分页结果。
PageHelper 的核心功能
- 自动分页:只需在查询前调用
PageHelper.startPage方法,后续的查询会自动进行分页。 - 支持多种数据库:PageHelper 支持 MySQL、Oracle、SQL Server 等多种数据库,并自动适配数据库的分页语法。
- 返回分页信息:除了分页数据外,还可以返回总条数、总页数等分页信息。
- 易于集成:只需简单的配置即可集成到 MyBatis 项目中。
PageHelper.startPage 的作用范围
- 生效范围:
PageHelper.startPage只会对紧随其后的第一条查询语句生效。 - 如果需要同时对多条查询语句进行分页,需要为每条查询语句单独调用
PageHelper.startPage。 - 失效条件:
- 如果执行了第一条查询语句,分页参数会自动清除。
- 如果调用了
PageHelper.clearPage(),分页参数也会被清除。
集成 PageHelper
-
在 Maven 项目中,添加 PageHelper 的依赖:
-
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.2</version> <!-- 以最新版本为准 --> </dependency>
-
-
配置插件
-
mybatis-config.xml中配置 PageHelper 插件: -
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 指定数据库方言 --> <property name="helperDialect" value="mysql"/> <!-- 分页合理化:当页码超出范围时,自动调整到合理范围 --> <property name="reasonable" value="true"/> <!-- 支持通过 Mapper 接口参数传递分页参数 --> <property name="supportMethodsArguments" value="true"/> <!-- 自动检测数据库方言 --> <property name="autoDialect" value="true"/> </plugin> </plugins> -
SpringBoot中配置
-
# PageHelper 配置 pagehelper.helperDialect=mysql pagehelper.reasonable=true pagehelper.supportMethodsArguments=true pagehelper.autoDialect=true
-
-
使用PageHelper 实现分页
-
在查询方法前调用
PageHelper.startPage,后续的查询会自动分页。-
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; public PageInfo<User> getUsersByPage(int pageNum, int pageSize) { // 开始分页,pageNum 是当前页码,pageSize 是每页条数 PageHelper.startPage(pageNum, pageSize); // 正常查询,无需手动编写分页 SQL List<User> users = userMapper.selectAllUsers(); // 返回分页信息 return new PageInfo<>(users); }
-
-
分页结果
PageInfo-
PageInfo是 PageHelper 提供的分页结果类,包含了分页信息和数据列表。常用属性如下:list:当前页的数据列表。pageNum:当前页码。pageSize:每页条数。total:总条数。pages:总页数。hasPreviousPage:是否有上一页。hasNextPage:是否有下一页。
-
PageInfo<User> pageInfo = getUsersByPage(1, 10); System.out.println("当前页: " + pageInfo.getPageNum()); System.out.println("每页条数: " + pageInfo.getPageSize()); System.out.println("总条数: " + pageInfo.getTotal()); System.out.println("总页数: " + pageInfo.getPages()); System.out.println("数据列表: " + pageInfo.getList());
-
-
方法的作用
PageHelper.startPage(pageNum, pageSize):- 调用 PageHelper 的
startPage方法,开启分页功能。 - 后续的查询会自动添加分页逻辑(如 MySQL 的
LIMIT和OFFSET)。
- 调用 PageHelper 的
userMapper.selectAllUsers():- 执行查询操作,获取当前页的数据。
- 由于开启了分页,查询结果只会返回当前页的数据(如第 1 页的 10 条数据)。
new PageInfo<>(users):- 将查询结果封装到
PageInfo对象中。 PageInfo会自动计算总条数、总页数等分页信息。
- 将查询结果封装到
-
注意事项:
- 线程安全问题
PageHelper.startPage是基于ThreadLocal实现的,因此必须确保每次分页查询都在独立的线程中执行。- 如果使用了线程池,需要在每次查询后调用
PageHelper.clearPage()清理分页参数。
- SQL 优化
- PageHelper 会自动生成分页 SQL,但对于复杂查询(如多表关联查询),可能需要手动优化 SQL。
- 数据库方言
- 如果项目需要支持多种数据库,建议将
autoDialect设置为true,PageHelper 会自动检测数据库类型。
- 如果项目需要支持多种数据库,建议将

浙公网安备 33010602011771号