Mybatis中几个重要类
来源:互联网 发布:斐波那契数列java输出 编辑:程序博客网 时间:2024/06/06 02:38
本文基于Mybatis3.2.0版本的代码。
1.org.apache.ibatis.mapping.MappedStatement
MappedStatement类在Mybatis框架中用于表示XML文件中一个sql语句节点,即一个<select />、<update />或者<insert />标签。Mybatis框架在初始化阶段会对XML配置文件进行读取,将其中的sql语句节点对象化为一个个MappedStatement对象。比如下面这个非常简单的XML mapper文件:
01
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
02
<!DOCTYPE mapper
03
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
04
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
05
<
mapper
namespace
=
"mybatis.UserDao"
>
06
07
<
cache
type
=
"org.mybatis.caches.ehcache.LoggingEhcache"
/>
08
09
<
resultMap
id
=
"userResultMap"
type
=
"UserBean"
>
10
<
id
property
=
"userId"
column
=
"user_id"
/>
11
<
result
property
=
"userName"
column
=
"user_name"
/>
12
<
result
property
=
"userPassword"
column
=
"user_password"
/>
13
<
result
property
=
"createDate"
column
=
"create_date"
/>
14
</
resultMap
>
15
16
<
select
id
=
"find"
parameterType
=
"UserBean"
resultMap
=
"userResultMap"
>
17
select * from user
18
<
where
>
19
<
if
test
=
"userName!=null and userName!=''"
>
20
and user_name = #{userName}
21
</
if
>
22
<
if
test
=
"userPassword!=null and userPassword!=''"
>
23
and user_password = #{userPassword}
24
</
if
>
25
<
if
test
=
"createDate !=null"
>
26
and create_date = #{createDate}
27
</
if
>
28
</
where
>
29
</
select
>
30
31
<!-- 说明mybatis中的sql语句节点和映射的接口中的方法,并不是一一对应的关系,而是独立的,可以取任意不重复的名称 -->
32
<
select
id
=
"find2"
parameterType
=
"UserBean"
resultMap
=
"userResultMap"
>
33
select * from user
34
<
where
>
35
<
if
test
=
"userName!=null and userName!=''"
>
36
and user_name = #{userName}
37
</
if
>
38
<
if
test
=
"userPassword!=null and userPassword!=''"
>
39
and user_password = #{userPassword}
40
</
if
>
41
<
if
test
=
"createDate !=null"
>
42
and create_date = #{createDate}
43
</
if
>
44
</
where
>
45
</
select
>
46
47
</
mapper
>
Mybatis对这个文件的配置读取和解析后,会注册两个MappedStatement对象,分别对应其中id为find和find2的<select />节点,通过org.apache.ibatis.session.Configuration类中的getMappedStatement(String id)方法,可以检索到一个特定的MappedStatement。为了区分不同的Mapper文件中的sql节点,其中的String id方法参数,是以Mapper文件的namespace作为前缀,再加上该节点本身的id值。比如上面生成的两个MappedStatement对象在Mybatis框架中的唯一标识分别是mybatis.UserDao.find和mybatis.UserDao.find2。
打开MappedStatement对象的源码,看一下其中的私有属性。
01
public
final
class
MappedStatement {
02
03
private
String resource;
04
private
Configuration configuration;
05
private
String id;
06
private
Integer fetchSize;
07
private
Integer timeout;
08
private
StatementType statementType;
09
private
ResultSetType resultSetType;
10
private
SqlSource sqlSource;
11
private
Cache cache;
12
private
ParameterMap parameterMap;
13
private
List<ResultMap> resultMaps;
14
private
boolean
flushCacheRequired;
15
private
boolean
useCache;
16
private
boolean
resultOrdered;
17
private
SqlCommandType sqlCommandType;
18
private
KeyGenerator keyGenerator;
19
private
String[] keyProperties;
20
private
String[] keyColumns;
21
private
boolean
hasNestedResultMaps;
22
private
String databaseId;
23
private
Log statementLog;
24
private
LanguageDriver lang;
25
26
private
MappedStatement() {
27
// constructor disabled
28
}
29
..........
30
}
我们可以看到其中的属性基本上和xml元素的属性有对应关系,其中比较重要的有表示查询参数的ParameterMap对象,表示sql查询结果映射关系的ResultMap列表resultMaps,当然最重要的还是执行动态sql计算和获取的SqlSource对象。通过这些对象的通力合作,MappedStatement接受用户的查询参数对象,动态计算出要执行的sql语句,在数据库中执行sql语句后,再将取得的数据封装为JavaBean对象返回给用户。MappedStatement对象的这些功能,也体现出了Mybatis这个框架的核心价值,“根据用户提供的查询参数对象,动态执行sql语句,并将结果封装为Java对象”。
2.org.apache.ibatis.mapping.SqlSource
SqlSource是一个接口类,在MappedStatement对象中是作为一个属性出现的,它的代码如下:
01
package
org.apache.ibatis.mapping;
02
03
/**
04
*
05
* This bean represets the content of a mapped statement read from an XML file
06
* or an annotation. It creates the SQL that will be passed to the database out
07
* of the input parameter received from the user.
08
*
09
*/
10
public
interface
SqlSource {
11
12
BoundSql getBoundSql(Object parameterObject);
13
14
}
01
package
org.apache.ibatis.scripting.xmltags;
02
03
import
java.util.Map;
04
05
import
org.apache.ibatis.builder.SqlSourceBuilder;
06
import
org.apache.ibatis.mapping.BoundSql;
07
import
org.apache.ibatis.mapping.SqlSource;
08
import
org.apache.ibatis.session.Configuration;
09
10
public
class
DynamicSqlSource
implements
SqlSource {
11
12
private
Configuration configuration;
13
private
SqlNode rootSqlNode;
14
15
public
DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
16
this
.configuration = configuration;
17
this
.rootSqlNode = rootSqlNode;
18
}
19
20
public
BoundSql getBoundSql(Object parameterObject) {
21
DynamicContext context =
new
DynamicContext(configuration, parameterObject);
22
rootSqlNode.apply(context);
23
SqlSourceBuilder sqlSourceParser =
new
SqlSourceBuilder(configuration);
24
Class<?> parameterType = parameterObject ==
null
? Object.
class
: parameterObject.getClass();
25
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
26
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
27
for
(Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
28
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
29
}
30
return
boundSql;
31
}
32
33
}
其中的
1
rootSqlNode.apply(context);
这句调用语句,启动了一个非常精密的递归实现的动态计算sql语句的过程,计算过程使用Ognl来根据传入的参数对象计算表达式,生成该次调用过程中实际执行的sql语句。
3.org.apache.ibatis.scripting.xmltags.DynamicContext
DynamicContext类中,有对传入的parameterObject对象进行“map”化处理的部分,也就是说,你传入的pojo对象,会被当作一个键值对数据来源来进行处理,读取这个pojo对象的接口,还是Map对象。从DynamicContext的源码中,能看到很明显的线索。
001
import
java.util.HashMap;
002
import
java.util.Map;
003
004
import
ognl.OgnlException;
005
import
ognl.OgnlRuntime;
006
import
ognl.PropertyAccessor;
007
008
import
org.apache.ibatis.reflection.MetaObject;
009
import
org.apache.ibatis.session.Configuration;
010
011
public
class
DynamicContext {
012
013
public
static
final
String PARAMETER_OBJECT_KEY =
"_parameter"
;
014
public
static
final
String DATABASE_ID_KEY =
"_databaseId"
;
015
016
static
{
017
OgnlRuntime.setPropertyAccessor(ContextMap.
class
,
new
ContextAccessor());
018
}
019
020
private
final
ContextMap bindings;
021
private
final
StringBuilder sqlBuilder =
new
StringBuilder();
022
private
int
uniqueNumber =
0
;
023
024
public
DynamicContext(Configuration configuration, Object parameterObject) {
025
if
(parameterObject !=
null
&& !(parameterObject
instanceof
Map)) {
026
MetaObject metaObject = configuration.newMetaObject(parameterObject);
027
bindings =
new
ContextMap(metaObject);
028
}
else
{
029
bindings =
new
ContextMap(
null
);
030
}
031
bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
032
bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
033
}
034
035
public
Map<String, Object> getBindings() {
036
return
bindings;
037
}
038
039
public
void
bind(String name, Object value) {
040
bindings.put(name, value);
041
}
042
043
public
void
appendSql(String sql) {
044
sqlBuilder.append(sql);
045
sqlBuilder.append(
" "
);
046
}
047
048
public
String getSql() {
049
return
sqlBuilder.toString().trim();
050
}
051
052
public
int
getUniqueNumber() {
053
return
uniqueNumber++;
054
}
055
056
static
class
ContextMap
extends
HashMap<String, Object> {
057
private
static
final
long
serialVersionUID = 2977601501966151582L;
058
059
private
MetaObject parameterMetaObject;
060
public
ContextMap(MetaObject parameterMetaObject) {
061
this
.parameterMetaObject = parameterMetaObject;
062
}
063
064
@Override
065
public
Object get(Object key) {
066
String strKey = (String) key;
067
if
(
super
.containsKey(strKey)) {
068
return
super
.get(strKey);
069
}
070
071
if
(parameterMetaObject !=
null
) {
072
Object object = parameterMetaObject.getValue(strKey);
073
if
(object !=
null
) {
074
super
.put(strKey, object);
075
}
076
077
return
object;
078
}
079
080
return
null
;
081
}
082
}
083
084
static
class
ContextAccessor
implements
PropertyAccessor {
085
086
public
Object getProperty(Map context, Object target, Object name)
087
throws
OgnlException {
088
Map map = (Map) target;
089
090
Object result = map.get(name);
091
if
(result !=
null
) {
092
return
result;
093
}
094
095
Object parameterObject = map.get(PARAMETER_OBJECT_KEY);
096
if
(parameterObject
instanceof
Map) {
097
return
((Map)parameterObject).get(name);
098
}
099
100
return
null
;
101
}
102
103
public
void
setProperty(Map context, Object target, Object name, Object value)
104
throws
OgnlException {
105
Map map = (Map) target;
106
map.put(name, value);
107
}
108
}
109
}
我们都知道,Mybatis中采用了Ognl来计算动态sql语句,DynamicContext类中的这个静态初始块,很好的说明了这一点
1
static
{
2
OgnlRuntime.setPropertyAccessor(ContextMap.
class
,
new
ContextAccessor());
3
}
ContextAccessor也是DynamicContext的内部类,实现了Ognl中的PropertyAccessor接口,为Ognl提供了如何使用ContextMap参数对象的说明,这个类也为整个参数对象“map”化划上了最后一笔。
现在我们能比较清晰的描述一下Mybatis中的参数传递和使用过程了:将传入的参数对象统一封装为ContextMap对象(继承了HashMap对象),然后Ognl运行时环境在动态计算sql语句时,会按照ContextAccessor中描述的Map接口的方式来访问和读取ContextMap对象,获取计算过程中需要的参数。ContextMap对象内部可能封装了一个普通的POJO对象,也可以是直接传递的Map对象,当然从外部是看不出来的,因为都是使用Map的接口来读取数据。
结合一个例子来理解一下:
01
@Test
02
public
void
testSqlSource()
throws
Exception {
03
String resource =
"mybatis/mybatis-config.xml"
;
04
InputStream inputStream = Resources.getResourceAsStream(resource);
05
SqlSessionFactory sqlSessionFactory =
new
SqlSessionFactoryBuilder()
06
.build(inputStream);
07
SqlSession session = sqlSessionFactory.openSession();
08
09
try
{
10
Configuration configuration = session.getConfiguration();
11
MappedStatement mappedStatement = configuration
12
.getMappedStatement(
"mybatis.UserDao.find2"
);
13
assertNotNull(mappedStatement);
14
15
UserBean param =
new
UserBean();
16
param.setUserName(
"admin"
);
17
param.setUserPassword(
"admin"
);
18
BoundSql boundSql = mappedStatement.getBoundSql(param);
19
String sql = boundSql.getSql();
20
21
Map<String, Object> map =
new
HashMap<String, Object>();
22
map.put(
"userName"
,
"admin"
);
23
map.put(
"userPassword"
,
"admin"
);
24
BoundSql boundSql2 = mappedStatement.getBoundSql(map);
25
String sql2 = boundSql2.getSql();
26
27
assertEquals(sql, sql2);
28
29
UserBean bean = session.selectOne(
"mybatis.UserDao.find2"
, map);
30
assertNotNull(bean);
31
32
}
finally
{
33
session.close();
34
}
35
36
}
上面这个Junit测试方法,是我写的一个测试用例中的一小段,其中的UserBean对象,就是一个有三个属性userName,userPassword,createDate的POJO对象,对应的Mapper文件是文章开头给出的配置文件。
第一次测试,我使用的是一个UserBean对象,来获取和计算sql语句,而第二次我是使用了一个HashMap对象,按照属性的名字,我分别设置了两个键值对象,我甚至还直接使用它来启动了一次session对象的查询selectOne。所有这些操作,都是测试通过(绿条)。这充分说明了,Mybatis参数获取过程中,对Map对象和普通POJO对象的无差别化,因为在内部,两者都会被封装,然后通过Map接口来访问!
转载地址:http://www.open-open.com/lib/view/open1363572227609.html
- Mybatis中几个重要类
- Mybatis中几个重要类
- Mybatis中几个重要类
- Mybatis中几个重要类
- Mybatis中几个重要类
- Mybatis中几个重要类
- NS2中几个重要的类
- java中重要的几个常用类
- hibernate中几个重要对象
- 无线通信中几个重要概念
- Zookeeper中几个重要概念
- MapReduce中几个重要组件
- java.util包中几个重要集合类描述
- Java Servlet API中有几个比较重要的类
- 集合中几个比较重要的接口和类简介
- Box2d学习笔记三:box2d中几个重要的类
- java中操作数据库中的几个重要类
- (重要!)Servlet中主要的几个接口和类
- wpa_supplicant 工具使用
- 区别 after()|append() after()|prepend() appendTo() | prependTo()
- Tengine (淘宝nginx ) + tomcat 安装整合
- Android http请求代码
- Android.mk的用法和基础
- Mybatis中几个重要类
- 2014校园招聘笔、面经历总结---华为双选会
- 优秀代码赏析(C++,链表)
- ARM-Linux开机自启动设置-mini2440开发板
- 视频监控客户端开发(IP Camera)总结
- TreeMap的使用
- linux命令之文件编写
- Android.mk文件语法规范及使用模板
- IDT检测 多CPU