Spring整合Mybatis一文讲透,手把手带你实操
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring整合Mybatis一文讲透,手把手带你实操相关的知识,希望对你有一定的参考价值。
参考技术A在介绍Spring整合Mybatis原理之前,我们得先来稍微介绍Mybatis的工作原理。
在Mybatis中,我们可以使用一个接口去定义要执行sql,简化代码如下: 定义一个接口,@Select表示要执行查询sql语句。
以下为执行sql代码:
Mybatis的目的是:使得程序员能够以调用方法的方式执行某个指定的sql,将执行sql的底层逻辑进行了封装。 这里重点思考以下mapper这个对象,当调用SqlSession的getMapper方法时,会对传入的接口生成一个 代理对象,而程序要真正用到的就是这个代理对象,在调用代理对象的方法时,Mybatis会取出该方法所对应的sql语句,然后利用JDBC去执行sql语句,最终得到结果。
UserService中的userMapper属性就会被自动注入为Mybatis中的代理对象。如果你基于一个已经完成整合的项目去调试即可发现,userMapper的类型为: org.apache.ibatis.binding.MapperProxy@41a0aa7d。证明确实是Mybatis中的代理对象。 好,那么现在我们要解决的问题的就是:如何能够把Mybatis的代理对象作为一个bean放入Spring容器中?要解决这个,我们需要对Spring的bean生成过程有一个了解。
Spring启动过程中,大致会经过如下步骤去生成bean
假设有一个A类,假设有如下代码: 一个A类
一个B类,不存在@Component注解
执行如下代码:
输出结果为:com.luban.util.A@6acdbdf5 A类对应的bean对象类型仍然为A类。但是这个结论是不确定的,我们可以利用BeanFactory后置处理器来 修改BeanDefinition,我们添加一个BeanFactory后置处理器:
这样就会导致,原本的A类对应的BeanDefiniton被修改了,被修改成了B类,那么后续正常生成的bean对 象的类型就是B类。此时,调用如下代码会报错:
但是调用如下代码不会报错,尽管B类上没有@Component注解:
并且,下面代码返回的结果是:com.luban.util.B@4b1c1ea0
之所以讲这个问题,是想说明 个问题:在Spring中,bean对象跟class没有直接关系,跟 BeanDefinition才有直接关系。 那么回到我们要解决的问题:如何能够把Mybatis的代理对象作为一个bean放入Spring容器中? 在Spring中,如果你想生成一个bean,那么得先生成一个BeanDefinition,就像你想new一个对象实 例,得先有一个class。
继续回到我们的问题,我们现在想自己生成一个bean,那么得先生成一个BeanDefinition,只要有了 BeanDefinition,通过在BeanDefinition中设置bean对象的类型,然后把BeanDefinition添加给 Spring,Spring就会根据BeanDefinition 动帮我们 成 个类型对应的bean对象。 所以,现在我们要解决两个问题:
注意:上文中我们使用的BeanFactory后置处理器,他只能修改BeanDefinition,并不能新增一个 BeanDefinition。我们应该使用Import技术来添加一个BeanDefinition。后面再详细介绍如果使用Import 技术来添加一个BeanDefinition,可以先看一下伪代码实现思路。
假设:我们有一个UserMapper接口,他的代理对象的类型为UserMapperProxy。 那么我们的思路就是这样的,伪代码如下:
但是,这里有一个严重的问题,就是上文中的UserMapperProxy是我们假设的,他表示一个代理类的类 型,然而Mybatis中的代理对象是利用的JDK的动态代理技术实现的,也就是代理对象的代理类是动态生成的,我们根本方法确定代理对象的代理类到底是什么。 所以回到我们的问题:Mybatis的代理对象的类型是什么? 本来可以有两个答案: 1. 代理对象对应的代理类 2. 代理对象对应的接口 那么答案1就相当于没有了,因为是代理类是动态生成的,那么我们来看答案2:代理对象对应的接口如果我们采用答案2,那么我们的思路就是:
但是,实际上给BeanDefinition对应的类型设置为一个接口是行不通的,因为Spring没有办法根据这个 BeanDefinition去new出对应类型的实例,接口是没法直接new出实例的。 那么现在问题来了,我要解决的问题:Mybatis的代理对象的类型是什么? 两个答案都被我们否定了,所以这个问题是无解的,所以我们不能再沿着这个思路去思考了,只能回到最 开始的问题:如何能够把Mybatis的代理对象作为一个bean放入Spring容器中?
总结上文的推理:我们想通过设置BeanDefinition的class类型,然后由Spring自动的帮助我们去生成对应的bean,但是这条路是行不通的。 终极解决方案 那么我们还有没有其他办法,可以去生成bean呢?并且生成bean的逻辑不能由Spring来帮我们做了,得 由我们自己来做。 FactoryBean 有,那就是Spring中的FactoryBean。我们可以利用FactoryBean去自定义我们要生成的bean对象,比如
我们定义了一个LubanFactoryBean,它实现了FactoryBean,getObject方法就是用来自定义生成bean 对象逻辑的。 执行如下代码:
将打印: lubanFactoryBean: com.luban.util.LubanFactoryBean 1@4d41cee &lubanFactoryBean: com.luban.util.LubanFactoryBean@3712b94 lubanFactoryBean-class: class com.sun.proxy. Proxy20 从结果我们可以看到,从Spring容器中拿名字为"lubanFactoryBean"的bean对象,就是我们所自定义的 jdk动态代理所生成的代理对象。
所以,我们可以通过FactoryBean来向Spring容器中添加一个自定义的bean对象。上文中所定义的 LubanFactoryBean对应的就是UserMapper,表示我们定义了一个LubanFactoryBean,相当于把 UserMapper对应的代理对象作为一个bean放入到了容器中。 但是作为程序员,我们不可能每定义了一个Mapper,还得去定义一个LubanFactoryBean,这是很麻烦的 事情,我们改造一下LubanFactoryBean,让他变得更通用,比如:
改造LubanFactoryBean之后,LubanFactoryBean变得灵活了,可以在构造LubanFactoryBean时,通 过构造传入不同的Mapper接口。 实际上LubanFactoryBean也是一个Bean,我们也可以通过生成一个BeanDefinition来生成一个 LubanFactoryBean,并给构造方法的参数设置不同的值,比如伪代码如下:
特别说一下注意二,表示表示当前BeanDefinition在生成bean对象时,会通过调用LubanFactoryBean的 构造方法来生成,并传入UserMapper的Class对象。那么在生成LubanFactoryBean时就会生成一个 UserMapper接口对应的代理对象作为bean了。 到此为止,其实就完成了我们要解决的问题:把Mybatis中的代理对象作为一个bean放入Spring容器中。
只是我们这是用简单的JDK代理对象模拟的Mybatis中的代理对象,如果有时间,我们完全可以调 Mybatis中提供的方法区生成一个代理对象。这里就不花时间去介绍了。 Import 到这里,我们还有一个事情没有做,就是怎么真正的定义一个BeanDefinition,并把它添加到Spring中, 上文说到我们要利用Import技术,比如可以这么实现: 定义如下类:
并且在AppConfig上添加@Import注解:
这样在启动Spring时就会新增一个BeanDefinition,该BeanDefinition会生成一个LubanFactoryBean对 象,并且在生成LubanFactoryBean对象时会传入UserMapper.class对象,通过LubanFactoryBean内部 的逻辑,相当于会自动生产一个UserMapper接口的代理对象作为一个bean。
总结一下,通过我们的分析,我们要整合Spring和Mybatis,需要我们做的事情如下:
作者:程序员周瑜
链接:https://juejin.cn/post/7089023062800236552
SpringMyBatis一篇文章带你理解Spring整合MyBatis的实质
Spring其实就是一个容器,管理所有注册到其中的bean,并通过IOC机制注入依赖。MyBatis是一个持久层框架,这个框架的作用就是规范Java与数据库的交互。
在MyBatis框架中,与数据库的交互通过如下步骤。
- 读取配置文件
mybatis-config.xml
。 - 根据配置文件通过
SqlSessionFactoryBuilder
对象创建sqlSessionFactory
对象。 - 调用
sqlSessionFactory
的openSession()
获得sqlSession
。 - 调用
sqlSession
的getMapper()
方法并传入接口类型获取这个接口类型的代理对象。 - 通过代理对象去执行该接口的配置文件中的SQL语句。
总结下来,MyBatis框架所必须的文件就是配置文件mybatis-config.xml
、与数据库表结构对应的实体bean、接口interface
及对应的xml
配置文件。
在Spring框架中,基本思想是通过Spring容器直接获得装配好的实体bean。在applicationContext.xml
文件中配置相应的实体bean并将依赖注入,然后根据配置文件创建context
上下文,由上下文通过传入id
和class
直接获取实体bean。
Spring和MyBatis整合后,显然不能再通过MyBatis框架中的方式获得sqlSession
,那么一个解决方案就是在*Mapper.java
中设置一个属性为sqlSession
,并在*Mapper.java
中通过sqlSession.getMapper()
获得代理对象,代理对象执行*Mapper.xml
配置的SQL。
可以把mybatis-config.xml
中的配置移动到Spring的配置文件中,并在Spring中配置Factory和SqlSession。
<!--Spring管理DataSource,即Spring管理与数据库的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value=""/>
<property name="username" value=""/>
<property name="password" value=""/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:"/>
</bean>
<!--sqlSessionTemplate-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
又因为接口中不能写方法的具体实现,因此可以通过一个接口的实现类,即*MapperImpl.java
,完成上文中提到的解决方案。
public class UserMapperImpl implements UserMapper
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession)
this.sqlSession = sqlSession;
public List<User> getUserList()
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUserList();
然后再在Spring配置文件中注册这个实现类,并进行sqlSession
属性的依赖注入。
这里还有另一种方法,就是继承Spring框架已经编写好的SqlSessionDaoSupport
类,这个类中有sqlSession
对象,可以通过getSession()
方法获得这个对象。这样不需要进行依赖注入。
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper
public List<User> getUserList()
UserMapper mapper = getSession().getMapper(UserMapper.class);
return mapper.getUserList();
值得注意的是,为了更规范,可以将Spring的配置文件分为spring-dao.xml
、spring-mvc.xml
、application-context.xml
,在application-context.xml
中importspring-dao.xml
、spring-mvc.xml
,并专注配置bean,通过Setter注入将sqlSession
注入。
这里的application-context.xml
就可以理解为这个Spring容器的特性,在创建context
上下文的时候,就导入这个配置文件。
以上是关于Spring整合Mybatis一文讲透,手把手带你实操的主要内容,如果未能解决你的问题,请参考以下文章
SpringMyBatis一篇文章带你理解Spring整合MyBatis的实质
springmvc+mybatis+spring 整合 bootstrap
SSM框架01:Spring的IoC和DI,以及手把手带你创建Spring核心配置文件
springmvc4 mybatis 整合 框架源码 bootstrap html5 mysql oracle sqlsever spring SSM