mybatis 3张表查询,为什么查询出了值但获取时却为空

mybatis 3张表查询,为什么查询出了值但获取时却为空,第1张

apper namespace="comhwsensorframeworkdaoIRolePermissionDao">

<!-- 联合permission,role,per-role的查询 -->

<resultMap id="detailedPerRoleResultMap" type="RolePerPO">

<id property="id" column="ID" />

<result property="roleId" column="ROLE_ID" />

<result property="permId" column="PERM_ID" />

<result property="action" column="ACTION" />

<result property="available" column="AVAILABLE" />

<association property="permId" column="perm_id" javaType="PermissionPO">

<id property="id" column="ID" />

<result property="parentId" column="PARENTID" />

<result property="name" column="NAME" />

<result property="url" column="URL" />

<result property="image" column="IMAGE" />

<result property="available" column="AVAILABLE" />

<result property="admin" column="ADMIN" />

<result property="orderNum" column="ORDERNUM" />

<result property="description" column="DESCRIPTION" />

</association>

<collection property="roleId" ofType="RolePO">

<id property="id" column="ID" />

<result property="parentId" column="PARENTID" />

<result property="name" column="NAME" />

<result property="description" column="DESCRIPTION" />

</collection>

</resultMap>

<!-- 根据permission的id查询三张表 -->

<select id="retrievePerRoleDetails" parameterType="javalangString" resultMap="detailedPerRoleResultMap">

SELECT

RPID RPID,

RPROLE_ID RPROLEID,

RPPERM_ID RPPERID,

RPACTION RPACTION,

RPAVAILABLE RPAVAILABLE,

PERID PERID,

PERPARENTID PERPARENTID,

PERNAME PERNAME,

PERURL PERURL,

PERIMAGE PERIMAGE,

PERAVAILABLE PERAVAILABLE,

PERADMIN PERADMIN,

PERORDERNUM PERORDERNUM,

PERDESCRIPTION PERDESCRIPTION,

ROLEID ROLEID,

ROLENAME ROLENAME,

ROLEDESCRIPTION ROLEDESCRIPTION

FROM SYS_ROLE_PERM RP,

SYS_PERMISSION PER,

SYS_ROLE ROLE

WHERE RPROLE_ID = ROLEID

AND RPPERM_ID = PERID

AND RPPERM_ID = #{permId}

</mapper>

在action中获得的对象的List<RolePermPO>为空,但是它的长度存在,在控制端也打印出了查询出的数据,就是获取的List<RolePermPO>为空?为什么啊?

这个是action代码:

rolePerList = new ArrayList<RolePermPO>();

rolePerList = rolePermissionServiceretrievePerRoleDetails(rolePerPOgetPermId());

Systemoutprintln("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:"+rolePerListsize());

for(RolePermPO rolePer:rolePerList){

Systemoutprintln("---跳转功能角色权限编辑页面----:"+rolePer);

}

控制台打印:

16:37:40,214 DEBUG [javasqlConnection] ==> Preparing: SELECT RPID AS RPID, RPROLE_ID AS RPROLEID, RPPERM_ID AS RPPERID, RPACTION AS RPACTION, RPAVAILABLE AS RPAVAILABLE, PERID AS PERID, PERPARENTID AS PERPARENTID, PERNAME AS PERNAME, PERURL AS PERURL, PERIMAGE AS PERIMAGE, PERAVAILABLE AS PERAVAILABLE, PERADMIN AS PERADMIN, PERORDERNUM AS PERORDERNUM, PERDESCRIPTION AS PERDESCRIPTION, ROLEID AS ROLEID, ROLENAME AS ROLENAME, ROLEDESCRIPTION AS ROLEDESCRIPTION FROM SYS_ROLE_PERM RP, SYS_PERMISSION PER, SYS_ROLE ROLE WHERE RPROLE_ID = ROLEID AND RPPERM_ID = PERID AND RPPERM_ID =

16:37:40,393 DEBUG [javasqlPreparedStatement] ==> Parameters: 491de6bd-5607-4c47-a8d9-67f1b5ad051d(String)

16:37:40,564 DEBUG [javasqlResultSet] <== Columns: RPID, RPROLEID, RPPERID, RPACTION, RPAVAILABLE, PERID, PERPARENTID, PERNAME, PERURL, PERIMAGE, PERAVAILABLE, PERADMIN, PERORDERNUM, PERDESCRIPTION, ROLEID, ROLENAME, ROLEDESCRIPTION

16:37:40,572 DEBUG [javasqlResultSet] <== Row: 2aac29dd-a9b1-4a5d-9466-443b9ba59854, 987a74d1-0225-4fc0-98af-7e648b37da1e, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 00000, 0, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 系统设置模块, pp, null, 0, 1, 0, null, 987a74d1-0225-4fc0-98af-7e648b37da1e, a角色, a角色描述

16:37:40,573 DEBUG [javasqlResultSet] <== Row: d875209d-5425-477d-a0b8-762d2252fe3f, f0c6046f-53d2-452f-a04d-c4ece35eabcc, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 10100, 0, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 系统设置模块, pp, null, 0, 1, 0, null, f0c6046f-53d2-452f-a04d-c4ece35eabcc, mmmm1, mmmmm

16:37:40,574 DEBUG [javasqlResultSet] <== Row: 62dfecae-9599-4bff-959e-43a496e5b61c, f7f50c6c-617f-4c18-851d-10bae545ff02, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 01010, 1, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 系统设置模块, pp, null, 0, 1, 0, null, f7f50c6c-617f-4c18-851d-10bae545ff02, test, test desc

16:37:40,574 DEBUG [javasqlResultSet] <== Row: f766f0ea-0cfd-4173-a363-81ce6068b5ed, d0a04544-6d82-4393-9266-ab39834bd2e8, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 01010, 1, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 系统设置模块, pp, null, 0, 1, 0, null, d0a04544-6d82-4393-9266-ab39834bd2e8, hello2334, nihao24

16:37:40,574 DEBUG [javasqlResultSet] <== Row: 426aea2f-5ae2-4ff3-a1ba-a1d8e2033808, 9d94020c-231a-4429-807a-3248b0be9e7b, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 01010, 1, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 系统设置模块, pp, null, 0, 1, 0, null, 9d94020c-231a-4429-807a-3248b0be9e7b, 管理员00, 管理员描述00

16:37:40,574 DEBUG [javasqlResultSet] <== Row: 18d0bb3a-ce33-4217-9bbe-621839c3e939, e73fe575-7542-416b-9ef1-e5c0e98ed48c, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 00000, 0, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 系统设置模块, pp, null, 0, 1, 0, null, e73fe575-7542-416b-9ef1-e5c0e98ed48c, 测试角色1, 测试角色1-sjl

16:37:40,575 DEBUG [javasqlResultSet] <== Row: 28962107-7b63-4054-bf95-d2835d94e9ff, 464ae26c-16dd-4758-9dce-29489ceb4607, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 10100, 0, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 系统设置模块, pp, null, 0, 1, 0, null, 464ae26c-16dd-4758-9dce-29489ceb4607, 测试角色2, --------------

16:37:40,575 DEBUG [javasqlResultSet] <== Row: 2f7cefdd-6a10-4826-a67b-e4a78294600f, f0b2c1ad-93ad-47d2-8a9c-b79b7c82afa6, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 01null01, 0, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 系统设置模块, pp, null, 0, 1, 0, null, f0b2c1ad-93ad-47d2-8a9c-b79b7c82afa6, 角色名称2, 描述

16:37:40,575 DEBUG [javasqlResultSet] <== Row: 0c8a1442-181b-48c6-a3a5-c61c1b024ccb, 89ea0332-1941-474e-8a7e-fb96e4378b63, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 10100, 0, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 491de6bd-5607-4c47-a8d9-67f1b5ad051d, 系统设置模块, pp, null, 0, 1, 0, null, 89ea0332-1941-474e-8a7e-fb96e4378b63, 系统管理员, 系统管理员的描述

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:9

---跳转功能角色权限编辑页面----:null

---跳转功能角色权限编辑页面----:null

---跳转功能角色权限编辑页面----:null

---跳转功能角色权限编辑页面----:null

---跳转功能角色权限编辑页面----:null

---跳转功能角色权限编辑页面----:null

---跳转功能角色权限编辑页面----:null

---跳转功能角色权限编辑页面----:null

---跳转功能角色权限编辑页面----:null

Java企业应用StrutsiBATISMyBatis

2012年5月21日 16:41

zc0725

21

0 0 1

添加评论

关注(0)

查看附件

1个答案按时间排序按投票排序

00

sql语句中查询出每列的别名 写成 小写

2012年6月01日 23:15

cfyme

14

0 0 3

添加评论

0 待解决问题

26168 已解决问题

95983 热门问题

我的问答FAQ | 勋章

已解决问题

使用Mybatis执行sql时如何统一校验输入参数

js里这种表述式是啥意思?return (y1 - y2 > 0 "Up" : "Down");

请问基于云架构的J2EE架构应该怎么做?

spring ,hibernate 都是用到了asm字节码技术,请问它们具体都拿ASM来实现了什么功能?或者说在哪个功能上用到的

如何判断 *** 作系统是32位还是64位?

DBA_OBJECTS + ROWNUM 和 DUAL + ROWNUM 一个结果比较

竟然Mybatis是对四大接口进行拦截的,那我们药先要知道Mybatis的四大接口对象 Executor, StatementHandler, ResultSetHandler, ParameterHandler。

上图Mybatis框架的整个执行过程。Mybatis插件能够对则四大对象进行拦截,可以包含到了Mybatis一次会议的所有 *** 作。可见Mybatis的的插件很强大。

Executor是 Mybatis的内部执行器,它负责调用StatementHandler *** 作数据库,并把结果集通过 ResultSetHandler进行自动映射,另外,他还处理了二级缓存的 *** 作。从这里可以看出,我们也是可以通过插件来实现自定义的二级缓存的。

StatementHandler是Mybatis直接和数据库执行sql脚本的对象。另外它也实现了Mybatis的一级缓存。这里,我们可以使用插件来实现对一级缓存的 *** 作(禁用等等)。

ParameterHandler是Mybatis实现Sql入参设置的对象。插件可以改变我们Sql的参数默认设置。

ResultSetHandler是Mybatis把ResultSet集合映射成POJO的接口对象。我们可以定义插件对Mybatis的结果集自动映射进行修改。

插件Interceptor

Mybatis的插件实现要实现Interceptor接口,我们看下这个接口定义的方法。

public interface Interceptor {

Object intercept(Invocation invocation) throws Throwable;

Object plugin(Object target);

void setProperties(Properties properties);

}

这个接口只声明了三个方法。

setProperties方法是在Mybatis进行配置插件的时候可以配置自定义相关属性,即:接口实现对象的参数配置

plugin方法是插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了示例:return Pluginwrap(target, this);

intercept方法就是要进行拦截的时候要执行的方法

理解这个接口的定义,先要知道java动态代理机制。plugin接口即返回参数target对象(Executor/ParameterHandler/ResultSetHander/StatementHandler)的代理对象。在调用对应对象的接口的时候,可以进行拦截并处理。

Mybatis四大接口对象创建方法

Mybatis的插件是采用对四大接口的对象生成动态代理对象的方法来实现的。那么现在我们看下Mybatis是怎么创建这四大接口对象的。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

//确保ExecutorType不为空(defaultExecutorType有可能为空)

executorType = executorType == null defaultExecutorType : executorType;

executorType = executorType == null ExecutorTypeSIMPLE : executorType;

Executor executor; if (ExecutorTypeBATCH == executorType) {

executor = new BatchExecutor(this, transaction);

} else if (ExecutorTypeREUSE == executorType) {

executor = new ReuseExecutor(this, transaction);

} else {

executor = new SimpleExecutor(this, transaction);

} if (cacheEnabled) {

executor = new CachingExecutor(executor);

}

executor = (Executor) interceptorChainpluginAll(executor);

return executor;

}

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);

statementHandler = (StatementHandler) interceptorChainpluginAll(statementHandler);

return statementHandler;

}

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {

ParameterHandler parameterHandler = mappedStatementgetLang()createParameterHandler(mappedStatement, parameterObject, boundSql);

parameterHandler = (ParameterHandler) interceptorChainpluginAll(parameterHandler);

return parameterHandler;

}

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {

ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);

resultSetHandler = (ResultSetHandler) interceptorChainpluginAll(resultSetHandler);

return resultSetHandler;

}

查看源码可以发现, Mybatis框架在创建好这四大接口对象的实例后,都会调用InterceptorChainpluginAll()方法。InterceptorChain对象是插件执行链对象,看源码就知道里面维护了Mybatis配置的所有插件(Interceptor)对象。

// target --> Executor/ParameterHandler/ResultSetHander/StatementHandler

public Object pluginAll(Object target) {

for (Interceptor interceptor : interceptors) {

target = interceptorplugin(target);

}

return target;

}

其实就是安顺序执行我们插件的plugin方法,一层一层返回我们原对象(Executor/ParameterHandler/ResultSetHander/StatementHandler)的代理对象。当我们调用四大接口对象的方法时候,实际上是调用代理对象的响应方法,代理对象又会调用十大接口对象的实例。

Plugin对象

我们知道,官方推荐插件实现plugin方法为:Pluginwrap(target, this);

public static Object wrap(Object target, Interceptor interceptor) {

// 获取插件的Intercepts注解

Map<Class<>, Set<Method>> signatureMap = getSignatureMap(interceptor);

Class<> type = targetgetClass();

Class<>[] interfaces = getAllInterfaces(type, signatureMap);

if (interfaceslength > 0) {

return ProxynewProxyInstance(typegetClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));

}

return target;

}

这个方法其实是Mybatis简化我们插件实现的工具方法。其实就是根据当前拦截的对象创建了一个动态代理对象。代理对象的InvocationHandler处理器为新建的Plugin对象。

插件配置注解@Intercepts

Mybatis的插件都要有Intercepts注解来指定要拦截哪个对象的哪个方法。我们知道,Pluginwarp方法会返回四大接口对象的代理对象(通过new Plugin()创建的IvocationHandler处理器),会拦截所有的执行方法。在代理对象执行对应方法的时候,会调用InvocationHandler处理器的invoke方法。Mybatis中利用了注解的方式配置指定拦截哪些方法。具体如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

try {

Set<Method> methods = signatureMapget(methodgetDeclaringClass());

if (methods != null && methodscontains(method)) {

return interceptorintercept(new Invocation(target, method, args));

}

return methodinvoke(target, args);

} catch (Exception e) {

throw ExceptionUtilunwrapThrowable(e);

}

}

可以看到,只有通过Intercepts注解指定的方法才会执行我们自定义插件的intercept方法。未通过Intercepts注解指定的将不会执行我们的intercept方法。

官方插件开发方式

@Intercepts({@Signature(type = Executorclass, method = "query",

args = {MappedStatementclass, Objectclass, RowBoundsclass, ResultHandlerclass})})

public class TestInterceptor implements Interceptor {

public Object intercept(Invocation invocation) throws Throwable {

Object target = invocationgetTarget(); //被代理对象

Method method = invocationgetMethod(); //代理方法

Object[] args = invocationgetArgs(); //方法参数

// do something 方法拦截前执行代码块

Object result = invocationproceed();

// do something 方法拦截后执行代码块

return result;

}

public Object plugin(Object target) {

return Pluginwrap(target, this);

}

}

以上就是Mybatis官方推荐的插件实现的方法,通过Plugin对象创建被代理对象的动态代理对象。可以发现,Mybatis的插件开发还是很简单的。

自定义开发方式

Mybatis的插件开发通过内部提供的Plugin对象可以很简单的开发。只有理解了插件实现原理,对应不采用Plugin对象我们一样可以自己实现插件的开发。下面是我个人理解之后的自己实现的一种方式。

public class TestInterceptor implements Interceptor {

public Object intercept(Invocation invocation) throws Throwable {

Object target = invocationgetTarget(); //被代理对象

Method method = invocationgetMethod(); //代理方法

Object[] args = invocationgetArgs(); //方法参数

// do something 方法拦截前执行代码块

Object result = invocationproceed();

// do something 方法拦截后执行代码块

return result;

}

public Object plugin(final Object target) {

return ProxynewProxyInstance(InterceptorclassgetClassLoader(), targetgetClass()getInterfaces(), new InvocationHandler() {

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

return intercept(new Invocation(target, method, args));

}

});

}

public void setProperties(Properties properties) {

}

}

当然,Mybatis插件的那这个时候Intercepts的注解起不到作用了。

作者:曹金桂

链接:>

1、MyBatis介绍

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)

2、CLOB

SQL CLOB 是内置类型,它将字符大对象 (Character Large Object) 存储为数据库表某一行中的一个列值。默认情况下,驱动程序使用 SQL locator(CLOB) 实现 Clob 对象,这意味着 CLOB 对象包含一个指向 SQL CLOB 数据的逻辑指针而不是数据本身。Clob 对象在它被创建的事务处理期间有效。

3、MyBatis对CLOB类型数据实现增删改查

oracle表结构

create table T_USERS  

(  

  ID      NUMBER not null,  

  NAME    VARCHAR2(30),  

  SEX     VARCHAR2(3),  

  BIRS    DATE,  

  MESSAGE CLOB  

)  

create sequence SEQ_T_USERS_ID  

minvalue 1  

maxvalue 99999999  

start with 1  

increment by 1  

cache 20;

配置mybatis配置文件UsersMapperxml

<xml version="10" encoding="UTF-8">

<!DOCTYPE mapper

  PUBLIC "-//mybatisorg//DTD Mapper 30//EN">

<mapper namespace="examplesmapperUsersMapper" >

<!-- Result Map-->

<resultMap type="examplesbeanUsers" id="BaseResultMap">

<result property="id" column="id" />

<result property="name" column="name" />

<result property="sex" column="sex" />

<result property="birs" column="birs" jdbcType="TIMESTAMP"/>

<result property="message" column="message" jdbcType="CLOB" 

javaType = "javalangString"  typeHandler ="examplesserviceOracleClobTypeHandler"/>

</resultMap>

<sql id="Tabel_Name">

t_users

</sql>

<!-- 表中所有列 -->

<sql id="Base_Column_List" >

id,name,sex,birs,message

</sql>

<!-- 查询条件 -->

<sql id="Example_Where_Clause">

where 1=1

<trim suffixOverrides=",">

<if test="id != null">

and id = #{id}

</if>

<if test="name != null and name != ''">

and name like concat(concat('%', '${name}'), '%')

</if>

<if test="sex != null and sex != ''">

and sex like concat(concat('%', '${sex}'), '%')

</if>

<if test="birs != null">

and birs = #{birs}

</if>

<if test="message != null">

and message = #{message}

</if>

</trim>

</sql>

<!-- 2查询列表 -->

<select id="queryByList" resultMap="BaseResultMap" parameterType="Object">

select

<include refid="Base_Column_List" />

from t_users 

<include refid="Example_Where_Clause"/>

</select>

</mapper>

Mapper类接口

package examplesmapper;

import javautilList;

public interface UsersMapper<T> {

public List<T> queryBySelective(T t);

public List<T> queryByList(T t);

}

类型转换工具类

package examplesservice;

import javasqlCallableStatement;

import javasqlPreparedStatement;

import javasqlResultSet;

import javasqlSQLException;

import oraclesqlCLOB;

import orgapacheibatistypeJdbcType;

import orgapacheibatistypeTypeHandler;

public class OracleClobTypeHandler implements TypeHandler<Object> {

public Object valueOf(String param) {

return null;

}

@Override

public Object getResult(ResultSet arg0, String arg1) throws SQLException {

CLOB clob = (CLOB) arg0getClob(arg1);

return (clob == null || cloblength() == 0)  null : clobgetSubString((long) 1, (int) cloblength());

}

@Override

public Object getResult(ResultSet arg0, int arg1) throws SQLException {

return null;

}

@Override

public Object getResult(CallableStatement arg0, int arg1) throws SQLException {

return null;

}

@Override

public void setParameter(PreparedStatement arg0, int arg1, Object arg2, JdbcType arg3) throws SQLException {

CLOB clob = CLOBempty_lob();

clobsetString(1, (String) arg2);

arg0setClob(arg1, clob);

}

}

Spring配置文件

<xml version="10" encoding="UTF-8">

<beans xmlns="

xmlns:xsi="

xmlns:mvc="

xmlns:tx="

xsi:schemaLocation="

default-autowire="byType">

<!-- 配置数据源 -->

<bean id="dataSource" class="orgspringframeworkjdbcdatasourceDriverManagerDataSource">

         <property name="driverClassName"><value>oraclejdbcdriverOracleDriver</value></property> 

         <property name="url"><value>jdbc:oracle:thin:@127001:1521:pms</value></property> 

         <property name="username"><value>pms</value></property> 

         <property name="password"><value>pms</value></property>

</bean>

<!-- 配完数据源 和 拥有的 sql映射文件 sqlSessionFactory 也可以访问数据库 和拥有 sql *** 作能力了 -->

<!-- 

<bean id="sqlSessionFactory" class="orgmybatisspringSqlSessionFactoryBean">

<property name="dataSource" ref="dataSource" />

   <property name="configLocation" value="classpath:mybatis-configxml"/>

</bean>

 -->

<bean id="sqlSessionFactory" class="orgmybatisspringSqlSessionFactoryBean">

<property name="dataSource" ref="dataSource" />

<property name="mapperLocations">

<list>

<value>classpath:examples/mybatis/oracle/UsersMapperxml</value>

</list>

</property>

</bean>

<!-- 通过设置 mapperInterface属性,使接口服务bean 和对应xml文件管理 可以使用其中的sql -->

<bean id="dao" class="orgmybatisspringmapperMapperFactoryBean">

<!-- 此处等同于 Mybatis 中 ServerDao serverDao = sqlSessiongetMapper(ServerDaoclass); 指明映射关系 -->

<property name="mapperInterface" value="examplesmapperUsersMapper" />

<property name="sqlSessionFactory" ref="sqlSessionFactory" />

</bean>

</beans>

测试类

package examplesservice;

import javatextParseException;

import javatextSimpleDateFormat;

import javautilList;

import orgspringframeworkcontextApplicationContext;

import orgspringframeworkcontextsupportClassPathXmlApplicationContext;

import examplesbeanUsers;

import examplesmapperUsersMapper;

public class TestUsersService {

@SuppressWarnings("unchecked")

public static void main(String[] args) throws ParseException {

ApplicationContext ac = 

new ClassPathXmlApplicationContext("classpath:/examples/service/springxml");

UsersMapper<Users> dao = (UsersMapper<Users>)acgetBean("dao");

//查询

 Users nullBean = new Users();

List<Users> list = daoqueryByList(nullBean);

if(list != null) {

for(Users user : list) {

Systemoutprintln(user);

}

}

}

}

KeyWords: Mybatis 原理,源码,Mybatis Mapper 接口实现类,代理模式,动态代理,Java动态代理,ProxynewProxyInstance,Mapper 映射,Mapper 实现

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。我们在使用 Mybaits 进行 ,通常只需要定义几个 Mapper 接口,然后在编写一个 xml 文件,我们在配置文件中写好 sql , Mybatis 帮我们完成 Mapper 接口道具体实现的调用。以及将结果映射到 model bean 中。

我们在项目中所编写的众多的 Mapper 类只是一个接口(interface ),根据 Java 的多态性我们知道,可以使用接口接口作为形参,进而在运行时确定具体实现的对象是什么。但是,对于 Mapper 接口,我们并没有编写其实现类!Mybatis是如何找到其实现类,进而完成具体的 CRUD 方法调用的呢?原理何在?

为了弄清楚 Mapper 接口是如何找到实现类的,我们先回忆一下 Mybatis 是怎么使用的,根据实际的例子,进而一点点的去分析。这里的使用指的是Mybatis 单独使用,而不是整合 spring , 因为整合 spring 的话,还需要涉及 Mapper dao 装载到 spring 容器的问题,spring 帮忙创建数据源配置等问题。

通常我们使用 Mybatis 的主要步骤是:

从一段代码看起

上面我们概括了使用 Mybatis 的4个步骤。这4个步骤看起来很简单,但是用代码写出来就很多。我们不妨先记着这4个步骤,再去看代码,会容易点。

在这块代码中,第 1 部分我们使用了 Java 编码的形式来实现 SqlSessionFactory ,也可以使用 xml 。如果使用xml的话,上面的第一部分代码就是这样的:

我们本次的目标是弄清楚 “ Mapper 是如何找到实现类的 ”,我们注意上面代码 3 , 4 的位置:

这里 mapper 可以调用selectBlog(1) 这个方法,说明 mapper 是个对象,因为对象才具有方法行为实现啊。BlogMapper接口是不能实例化的,更没有具体方法实现。我们并没有定义一个类,让它实现BlogMapper接口,而在这里它只是通过调用sessiongetMapper() 所得到的。由此,我们可以推断:肯定是sessiongetMapper() 方法内部产生了BlogMapper的实现类。有什么技术可以根据BlogMapper 接口生成了一个实现类呢?想到这里,对于有动态代理 使用经验的程序员来说,很容易想到,这背后肯定是基于动态代理技术,具体怎么实现的呢?下面我们来根据源码一探究竟。

Mapper 接口的注册

从上面的代码中,我们知道 BlogMapper 接口的实现类是从sessiongetMapper中得来的,大概是基于动态代理技术实现。我们既然能够从SqlSession中得到BlogMapper接口的,那么我们肯定需要先在哪里把它放进去了,然后 SqlSession 才能生成我们想要的代理类啊。上面代码中有这么一行:

跟着这个 addMapper 方法的代码实现是这样的:

我们看到这里 mapper 实际上被添加到 mapperRegissry 中。继续跟进代码:

看到这里我们知道上面所执行的configurationaddMapper(BlogMapperclass); 其实最终被放到了HashMap中,其名为knownMappers ,knowMappers是MapperRegistry 类的一个私有属性,它是一个HashMap 。其Key 为当前Class对象,value 为一个MapperProxyFactory 实例。

这里我们总结一下: 诸如BlogMapper 之类的Mapper接口被添加到了MapperRegistry 中的一个HashMap中。并以 Mapper 接口的 Class 对象作为 Key , 以一个携带Mapper接口作为属性的MapperProxyFactory 实例作为value 。MapperProxyFacory从名字来看,好像是一个工厂,用来创建Mapper Proxy的工厂。我们继续往下看。

Mapper接口的动态代理类的生成

上面我们已经知道,Mapper 接口被到注册到了MapperRegistry中——放在其名为knowMappers 的HashMap属性中,我们在调用Mapper接口的方法的时候,是这样的:

这里,我们跟踪一下sessiongetMapper() 方法的代码实现,这里 SqlSession 是一个接口,他有两个实现类,一个是DefaultSqlSession,另外一个是SqlSessionManager,这里我们用的是DefaultSqlSession 为什么是DefaultSqlSession呢?因为我们在初始化SqlSessionFactory的时候所调用的SqlSessionFactoryBuilder的build()方法里边配置的就是DefaultSqlSession, 所以,我们进入到DefaultSession类中,看看它对sessiongetMapper(BlogMapperclass)是怎么实现的:

如代码所示,这里的 getMapper 调用了 configurationgetMapper , 这一步 *** 作其实最终是调用了MapperRegistry,而此前我们已经知道,MapperRegistry是存放了一个HashMap的,我们继续跟踪进去看看,那么这里的get,肯定是从这个hashMap中取数据。我们来看看代码:

我们调用的sessiongetMapper(BlogMapperclass);最终会到达上面这个方法,这个方法,根据BlogMapper的class对象,以它为key在knowMappers 中找到了对应的value —— MapperProxyFactory(BlogMapper) 对象,然后调用这个对象的newInstance()方法。根据这个名字,我们就能猜到这个方法是创建了一个对象,代码是这样的:

看到这里,就清楚了,最终是通过ProxynewProxyInstance产生了一个BlogMapper的代理对象。Mybatis 为了完成 Mapper 接口的实现,运用了代理模式。具体是使用了JDK动态代理,这个ProxynewProxyInstance方法生成代理类的三个要素是:

代理模式中,代理类(MapperProxy)中才真正的完成了方法调用的逻辑。我们贴出MapperProxy的代码,如下:

我们调用的 Blog blog = mapperselectBlog(1); 实际上最后是会调用这个MapperProxy的invoke方法。这段代码中,if 语句先判断,我们想要调用的方法是否来自Object类,这里的意思就是,如果我们调用toString()方法,那么是不需要做代理增强的,直接还调用原来的methodinvoke()就行了。只有调用selectBlog()之类的方法的时候,才执行增强的调用——即mapperMethodexecute(sqlSession, args);这一句代码逻辑。

而mapperMethodexecute(sqlSession, args);这句最终就会执行增删改查了,代码如下:

再往下一层,就是执行JDBC那一套了,获取链接,执行,得到ResultSet,解析ResultSet映射成JavaBean。

至此,我们已经摸清楚了Blog blog = mapperselectBlog(1); 中,BlogMapper接口调用到得到数据库数据过程中,Mybaitis 是如何为接口生成实现类的,以及在哪里出发了最终的CRUD调用。实际上,如果我们在调用Blog blog = mapperselectBlog(1);之前,把从slqSession中得到的 mapper 对象打印出来就会看到,输出大概是这样的:

动态代理没错吧,Java动态代理实在是太美妙了。

上面我们用层层深入的方式摸清楚了 Mapper接口是如何找到实现类的。我们分析了 Mapper接口是如何注册的,Mapper接口是如何产生动态代理对象的,Maper接口方法最终是如何执行的。总结起来主要就是这几个点:

MyBatis提供了orgapacheibatistypeBaseTypeHandler类用于我们自己扩展类型转换器,上面的EnumTypeHandler和EnumOrdinalTypeHandler也都实现了这个接口。

1 定义接口

我们需要一个接口来确定某部分枚举类的行为。如下:

public interface BaseCodeEnum {

int getCode();

}

该接口只有一个返回编码的方法,返回值将被存入数据库。

2 改造枚举

就拿上面的ComputerState来实现BaseCodeEnum接口:

public enum ComputerState implements BaseCodeEnum{

OPEN(10), //开启

CLOSE(11), //关闭

OFF_LINE(12), //离线

FAULT(200), //故障

UNKNOWN(255); //未知

private int code;

ComputerState(int code) { thiscode = code; }

@Override

public int getCode() { return thiscode; }

}

3 编写一个转换工具类

现在我们能顺利的将枚举转换为某个数值了,还需要一个工具将数值转换为枚举实例。

public class CodeEnumUtil {

public static <E extends Enum<> & BaseCodeEnum> E codeOf(Class<E> enumClass, int code) {

E[] enumConstants = enumClassgetEnumConstants();

for (E e : enumConstants) {

if (egetCode() == code)

return e;

}

return null;

}

}

4 自定义类型转换器

准备工作做的差不多了,是时候开始编写转换器了。

BaseTypeHandler<T> 一共需要实现4个方法:

void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)

用于定义设置参数时,该如何把Java类型的参数转换为对应的数据库类型

T getNullableResult(ResultSet rs, String columnName)

用于定义通过字段名称获取字段数据时,如何把数据库类型转换为对应的Java类型

T getNullableResult(ResultSet rs, int columnIndex)

用于定义通过字段索引获取字段数据时,如何把数据库类型转换为对应的Java类型

T getNullableResult(CallableStatement cs, int columnIndex)

用定义调用存储过程后,如何把数据库类型转换为对应的Java类型

使用现有的Service接口,或者自己在编写一些用到的接口,手动使用Java代码来分别调用Service接口来查出各个model,然后在业务层将model转换为vo,最后返回给前端json串。

为需求相关的页面定义自己的vo,在vo中只定义前端用到的字段。而不是像第一种方式一样vo中一层一层的嵌套model。然后使用sql语句进行表关联,查询用到的字段。组装为vo直接返回

<resultMap id="checkAccountReturn" type="javautilHashMap">

<id property="account_id" column="account_id" />

<result property="sub_account_id" column="sub_account_id"/>

<result property="status" column="status"/>

<result property="account_status" column="account_status"/>

<result property="total_count" column="total_count"/>

<result property="sms_sign" column="sms_sign"/>

</resultMap>

<select id="checkSub_account" resultMap="checkAccountReturn">

SELECT account_id,sub_account_id,status,account_status,total_count,sms_sign

FROM sub_account

<where>

<if test="account_id!=null">

account_id=#{account_id}

</if>

<if test="sub_account_id!=null">

and sub_account_id = #{sub_account_id} and status=1 and total_count!=0 and account_status=1

</if>

</where>

</select>

我把mybatis32的源码下载之后,把里面PooledDataSource类的log输出部分,换成logwarn之后,重新打jar包,放到项目中,日志级别改为info,如:

Java代码

log4jrootLogger=info, stdout, R

log4jappenderstdout=orgapachelog4jConsoleAppender

log4jappenderstdoutlayout=orgapachelog4jPatternLayout

log4jappenderstdoutlayoutConversionPattern=[service] %d - %c -%-4r [%t] %-5p %c %x - %m%n

log4jappenderR=orgapachelog4jDailyRollingFileAppender

log4jappenderRFile=/logs/servicelog

log4jappenderRlayout=orgapachelog4jPatternLayout

log4jappenderRlayoutConversionPattern=[service] %d - %c -%-4r [%t] %-5p %c %x - %m%n

log4jloggercomibatis = debug

log4jloggercomibatiscommonjdbcSimpleDataSource = debug

log4jloggercomibatiscommonjdbcScriptRunner = debug

log4jloggercomibatissqlmapengineimplSqlMapClientDelegate = debug

log4jloggerjavasqlConnection = debug

log4jloggerjavasqlStatement = debug

log4jloggerjavasqlPreparedStatement = debug

log4jloggerjavasqlResultSet =debug

此时,info级别能够输出warn级别的日志,我的日志显示如下:

Java代码

[service] 2013-04-19 10:30:57,982 - orgapacheibatisdatasourcepooledPooledDataSource -0 [main] WARN orgapacheibatisdatasourcepooledPooledDataSource - PooledDataSource forcefully closed/removed all connections

[service] 2013-04-19 10:30:57,985 - orgapacheibatisdatasourcepooledPooledDataSource -3 [main] WARN orgapacheibatisdatasourcepooledPooledDataSource - PooledDataSource forcefully closed/removed all connections

[service] 2013-04-19 10:30:57,986 - orgapacheibatisdatasourcepooledPooledDataSource -4 [main] WARN orgapacheibatisdatasourcepooledPooledDataSource - PooledDataSource forcefully closed/removed all connections

[service] 2013-04-19 10:30:57,996 - orgapacheibatisdatasourcepooledPooledDataSource -14 [main] WARN orgapacheibatisdatasourcepooledPooledDataSource - PooledDataSource forcefully closed/removed all connections

[service] 2013-04-19 10:30:59,025 - orgapacheibatisdatasourcepooledPooledDataSource -1043 [main] WARN orgapacheibatisdatasourcepooledPooledDataSource - Created connection 22323092

[service] 2013-04-19 10:30:59,076 - orgapacheibatisdatasourcepooledPooledDataSource -1094 [main] WARN orgapacheibatisdatasourcepooledPooledDataSource - Returned connection 22323092 to pool

很明显,这是显示warn的日志信息了。说明mybatis3只是打印了debug级别的日志。

MyBatis学习 之 一、MyBatis简介与配置MyBatis+Spring+MySql

MyBatis学习 之 二、SQL语句映射文件(1)resultMap

MyBatis学习 之 二、SQL语句映射文件(2)增删改查、参数、缓存

MyBatis学习 之 三、动态SQL语句

MyBatis学习 之 四、MyBatis配置文件

在定义sqlSessionFactory时需要指定MyBatis主配置文件:

Xml代码 收藏代码

<bean id="sqlSessionFactory" class="orgmybatisspringSqlSessionFactoryBean">

<property name="configLocation" value="classpath:mybatis-configxml" />

<property name="dataSource" ref="dataSource" />

</bean>

MyBatis配置文件中大标签configuration下子标签包括:

configuration

|--- properties

|--- settings

|--- typeAliases

|--- typeHandlers

|--- objectFactory

|--- plugins

|--- environments

|--- |--- environment

|--- |--- |--- transactionManager

|--- |--- |__ dataSource

|__ mappers

41 properties属性

properties和java的properties的配置文件有关。配置properties的resource指定properties的路径,然后再在properties标签下配置property的name和value,则可以替换properties文件中相应属性值。

Xml代码 收藏代码

<!-- 属性替换 -->

<properties resource="mysqlproperties">

<property name="jdbcdriverClassName" value="commysqljdbcDriver"/>

<property name="jdbcurl" value="jdbc:mysql://localhost:3306/student_manager"/>

<property name="username" value="root"/>

<property name="password" value="limingnihao"/>

</properties>

42 settings设置

这是MyBatis 修改 *** 作运行过程细节的重要的步骤。下方这个表格描述了这些设置项、含义和默认值。

设置项

描述

允许值

默认值

cacheEnabled

对在此配置文件下的所有cache 进行全局性开/关设置。

true | false

true

lazyLoadingEnabled

全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。

true | false

true

aggressiveLazyLoading

当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。

true | false

true

multipleResultSetsEnabled

允许和不允许单条语句返回多个数据集(取决于驱动需求)

true | false

true

useColumnLabel

使用列标签代替列名称。不同的驱动器有不同的作法。参考一下驱动器文档,或者用这两个不同的选项进行测试一下。

true | false

true

useGeneratedKeys

允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。

true | false

false

autoMappingBehavior

指定MyBatis 是否并且如何来自动映射数据表字段与对象的属性。PARTIAL将只自动映射简单的,没有嵌套的结果。FULL 将自动映射所有复杂的结果。

NONE,

PARTIAL,

FULL

PARTIAL

defaultExecutorType

配置和设定执行器,SIMPLE 执行器执行其它语句。REUSE 执行器可能重复使用prepared statements 语句,BATCH执行器可以重复执行语句和批量更新。

SIMPLE

REUSE

BATCH

SIMPLE

defaultStatementTimeout

设置一个时限,以决定让驱动器等待数据库回应的多长时间为超时

正整数

Not Set

(null)

例如:

Xml代码 收藏代码

<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="enhancementEnabled" value="false" />

<setting name="defaultExecutorType" value="SIMPLE" />

</settings>

43 typeAliases类型别名

类型别名是Java 类型的简称。

它仅仅只是关联到XML 配置,简写冗长的JAVA 类名。例如:

Xml代码 收藏代码

<typeAliases>

<typeAlias alias="UserEntity" type="commanagerdatamodelUserEntity" />

<typeAlias alias="StudentEntity" type="commanagerdatamodelStudentEntity" />

<typeAlias alias="ClassEntity" type="commanagerdatamodelClassEntity" />

</typeAliases>

使用这个配置,“StudentEntity”就能在任何地方代替“commanagerdatamodelStudentEntity”被使用。

对于普通的Java类型,有许多内建的类型别名。它们都是大小写不敏感的,由于重载的名字,要注意原生类型的特殊处理。

别名

映射的类型

_byte

byte

_long

long

_short

short

_int

int

_integer

int

_double

double

_float

float

_boolean

boolean

string

String

byte

Byte

long

Long

short

Short

int

Integer

integer

Integer

double

Double

float

Float

boolean

Boolean

date

Date

decimal

BigDecimal

bigdecimal

BigDecimal

object

Object

map

Map

hashmap

HashMap

list

List

arraylist

ArrayList

collection

Collection

iterator

Iterator

44 typeHandlers类型句柄

无论是MyBatis在预处理语句中设置一个参数,还是从结果集中取出一个值时,类型处理器被用来将获取的值以合适的方式转换成Java类型。下面这个表格描述了默认的类型处理器。

类型处理器

Java类型

JDBC类型

BooleanTypeHandler

Boolean,boolean

任何兼容的布尔值

ByteTypeHandler

Byte,byte

任何兼容的数字或字节类型

ShortTypeHandler

Short,short

任何兼容的数字或短整型

IntegerTypeHandler

Integer,int

任何兼容的数字和整型

LongTypeHandler

Long,long

任何兼容的数字或长整型

FloatTypeHandler

Float,float

任何兼容的数字或单精度浮点型

DoubleTypeHandler

Double,double

任何兼容的数字或双精度浮点型

BigDecimalTypeHandler

BigDecimal

任何兼容的数字或十进制小数类型

StringTypeHandler

String

CHAR和VARCHAR类型

ClobTypeHandler

String

CLOB和LONGVARCHAR类型

NStringTypeHandler

String

NVARCHAR和NCHAR类型

NClobTypeHandler

String

NCLOB类型

ByteArrayTypeHandler

byte[]

任何兼容的字节流类型

BlobTypeHandler

byte[]

BLOB和LONGVARBINARY类型

DateTypeHandler

Date(javautil)

TIMESTAMP类型

DateOnlyTypeHandler

Date(javautil)

DATE类型

TimeOnlyTypeHandler

Date(javautil)

TIME类型

SqlTimestampTypeHandler

Timestamp(javasql)

TIMESTAMP类型

SqlDateTypeHandler

Date(javasql)

DATE类型

SqlTimeTypeHandler

Time(javasql)

TIME类型

ObjectTypeHandler

Any

其他或未指定类型

EnumTypeHandler

Enumeration类型

VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)。

你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。要这样做的话,简单实现TypeHandler接口(orgmybatistype),然后映射新的类型处理器类到Java类型,还有可选的一个JDBC类型。然后再typeHandlers中添加这个类型处理器。

新定义的类型处理器将会覆盖已经存在的处理Java的String类型属性和VARCHAR参数及结果的类型处理器。要注意MyBatis不会审视数据库元信息来决定使用哪种类型,所以你必须在参数和结果映射中指定那是VARCHAR类型的字段,来绑定到正确的类型处理器上。这是因为MyBatis直到语句被执行都不知道数据类型的这个现实导致的。

Java代码 收藏代码

public class LimingStringTypeHandler implements TypeHandler {

@Override

public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {

Systemoutprintln("setParameter - parameter: " + ((String) parameter) + ", jdbcType: " + jdbcTypeTYPE_CODE);

pssetString(i, ((String) parameter));

}

@Override

public Object getResult(ResultSet rs, String columnName) throws SQLException {

Systemoutprintln("getResult - columnName: " + columnName);

return rsgetString(columnName);

}

@Override

public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {

Systemoutprintln("getResult - columnIndex: " + columnIndex);

return csgetString(columnIndex);

}

}

在配置文件的typeHandlers中添加typeHandler标签。

Xml代码 收藏代码

<typeHandlers>

<typeHandler javaType="String" jdbcType="VARCHAR" handler="limingstudentmanagertypeLimingStringTypeHandler"/>

</typeHandlers>

以上就是关于mybatis 3张表查询,为什么查询出了值但获取时却为空全部的内容,包括:mybatis 3张表查询,为什么查询出了值但获取时却为空、mybatis自定义插件要实现什么接口、mybatis如何读取clob数据 详细过程等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9543359.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-29
下一篇 2023-04-29

发表评论

登录后才能评论

评论列表(0条)

保存