深入理解MyBatis-Spring中间件(spring/mybatis整合)
转:http://blog.csdn.net/fqz_hacker/article/details/53485833
Mybatis-Spring
1.应用
- public interface UserMapper {
- int createUser(@Param("user") User user);
- }
然后,定义对应的mybatis xml文件,
- <?xml version="1.0" encoding="utf-8" ?>
- <!--PUBLIC后面跟着的可以用于验证文档结构的 DTD 系统标识符和公共标识符。-->
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.fqz.mybatis.dao.UserMapper"><!--namespace是必须的,指向对应的java interface,要把包名写全,此例中为com.fqz.mybatis.dao.UserMapper -->
- <resultMap id="User" type="User">
- <id property="id" column="id"/>
- <result property="name" column="name"/>
- <result property="sex" column="sex"/>
- <result property="mobile" column="mobile"/>
- </resultMap>
- <insert id="createUser" parameterType="User" useGeneratedKeys="true" keyColumn="id" keyProperty="user.id">
- INSERT INTO
- User
- (name,sex,mobile)
- VALUES
- (#{user.name},#{user.sex},#{user.mobile})
- </insert>
- </mapper>
紧接着,添加配置文件,命名为spring-dao.mxl,
- <?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"
- 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.xsd">
- <context:component-scan base-package="com.fqz.mybatis"/>
- <!-- Data Source -->
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
- <property name="driverClass" value="com.mysql.jdbc.Driver"/>
- <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/fqz"/>
- <property name="user" value="root"/>
- <property name="password" value="abcd1234"/>
- <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数 -->
- <property name="acquireIncrement" value="5"></property>
- <!-- 初始连接池大小 -->
- <property name="initialPoolSize" value="10"></property>
- <!-- 连接池中连接最小个数 -->
- <property name="minPoolSize" value="5"></property>
- <!-- 连接池中连接最大个数 -->
- <property name="maxPoolSize" value="20"></property>
- </bean>
- <!-- 扫描对应的XML Mapper -->
- <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
- <!-- 数据源 -->
- <property name="dataSource" ref="dataSource"></property>
- <!-- 别名,它一般对应我们的实体类所在的包,这个时候会自动取对应包中不包括包名的简单类名作为包括包名的别名。多个package之间可以用逗号或者分号等来进行分隔。 -->
- <property name="typeAliasesPackage" value="com.fqz.mybatis.entity"></property>
- <!-- sql映射文件路径,它表示我们的Mapper文件存放的位置,当我们的Mapper文件跟对应的Mapper接口处于同一位置的时候可以不用指定该属性的值。 -->
- <property name="mapperLocations" value="classpath*:mybatis/*.xml"></property>
- </bean>
- <!-- 扫描对应的Java Mapper -->
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="com.fqz.mybatis.dao"/>
- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
- </bean>
- </beans>
- @Service
- public class UserServiceImpl implements UserService {
- @Resource
- UserMapper userMapper;
- public int createUser(UserDTO userDTO) {
- User user = new User();
- BeanUtils.copyProperties(userDTO,user,new String[]{"id"});
- int row = userMapper.createUser(user);//插入返回值为作用的记录数;生成的主键已经被赋值到user对象上
- if(row >= 1)
- return user.getId();
- return -1;
- }
- public UserDTO getUserById(Integer id) {
- return null;
- }
- }
完成接口定义、mybatis xml定义和配置文件,就可以直接使用接口来操作数据库,
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = {"classpath*:spring/spring-*.xml"})
- public class TestUser{
- private static UserService userService;
- @BeforeClass
- public static void beforeClass(){
- ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-dao.xml");
- userService = context.getBean(UserService.class);
- }
- @Test
- public void testCreatUser(){
- UserDTO userDTO = new UserDTO();
- userDTO.setName("your name");
- userDTO.setSex(true);
- userDTO.setMobile("12134232211");
- Integer userId = userService.createUser(userDTO);
- System.out.println(userId);
- System.out.println(userDTO.getId());
- }
- }
2. 原理
从UserServiceImpl实现类中可以看出,服务类直接使用的UserMapper接口来操作数据库,而UserMapper接口没有对应的实现
类,这一切都是由spring-mybatis库通过动态代理实现的,接下来分析下它的实现原理,先由SqlSessionFactoryBean生成
SQLSessionFactory和并扫描接口,为接口生成动态代理
1. SqlSessionFactoryBean配置
- public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean
SqlSessionFactoryBean
实现了FactoryBean和InitializingBean,因此会首先执行afterPropertiesSet()方法,然后根据
getObject()方法返回的对象生产Spring
Bean,这里生产的是SqlSessionFactory类型的对象,在afterPropertiesSet方法中,执行了
buildSqlSessionFactory方法来初始化SqlSessionFactory对象,来看看其中的一段,
- if (!isEmpty(this.mapperLocations)) {
- for (Resource mapperLocation : this.mapperLocations) {
- if (mapperLocation == null) {
- continue;
- }
- try {
- XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
- configuration, mapperLocation.toString(), configuration.getSqlFragments());
- xmlMapperBuilder.parse();
- }
进入XmlMapperBuilder.parse方法,
- private void bindMapperForNamespace() {
- String namespace = builderAssistant.getCurrentNamespace();
- if (namespace != null) {
- Class<?> boundType = null;
- try {
- boundType = Resources.classForName(namespace);
- } catch (ClassNotFoundException e) {
- //ignore, bound type is not required
- }
- if (boundType != null) {
- if (!configuration.hasMapper(boundType)) {
- // Spring may not know the real resource name so we set a flag
- // to prevent loading again this resource from the mapper interface
- // look at MapperAnnotationBuilder#loadXmlResource
- configuration.addLoadedResource("namespace:" + namespace);
- configuration.addMapper(boundType);
- }
- }
- }
- }
namespace指定了mybatis dao接口的路径,通过configuration.addMapper(boundType)将接口的动态代理委托给MapperRegistry,MapperRegistry中通过MapperFactory生成动态代理,
- public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
- final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
- if (mapperProxyFactory == null) {
- throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
- }
- try {
- return mapperProxyFactory.newInstance(sqlSession);
- } catch (Exception e) {
- throw new BindingException("Error getting mapper instance. Cause: " + e, e);
- }
- }
- public <T> boolean hasMapper(Class<T> type) {
- return knownMappers.containsKey(type);
- }
- public <T> void addMapper(Class<T> type) {
- if (type.isInterface()) {
- if (hasMapper(type)) {
- throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
- }
- boolean loadCompleted = false;
- try {
- knownMappers.put(type, new MapperProxyFactory<T>(type));
- // It's important that the type is added before the parser is run
- // otherwise the binding may automatically be attempted by the
- // mapper parser. If the type is already known, it won't try.
- MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
- parser.parse();
- loadCompleted = true;
- } finally {
- if (!loadCompleted) {
- knownMappers.remove(type);
- }
- }
- }
- }
MapperProxyFactory中使用Proxy.newProxyInstance来生成动态代理
- protected T newInstance(MapperProxy<T> mapperProxy) {
- return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
- }
- public T newInstance(SqlSession sqlSession) {
- final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
- return newInstance(mapperProxy);
- }
MapperProxy为动态代理的处理类,实际上将SqlSession操作db的过程封装在了MapperMethod类中,
- public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
- this.sqlSession = sqlSession;
- this.mapperInterface = mapperInterface;
- this.methodCache = methodCache;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- if (Object.class.equals(method.getDeclaringClass())) {
- try {
- return method.invoke(this, args);
- } catch (Throwable t) {
- throw ExceptionUtil.unwrapThrowable(t);
- }
- }
- final MapperMethod mapperMethod = cachedMapperMethod(method);
- return mapperMethod.execute(sqlSession, args);
- }
简单看下结构,
总结,SqlSessionFactoryBean实际上对应的是SqlSessionFactory类,它会扫描sql
xml文件,并对接口创建动态代理,将接口类的Class和动态代理关系保存在SqlSessionFactory中,这仅仅是完成了动态代理的生成,而
动态代理在哪里被使用到,怎么使用,这些都是由MapperScannerConfigurer完成,接下来看看
MapperScannerConfigurer都做了些什么?
2. MapperScannerConfigurer
接口所在package,之前也说过SqlSessionFactoryBean实际上对应的是SqlSessionFactory,它可以提供
Mapper接口的动态代理类,而Mapper所在package提供了扫描的路径,在扫描过程中,会把每个Mapper接口对应到一个
MapperFactoryBean,MapperFactoryBean实际上对应的是动态代理类,这一切也就说通了,下面来看看源码,
factory初始化的时候,被调用到,调用的方法为postProcessBeanDefinitionRegistry,因此看看
postProcessBeanDefinitionRegistry的源码,
- public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
- if (this.processPropertyPlaceHolders) {
- processPropertyPlaceHolders();
- }
- ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
- scanner.setAddToConfig(this.addToConfig);
- scanner.setAnnotationClass(this.annotationClass);
- scanner.setMarkerInterface(this.markerInterface);
- scanner.setSqlSessionFactory(this.sqlSessionFactory);
- scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
- scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
- scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
- scanner.setResourceLoader(this.applicationContext);
- scanner.setBeanNameGenerator(this.nameGenerator);
- scanner.registerFilters();
- scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
- }
调用了ClassPathMapperScanner的scan()方法,
- public int scan(String... basePackages) {
- int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
- doScan(basePackages);
- // Register annotation config processors, if necessary.
- if (this.includeAnnotationConfig) {
- AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
- }
- return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
- }
scan
方法又调用了doScan方法,看看ClassPathMapperScanner的doScan方法,Spring会首先把需要实例化的bean加载的
BeanDefinitionHolder的集合中,doScan方法,就是添加mybatis
mapper接口的bean定义到BeanDefinitionHolder集合,
- public Set<BeanDefinitionHolder> doScan(String... basePackages) {
- Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
- if (beanDefinitions.isEmpty()) {
- logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
- } else {
- processBeanDefinitions(beanDefinitions);
- }
- return beanDefinitions;
- }
- private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
- GenericBeanDefinition definition;
- for (BeanDefinitionHolder holder : beanDefinitions) {
- definition = (GenericBeanDefinition) holder.getBeanDefinition();
- if (logger.isDebugEnabled()) {
- logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
- + "' and '" + definition.getBeanClassName() + "' mapperInterface");
- }
- // the mapper interface is the original class of the bean
- // but, the actual class of the bean is MapperFactoryBean
- definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
- definition.setBeanClass(this.mapperFactoryBean.getClass());//将其bean Class类型设置为mapperFactoryBean,放入BeanDefinitions
- definition.getPropertyValues().add("addToConfig", this.addToConfig);
- boolean explicitFactoryUsed = false;
- if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
- definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
- explicitFactoryUsed = true;
- } else if (this.sqlSessionFactory != null) {
- definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
- explicitFactoryUsed = true;
- }
- if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
- if (explicitFactoryUsed) {
- logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
- }
- definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
- explicitFactoryUsed = true;
- } else if (this.sqlSessionTemplate != null) {
- if (explicitFactoryUsed) {
- logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
- }
- definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
- explicitFactoryUsed = true;
- }
- if (!explicitFactoryUsed) {
- if (logger.isDebugEnabled()) {
- logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
- }
- definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
- }
- }
- }
那MapperFactoryBean究竟又做了什么呢,看源码,
- MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean
通过集成SqlSessionDaoSupport获得SqlSessionFactory,通过实现FactoryBean,生产动态代理对象,
- @Override
- public T getObject() throws Exception {
- return getSqlSession().getMapper(this.mapperInterface);
- }
一
切到这里就已经很显而易见了,Mapper接口对应的Spring
Bean实际上就是getSqlSession().getMapper(this.mapperInterface)返回的动态代理,每次装配
Mapper接口时,就相当于装配了此接口对应的动态代理,这样就顺利成章的被代理成功了。
深入理解MyBatis-Spring中间件(spring/mybatis整合)的更多相关文章
- Spring、Spring MVC、MyBatis整合文件配置详解
原文 http://www.cnblogs.com/wxisme/p/4924561.html 主题 MVC模式MyBatisSpring MVC 使用SSM框架做了几个小项目了,感觉还不错是时候总 ...
- spring、spring mvc、mybatis框架整合基本知识
学习了一个多月的框架知识了,这两天很想将它整合一下.网上看了很多整合案例,基本都是基于Eclipse的,但现在外面公司基本都在用Intellij IDEA了,所以结合所学知识,自己做了个总结,有不足之 ...
- 转载 Spring、Spring MVC、MyBatis整合文件配置详解
Spring.Spring MVC.MyBatis整合文件配置详解 使用SSM框架做了几个小项目了,感觉还不错是时候总结一下了.先总结一下SSM整合的文件配置.其实具体的用法最好还是看官方文档. ...
- Spring、Spring MVC、MyBatis整合文件配置详解2
使用SSM框架做了几个小项目了,感觉还不错是时候总结一下了.先总结一下SSM整合的文件配置.其实具体的用法最好还是看官方文档. Spring:http://spring.io/docs MyBatis ...
- Spring、Spring MVC、MyBatis 整合文件配置详解
使用SSM框架做了几个小项目了,感觉还不错是时候总结一下了.先总结一下SSM整合的文件配置.其实具体的用法最好还是看官方文档. Spring:http://spring.io/docs MyBatis ...
- MyBatis原理,Spring、SpringBoot整合MyBatis
1. MyBatis概述 MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可 ...
- spring mvc与mybatis与maven+mysql框架整合
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"% ...
- Spring MVC Spring MyBatis 整合 - 快速上手
我个人比较喜欢写注释,在工作中对注释的重要性看的也比较高,所以大部分文字都在注释中,代码外的文字会写的偏少,关键能懂就行 先看一下整合后的工程目录(单工程,多工程以后会采用maven) 5个packa ...
- 搭建Spring + SpringMVC + Mybatis框架之二(整合Spring和Mybatis)
整合Spring和Mybatis 首先给出完整的项目目录: (1)引入项目需要的jar包 使用http://maven.apache.org作为中央仓库即可. Spring核心包,mybatis核心包 ...
- MyBatis 3 与 Spring 4 整合关键
MyBatis 3 与 Spring 4 整合关键 MyBatis与Spring整合,首先需要一个Spring数据源.其次有两个关键,配置sqlSessionFactory时需要配置扫描sql映射xm ...
随机推荐
- my05_mysql检查点简述
简单描述一下mysql 检查点,对mysql数据库恢复的理解有所帮助. 数据库版本 mysql> select version(); +-----------+ | version() | +- ...
- 转 Python 操作 MySQL 数据库
#########http://www.runoob.com/python/python-mysql.html Python 标准数据库接口为 Python DB-API,Python DB-API为 ...
- requirej入门(一)
随着网站功能逐渐丰富,网页中的js也变得越来越复杂和臃肿,原有通过script标签来导入一个个的js文件这种方式已经不能满足现在互联网开发模式,我们需要团队协作.模块复用.单元测试等等一系列复杂的需求 ...
- vector 中需要注意的东西!
vector的erase方法注意点!!! C++11是这样的: iterator erase (const_iterator position); iterator erase (const_iter ...
- (转)Memcached用法--参数和命令详解
Memcached用法--参数和命令详解 1. memcached 参数说明: # memcached -h 1.1 memcached 的参数 常用参数 -p <num> 监听的TCP端 ...
- 查看服务器配置信息prtdiag与systeminfo实用命令
UNIX(SUN服务器)bash-2.05# prtdiag -v系统配置: Sun Microsystems sun4u Sun Fire V890系统时钟频率:150 MHz内存大小:3276 ...
- HDU 5419——Victor and Toys——————【线段树|差分前缀和】
Victor and Toys Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/131072 K (Java/Others ...
- Linux 启动盘命令
linux下有很多工具可以制作启动盘, 例如 unetbootin 和 wubi, 不过我们可以使用linux下的一条命令来完成-----dd 操作方法: 1 卸载你的U盘 假设你的u盘对应的设备是s ...
- java的wait/notify小结
wait()是使线程停止运行,而notify使停止的线程继续运行 wait()锁释放与notify()锁不释放 当线程呈wait状态时,调用线程对象的interrupt()方法会出现异常 带一个参数的 ...
- c# 小数点格式化
1.只要求保留N位不四舍5入 float f = 0.55555f; int i =(int)(f * 100); f = (float)(i*1.0)/100 ...