在Java持久层开发领域,MyBatis凭借其轻量化、高灵活性的特性,成为连接Java应用与数据库的主流框架。它摒弃了JDBC繁琐的代码编写,通过“接口+XML”的映射模式,让开发者专注于SQL逻辑本身。本文将基于一套完整的用户数据操作代码,从核心架构、模块解析到实战运行逻辑,全方位拆解MyBatis实现CRUD(增删改查)、主键返回、模糊查询的核心流程,帮助开发者快速掌握MyBatis的实战应用精髓。
一、核心架构与前置依赖说明
在深入代码解析前,我们先明确这套实战代码的核心架构与运行依赖。整套代码遵循MyBatis“接口绑定SQL”的核心设计思想,主要分为三大核心模块:
-
Mapper映射文件(UserDao.xml):存储所有数据库操作的SQL语句,通过命名空间(namespace)与Dao接口建立绑定关系,是MyBatis实现SQL与Java代码解耦的核心载体。
-
Dao接口(UserDao.java):定义用户数据操作的抽象方法,无需编写实现类——MyBatis会通过动态代理机制,自动生成接口的实现类,完成接口方法与Mapper中SQL语句的关联。
-
测试类(UserTest.java):基于JUnit框架实现单元测试,封装了MyBatis的初始化(加载配置、创建SqlSession)与资源释放逻辑,为每个数据操作方法提供独立的测试入口。

运行这套代码需提前引入以下核心依赖(以Maven为例,配置在pom.xml中),确保MyBatis、数据库驱动及测试框架正常工作:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
此外,需准备MyBatis核心配置文件(SqlMapConfig.xml),用于配置数据库连接信息、Mapper映射文件扫描路径等核心参数,这是MyBatis初始化的基础。
SqlMapConfig.xml:
<?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>
<environments default="mysql">
<environment id="mysql">
<!--配置事务的类型,使用本地事务策略-->
<transactionManager type="JDBC"></transactionManager>
<!--是否使用连接池 POOLED表示使用链接池,UNPOOLED表示不使用连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
<property name="username" value="用户名"/>
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
二、核心模块深度解析
模块1:Mapper映射文件(UserDao.xml)—— SQL与接口的桥梁
Mapper文件是MyBatis的核心配置文件之一,每个SQL标签对应一个数据库操作,通过属性与Dao接口的方法精准绑定。下面逐一对标签进行解析:
1.1 命名空间(namespace)绑定Dao接口
文件开头的namespace="dao.UserDao"是核心绑定配置,它指定当前Mapper文件对应dao包下的UserDao接口。MyBatis正是通过这个配置,将SQL标签与接口中的方法建立映射关系。
<mapper namespace="dao.UserDao">
//写Mybatis标签...
</mapper>
1.2 查操作:select标签的应用
查操作是数据库最常用的操作,对应两个核心方法:查询所有用户(findAll)和根据ID查询用户(findById)。
<select id="findAll" resultType="entity.User">
select * from user
</select>
<select id="findById" resultType="entity.User" parameterType="java.lang.Integer">
select * from user where id = #{id}
</select>
-
findAll方法:对应标签
<select id="findAll" resultType="entity.User">。id="findAll"与UserDao接口中的findAll()方法名一致;resultType="entity.User"指定查询结果的返回类型——将查询到的每条记录封装为entity包下的User实体类对象,最终返回User对象的集合(List<User>)。SQL语句select * from user用于查询user表中的所有记录。 -
findById方法:对应标签
<select id="findById" resultType="entity.User" parameterType="java.lang.Integer">。新增的parameterType="java.lang.Integer"指定方法的参数类型为Integer(即接口中findById(int id)的int类型,自动装箱为Integer);SQL语句select * from user where id = #{id}中的#{id}是MyBatis的参数占位符,用于接收接口方法传入的id参数,实现精准查询。
1.3 增操作:insert标签的两种场景
增操作对应两个标签:普通插入(insertUser)和插入并返回主键(insertGetId),满足不同业务场景需求。
<insert id="insertUser" parameterType="entity.User">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
<insert id="insertGetId" parameterType="entity.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
-
普通插入(insertUser):标签
<insert id="insertUser" parameterType="entity.User">中,parameterType="entity.User"指定参数类型为User实体类——接口方法insertUser(User user)接收User对象,SQL语句中#{username}、#{birthday}等占位符,分别对应User对象的username、birthday等属性(通过getter方法获取属性值),实现将User对象的数据插入到user表的对应字段中。 -
插入并返回主键(insertGetId):在很多业务场景中,插入数据后需要获取自动生成的主键(如user表的id字段为自增主键),此时需借助
<selectKey>子标签。标签中keyProperty="id"指定将查询到的主键值封装到User对象的id属性中;resultType="int"指定主键类型为int;order="AFTER"表示先执行插入操作,再执行查询主键的操作(因为自增主键需先插入数据才能生成)。子标签内的select LAST_INSERT_ID()是MySQL的内置函数,用于获取最后一次插入操作生成的自增主键值。后续的插入SQL与普通插入一致,最终插入成功后,传入的User对象的id属性会被赋值为生成的主键。
1.4 改操作:update标签的应用
<update id="update" parameterType="entity.User">
update user set username = #{username},birthday = #{birthday},sex = #{sex},address = #{address} where id = #{id}
</update>
改操作对应update标签:<update id="update" parameterType="entity.User">。id="update"与接口的update(User user)方法绑定,参数类型仍为User实体类。SQL语句update user set username = #{username},birthday = #{birthday},sex = #{sex},address = #{address} where id = #{id}中,通过User对象的属性值更新user表的对应字段,where条件中的id用于定位要更新的记录,确保更新的精准性。
1.5 删操作:delete标签的应用
<delete id="delete" parameterType="java.lang.Integer">
delete from user where id = #{id}
</delete>
删操作对应delete标签:<delete id="delete" parameterType="java.lang.Integer">。id="delete"与接口的delete(int id)方法绑定,参数类型为Integer。SQL语句delete from user where id = #{id}通过传入的id参数,删除user表中对应的记录。
1.6 模糊查询:likeByName方法
<select id="likeByName" resultType="entity.User" parameterType="java.lang.String">
select * from user where username like concat('%',#{username},'%')
</select>
模糊查询对应likeByName方法,标签为<select id="likeByName" resultType="entity.User" parameterType="java.lang.String">。参数类型为String(接收用户名关键字),SQL语句select * from user where username like concat('%',#{username},'%')中,使用MySQL的concat函数拼接模糊查询的通配符%(%表示任意长度的字符),实现“包含关键字”的模糊查询。例如传入“小”,则查询所有用户名为“小明”“小王”等包含“小”字的用户。这种拼接方式可避免SQL注入风险,比直接拼接字符串更安全。
模块2:Dao接口(UserDao.java)—— 数据操作的抽象定义
UserDao是一个纯接口,仅定义数据操作的方法签名,无需编写实现类。MyBatis的动态代理机制会在运行时自动生成接口的实现类,将接口方法与Mapper中的SQL标签关联起来。接口方法的设计需遵循以下核心规则:
-
方法名必须与Mapper文件中对应SQL标签的id属性完全一致(如findAll对应id="findAll")。
-
方法的参数类型必须与Mapper标签的parameterType属性一致(或兼容,如int与Integer可兼容)。
-
方法的返回值类型必须与Mapper标签的resultType属性一致(集合类型需对应单个实体类类型,如List<User>对应resultType="entity.User")。
public interface UserDao {
public List<User> findAll();
public User findById(int id);
public Integer insertUser(User user);
public int update(User user);
public int delete(int id);
public Integer insertGetId(User user);
public List<User> likeByName(String username);
}
接口中7个方法的对应关系清晰:findAll()对应查询所有用户,返回List<User>;findById(int id)对应根据ID查询单个用户,返回User;insertUser(User user)对应普通插入,返回Integer(影响的行数);update(User user)对应更新操作,返回int(影响的行数);delete(int id)对应删除操作,返回int(影响的行数);insertGetId(User user)对应插入并返回主键,返回Integer(影响的行数,主键通过User对象的id属性获取);likeByName(String username)对应模糊查询,返回List<User>。
模块3:测试类(UserTest.java)—— 验证数据操作的正确性
UserTest类基于JUnit 4框架编写,通过@Before、@After、@Test注解实现测试流程的自动化。核心逻辑是封装MyBatis的初始化与资源释放,确保每个测试方法独立、可重复执行。
3.1 成员变量与初始化方法(init)
类中定义了三个核心成员变量:InputStream in(用于读取MyBatis核心配置文件)、SqlSession session(MyBatis的核心会话对象,用于执行SQL操作)、UserDao mapper(UserDao接口的代理对象,通过它调用接口方法)。
@Before注解标记的init()方法,会在每个@Test方法执行前执行,完成MyBatis的初始化:
-
通过
Resources.getResourceAsStream("SqlMapConfig.xml")加载核心配置文件,获取输入流in。 -
通过SqlSessionFactoryBuilder的build()方法,传入输入流in创建SqlSessionFactory对象(SqlSessionFactory是创建SqlSession的工厂,线程安全,通常整个应用只创建一个)。
-
通过SqlSessionFactory的openSession()方法创建SqlSession对象(SqlSession是非线程安全的,每个测试方法需独立创建,用完及时关闭)。
-
通过SqlSession的getMapper(UserDao.class)方法,获取UserDao接口的动态代理对象mapper,后续通过mapper调用接口方法即可执行对应的SQL操作。
public class UserTest {
private InputStream in = null;
private SqlSession session = null;
private UserDao mapper = null;
@Before //前置通知, 在方法执行之前执行
public void init() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession();
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
}
}
3.2 资源释放方法(destory)
@After注解标记的destory()方法,会在每个@Test方法执行后执行,完成资源的释放:关闭SqlSession对象和输入流in。这是避免资源泄漏的关键步骤,确保数据库连接等资源被及时回收。
@After //@After: 后置通知, 在方法执行之后执行 。
public void destory() throws IOException {
//释放资源
session.close();
in.close();
}
3.3 各操作的测试方法
每个@Test注解标记的方法对应一个数据操作的测试,逻辑清晰,可独立运行验证:
-
findAll测试:调用mapper.findAll()获取所有用户,通过for循环遍历集合并打印User对象(需确保User类重写了toString()方法,否则打印的是对象地址)。
-
findById测试:调用mapper.findById(1)查询id为1的用户,打印查询到的User对象,验证精准查询功能。
-
insertUser测试:创建User对象,通过setter方法设置用户名、生日、性别、地址、id等属性,调用mapper.insertUser(user)执行插入操作,打印影响的行数(插入成功返回1)。注意:MyBatis的SqlSession默认不自动提交事务,因此需手动调用session.commit()提交事务,否则插入操作不会生效。
-
update测试:创建User对象,设置要更新的属性值(重点是id,用于定位记录),调用mapper.update(user)执行更新操作,打印影响的行数,同样需调用session.commit()提交事务。
-
delete测试:调用mapper.delete(2)删除id为2的用户,打印影响的行数,调用session.commit()提交事务。
-
insertGetId测试:创建User对象,设置除id外的其他属性(id为自增),调用mapper.insertGetId(user)执行插入操作,插入成功后,通过user.getId()即可获取生成的自增主键并打印,最后提交事务。
-
likeByName测试:调用mapper.likeByName("小")查询所有用户名包含“小”字的用户,遍历集合并打印,验证模糊查询功能。
@Test
public void findAll(){
List<User> users = mapper.findAll();
for (User user:users){
System.out.println(user.toString());
}
}
@Test
public void findById(){
User user = mapper.findById(1);
System.out.println(user.toString());
}
@Test
public void InsertUser(){
User user = new User();
user.setUsername("小王");
user.setBirthday(new Date());
user.setSex("女");
user.setAddress("云南");
user.setId(1);
int count = mapper.insertUser(user);
System.out.println(count);
session.commit();
}
@Test
public void update(){
User user = new User();
user.setUsername("小王");
user.setBirthday(new Date());
user.setSex("女");
user.setAddress("云南");
user.setId(1);
int count = mapper.update(user);
System.out.println(count);
session.commit();
}
@Test
public void delete(){
int count = mapper.delete(2);
System.out.println(count);
session.commit();
}
@Test
public void insertGetId(){
User user = new User();
user.setUsername("王六");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("河北");
mapper.insertGetId(user);
System.out.println(user.getId());
session.commit();
}
@Test
public void likeByName(){
List<User> users = mapper.likeByName("小");
for (User user:users){
System.out.println(user.toString());
}
}
三、核心运行流程与原理总结
结合上述三个模块,整套MyBatis用户操作的核心运行流程可总结为以下几步:
-
加载配置:测试方法执行前,通过init()方法加载SqlMapConfig.xml配置文件,创建SqlSessionFactory对象。
-
创建会话与代理对象:通过SqlSessionFactory创建SqlSession对象,再通过SqlSession获取UserDao接口的动态代理对象mapper。
-
执行SQL:调用mapper的对应方法(如findAll()),MyBatis通过动态代理机制,根据接口方法名匹配Mapper文件中对应id的SQL标签,解析SQL语句并传入参数。
-
结果封装:MyBatis执行SQL后,将查询结果封装为指定的实体类对象(或集合),返回给调用者;增删改操作则返回影响的行数。
-
事务提交与资源释放:增删改操作需手动提交事务(session.commit()),所有操作执行完成后,通过destory()方法关闭SqlSession和输入流,释放资源。
四、实战价值与拓展思考
这套代码覆盖了MyBatis的核心使用场景,是实际开发中用户模块的基础模板。其核心价值在于:
-
解耦:通过Mapper文件将SQL与Java代码分离,便于SQL的维护与优化(无需修改Java代码即可调整SQL)。
-
高效:MyBatis自动完成参数映射与结果封装,避免了JDBC繁琐的手动封装操作,提升开发效率。
-
灵活:支持原生SQL编写,可根据复杂业务场景定制SQL语句,比全自动化ORM框架(如Hibernate)更灵活。
拓展思考:在实际开发中,可基于这套代码进行优化,例如:使用分页插件实现分页查询、通过resultMap解决实体类属性与数据库字段名不一致的问题、配置事务管理器实现自动提交事务、使用注解替代部分XML配置等。掌握这套基础流程后,再学习这些高级特性会更加轻松。
总结:本文通过完整的代码案例,从模块解析、运行流程到实战价值,全面讲解了MyBatis实现用户CRUD操作的核心逻辑。希望通过本文的解析,能帮助开发者快速掌握MyBatis的实战应用,为后续开发复杂的持久层模块奠定基础。
884

被折叠的 条评论
为什么被折叠?



