SpringBoot和mybatis采用junit测试时找不到mapper.xml

SpringBoot和mybatis采用junit测试时找不到mapper.xml,第1张

mybatis 的springboot 集成项目已经发布了

maven

12345<dependency> <groupId>orgmybatisspringboot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>100</version></dependency>

properties 配置

123456mybatisconfig= # mybatis config filemybatismapperLocations= # mappers filemybatistypeAliasesPackage= # domain object's package mybatistypeHandlersPackage= # handler's packagemybatischeck-config-location= # check the mybatis configuration existsmybatisexecutorType= # mode of execution Default is SIMPLE

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 是一个可以自定义SQL、存储过程和高级映射的持久层框架。MyBatis 摒除了大部分的JDBC代码、手工设置参数和结果集重获。MyBatis 只使用简单的XML 和注解来配置和映射基本数据类型、Map 接口和POJO 到数据库记录。相对Hibernate和Apache OJB等“一站式”ORM解决方案而言,Mybatis 是一种“半自动化”的ORM实现。

需要使用的Jar包:mybatis-302jar(mybatis核心包)。mybatis-spring-100jar(与Spring结合包)。

MyBatis的前身是ibatis,但是在配置sql的语法上有明显的区别,并且spring目前的版本封装mybatis,至于mybatis-springjar文件也是mybatis团队复杂开发的jar包,用于和spring整合。之前ibatis的源码托管方是apache,而mybatis是google。

此处仅描述问题处理方法,关于mysql、mybatis的内容不在此赘述。

因为项目需要,需要在mybatis同个标签中执行多条sql语句。如下:

寻思着,在mysql中同时执行多条语句时,mysql是一条一条执行的。如果我一次性向mysql发送多条语句,它应该也会这么执行的。

我承认一开始想的太简单了,直接这样做的结果,是会在jdbc驱动层面报出语法错误异常,程序无法执行下去。

debug后发现,从mybatis拼装sql,到语句传入jdbc驱动,语句都是正常的,但是在mysql驱动验证sql合法性时,sql被截断,它认为一次性发过去的多条sql是不合法的。

看了网上很多说法,一说是mybatis本身不支持;一说是mysql驱动不支持。困扰很久,没能解决,后为了项目进度,暂时将sql分开执行了。

---------------------------------------------------------allowMultiQueries=true----------------------------------------------------------------------------------

但今天,有高人,跟我说,这样做不行,是因为你没有让mysql驱动开启批量执行sql的开关。

怎么开启呢?在拼装mysql链接的url时,为其加上allowMultiQueries参数,设置为true,如下:

加了参数后,我又将mybatis配置文件改了回去,测试一下,执行成功。

这里记录一下,希望对看到的人,有所帮助。

附MYSQL 多表更新 语句

一、mybatis的工作原理:

MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。

MyBatis使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java对象)映射成数据库中的记录。

每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。用xml文件构建SqlSessionFactory实例是非常简单的事情。

推荐在这个配置中使用类路径资源,但可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。

二、使用mybatis的原因:因为mybatis具有许多的优点,具体如下:

1、简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

2、灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足 *** 作数据库的所有需求。

3、解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

4、提供映射标签,支持对象与数据库的orm字段关系映射。

5、提供对象关系映射标签,支持对象关系组建维护。

6、提供xml标签,支持编写动态sql。

扩展资料:

mybatis的功能构架:

1、API接口层:提供给外部使用的接口API,开发人员通过这些本地API来 *** 纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。

2、数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库 *** 作。

3、基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

参考资料来源:百度百科-MyBatis

参考资料来源:百度百科-MyBatis从入门到精通

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);

}

}

}

}

在实际开发项目中,我们查询条件不可能很单一,查询字段可能包括很多字段,比如:查询条件可以有用户信息,商品信息,订单信息等。

这里我们的基础实体类是用户类,我在这个基础上扩展他,之后包装他作为我们的查询条件。

包装类:用于parameterType

UserMapper接口

mapper配置文件编写sql:

上图中可看到在mapperxml配置文件中用 userCustomusername 获取父类中的username私有成员变量,实际上是通过 userCustomgetUsername() 方法获得的

以下文章通过一个简单的例子来看看ognl的用法

另还有一篇文章展示了mybatis中的ognl教程

通过以上的了解,现在把User类中的 getUsername 方法注释掉

再次运行程序,得到以下报错:

接着,我们仍旧保留对 getUsername 方法的注释,但是现在把 username 设置为 public

再次运行测试程序,得到以下正常的结果显示

以上就证明了在mapperxml中, userCustomusername 实际上是通过调用了 userCustomgetUsername() 方法获得了user中的username属性,而不是userCustom直接访问父类User中的private修饰的username成员变量。

以上就是关于SpringBoot和mybatis采用junit测试时找不到mapper.xml全部的内容,包括:SpringBoot和mybatis采用junit测试时找不到mapper.xml、Mybatis Mapper接口是如何找到实现类的-源码分析、什么是mybatis等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

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

原文地址:https://54852.com/zz/10214743.html

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

发表评论

登录后才能评论

评论列表(0条)

    保存