MyBatis核心对象
根据以下这四大核心对象,我们就能理清MyBatis的工作原理。
SqlSession对象,该对象中包含了执行SQL语句的所有方法。类似于JDBC里面的Connection。
Executor接口,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。类似于JDBC里面的Statement/PrepareStatement。
MappedStatement对象,该对象是对映射SQL的封装,用于存储要映射的SQL语句的id、参数等信息。
ResultHandler对象,用于对返回的结果进行处理,最终得到自己想要的数据格式或类型。可以自定义返回类型。
MyBatis工作原理
上面中流程就是MyBatis内部核心流程,每一步流程的详细说明如下文所述:
读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。
加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
构造会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。
创建会话对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
MappedStatement对象。在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
输入参数映射。输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
输出结果映射。输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。
注意:在MyBatis中,SqlSession对象包含了执行SQL语句的所有方法。但是它是委托Executor执行的。从某种意义上来看,MyBatis里面的SqlSession类似于JDBC中的Connection,它们都是委托给其它类去执行。
MyBatis整合Log4j框架
Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
SLF4J
Apache Commons Logging
Log4j 2
Log4j
JDK logging
sqlSession.close();MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。
通过在 MyBatis 配置文件 mybatis-config.xml 里面添加一项 setting来选择其它日志实现。这里以log4j为例。
<configuration> <settings> ... <setting name="logImpl" value="LOG4J"/> ... </settings> </configuration>配置log4j,假设你需要记录这个映射器的日志:
package com.xyr.dao; import com.xyr.domain.User; import org.apache.ibatis.annotations.Select; public interface UserMapper { @Select("select * from user where id = #{id}") User selectUserById(Integer id); }在应用的类路径中创建一个名为 log4j.properties 的文件,文件的具体内容如下:
# 全局日志配置 log4j.rootLogger=ERROR, stdout # MyBatis 日志配置 log4j.logger.com.xyr.dao.UserMapper=TRACE # 控制台输出 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n实现结果如下:
上述配置将使 Log4J 详细打印 com.xyr.dao.UserMapper 的日志,对于应用的其它部分,只打印错误
信息。
为了实现更细粒度的日志输出,你也可以只打印特定语句的日志。以下配置将只打印语句selectUserById 的日志:
log4j.logger.com.xyr.dao.UserMapper.selectUserById=TRACE为了实现更粗粒度的日志输出,你也可以打印所有mapper的日志。以下配置将只打印所有mapper 的日志:
log4j.logger.com.xyr.dao=TRACE在实际开发中往往查出来的数据经常可能会有成百上千条,如果希望不显示查询出来的数据,让控制台更加清晰,可以设置日志的显示级别,设置为DEBUG级别可以不显示查询结果,如下所示:
log4j.logger.com.xyr.dao.UserMapper=DEBUG注意:为映射器和 XML 文件打开日志功能的语句毫无差别。
MyBatis基于XML配置实现CRUD
引入自定义代码工具类MyBatisUtils
package com.xyr.utils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; /** * @Title 针对MyBatis框架的一些固定步骤进行二次封装 * @Author zhk * @Description: */ public class MyBatisUtils { private static SqlSessionFactory factory; private MyBatisUtils() {} static { try { InputStream is = Resources.getResourceAsStream("mybatisconfig.xml"); factory = new SqlSessionFactoryBuilder().build(is); } catch (Exception e) { e.printStackTrace(); } } public static SqlSession getSqlSession() { return factory.openSession(); } public static void close(SqlSession session) { if (Objects.nonNull(session)) { session.close(); } } }SqlSession默认不自动提交事务,可使用sqlSessionFactory.openSession(true); 来自动提交事务。
添加操作
添加用户记录
UserMapper类代码如下所示:
public interface UserMapper { int insertUser(User u); }UserMapper.xml配置如下所示:
<insert id="insertUser" parameterType="com.xyr.domain.User"> insert into user(name, password, age) values(#{name}, #{password}, #{age}) </insert>测试代码如下所示:
package com.xyr.dao; import com.xyr.domain.User; import com.xyr.utils.MyBatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Before; import org.junit.Test; /** * @Title * @Author Xyr * @Description: */ public class UserMapperTest { private SqlSession sqlSession; private UserMapper mapper; @Before public void init() { sqlSession = MyBatisUtils.getSqlSession(); mapper = sqlSession.getMapper(UserMapper.class); } @After public void destroy() { MyBatisUtils.close(sqlSession); } @Test public void testInsert() { User u = new User(); u.setName("张三"); u.setPassword("123456"); u.setAge(18); mapper.insertUser(u); sqlSession.commit(); } }注意:添加操作属于DML语句,针对DML语句需要进行事务的提交,所以需要手动调用sqlSession对象的commit方法进行显示提交。
主键回填
在增加操作中,如果设置了主键id是自增的,那么插入数据时id的值一般写null,因为存入数据库时id都会自动+1,看似操作没毛病,非常完美。
但是在购物交易的添加模块这可不兴这样,细想一下在上网买东西时,用户付完钱之后一般都会返回一个订单编号,其实这个订单编号在数据库中就是主键,但是因为在插入数据时id的值写了null,所以订单编号也为null,也就是说返回不了订单编号的数据。
方式一:使用 useGeneratedKeys 属性
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id"> insert into user(name, password, age) values(#{name}, #{password}, #{age}) </insert>方式二:使用 selectKey 标签
<insert id="insertUser" parameterType="User"> <selectKey keyProperty="id" keyColumn="id" resultType="java.lang.Integer" order="AFTER"> select last_insert_id() </selectKey> insert into user(name, password, age) values(#{name}, #{password}, #{age}) </insert>如果id的值是字符串类型的uuid时,数据库就不能够主键自动生成,那么可以考虑使用方式二来实现
<insert id="insertUser2" parameterType="com.xyr.domain.User2"> <selectKey resultType="java.lang.String" keyColumn="id" keyProperty="id" order="BEFORE"> select uuid(); </selectKey> insert into t_user(id, name, password, age) values(#{id}, #{name}, #{password}, #{age}) </insert>详细请参考: Mybatis---主键回填 (*^▽^*)_数据库_Lynn Lu-DevPress官方社区 (csdn.net)
删除操作
通过id删除用户记录
更新操作
通过id更新用户记录
测试代码如下:
@Test public void testUpdate() { User u = new User(); u.setId(11); u.setName("宋小宝"); u.setPassword("666666"); u.setAge(30); mapper.updateUserById(u); sqlSession.commit(); }查询操作
通过id查询单个用户
查询所有用户
测试代码如下:
@Test public void testSelectAllUsers() { mapper.selectAllUsers(); }