Mybatis从门外到入门
对比介绍:
- JDBC:sql在代码中耦合度高 编写sql ——>预编译——>设置参数——>执行sql——>封装结果;
- Hibernate:全自动ORM框架 JavaBean ——>DataBase (hibernate不容易定制sql,初学容易学好较难);
- MyBatis:半自动ORM框架 ,sql和代码分开,自己定制sql JavaBean ——>编写sql(配置文件) ——>DataBase;
MyBatis的基本使用:
- 在xml中配置数据库连接池,SessionFactory,SqlSession等Bean。
- 准备好数据库对应的实体类和sql映射文件,将对应的目录配置在SessionFactory中。
- 使用SqlSession通过命名空间和Id组成唯一标识找到sql执行,一个SqlSession表示一次和数据库交互,用完关闭。
接口式编程:
创建一个接口,利用SqlSession的getMapper(Class<T> var1)方法使接口和映射文件绑定。
mybatis会为接口创建一个代理实现类执行sql ,命名空间=接口的全类名 sql标签id=方法名
接口类:EmployeeDao 实体类:Employee
EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class); Employee employee = employeeDao.selectById(1); 映射文件: <?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.example.dao.EmployeeDao"> <resultMap id="BaseResultMap" type="com.example.pojo.Employee" > <id column="ID" property="id" jdbcType="INTEGER"/> <result column="LAST_NAME" property="lastName" jdbcType="VARCHAR" /> <result column="GENDER" property="gender" jdbcType="VARCHAR" /> <result column="EMAIL" property="email" jdbcType="VARCHAR" /> </resultMap> <select id="selectById" resultMap="BaseResultMap" parameterType="map" > select * from employee where ID = #{id, jdbcType=INTEGER} </select> </mapper>
#{}与${}的区别:
#{}表示是以预编译的形式,将参数设置到sql中,PreStatement,可以防止sql注入;
${}是将取出的值直接拼装在sql中会有安全问题;
#{}可以规定参数的一些规则,javaType,jdbcType,mode等,例如: ID = #{id,jdbcType=INTEGER}
大部分情况下都应该使用#{},${}适用于jdbc不支持占位符的地方,比如分表、排序时使用 select * from ${tableName} where ....
MyBatis缓存机制:
MyBatis默认定义了两级缓存,缓存可以提高查询效率。默认情况下一级缓存开启,二级缓存需要手动开启。
一级缓存:SqlSession级的缓存,也称为本地缓存。
二级缓存:NameSpace级别的缓存,也称为全局缓存,需要手动开启。
为了提高扩展性,Mybatis提供了Cache接口,可以通过实现Cache接口自定义二级缓存。
MyBatis的缓存可能会出现一致性问题,尤其是在分布式环境下,可以使用redis作为MyBatis的二级缓存。
MyBatis处理流程:
MyBatis插件:
利用动态代理的方式在方法级进行拦截,支持拦截的方法:
- 执行器Executor(update、query、commit、rollback等方法);
- SQL语法构建器StatementHandler(prepare、parameterize、batch、update、query等方法);
- 参数处理器ParameterHandler(getParameterObject、setParameters方法);
- 结果集处理器ResultSetHandler(handleResultSets、handleOutputParameters等方法);
如果配置了多个拦截器,会通过责任链模式形成一个代理链条,即代理对象代理了另一个代理对象,要注意:
- 不要定义过多的插件,代理嵌套过多,执行方法的时候,比较耗性能;
- 拦截器实现类的intercept方法里最后不要忘了执行invocation.proceed()方法,否则多个拦截器情况下,执行链条会断掉;
插件开发过程:
- 实现Interceptor接口,完成实现类
- 通过@Intercepts注解指定拦截的对象和方法
- 在全局配置中注册插件