
在介绍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
本篇文章主要介绍了使用MyBatis框架完成数据库的增、删、改、查 *** 作。
准备工作
运行schema.sql和data.sql脚本文件中的 SQL 语句创建t_user表并添加部分测试数据。
schema.sql:
data.sql:
使用MyBatis框架 *** 作数据库步骤:
(1)定义Java实体
User.java
(2)定义Mapper接口
UserDao.java
(3)编写XML映射文件
UserDao.xml
(4)编写MyBatis主配置文件
MyBatis使用 XML 文件格式描述配置信息,内容如下:
(5)编写测试类
Demo.java
输出结果:
一、简单说明用到的框架:spring、springmvc,mybatis
开发工具:eclipse,apache-tomcat-6.0.39
jar包管理:maven
开发过程
一、建立工程
1、引入相关jar包:
<dependencies>
<!--测试包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<!-- servlet的jar包 添加<scope>provided</scope>, 因为provided表明该包只在编译和测试的时候用-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
二、引入mybatis相关内容并测试
1、引入JAR包
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
<mybatis.version>3.3.1</mybatis.version>
</properties>
<dependencies>
<!--测试包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<!-- servlet的jar包 添加<scope>provided</scope>,因为provided表明该包只在编译和测试的时候用-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency>
</dependencies>
这里面在pom里面使用了<properties>标签重点看一下,在这个环节添加了数据库连接的jar包和mybatis的jar包。这里要想可以 *** 作数据库需要编程式的读取配置文件一般放在classpath下面。这里取名为conf.xml.这个里面主要完成2件事:
a:对数据库4个基本信息的配置, b:引入mapper.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="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/SSM_XML"/>
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- 注册userMapper.xml文件,-->
<mapper resource="im/fenqi/study/user/mapper/UserMapper.xml"/>
</mappers>
</configuration>
下面对这个步骤里面的内容进行详细说明:
1、创建im.fenqi.study.user.entity.User实体对象,这里导入一个lombok插件,这个插件要安装(具体百度),可以不用显示的写set/get方法。
2、创建im/fenqi/study/user/mapper/UserMapper.xml文件,这个里面是对数据库进行 *** 作一般和 im.fenqi.study.user.mapper.UserMapper配对使用,里面注意点。
标签mapper里面的namespace的值就是配对的JAVA类im.fenqi.study.user.mapper.UserMapper
标签resultMap:是为了解决实体类的属性名字和数据库字段名字不一致的问题。主键和其他键是有区别的。
resultMap里面定义的字段和属性在对应的表和实体必须有对应的内容否则会报错。
标签trim:可以处理user(user_id,user_name,password)等问题,里面的子标签为:
prefix:前缀覆盖并增加其内容
suffix:后缀覆盖并增加其内容
prefixOverrides:前缀判断的条件
找到最前面的和条件一样的内容然后变成prefix里面的内容
例:<trim prefix="where" prefixOverrides="AND">
</trim>
根据条件判断trim里面的字符串为"AND id=#{id} AND name like #{name}"
找到最前面的AND(prefixOverrides的内容)替换成where(prefix内容)
如果没有prefixOverrides的内容就直接加在前面。
suffixOverrides:后缀判断的条件
和prefixOverrides类似。
标签if:比较简单,<if test="判断条件"></if>
标签sql:
<sql id="Base_Column_List">
id, user_id,user_name,password
</sql>
使用场景:
select <include refid="Base_Column_List" />from user
需要返回的字段很多的时候不用每次都写一遍
3、创建im.fenqi.study.user.mapper.UserMapper类这个和上面的文件是成对出现的,这是一个接口接口里面的方法名和UserMapper.xml里面的id要一致
4、测试类userTest
在本地测试主要使用SqlSession对象二这个对象需要使用SqlSessionFactory对象才能创建,下面先说SqlSessionFactory对象是怎么得到的
a:获取配置文件config.xml的输入流:
String resource = "/conf.xml"
//从classpath里面寻找
InputStream resourceAsStream = Object.class.getResourceAsStream(resource)
b:获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream)
c:获取SqlSession对象:
SqlSession session = factory.openSession()
d:得到dao层的对象
UserMapper userMapper = session.getMapper(UserMapper.class)
d:插入数据
User user = new User()
user.setUserId(UUID.randomUUID().toString())
user.setUserName("zw1")
user.setPassword("123456")
int count = userMapper.saveUserInfo(user)
session.commit()
System.out.println("insert:"+count)
三、引入spring框架并测试
首先需要引入必要的jar包,主要是spring必要的包和spring支持mybatis,jdbc的包
<!--spring基础的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring+mybatis+jdbc-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
这个时候conf.xml这个配置文件已经不需要了,统一在spring.xml里面进行配置
1、配置数据库连接
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/SSM_XML"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
当然这个配置可以写在properties文件里面(用到再说)
2、spring引入以后主要目的就是利用IOC容器进行创建对象,没有加入spring之前SqlSessionFactory、SqlSession、UserMapper 对象的创建都是我们硬编码实现,spring就是为了解决这些问题,最后直接返回UserMapper 给我们使用。其他的事情都在配置里完成。这里面具体 *** 作流程以后会详细分析
3、创建SqlSessionFactory
创建这个对象需要数据源和mapper.xml配置文件的位置
<!--创建 sqlSessionFactory,给了数据源和配置路径 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations" value="classpath:im/fenqi/study/*/mapper/*.xml"></property>
</bean>
4、创建UserMapper 对象(这一部分还要重点研究)
需要SqlSessionFactory对象创建SqlSession对象,然后创建UserMapper 对象,所以需要SqlSessionFactory和UserMapper 接口的位置,MapperScannerConfigurer是扫描包,不要每次都创建MapperFactoryBean
<!-- 创建Mapper层的对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="im.fenqi.study.*.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
5、测试获取对象
ApplicationContext context = new ClassPathXmlApplicationContext("/spring.xml")
UserMapper bean = context.getBean("userMapper",UserMapper.class)
List<User>userByUser = bean.getUserByUser(null)
6、这个测试还可以使用spring自带的test类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring.xml")
public class springtest {
@Autowired
private UserMapper userMapper
@Test
public void save()
{
User user = new User("zw1", 20)
userMapper.save(user)
}
四、引入springmvc框架
首先引入springmvc的JAR包
<!--springMVC相关框架 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
这个时候需要配置web.xml,也要配置springmvc.xml
1、配置web.xml
添加加载spring.xml的监听器和spring文件路径
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
配置Dispatcherservlet和springmvx.xml配置文件的位置
2、配置springmvc.xml文件
springmvc.xml主要配置控制器、handlerMapper、HandlerAdapter、渲染器
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 注解扫描,以包为单位 -->
<context:component-scan base-package="im.fenqi.study"/>
<!--创建控制器 -->
<bean id="/user.do" class="im.fenqi.study.user.rest.UserController">
<property name="userService" ref="userService"></property>
</bean>
<!--创建handlermapper -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!--创建handlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 配置渲染器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
3、编写控制器控制器和配置的类型要一致
public class UserController implements Controller{
private UserService userService
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView()
List<User>userList = userService.getUserByUser(null)
modelAndView.addObject("user", userList.get(0))
modelAndView.setViewName("mypage")
return modelAndView
}
}
五、整合过程中出现的问题
1、IOC容器。
如果我使用XML配置IOC容器,自己定义一个类这个类里面的属性想用IOC容器里面的对象初始化,必须提供set/get方法。
2、JSP不能使用el表达式,这个问题要解决
这个问题是缺少jar包的问题要想jsp使用起来不出现问题需要添加下面的包,最上面的包之前添加过了
<!-- servlet的jar包 添加<scope>provided</scope>,因为provided表明该包只在编译和测试的时候用-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!--JSP -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
3、mybatis和spring的配置过程还要具体分析
A:这个里面有很多种配置这一次就详细说明一下我们这里使用的sqlSessionFactory和 MapperScannerConfigurer
sqlSessionFactory:创建的时候需要dataSource和mapper.xml的配置文件
MapperScannerConfigurer:可以整体扫描,提供基础的包名可以使用通配符,需要sqlSessionFactoryBeanName用来创建sqlSessionFactory对象再创建SqlSession对象再通过SqlSession对象创建各个mapper对象。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)