一、MyBatis之工作原理
MyBatis是一个半自动映射框架。所谓半自动,是相对Hibernate全表映射而言的,MyBatis需要手动匹配提供POJO、SQL和映射关系。
我们知道,jdbc有四个核心对象
(1)DriverManager,用于注册数据库连接
(2)Connection,与数据库连接对象
(3)Statement/PrepareStatement,操作数据库SQL语句的对象
(4)ResultSet,结果集或一张虚拟表
而MyBatis也有四大核心对象:
(1)SqlSession对象,该对象中包含了执行SQL语句的所有方法。类似于JDBC里面的Connection。
(2)Executor接口,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。类似于JDBC里面的Statement/PrepareStatement。
(3)MappedStatement对象,该对象是对映射SQL的封装,用于存储要映射的SQL语句的id、参数等信息。
(4)ResultHandler对象,用于对返回的结果进行处理,最终得到自己想要的数据格式或类型。可以自定义返回类型。
MyBatis的工作原理如下图所示:
上面中流程就是MyBatis内部核心流程,每一步流程的详细说明如下文所述:
(1)读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。
(2)加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
(3)构造会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。
(4)创建会话对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
(5)Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
(6)MappedStatement对象。在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
(7)输入参数映射。输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
(8)输出结果映射。输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。
简单实操一下
二、实体的增加、修改、删除、查询
1、环境
mybatis-3.2版本,jdk1.8版本,Oracle数据库
2、准备工作
2.1 下载mybatis-3.2,需要引用其中的若干包,包括数据库JDBC驱动包
mybatis-3.2.0.jar
lib/asm-3.3.1.jar
lib/cglib-2.2.2.jar
lib/commons-logging-1.1.1.jar
lib/javassist-3.17.1-GA.jar
lib/log4j-1.2.17.jar
lib/slf4j-api-1.7.2.jar
lib/slf4j-log4j12-1.7.2.jarojdbc6.jar
2.2 项目的目录结构
com.clzhang.mybatis.mapper MemberUserMapper.java MyBatis的mapper处理类(非必须)
com.clzhang.mybatis.entity MemberUserBean.java MyBatis的entity类
com.clzhang.mybatis MyBatisTest1.java/MyBatisTest2.java 测试类 resources\config mybatis-config.xml MyBatis的配置文件 resources\com\clzhang\mybatis\mapper MemberUserMapper.xml MyBatis的mapper配置文件
2.3 假定数据库已经创建,表结构也已经创建,并且有测试数据
CREATE TABLE MEMBER_USER (
ID NUMBER NOT NULL PRIMARY KEY,
NAME VARCHAR2(30),
PERSONMOBILE VARCHAR2(20),
ADDRESS VARCHAR2(255),
AGE NUMBER);
CREATE SEQUENCE SEQ_MEMBER_USER;
3、具体步骤
3.1 创建entity类
package com.clzhang.mybatis.entity;
public class MemberUserBean {
private int id;
private String name;
private String personMobile;
private String address;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPersonMobile() {
return personMobile;
}
public void setPersonMobile(String personMobile) {
this.personMobile = personMobile;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "MemberUser [id=" + id + ", name=" + name +
", personMobile=" + personMobile + ", address=" + address + ", age=" + age + "]";
}
}
3.2 创建Mapper接口(非必须)
package com.clzhang.mybatis.mapper;
import java.util.*;
import com.clzhang.mybatis.entity.MemberUserBean;
public interface MemberUserMapper {
public void insertUser(MemberUserBean user);
public void updateUser(MemberUserBean user);
public void deleteUser(int id);
public MemberUserBean getUserById(int id);
public List getUsersByAge(int startAge, int endAge);
}
3.3 创建mybatis-config.xml的配置文件,位置于resources/config目录
<?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>
<settings>
<setting name="cacheEnabled" value="false" />
<setting name="useGeneratedKeys" value="true" />
<setting name="defaultExecutorType" value="REUSE" />
</settings>
<typeAliases>
<typeAlias alias="MemberUser" type="com.clzhang.mybatis.entity.MemberUserBean"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
<property name="username" value="mytest"/>
<property name="password" value="test001"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/clzhang/mybatis/mapper/MemberUserMapper.xml" />
</mappers>
</configuration>
3.4 创建MemberUserMapper.xml配置文件,位置于:resources\com\clzhang\mybatis\mapper
<?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.clzhang.mybatis.mapper.MemberUserMapper">
<!--
默认情况下是没有开启缓存的,下条代码是开启二级缓存的,作用如下:
1.?映射语句文件中的所有 select 语句将会被缓存。
2.?映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
3.?缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
......
-->
<cache />
<!--以单个对象方式返回-->
<select id="getUserById" resultType="MemberUser" parameterType="int">
select
ID,
NAME,
PERSONMOBILE,
ADDRESS,
AGE
FROM MEMBER_USER
WHERE ID = #{id}
</select>
<resultMap type="MemberUser" id="userAgeMap">
<id property="id" column="id"/>
<!--有需要做数据库到实体类名称转换的,可以写在这里-->
<!--
<result property="objname" column="dbname"/>
-->
</resultMap>
<!--以List方式返回多个结果-->
<!--参数名称目前不可以自行指定(无法与Mapper中保持一致),待查-->
<select id="getUsersByAge" resultMap="userAgeMap" parameterType="int">
<![CDATA[
select
ID,
NAME,
PERSONMOBILE,
ADDRESS,
AGE
FROM MEMBER_USER
WHERE AGE > #{param1} AND AGE < #{param2}
]]>
</select>
<!--Oracle的实现自增长主键的方式-->
<insert id="insertUser" parameterType="MemberUser">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select SEQ_MEMBER_USER.nextval from DUAL
</selectKey>
INSERT INTO MEMBER_USER (ID, NAME, PERSONMOBILE, ADDRESS, AGE)
VALUES(#{id}, #{name}, #{personMobile}, #{address}, #{age})
</insert>
<update id="updateUser" parameterType="MemberUser">
update MEMBER_USER set
NAME = #{name},
PERSONMOBILE = #{personMobile},
ADDRESS = #{address},
AGE = #{age}
where id = #{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from MEMBER_USER where ID = #{id}
</delete>
</mapper>
3.5 创建测试类一,调用SqlSession实现CRUD
package com.clzhang.mybatis;
import java.io.IOException;
import java.io.Reader;
import java.util.*;
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 org.junit.Test;
import com.clzhang.mybatis.entity.MemberUserBean;
public class MyBatisTest1 {
private static final String MYBATIS_CONFIG_FILENAME = "config/mybatis-config.xml";
private static SqlSessionFactory sqlSessionFactory;
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader(MYBATIS_CONFIG_FILENAME);
} catch (IOException e) {
System.out.println(e.getMessage());
}
// 一旦你创建了 SqlSessionFactory后,SqlSessionFactoryBuilder这个类就不需要存在了。
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
/**
* SqlSessionFactory 应该在你的应用执行期间都存在。没有理由来处理或重新创建它。
*
* @return
*/
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
// @Test
public void testInert() {
// SqlSession 的实例不能被共享,也是线程不安全的。因此最佳的范围是请求或方法范围。
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
MemberUserBean memberUser = new MemberUserBean();
memberUser.setName("李勇");
memberUser.setPersonMobile("998877");
memberUser.setAddress("江苏某地方");
memberUser.setAge(38);
int rows = sqlSession.insert("com.clzhang.mybatis.mapper.MemberUserMapper.insertUser", memberUser);
if (rows > 0) {
System.out.println("您成功插入" + rows + "条数据!");
} else {
System.out.println("执行插入操作失败!!!");
}
sqlSession.commit();
// 查看新加的对象主键
System.out.println("新加对象的id:" + memberUser.getId());
} finally {
sqlSession.close();
}
}
// @Test
public void testUpdate() {
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
MemberUserBean memberUser = new MemberUserBean();
memberUser.setId(5);
memberUser.setName("赵五");
memberUser.setPersonMobile("12345");
memberUser.setAddress("天津上地某公司");
memberUser.setAge(29);
int rows = sqlSession.update("com.clzhang.mybatis.mapper.MemberUserMapper.updateUser", memberUser);
if (rows > 0) {
System.out.println("您成功更新" + rows + "条数据!");
} else {
System.out.println("执行更新操作失败!!!");
}
sqlSession.commit();
} finally {
sqlSession.close();
}
}
// @Test
public void testDelete() {
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
int rows = sqlSession.delete("com.clzhang.mybatis.mapper.MemberUserMapper.deleteUser", 5);
if (rows > 0) {
System.out.println("您成功删除" + rows + "条数据!");
} else {
System.out.println("执行删除操作失败!!!");
}
sqlSession.commit();
} finally {
sqlSession.close();
}
}
// @Test
public void getUser() {
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
MemberUserBean memberUser = sqlSession.selectOne("com.clzhang.mybatis.mapper.MemberUserMapper.getUserById", 1);
System.out.println(memberUser.toString());
} finally {
sqlSession.close();
}
}
// @Test
public void getUserByAge() {
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("param1", 20);
paramMap.put("param2", 30);
List<MemberUserBean> memberUsers = sqlSession.selectList("com.clzhang.mybatis.mapper.MemberUserMapper.getUsersByAge", paramMap);
for (MemberUserBean memberUser : memberUsers) {
System.out.println(memberUser);
}
} finally {
sqlSession.close();
}
}
}
3.6 创建测试类二,调用Mapper接口实现CRUD
package com.clzhang.mybatis;
import java.io.IOException;
import java.io.Reader;
import java.util.*;
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 org.junit.Test;
import com.clzhang.mybatis.mapper.MemberUserMapper;
import com.clzhang.mybatis.entity.MemberUserBean;
public class MyBatisTest2 {
private static final String MYBATIS_CONFIG_FILENAME = "config/mybatis-config.xml";
private static SqlSessionFactory sqlSessionFactory;
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader(MYBATIS_CONFIG_FILENAME);
} catch (IOException e) {
System.out.println(e.getMessage());
}
// 一旦你创建了 SqlSessionFactory后,SqlSessionFactoryBuilder这个类就不需要存在了。
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
/**
* SqlSessionFactory 应该在你的应用执行期间都存在。没有理由来处理或重新创建它。
*
* @return
*/
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
// @Test
public void testInert() {
// SqlSession 的实例不能被共享,也是线程不安全的。因此最佳的范围是请求或方法范围。
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
MemberUserBean memberUser = new MemberUserBean();
memberUser.setName("李勇");
memberUser.setPersonMobile("998877");
memberUser.setAddress("江苏某地方");
memberUser.setAge(38);
MemberUserMapper mapper = sqlSession
.getMapper(MemberUserMapper.class);
mapper.insertUser(memberUser);
sqlSession.commit();
// 查看新加的对象主键
System.out.println("新加对象的id:" + memberUser.getId());
} finally {
sqlSession.close();
}
}
// @Test
public void testUpdate() {
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
MemberUserBean memberUser = new MemberUserBean();
memberUser.setId(6);
memberUser.setName("赵五");
memberUser.setPersonMobile("12345");
memberUser.setAddress("天津上地某公司");
memberUser.setAge(29);
MemberUserMapper mapper = sqlSession.getMapper(MemberUserMapper.class);
mapper.updateUser(memberUser);
sqlSession.commit();
} finally {
sqlSession.close();
}
}
// @Test
public void testDelete() {
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
MemberUserMapper mapper = sqlSession.getMapper(MemberUserMapper.class);
mapper.deleteUser(6);
sqlSession.commit();
} finally {
sqlSession.close();
}
}
// @Test
public void getUser() {
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
MemberUserMapper mapper = sqlSession.getMapper(MemberUserMapper.class);
MemberUserBean memberUser = mapper.getUserById(1);
System.out.println(memberUser.toString());
} finally {
sqlSession.close();
}
}
@Test
public void getUserByAge() {
SqlSession sqlSession = getSqlSessionFactory().openSession();
try {
MemberUserMapper mapper = sqlSession.getMapper(MemberUserMapper.class);
List<MemberUserBean> myList = mapper.getUsersByAge(20, 40);
for (MemberUserBean entry : myList) {
int key = entry.getId();
String value = entry.getAddress();
System.out.println(key + ":" + value);
}
} finally {
sqlSession.close();
}
}
}
4、单独测试各个模块
解除注释JUnit各单元,然后键盘输入:alt+shift+X,T,分别进行测试。
怎么样,现在对mybatis是不是有了更加深入的了解了呢。
一,Mybatis-原理总结
1.什么是Mybatis?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。1、Mybatis是一个半ORM(对象关系映射)框架,底层封装了JDBC,是程序员在开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。使得程序员可以花更多的精力放到业务开发中。另外,程序员直接编写原生态sql,严格控制sql执行性能,灵活度高。2、MyBatis 可以使用简单的 XML文件 或注解方式来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。3、通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。
2.Mybaits的优点:
1、基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。2、与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;3、很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。4、能够与Spring很好的集成;5、提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
3 .MyBatis框架的缺点:
1、SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。2、SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
4.MyBatis框架适用场合:
1、MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。2、对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
5.MyBatis与Hibernate有哪些不同?
1、Mybatis是一个半自动的ORM框架,在查询关联对象或关联集合对象时,需要手动编写sql语句来完成;Hibernate是全自动ORM映射工具,查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,不需要编写sql.2、Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对性能要求高,需求变化频繁的项目;但是如果涉及到较多的字段或者关联多表时,sql语句编写量大且对开发人的sql语句编写功底要求高。3、Hibernate对象/关系映射能力强,数据库无关性好,适合需求变化不大的项目,使用hibernate开发可以节省很多代码,提高效率。
二、架构
我们把Mybatis的功能架构分为三层:
(1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
(2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
(3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
1、SqlSessionFactoryBuilder【全局的对象】
每一个MyBatis的应用程序的入口是:SqlSessionFactoryBuilder。它的作用是通过XML配置文件创建Configuration对象(当然也可以在程序中自行创建),然后通过build方法创建SqlSessionFactory对象。
2、SqlSessionFactory【全局的对象】
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory是由SqlSessionFactoryBuilder 从 XML 配置文件或通过Java的方式构建出 的实例,主要功能是创建SqlSession(会话)对象;SqlSessionFactory对象一个必要的属性是Configuration对象;SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,建议使用单例模式或者静态单例模式。一个SqlSessionFactory对应配置文件中的一个环境(environment),如果你要使用多个数据库就配置多个环境分别对应一个SqlSessionFactory。
3、 SqlSession
作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能。SqlSession通过调用api的Statement ID找到对应的MappedStatement对象。SqlSession是一个接口,它有2个实现类,分别是DefaultSqlSession(默认使用)以及SqlSessionManager;默认使用DefaultSqlSession,它有两个必须配置的属性:Configuration和Executor,SqlSession通过内部存放的执行器(Executor)来对数据进行CRUD。由于不是线程安全的,所以SqlSession对象的作用域需限制方法内;每一次操作完数据库后都要调用close对其进行关闭,官方建议通过try-finally来保证总是关闭SqlSession。
4、 Executor
MyBatis执行器,是MyBatis 调度的核心。Executor对象在创建Configuration对象的时候创建,并且缓存在Configuration对象里。Executor:负责SQL语句的生成,调用StatementHandler访问数据库,查询缓存的维护;Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象;**Executor(执行器)接口有两个实现类,其中BaseExecutor有三个继承类分别是BatchExecutor(重用语句并执行批量更新),ReuseExecutor(重用预处理语句prepared statements),SimpleExecutor(普通的执行器)。
5、 StatementHandler
封装了JDBC Statement操作,负责对JDBCstatement的操作,如设置参数、将Statement结果集转换成List集合,是真正访问数据库的地方,并调用ResultSetHandler处理查询结果。
6、ResultSetHandler
ParameterHandler 负责将用户传递的参数转换成JDBC Statement 所需要的参数ResultSetHandler负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;处理查询结果。TypeHandler负责java数据类型和jdbc数据类型之间的映射和转换7、 MappedStatement :
MappedStatement就是用来存放我们SQL映射文件中的信息包括sql语句,输入参数,输出参数等等。一个SQL节点对应一个MappedStatement对象。借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。
8、 SqlSource
负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql表示动态生成的SQL语句以及相应的参数信息ConfigurationMyBatis所有的配置信息都维持在Configuration对象之中三,
三,Mybatis面试题
1: #{}和${}的区别是什么?
1-a) #{}是占位符,可以接收参数,可以是简单类型也可以是pojo 类型;使用#{}可以有效地防止SQL注入,提高系统安全性。Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
{}时,就是把${}替换成变量的值。
3: 模糊查询like语句该怎么写?
第1种:在Java代码中添加sql通配符。第2种:在sql语句中拼接通配符,会引起sql注入
4:通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
Dao接口即Mapper接口。接口的全限名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中Mapper的Statement的id值;接口方法内的参数,就是传递给sql的参数。
Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MapperStatement。在Mybatis中,每一个 、、、 标签,都会被解析为一个MapperStatement对象。
举例: com.mybatis3.mappers.StudentDao.findStudentById ,可以唯一找到namespace为 com.mybatis3.mappers.StudentDao 下面 id 为 findStudentById 的 MapperStatement。
Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,转而执行MapperStatement所代表的sql,然后将sql执行结果返回。
5:Mybatis是如何进行分页的?分页插件的原理是什么?
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
6:Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
第一种:通过 <resultMap> 来映射字段名和实体类属性名的一一对应的关系。第二种是使用sql列的别名功能,让字段名的别名和实体类的属性名一致。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
8:如何获取自动生成的(主)键值?
insert 方法总是返回一个int值 ,这个值代表的是插入的行数。如果采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。
select last_insert_id(); insert into stu2.user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}); 9:在mapper中如何传递多个参数?
1、第一种:DAO层的函数2、第二种: 使用 @param 注解:然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):3、第三种:多个参数封装成map
10:Mybatis动态sql有什么用?执行原理?有哪些动态sql?
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。
Mybatis提供了9种动态sql标签: trim|where|set|foreach|if|choose|when|otherwise|bind。
11:Xml映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标签?
答: <resultMap>、<parameterMap>、<sql>、<include>、<selectKey> ,加上动态sql的9个标签,其中 为sql片段标签,通过 标签引入sql片段, 为不支持自增的主键生成策略标签。
12:Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?
不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;
原因就是namespace+id是作为Map <String,MapperStatement> 的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。
14: 一对一、一对多的关联查询 ?
14-1: MyBatis实现一对一有几种方式?具体怎么操作的?
有联合查询和嵌套查询;联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
14-2: MyBatis实现一对多有几种方式,怎么操作的?
有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。
15: Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
答:Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB.getName,拦截器invoke方法发现a.getB是值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB.getName方法的调用。这就是延迟加载的基本原理。
当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。
16: Mybatis的一级、二级缓存:
1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;
3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
17: 什么是MyBatis的接口绑定?有哪些实现方式?
接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。
接口绑定有两种实现方式:一种是通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;另外一种就是通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。
18: 使用MyBatis的mapper接口调用时有哪些要求?
1、Mapper接口方法名和mapper.xml中定义的每个sql的id相同;2、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;3、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;4、Mapper.xml文件中的namespace即是mapper接口的类路径。
19:Mapper编写有哪几种方式?
第一种:接口实现类继承SqlSessionDaoSupport:使用此种方法需要编写mapper接口,mapper接口实现类、mapper.xml文件。1、在sqlMapConfig.xml中配置mapper.xml的位置2、定义mapper接口3、实现类集成SqlSessionDaoSupport mapper方法中可以this.getSqlSession进行数据增删改查。4、spring 配置
第二种:使用 org.mybatis.spring.mapper.MapperFactoryBean :1、在sqlMapConfig.xml中配置mapper.xml的位置,如果mapper.xml和mappre接口的名称相同且在同一个目录,这里可以不用配置2、定义mapper接口:3、mapper.xml中的namespace为mapper接口的地址4、mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致5、Spring中定义
第三种:使用mapper扫描器:1、mapper.xml文件编写:mapper.xml中的namespace为mapper接口的地址;mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致;如果将mapper.xml和mapper接口的名称保持一致则不用在sqlMapConfig.xml中进行配置。2、定义mapper接口:注意mapper.xml的文件名和mapper的接口名称保持一致,且放在同一个目录3、配置mapper扫描器:4、使用扫描器后从spring容器中获取mapper的实现对象。
20: 简述Mybatis的插件运行原理,以及如何编写一个插件。
答:Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke方法,当然,只会拦截那些你指定需要拦截的方法。
编写插件:实现Mybatis的Interceptor接口并复写intercept方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
好了现在对mybatis是不是手到擒来了呢?