深入理解MyBatis框架

本文深入讲解MyBatis的入门知识、执行流程、自定义类型转换器、对象工厂及拦截器,帮助读者理解MyBatis的工作原理及高级特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、MyBatis入门

MyBatis下载

MyBatis中文文档

关于Mybatis入门相关的知识,我相信没有人比官方文档更详细了,详见中文官方文档,新手半天时间即可入门。

 

二、MyBatis执行流程

1.将sql语句和数据库配置信息保存在XML配置文件


2.在MyBatis运行时,将配置信息存储Configuration对象【因为SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory的实例(实际为DefaultSqlSessionFactory),不管是自己创建Configuration 构建器还是通过读取xml构建Configuration对象,本质上都是把配置信息存储Configuration对象中】


3.通过SqlSessionFactory创建SqlSession对象提供属性

         1)    autoCommit = false  是否自动提交
        2) Configuration对象
        3) dirty:true   sql语句执行完毕后      可以事务提交
                        false  sql语句执行发送错误  事务进行回滚(一开始为false,在执行sql语句后改为true)
        4) Executor执行器对象:
                         创建Statement对象,在创建过程中依靠MapperStatement对象将赋值内容与sql占位符进行绑定

4.此时获得SqlSession即可以对数据库进行操作

    疑问:  如代码 sqlSession.insert("insertProduct",Product)

<mapper namespace="Product">
    <insert id = "insertProduct">
        INSERT INTO product(pname,price) VALUES(#{pname},#{price})
    </insert>
</mapper>

(1)mybatis是如何将sql语句的id与xml中的sql语句进行对应呢?

答: MappedStatement ms = configuration.getMappedStatement(statement);   //在构建Configuration,存在一个HashMap,key为xml中的namespace+id值,如上述例子所示的话key即为"Product.insertProduct",value为MappedStatement对象。这个MappedStatement对象中有几个重要的属性,如id即为"Product.insertProduct",resource为"ProductMapper.xml",sqlSource属性中存储着具体要执行的sql语句"INSERT INTO product(pname,price) VALUES(?,?)"

 

(2)mybatis如何将实体类数据绑定到sql语句中呢? 

<insert id = "userSave">
	insert into user values(#{userId},#{username},#{job})
</insert>

首先所有的namespace不能为null或"",把所有的#{}都换为占位符?   ,后把参数名"userId"、"username"、"job"三个参数名放到一个ArrayList集合里面【顺序需要与sql参数顺序一致】,在使用时候使用反射机制从当前对象中读取属性进行反射赋值。

用户传递一个装载着数据的实体对象,如何把这个实体对象添加到上述的占位符中呢?首先它会判断这个实体对象是集合,是否是数组,如果都不是则直接返回这个实体对象继续处理。

在执行完prepareStatement(...)方法后,实体数据封装到了sql语句占位符中,实际上这个stmt的真实类型为JDBC4PreparedStatement对象,在后续代码中又被强转为了PreparedStatement

 

(3)mybatis如何传输sql语句到数据库呢?      

boundSql内部存储了sql属性(存放着sql语句),还有parameterMappings(读取实体类的哪个属性,从哪读取等信息)

接合上述的三个问题,不难看出,Mybatis就是对jdbc的良好封装!!!

5.SqlSession.commit(); 根据此时dirty属性决定提交和回滚

底层最终调用的是connection.commit();


6.SqlSession.close();   关闭SqlSession,实际上是将connection放回连接池中

 

三、Mybatis自定义类型转换器

下面我们将来自定义我们的类型转换器,参照《mybatis官方文档——类型转换器TypeHandler》

Mybatis中的核心配置mybatis-config.xml标签都对应一个接口,而接口下又会有若干的实现类针对不同的情况进行相应的处理,mybatis允许我们对某些接口的实现类进行重新定义,这是mybatis的设计理念,当mybatis的内置接口实现类不能满足需求时,则开发者可以自己声明一个实现类,就如我们正要说的TypeHandler类型转换器

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集(ResultSet)中取出一个值时, 都会用类型处理器(TypeHandler)处理数据库类型与Java类型之间的转换。

比如有这么一个业务场景,在mysql部门表有包含一个字段flag(int类型)

但在Java实体类Dept部门类中含有一个boolean flag属性,此时Java Types(boolean)和JDBC Types(int)并不对应。

由上标可知,Java中的boolean类型就只和数据库中的boolean类型对应,并不会帮我们转换成int类型作为解析,此时就需要我们定义一个类型转换器规则来告诉Mybatis,当将Java Types中的boolean转换为JDBC Types中的int,就是用我们自定义的类型转换器,做法如下:

(1)创建自定义类型转换器MyTypeHandler实现TypeHandler接口

/*
 *     setParameter方法:
 *                  在生成SQL语句时被调用
 *     getResult:
 *               查询结束之后,在将ResultSet数据行装换为实体类对象时
 *               通知TypeHandler将当前数据行某个字段转换为何种类型
 * */
public class MyTypeHandler implements TypeHandler {

	// 将被生成的PreparedStatement对象,i表示占位符的位置,parameter代表所给的参数(即Dept实体类中的flag属性)
	// 为SQL语句占位符赋值操作
	public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
		if (parameter == null) {// dept.flag=null insertsql flag设置0(根据业务灵活设置)
			ps.setInt(i, 0);
			return;
		}
		System.out.println("类型转换器开始工作");
		Boolean flag = (Boolean) parameter;
		if (flag == true) {
			ps.setInt(i, 1);
		} else {
			ps.setInt(i, 0);
		}
	}

	public Object getResult(ResultSet rs, String columnName) throws SQLException {
		int flag = rs.getInt(columnName);
		Boolean myFlag = Boolean.FALSE;
		if (flag == 1) {
			myFlag = Boolean.TRUE;
		}
        //返回后会将值设置到columnName所对应的实体类对象
		return myFlag;
	}

	public Object getResult(ResultSet rs, int columnIndex) throws SQLException {
		return null;
	}

	public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {
		return null;
	}
}

(2)在MyBatis核心配置文件mybatis-config.xml注册自定义类型转换器

注意标签是有先后顺序的!!!

<typeHandlers>
  <!-- 声明自定义类型转换器的位置 当Java类型为Boolean,对应jdbcType为NUMERIC类型时,使用自定义转换器自动转换 -->
  <typeHandler handler="cn.itcats.utils.MyTypeHandler"
  javaType="Boolean" jdbcType="NUMERIC" />
</typeHandlers>

(3)在Mapper.xml文件中指定使用自定义类型转换器场合

<resultMap type="Dept" id="DeptResultMap">
  <result column="flag" property="flag"
  typeHandler="cn.itcats.utils.MyTypeHandler" />
</resultMap>

(4)在查询Statement中指定对应的ResultMap

<select id="deptFindAll" resultMap="DeptResultMap">
  select * from dept
</select>

 

四、Mybatis自定义对象工厂(objectFactory)

MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。比如存在该业务场景:

Dept类中包含字段country,而数据库中无对应的country字段,那么在查询数据库时候生成的对应实体类对象中country字段应为null,而业务需要该实体类对象应为"China",如果每次查询再set进去会十分麻烦,这时就可以使用对象工厂。

(1)书写自定义工厂类继承与DefaultObjectFactory

   因继承DefaultObjectFactory的原因,可以利用父类中super.create(type)方法帮助我们创建一个实例对象

public class MyObjectFactory extends DefaultObjectFactory {

	public Object create(Class type) {// 重新定义Dept类实例对象创建规则,其他类实例对象创建规则不想改变
		if (Dept.class == type) {
			// 依靠父类提供create方法创建Dept实例对象
			Dept dept = (Dept) super.create(type);
			// 设置自定义规则
			dept.setCountry("China");
			return dept;
		}
		return super.create(type);
	}
}

(2)在MyBatis核心文件mybatis-config.xml中注册自定义工厂

<objectFactory type="cn.itcats.util.MyObjectFactory"></objectFactory>

(3)测试查询方法返回的Dept实体类country属性是否为"China"

 

五、plugins拦截器

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值