MyBatis是怎么玩的

mybatis如何配置使用多个数据源(environment)? - 握住一缕风 - ITeye博客
博客分类:
mybatis如何配置使用多个数据源?
一、数据库连接properties配置文件,两个数据源的地址:
hd.jdbc.driverClassName=com.mysql.jdbc.Driver
hd.jdbc.url=jdbc:mysql://127.0.0.1::3306/hd?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
hd.jdbc.username=root
hd.jdbc.password=root
ho.jdbc.driverClassName=com.mysql.jdbc.Driver
ho.jdbc.url=jdbc:mysql://127.0.0.1:3306/ho?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
ho.jdbc.username=root
ho.jdbc.password=root
二、mybatis配置文件,配置两个environment:
&?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&
&properties resource="mybatis/jdbc.properties"/&
&environments default="HO"&
&environment id="HD"&
&transactionManager type="JDBC" /&
&dataSource type="POOLED"&
&property name="driver" value="${hd.jdbc.driverClassName}" /&
&property name="url" value="${hd.jdbc.url}" /&
&property name="username" value="${hd.jdbc.username}" /&
&property name="password" value="${hd.jdbc.password}" /&
&/dataSource&
&/environment&
&environment id="HO"&
&transactionManager type="JDBC" /&
&dataSource type="POOLED"&
&property name="driver" value="${ho.jdbc.driverClassName}" /&
&property name="url" value="${ho.jdbc.url}" /&
&property name="username" value="${ho.jdbc.username}" /&
&property name="password" value="${ho.jdbc.password}" /&
&/dataSource&
&/environment&
&/environments&
&/configuration&
三、获取SqlSessionFactory,获取Mapper代理,便于在执行完Mapper方法后关闭SqlSession。
SqlSessionFactory的获取:
* 根据mybatis.xml中配置的不同的environment创建对应的SqlSessionFactory
* @author boyce
* @version
public final class DataSourceSqlSessionFactory {
private static final String CONFIGURATION_PATH = "mybatis/mybatis.xml";
private static final Map&DataSourceEnvironment, SqlSessionFactory& SQLSESSIONFACTORYS
= new HashMap&DataSourceEnvironment, SqlSessionFactory&();
* 根据指定的DataSourceEnvironment获取对应的SqlSessionFactory
* @param environment 数据源environment
* @return SqlSessionFactory
public static SqlSessionFactory getSqlSessionFactory(DataSourceEnvironment environment) {
SqlSessionFactory sqlSessionFactory = SQLSESSIONFACTORYS.get(environment);
if (ObjectUtils.isNotNull(sqlSessionFactory))
return sqlSessionF
InputStream inputStream =
inputStream = Resources.getResourceAsStream(CONFIGURATION_PATH);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, environment.name());
("Get {} SqlSessionFactory successfully.", environment.name());
} catch (IOException e) {
logger.warn("Get {} SqlSessionFactory error.", environment.name());
logger.error(e.getMessage(), e);
IOUtils.closeQuietly(inputStream);
SQLSESSIONFACTORYS.put(environment, sqlSessionFactory);
return sqlSessionF
* 配置到Configuration.xml文件中的数据源的environment的枚举描述
* @author boyce
* @version
public static enum DataSourceEnvironment {
定义一个统一的Mapper标识接口,每一个具体的Mapper接口继承该接口:
* Mapper Interface
* @author boyce
* @version
public interface Mapper {
定义一个Mapper代理工厂:
* Mapper Factory
* @author boyce
* @version
public final class MapperFactory {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MapperFactory.class);
* Create a mapper of environment by Mapper class
* @param clazz Mapper class
* @param environment A datasource environment
* @return a Mapper instance
@SuppressWarnings("unchecked")
public static &T& T createMapper(Class&? extends Mapper& clazz, DataSourceEnvironment environment) {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(environment);
SqlSession sqlSession = sqlSessionFactory.openSession();
Mapper mapper = sqlSession.getMapper(clazz);
return (T)MapperProxy.bind(mapper, sqlSession);
* Mapper Proxy
* executing mapper method and close sqlsession
* @author boyce
* @version
private static class MapperProxy implements InvocationHandler {
private SqlSession sqlS
private MapperProxy(Mapper mapper, SqlSession sqlSession) {
this.mapper =
this.sqlSession = sqlS
@SuppressWarnings("unchecked")
private static Mapper bind(Mapper mapper, SqlSession sqlSession) {
return (Mapper) Proxy.newProxyInstance(mapper.getClass().getClassLoader(),
mapper.getClass().getInterfaces(), new MapperProxy(mapper, sqlSession));
* execute mapper method and finally close sqlSession
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object =
object = method.invoke(mapper, args);
} catch(Exception e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
} finally {
sqlSession.close();
//Get a SqlSessionFactory of environment
private static SqlSessionFactory getSqlSessionFactory(DataSourceEnvironment environment) {
return DataSourceSqlSessionFactory.getSqlSessionFactory(environment);
大功告成,客户端使用:
UserMapper mapper = MapperFactory.createMapper(UserMapper.class, DataSourceEnvironment.HD);
User user = mapper.getUserById(162L);
System.out.println(user);
OK,到此基本上所有的工作就完成了,以上的方式就可以支持多个数据源了。
但是代码还不够优雅,以上代码我们发现DataSourceEnvironment这个枚举变量在客户端,MapperFactory以及DataSourceEnvironment
中传来传去,我们应该尽量减少一个类对外界类的耦合,也就是不符合Java编程原则中的迪米特法则。
好了,那我们来改良一下。
将MapperFactory设计成枚举策略模式:
* Mapper Creator
* @author boyce
* @version
public enum MapperFactory {
private SqlSessionFactory sqlSessionF
public &T& T createMapper(Class&? extends Mapper& clazz) {
return createMapper(clazz, this);
protected void createSqlSessionFactory() {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, this.name());
public SqlSessionFactory getSqlSessionFactory() {
return sqlSessionF
private SqlSessionFactory sqlSessionF
public &T& T createMapper(Class&? extends Mapper& clazz) {
return createMapper(clazz, this);
protected void createSqlSessionFactory() {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, this.name());
public SqlSessionFactory getSqlSessionFactory() {
return sqlSessionF
* Create a mapper of environment by Mapper class
* @param clazz Mapper class
* @param environment A datasource environment
* @return a Mapper instance
public abstract &T& T createMapper(Class&? extends Mapper& clazz);
* Create SqlSessionFactory of environment
protected abstract void createSqlSessionFactory();
* get SqlSessionFactory
public abstract SqlSessionFactory getSqlSessionFactory();
private static InputStream inputStream =
inputStream = Resources.getResourceAsStream("mybatis/mybatis.xml");
HO.createSqlSessionFactory();
HD.createSqlSessionFactory();
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(inputStream);
@SuppressWarnings("unchecked")
private static &T& T createMapper(Class&? extends Mapper& clazz, MapperFactory MapperFactory) {
SqlSession sqlSession = MapperFactory.getSqlSessionFactory().openSession();
Mapper mapper = sqlSession.getMapper(clazz);
return (T)MapperProxy.bind(mapper, sqlSession);
* Mapper Proxy
* executing mapper method and close sqlsession
* @author boyce
* @version
private static class MapperProxy implements InvocationHandler {
private SqlSession sqlS
private MapperProxy(Mapper mapper, SqlSession sqlSession) {
this.mapper =
this.sqlSession = sqlS
private static Mapper bind(Mapper mapper, SqlSession sqlSession) {
return (Mapper) Proxy.newProxyInstance(mapper.getClass().getClassLoader(),
mapper.getClass().getInterfaces(), new MapperProxy(mapper, sqlSession));
* execute mapper method and finally close sqlSession
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object =
object = method.invoke(mapper, args);
} catch(Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
客户端使用场景:
UserMapper mapper = MapperFactory.HO.createMapper(UserMapper.class);
User user = mapper.getUserById(162L);
System.out.println(user);
ok,如果大家有什么更优雅的设计方案请不吝分享分享。
浏览 45210
该用户名已经存在
浏览: 183338 次
来自: 上海
引用另一方面当串行化这个子类实例时外部类也会被不知不觉的串行化 ...
不过受教了 ,不过稍稍修改 还是很好用的 。顶
程序还是有BUG
楼主说的没错,如果当前线程在线程池中会被重复利用,那么thre ...我们一起读文档,学习MyBatis(二)----------- Insert Update Delete操作的实现 - 奉献获取 - ITeye博客
博客分类:
在这里,我会直接写Insert Update Delete操作的简单实现,文档中从一个简单的demo到这些操作中间介绍了很多关于配置文件的详细信息,由于我水平有限,仅仅把这些具体的实现做到了,配置中还有很多东西不能够完全理解,为了不误导那些意外看到这个博客的人,便不多讲述。如果想要深入理解的话,可以看文档,或者再找一些其他好的资料。
还有,时间可能会比较紧张,所以,我在这里吧源码贴出来,具体步骤和以前的是一样,仅仅在我有体会或者有过问题的地方加下解释。源码我也会上传到下面,还有文档,建议大家自己读一下。
首先:BlogMpaper.xml
&?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="mapper.BlogMapper"&
&!-- 查询单个Blog --&
&select id="selectBlog" parameterType="int" resultType="model.Blog"&
from Blog where id = #{id}
&!-- 结果的匹配,文档中的解释为:resultMap – The most complicated and powerful element
that describes how to load your objects from the database result sets. --&
&resultMap type="model.Blog" id="blogMap"&
&id column="id" property="id" /&
&result column="title" property="title" /&
&result column="content" property="content" /&
&/resultMap&
&!-- 获取所有的数据 --&
&select id="getBlog" resultType="Blog"&
select * from blog
&!-- 插入一条记录 --&
&insert id="insert1" parameterType="model.Blog" useGeneratedKeys="true" keyProperty="id"&
insert into Blog (title, content) values (#{title}, #{content})
&!-- 更新一条记录 --&
&update id="updateBlog" parameterType="model.Blog"&
update Blog set title = #{title}, content = #{content} where id = #{id}
&!-- 删除一条记录 --&
&delete id="deleteBlog" parameterType="int"&
delete from blog where id = #{id}
BlogMapper.java
public interface BlogMapper {
Blog selectBlog(int id);
List&Blog& getBlog();
int insert1(Blog blog);
void deleteBlog(int id);
void updateBlog(Blog blog);
此处应该注意:虽然Mapper.xml配置文件中的参数都是单个的,但是由于parameterType="model.Blog",所以,这里在定义方法中的形参的时候,得定义为Blog类型的,如上,否则会出现类似的异常,具体的解释请参照:http://-qq-/admin/blogs/1827391。
Demo1.java
public class Demo1 {
public static void main(String[] args) {
String resource = "mybatis-config.xml";
InputStream inputStream =
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
List&Blog& blogs = mapper.getBlog();
for (Blog blog : blogs) {
System.out.println(blog + "\n" + blog.getId() + "
+ blog.getTitle() + "
" + blog.getContent());
System.out.println();
Blog b = new Blog();
b.setTitle("dd");
b.setContent("content");
int id = mapper.insert1(b);
System.out.println(id);
Blog blog = new Blog();
blog.setId(1);
mapper.updateBlog(blog);
mapper.deleteBlog(1);
} finally {
session.close();
注意:在执行完方法后,必须有 mit();这句话进行事务提交。因为在做Insert
Delete的时候,会先开启事务,而Mybatis不会自动提交(或许可以设置,我还不知道),所以,必须手动提交事务。
下载次数: 66
(226.2 KB)
下载次数: 117
浏览 39880
浏览: 777707 次
来自: 河北邯郸
u 写道老乡 ,邱县d老相好
老乡 ,邱县d
* 第一个参数为执行sqlMyBatis如何使用(三)
作者:迷茫中守候
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了MyBatis如何使用(三)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
在前边阐述了单独使用mybatis的方法,在实际开发过程中mybatis经常和spring一起使用,即mybatis和spring进行集成,现在我们来看如何集成。
mybatis和spring进行集成需要用到集成包:mybatis-spring-1.1.1.jar,此包提供mybatis和spring集成的支持,把此包导入到项目的lib目录下。
我们先看mybatis单独使用的时候的过程,mybatis配置文件==》读取配置文件==》操作数据库,具体的使用方法可参照前两篇文章。
下面进行mybatis和spring的集成,
一、mybatis配置文件
在和spring做集成时mybatis的配置文件中有些配置不再需要了,spring会使用它自己的。如数据源,下面看下mybatis的配置文件,MybatisConfiguration.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&
&typeAliases&
&typeAlias alias="Message" type=".imooc.entity.Message"/&
&/typeAliases&
&mapper resource="com/cn/mappers/message.xml"/&
&/mappers&
&/configuration&
上面的配置文件配置了别名和mappers映射文件,和之前的配置文件相比,可以看出没有了关于数据源的信息,这里在mybatis的配置文件中不再需要配置数据源,需要在spring的配置文件中配置。
二、spring配置文件
既然和spring做集成,那么必须导入spring的包,关于spring的包可以从前面的文章中获得;导入spring的包之后,就需要配置spring的配置文件,我们把spring的配置文件放在src下,名字为spring-application.xml,
&?xml version="1.0" encoding="UTF-8"?&
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"&
&bean id="address" class=".test.spring.Address"&&/bean&
&!-- 引入jdbc配置文件 --&
&context:property-placeholder location="jdbc.properties"/&
&!--1、创建jdbc数据源 --&
&bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" &
&property name="driverClassName" value="com.mysql.jdbc.Driver"/&
&property name="url" value="jdbc:mysql://127.0.0.1:3306/weixin?useUnicode=true&characterEncoding=UTF-8" /&
&property name="username" value="root"/&
&property name="password" value="123456"/&
&!--2、sqlSessionFactoryBean--& &bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"&
&property name="dataSource" ref="dataSource"&&/property&
&property name="configLocation" value="classpath:MybatisConfiguration.xml"&&/property&
&property name="mapperLocations" value="classpath:com/cn/mappers/message.xml"&&/property&
&bean id="messageMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"&
&property name="mapperInterface" value=".inter.IMessageOperation" /&
&property name="sqlSessionFactory" ref="sqlSessionFactory" /&
首先,我们配置了一个数据源,这里如果引入了context的命名空间,可以使用&context:property-placeholder location="jdbc.properties"/&,引入src下的配置文件。
其次,配置了sqlSessionFactoryBean,这里使用sqlSessionFactoryBean生成sqlSessionFactory(在mybatis中sqlSessionFactory由sqlSessionFactoryBuilder生成)。要通过sqlSessionFactroyBean生成sqlSessionFactroy有以下几个属性,dataSource 即刚才配置的数据源,指定生成sqlSessionFactory使用的数据源
configLocation 这个属性指定mybatis的配置文件的路径,在本例中我们使用了src下的MybatisConfiguration.xml,如果在此文件中配置了mappers映射文件,则不需要第三个属性,如果没配置映射文件则需要第三个属性;假如,在MybatisConfiguration.xml文件中没有配置映射文件,也没有配置mapperLocations属性,则映射文件必须必须和映射器类在同一个包下,且映射文件和映射器类必须名字相同。
mapperLocations 指定mappers映射文件,这个属性可以配置为一个list的值
最后,使用动态代理生成访问数据库的代码,MapperFactoryBean作为一个工厂类,可以用来生成访问数据库的动态代理,有两种方式可以生成一个动态代理,这里使用了mapperInterface和sqlSessionFactory两个属性,第一个指定映射器类的全限路径,第二个就是上面的sqlSessionFactory;另一种方式是使用注解的方式。
至此,spring的配置文件完成,可以进行测试,测试代码如下,
import org.apache.ibatis.session.SqlS
import org.apache.ibatis.session.SqlSessionF
import org.mybatis.spring.support.SqlSessionDaoS
import org.springframework.context.support.ClassPathXmlApplicationC
.imooc.entity.M
.inter.IMessageO
public class TestSpringAndMybatis {
public static void main(String[] args) {
// TODO Auto-generated method stub
//获得spring的配置
ClassPathXmlApplicationContext cpxac=new ClassPathXmlApplicationContext("spring-application.xml");
//获得IMessageOperation接口类
IMessageOperation imo=(IMessageOperation)cpxac.getBean("messageMapper");
Message m=imo.selectMessageById("2");
System.out.println(m);
上边完成了mybatis和spring集成的一种方式,我们会发现在生成代理的时候如果有多个映射器类,则需要配置多次,比较麻烦,下篇文章使用另一种方式。
以上所述是小编给大家介绍的MyBatis如何使用(三),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
在深入的了解MyBatis之前,我们先来看一下,没有MyBatis的时候,我们是怎么去处理与数据库的交互的。
一、从JDBC开始
JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问。
看一下下面这个例子,逻辑非常简单,就是从数据库中查询出所有的的用户:
import java.sql.*;
public class FirstExample {
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/EMP";
static final String USER = "username";
static final String PASS = "password";
public static void main(String[] args) {
Connection conn =
Statement stmt =
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
stmt = conn.createStatement();
String sql = "SELECT id, first, last, age FROM user";
ResultSet rs = stmt.executeQuery(sql);
List&User& userList = new ArrayList&&();
while(rs.next()){
//Retrieve by column name
= rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
User newUser = new User();
newUser.setId(id);
newUser.setAge(age);
newUser.setFirst(first);
newUser.setLast(last);
userList.add(newUser);
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
//finally block used to close resources
if(stmt!=null)
stmt.close();
}catch(SQLException se2){
}// nothing we can do
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
这段代码看起来没什么复杂的,这样进行数据库操作也是完全的可以,可以借助代码生成工具快速的生成对一个表的CRUD操作,但是对于比较复杂的查询,生成工具就力不从心了。
那么,我们就来对比一下MyBatis是怎么与数据库进行交互的。
二、Get started with MyBatis
和很多的Java框架一样,如果你需要使用MyBatis,那么就需要MyBatis的jar包出现在classpath中,可以通过下载、Maven、Gradle等等方式来处理这个问题。
根据国际惯例,框架都需要配置。
下面是一个非常基础的配置mybatis-config.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="development"&
&environment id="development"&
&transactionManager type="JDBC"/&
&dataSource type="POOLED"&
&property name="driver" value="${driver}"/&
&property name="url" value="${url}"/&
&property name="username" value="${username}"/&
&property name="password" value="${password}"/&
&/dataSource&
&/environment&
&/environments&
&mapper resource="org/mybatis/example/BlogMapper.xml"/&
&/mappers&
&/configuration&
BlogMapper.xml
&?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="org.mybatis.example.BlogMapper"&
&select id="selectBlog" resultType="Blog"&
select * from Blog where id = #{id}
2.2 加载配置
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
2.3 执行数据库操作
SqlSession session = sqlSessionFactory.openSession();
Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
session.close();
使用MyBatis对数据库进行操作的风格与使用JDBC是完全不一样的,接下来,我们就来详细的看一下。
三、MyBatis
Mybatis大致可以分为两个phase,一,配置阶段,二,执行阶段
在启动阶段主要完成的:
配置文件的解析
创建核心的Configuration对象,并通过配置创建相关联的对象,并用这些对象填充Configuration对象
创建SqlSession实例
在执行阶段,根据之前的配置,通过SqlSession执行,参数处理,SQL语句解析处理,语句执行,处理返回。
3.1 配置阶段
3.1.1 配置都包含了什么内容
对于配置文件的读取,Mybatis抽象了一个名为Resources的类,用于从classpath中找到配置文件,并将其转为InputStream或者是Reader。
将上一步中获得的InputStream或者Reader对象传递给SqlSessionFactoryBuilder,builder会new XMLConfigBuilder,调用其parse方法,得到Configuration的实例,将Configuration的实例传递给DefaultSqlSessionFactory。由此可见,Mybatis的配置的解析是通过XMLConfigBuilder完成的。
3.1.1.1 包罗万象的Configuration
在详细了解XMLConfigBuilder之前,我们需要看一下Mybatis的Configuration都包含了哪些内容。从官方给出的文档上来看,Configuration包含以下的配置信息:
properties
These are externalizable, substitutable properties that can be configured in a typical Java Properties file instance, or passed in through sub-elements of the properties element. For example:
&properties resource="org/mybatis/example/config.properties"&
&property name="username" value="dev_user"/&
&property name="password" value="F2Fa3!33TYyg"/&
&/properties&
&settings&
&setting name="cacheEnabled" value="true"/&
&setting name="lazyLoadingEnabled" value="true"/&
&setting name="multipleResultSetsEnabled" value="true"/&
&setting name="useColumnLabel" value="true"/&
&setting name="useGeneratedKeys" value="false"/&
&setting name="autoMappingBehavior" value="PARTIAL"/&
&setting name="autoMappingUnknownColumnBehavior" value="WARNING"/&
&setting name="defaultExecutorType" value="SIMPLE"/&
&setting name="defaultStatementTimeout" value="25"/&
&setting name="defaultFetchSize" value="100"/&
&setting name="safeRowBoundsEnabled" value="false"/&
&setting name="mapUnderscoreToCamelCase" value="false"/&
&setting name="localCacheScope" value="SESSION"/&
&setting name="jdbcTypeForNull" value="OTHER"/&
&setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/&
&/settings&
typeAliases
&typeAliases&
&typeAlias alias="Author" type="domain.blog.Author"/&
&typeAlias alias="Blog" type="domain.blog.Blog"/&
&typeAlias alias="Comment" type="ment"/&
&typeAlias alias="Post" type="domain.blog.Post"/&
&typeAlias alias="Section" type="domain.blog.Section"/&
&typeAlias alias="Tag" type="domain.blog.Tag"/&
&/typeAliases&
&typeAliases&
&package name="domain.blog"/&
&/typeAliases&
@Alias("author")
public class Author {
typeHandlers
Whenever MyBatis sets a parameter on a PreparedStatement or retrieves a value from a ResultSet, a TypeHandler is used to retrieve the value in a means appropriate to the Java type. The following table describes the default TypeHandlers
objectFactory
Each time MyBatis creates a new instance of a result object, it uses an ObjectFactory instance to do so. The default ObjectFactory does little more than instantiate the target class with a default constructor, or a parameterized constructor if parameter mappings exist. If you want to override the default behaviour of the ObjectFactory
MyBatis allows you to intercept calls to at certain points within the execution of a mapped statement. By default, MyBatis allows plug-ins to intercept method calls of:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
environments
environment
MyBatis can be configured with multiple environments. This helps you to apply your SQL Maps to multiple databases for any number of reasons.
One important thing to remember though: While you can configure multiple environments, you can only choose ONE per SqlSessionFactory instance.
transactionManager
There are two TransactionManager types (i.e. type="[JDBC|MANAGED]") that are included with MyBatis
dataSource
databaseIdProvider
MyBatis is able to execute different statements depending on your database vendor.
Now that the behavior of MyBatis is configured with the above configuration elements, we’re ready to define our mapped SQL statements. But first, we need to tell MyBatis where to find them. Java doesn’t really provide any good means of auto-discovery in this regard, so the be.
在Configuration类中,包含了
environment
databaseId
setting的配置项
jdbcTypeForNull
lazyLoadTriggerMethods
defaultStatementTimeout
defaultFetchSize
defaultExecutorType
autoMappingBehavior
Disables auto-mapping.
PARTIAL(default)
Will only auto-map results with no nested result mappings defined inside
Will auto-map result mappings of any complexity (containing nested or otherwise)
autoMappingUnknownColumnBehavior
NONE(default)
Do nothing
Output warning log
Fail mapping
variables(properties)
reflectorFactory
Reflector represents a cached set of class definition information that allows for easy mapping between property names and getter/setter methods
objectFactory
MyBatis uses an ObjectFactory to create all needed new Objects.
objectWrapperFactory
Wrapper of the object, 主要在MetaObject中使用
proxyFactory
proxyFactory,default is JavassistProxyFactory
configurationFactory
Configuration factory class. Used to create Configuration for loading deserialized unread properties.
mapperRegistry
通过字段Map&Class&?&, MapperProxyFactory&?&& knownMappers缓存mapper,有两个重要的方法:
public &T& void addMapper(Class&T& type)
首先会添加一个type与MapperProxyFactory实例的对应关系到knownmapper中,随后创建一个MapperAnnotationBuilder的实例,并调用其parse方法
public &T& T getMapper(Class&T& type, SqlSession sqlSession)
尝试从knownmapper中,通过type找到对应的MapperProxyFactory,如果有MapperProxyFactory,则调用其newInstance()创建一个mapper的代理实例。
interceptorChain
使用一个ArrayList保存了所有的Interceptor,其最重要的方法为:
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
typeHandlerRegistry
//JDBC Type
private final Map&JdbcType, TypeHandler&?&& JDBC_TYPE_HANDLER_MAP = new EnumMap&JdbcType, TypeHandler&?&&(JdbcType.class);
//Java Type
private final Map&Type, Map&JdbcType, TypeHandler&?&&& TYPE_HANDLER_MAP = new ConcurrentHashMap&Type, Map&JdbcType, TypeHandler&?&&&();
//Unknown Type
private final TypeHandler&Object& UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
//All Type
private final Map&Class&?&, TypeHandler&?&& ALL_TYPE_HANDLERS_MAP = new HashMap&Class&?&, TypeHandler&?&&();
//NULL Type
private static final Map&JdbcType, TypeHandler&?&& NULL_TYPE_HANDLER_MAP = new HashMap&JdbcType, TypeHandler&?&&();
typeAliasRegistry
languageRegistry
mappedStatements(Map&String, MappedStatement&)
caches (Map&String, Cache&)
resultMaps(Map&String, ResulMap&)
parameterMaps(Map&String, ParameterMap&)
keyGenerators(Map&String, KeyGenerator&)
loadedResources(Set&String&)
sqlFragments(&String, XNode&)
cacheRefMap(Map&String, String&)
incompleteStatements(Collection&XMLStatementBuilder&)
incompleteCacheRefs(Collection&CacheRefResolver&)
incompleteResultMaps(Collection&ResultMapResolver&)
incompleteMethods(Collection&MethodResolver&)
3.1.1.2 new Configuration时都做了些什么
在new Configuration的时候,初始化了一些默认的type alias,
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
同时设置了默认的language driver为XMLLanguageDriver,并注册是RawLanguageDriver。
3.1.2 配置文件的处理过程
3.1.2.1 XML
在Mybatis中Configuration实例的各项配置是通过XMLConfigBuilder完成的,具体的来看,主要是通过:
private void parseConfiguration(XNode root) {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
这里需要关注一下的是mapperElement,因为在Mybatis的xml配置中,mapper的配置方式有两类四种:
第一类是通过XML,
另外一类是通过java的interface,
两种类型的配置可以混合。
XML的配置是通过:XMLMapperBuilder进行解析处理的。
Interface的配置是通过:MapperAnnotationBuilder进行解析处理的。
&!-- 1. Using classpath relative resources --&
&mapper resource="org/mybatis/builder/AuthorMapper.xml"/&
&mapper resource="org/mybatis/builder/BlogMapper.xml"/&
&mapper resource="org/mybatis/builder/PostMapper.xml"/&
&/mappers&
&!-- 2. Using url fully qualified paths --&
&mapper url="file:///var/mappers/AuthorMapper.xml"/&
&mapper url="file:///var/mappers/BlogMapper.xml"/&
&mapper url="file:///var/mappers/PostMapper.xml"/&
&/mappers&
&!-- 3. Using mapper interface classes --&
&mapper class="org.mybatis.builder.AuthorMapper"/&
&mapper class="org.mybatis.builder.BlogMapper"/&
&mapper class="org.mybatis.builder.PostMapper"/&
&/mappers&
&!-- 4. Register all interfaces in a package as mappers --&
&package name="org.mybatis.builder"/&
&/mappers&
在XMLMapperBuilder解析xml配置的时候,首先会处理namespace,然后依次处理:
读取出cache-ref中的namespace,然后通过CacheRefResolver的resolveCacheRef()方法处理
resolveCacheRef()将处理逻辑代理到MapperBuilderAssistant
private final MapperBuilderA
public Cache resolveCacheRef() {
return assistant.useCacheRef(cacheRefNamespace);
而MapperBuilderAssistant则是从Configuration中getCache(namespace)
public Cache useCacheRef(String namespace) {
if (namespace == null) {
throw new BuilderException("cache-ref element requires a namespace attribute.");
unresolvedCacheRef =
Cache cache = configuration.getCache(namespace);
if (cache == null) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
currentCache =
unresolvedCacheRef =
} catch (IllegalArgumentException e) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
与其他的配置类似,最终的信息都会汇总到Configuration对象中去。
读取xml中的配置
通过builderAssistant完成Cache对象的创建,并将新创建出来的Cache对象添加到Configuration中。
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
parameterMapDeprecated
抓出xml文件中,所有的resultMap,逐个进行解析
在处理每一个resultMap的时候,XMLMapperBuilder从xml中读取resultMap的各项属性配置,然后同样是将处理代理到builderAssistant完成resultMap的创建
在获得resultMap对象之后,因为resultMap是可以extends其他的resultMap的,且被继承的resultMap有可能已经解析,也有可能还没有解析,此时,ResultMapResolver负责处理这个问题,而ResultMapResolver又将问题抛给builderAssistant来完成最终的处理。
在builderAssistant中
* 如果`parentResultMap`还没有加载完成,Mybatis会抛出异常
`IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");`
在`XMLMapperBuilder`会捕获`IncompleteElementException`,并将`ResultMapResolver`添加到`Configuration`中:`configuration.addIncompleteResultMap`
* 如果`parentResultMap`已经加载完成,
1. 则使用`subResultMap`的`constructor`替换`parentResultMap`的`constructor`
2. 合并`subResultMap`和`parentResultMap`
3. 通过`ResultMap.Builder`完成`ResultMap`实例的创建,并将其添加到`Configuration`中
This element can be used to define a reusable fragment of SQL code that can be included in other statements. It can be statically (during load phase) parametrized. Different property values can vary in include instances. For example:
&sql id="userColumns"& ${alias}.id,${alias}.username,${alias}.password &/sql&
The SQL fragment can then be included in another statement, for example:
&select id="selectUsers" resultType="map"&
&include refid="userColumns"&&property name="alias" value="t1"/&&/include&,
&include refid="userColumns"&&property name="alias" value="t2"/&&/include&
from some_table t1
cross join some_table t2
从上述可以看出,sql配置的解析应该是比较简单:将sql作为应该XNode,以(Id,XNode)的形式,存储到XMLMapperBuilder.sqlFragments中。
select|Insert|update|delete
select|Insert|update|delete是最为复杂的配置项,Mybatis将其处理交由XMLStatementBuilder进行处理。
在XMLStatementBuilder的parseStatementNode()中,包括以下的处理:
简单配置项的读取
通过XMLIncludeTransformer.applyIncludes完成对include节点的处理
处理selectKey
处理完include和selectKey,Mybatis会将初始的include和selectKey标签进行替换或者移除,然后通过LanguageDriver完成对SQL语句的处理,返回一个SqlSource的实例。
默认的LanguageDriver为XMLLanguageDriver,其将SQL语句的解析处理代理给XMLScriptBuilder.parseScriptNode():
public SqlSource parseScriptNode() {
List&SqlNode& contents = parseDynamicTags(context);
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
SqlSource sqlSource =
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
return sqlS
在parseDynamicTags中返回的List&SqlNode&,包含以下几种SqlNode
TextSqlNode
TrimSqlNode
TrimHandler
WhereSqlNode
WhereHandler
SetSqlNode
SetHandler
ForEachSqlNode
ForEachHandler
ChooseSqlNode
ChooseHandler
MixedSqlNode
OtherwiseHandler
VarDeclSqlNode
BindHandler
StaticTextSqlNode
在获得SqlSource对象之后,Mybatis更加配置初始化KeyGenerator
最后通过MappedStatement.Builder生成MappedStatement,并将其添加到Configuration中。
到此为止,一个mapper的xml配置已经处理完成,接下来就是要把namespace所对应的java class通过configuration.addMapper添加到Configuration上去。
最后处理,分别调用pending的resolver:
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
3.1.2.2 Annotation
那么,现在还有一个问题没有解决,那就是Mapper的定义,可以通过XML的方式,也可以通过Annotation的方式,或者是两种混合使用,我们已经清楚了XML方式的配置是怎么处理的,那么Annotation是在哪里处理的,是怎么处理的呢?
当调用configuration.addMapper的时候,Mybatis会代理到MapperRegistry,进而通过MapperAnnotationBuilder来完成Annotation的处理,Mybatis支持的Annotation如下表:
Annotation
XML equivalent
@CacheNamespace
&property&
@CacheNamespaceRef
&cacheRef&
@ConstructorArgs
&constructor&
&arg&&idArg&
@TypeDiscriminator
&discriminator&
&resultMap&
&result&&id&
&association&
&collection&
Attributes of mapped statements.
@Insert @Update @Delete @Select
&insert& &update& &delete& &select&
@InsertProvider @UpdateProvider @DeleteProvider @SelectProvider
&insert& &update& &delete& &select&
@SelectKey
&selectKey&
@ResultMap
@ResultType
当MapperAnnotationBuilder开始处理Annotation之前,MapperAnnotationBuilder会通过configuration.isResourceLoaded(resource)检查以Mapper为namespace的相关资源有没有被加载,如果没有被加载过,则先加载并解析XML的配置,然后处理Cache及CacheRef,因为这两个Annotation是在Type层面的,所以优先处理,处理的方式和XML没有太多的区别。
接下来遍历Type上所有的methods:
处理ParameterType
如果是单个parameter,则ParameterType为该参数的Type,否则,则为ParamMap.class。
获取LanguageDriver
通过LanguageDriver获取SqlSource
获取@Insert/@Update/@Delete/@Select
获取@InsertProvider/@UpdateProvider/@DeleteProvider/@SelectProvider
两种注解不能同时出现,只能二选一,
如果是第1种,则:
从sqlAnnotation中获取sql语句,结果为一个String类型的数组
用空格连接String数组
通过LanguageDriver创建SqlSource,
如果在sql语句中有&script&,则通过XMLScriptBuilder来处理SQL。
如果没有&script&,则通过PropertyParser.parse,然后通过创建SqlSource。
如果是第2种,则返回一个ProviderSqlSource的实例。
如果sql语句类型是INSERT或者是UPDATE,则根据SelectKey和Options这两个Annotation确定KeyGenerator
处理@ResultMap,将ResultMap的value,以,拼接到一起,形成resultMapId。
如果是SELECT,则通过调用resultMapId = parseResultMap(method);
获取returnType,ConstructorArgs,Results,TypeDiscriminator
如果Results有指定id的话,则
resultMapId=type.getName() + "." + results.id()
如果没有指定的话,则
resultMapId=type.getName() + "." + method.getName() + suffix
将ConstructorArg转为ResultMapping对象。
处理Results,将其转为ResultMapping对象。
处理Discriminator
最终通过MapperBuilderAssistant得到MappedStatement对象。
3.2 数据库操作的执行过程
到目前为止,无论是XML的配置,还是Annotation的配置,都已经加载完成了,接下来就可以对mapper的方法进行调用了。
通过SqlSession获取Mapper的代理实例:
//1. DefaultSqlSession.java
public &T& T getMapper(Class&T& type) {
return configuration.&T&getMapper(type, this);
//2. Configuration.java
public &T& T getMapper(Class&T& type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
//3. MapperRegistry.java
public &T& T getMapper(Class&T& type, SqlSession sqlSession) {
final MapperProxyFactory&T& mapperProxyFactory = (MapperProxyFactory&T&) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
从上面的代码可以看出,我们得到的Mapper的对象,是一个MapperProxy的实例,MapperProxy的声明如下:
public class MapperProxy&T& implements InvocationHandler, Serializable {...}
当我们执行一个方法时,其执行过程是这样的:
MapperProxy尝试从methodCache中获取MapperMethod实例,如果没有,则创建一个实例,并添加到methodCache中。
执行mapperMethod.execute(sqlSession, args);
MapperMethod包含两个实例:
* SqlCommand
通过MappedStatement获取类型和MappedStatement的ID
* MethodSignature
private final boolean returnsM
private final boolean returnsM
private final boolean returnsV
private final boolean returnsC
private final Class&?& returnT
private final String mapK
private final Integer resultHandlerI
private final Integer rowBoundsI
private final ParamNameResolver paramNameR
在执行execute方法时,
1. 通过SqlCommand判断是INSERT/UPDATE/DELETE/SELECT/FLUSH,下面以INSERT为例。
2. 通过MethodSignature的ParamNameResolver获取到参数对象,会有三种情况:
* 没有任何参数时,返回null
* 如果只有一个参数,则是参数本身
* 如果有多个参数,则返回一个Map
3. 执行`rowCountResult(sqlSession.insert(command.getName(), param))`
DefaultSqlSession通过MappedStatement的ID,从Configuration中获取到MappedStatement,然后交由Executor执行。
在配置Mybatis的时候,我们可以通过defaultExecutorType配置Executor为SIMPLE/REUSE/ BATCH,默认为SIMPLE。
清理本地缓存
通过MappedStatement获取到Configuration对象,通过:
configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
获取到StatementHandler对象,此时得到的对象为RoutingStatementHandler,该对象会根据MappedStatement的类型,将实际的操作代理给SimpleStatementHandler/PreparedStatementHandler(默认)/CallableStatementHandler。
在创建PreparedStatementHandler的时候,会通过MappedStatement.getBoundSql-&sqlSource.getBoundSql(parameterObject)得到BoundSql。
最后,将所有的interceptor实例plugin到这个StatementHandler上去。
plugin的动作,其实就是对这个StatementHandler进行逐层的动态代理。
Mybatis封装了一个Transaction对象,从实现类JdbcTransaction中获取Connection,同时设定TransactionIsolation:
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
setDesiredAutoCommit(autoCommmit);
调用StatementHandler的prepare方法,获取到Statement对象:
//1. 创建Statement实例
stmt = handler.prepare(connection, transaction.getTimeout());
//2. 添加参数
handler.parameterize(stmt);
1. 通过boundSql获取到List&ParameterMapping&
2. 逐个遍历,通过相对应的TypeHandler往Statement上添加参数。
parameterMapping.getTypeHandler();
//PreparedStatementHandler.java,创建Statement实例,
//最终还是通过JDBC的connection对象创建处理PreparedStatement对象。
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
return connection.prepareStatement(sql, keyColumnNames);
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
return connection.prepareStatement(sql);
调用Handler的update/query方法执行:
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement)
ps.execute();
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
public &E& List&E& query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)
ps.execute();
return resultSetHandler.&E& handleResultSets(ps);
这两个方法的区别在于,update需要对key进行处理,而query则需要对resultSet进行处理。
* Jdbc3KeyGenerator
1. 通过PreparedStatement.getGeneratedKeys
2. 获取配置中的keyProperties
3. 获取相对应类型的TypeHandler,将TypeHandler处理过的值设置到对象中去。
* SelectKeyGenerator
1. 通过SimpleExecutor执行KeyStatement获取到Key的值
2. 将值设置到Parameter对象中去
* ResultHandler
执行SELECT的时候,MapperMethod需要根据返回值的类型,选择不同的执行策略:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
//使用指定的resultHandler或者null
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//executor.query时传入的resultHandler为null
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
//executor.query时传入的resultHandler为null
//返回前,通过DefaultMapResultHandler对结果进行处理
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
//executor.query时传入的resultHandler为null
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
//executor.query时传入的resultHandler为null
在Executor执行是,如果传入的ResultHandler为null,则尝试从localCache获取结果,如果结果为null才会从Database中查询。
SimpleExecutor在doQuery时,会通过`configuration.newStatementHandler`时,会通过
`configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);`
创建ResultSetHandler,将resultHandler作为传入参数。
ResultSetHandler在其方法:
public List&Object& handleResultSets(Statement stmt) throws SQLException {...}
中,如果发现resultHandler为null,则会new DefaultResultHandler的实例,对result进行处理。
四、Mybatis中有哪些优秀的设计及值得借鉴的地方?
版权声明:本文内容由互联网用户自发贡献,本社区不拥有所有权,也不承担相关法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至: 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
用云栖社区APP,舒服~
【云栖快讯】阿里云数据库MySQL金融版发布,实现日志多副本同步复制,提供金融级可靠性!8月10日,阿里云数据库掌门人褚霸等大牛直播,揭开它的背后故事!赶紧报名吧&&
业内领先的面向企业的一站式研发提效平台,通过项目流程管理和专项自动化提效工具,能够很好地支持互联网敏捷项目的快速...
支持以数据库为核心的结构化存储产品之间的数据传输。 它是一种集数据迁移、数据订阅及数据实时同步于一体的数据传输服...
提供海量、安全和高可靠的云存储服务。RESTful API的平台无关性,容量和处理能力的弹性扩展,按实际容量付费...
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
2017杭州云栖大会火热抢票
Loading...

我要回帖

 

随机推荐