一、搭建Spring中使用Mybatis环境
工程结构如下图所示
1、引入依赖
<properties> <spring.version>4.1.2.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.8</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.29</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies>
2、User表结构和Mybatis 源码解析是一样的
3、创建User实体类
public class User { private int id; private String name; private String dept; private String phone; private String website; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDept() { return dept; } public void setDept(String dept) { this.dept = dept; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getWebsite() { return website; } public void setWebsite(String website) { this.website = website; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", dept='" + dept + '\'' + ", phone='" + phone + '\'' + ", website='" + website + '\'' + '}'; } }
3、resources/configs/下创建Configure.xml
mybatis 配置文件。里面主要包含Java类对应的别名
<?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> <typeAliases> <typeAlias alias="User2" type="com.example.mybatis.model.User" /> </typeAliases> <mappers> <mapper resource="mapper/User.xml" /> </mappers> </configuration>
这里<mapper resource="mapper/User.xml" /> 要映射类的xml配置文件
4、beans.xml 配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.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://127.0.0.1:3306/mybatis"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:config/Configure.xml"></property> <property name="dataSource" ref="dataSource" /> </bean> <bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.example.mybatis.dao.UserMapper"></property> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean> </beans>
5、resources/mapper下创建User.xml
包含了各种SQL语句
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.mybatis.dao.UserMapper"> <select id="getUserById" parameterType="int" resultType="User2"> select * from user where id = #{id} </select> </mapper>
6、创建接口com.example.mybatis.dao.UserMapper
public interface UserMapper { User getUserById(int id); }
7、创建测试类
public class SpringMybatisTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserMapper userMapper = (UserMapper)context.getBean("userDao"); User user = userMapper.getUserById(1); System.out.println(user); } }
8、输出结果
通过上面的过程可知,MyBatis和Spring整合最重要的是beans.xml配置
beans.xml将dataSource、SQLSessionFactory及UserMapper注入到Spring容器中,然后获取对应的UserMapper,进行CRUD操作
二、beans.xml 分析
1、SqlSessionFactory生成
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:config/Configure.xml"></property> <property name="dataSource" ref="dataSource" /> </bean>
类似于Mybatis代码
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
2、userDao获取
<bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.example.mybatis.dao.UserMapper"></property> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean>
类似于Mybatis代码
SqlSession session = sqlSessionFactory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class);
3、Spring中SqlSessionFactory生成
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:config/Configure.xml"></property> <property name="dataSource" ref="dataSource" /> </bean>
sqlSessionFactory的实现类为SqlSessionFactoryBean,定义了属性configLocation和dataSource
1) SqlSessionFactoryBean这个类
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { ...... }
从容器中获取SqlSessionFactoryBean,实际上调用的是getObject方法
public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { //sqlSessionFactory默认为空,直接走afterPropertiesSet这个方法 //实际上不是这样的,由于SqlSessionFactroyBean实现了InitializingBean接口,则在该Bean生成 //之后,直接调用afterPropertiesSet()方法来创建sqlSessionFactory,所有sqlSessionFactory是 //已经被创建好的 this.afterPropertiesSet(); } return this.sqlSessionFactory; } //afterPropertiesSet() public void afterPropertiesSet() throws Exception { ... this.sqlSessionFactory = this.buildSqlSessionFactory(); } //this.buildSqlSessionFactory protected SqlSessionFactory buildSqlSessionFactory() throws IOException { Configuration configuration; ... ... else if (this.configLocation != null) { //1、解析Configure.xml配置文件 xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); //2、获取配置中心Configuration configuration = xmlConfigBuilder.getConfiguration(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration(); if (this.configurationProperties != null) { configuration.setVariables(this.configurationProperties); } } //3、以下的操作是对Configuration中各种属性进行set操作 if (this.objectFactory != null) { configuration.setObjectFactory(this.objectFactory); } if (this.objectWrapperFactory != null) { configuration.setObjectWrapperFactory(this.objectWrapperFactory); } if (this.vfs != null) { configuration.setVfsImpl(this.vfs); } if (hasLength(this.typeAliasesPackage)) { String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeAliasPackageArray) { configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } if (!isEmpty(this.typeAliases)) { for (Class<?> typeAlias : this.typeAliases) { configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type alias: '" + typeAlias + "'"); } } } .... .... configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); //4、最终还是通过sqlSessionFactoryBuilder生成sqlSessionFactory return this.sqlSessionFactoryBuilder.build(configuration); }
最终还是通过sqlSessionFactoryBuilder生成sqlSessionFactory,和原生Mybatis代码是一致的。
4、Spring中的Mapper接口(也就是这里的userDao实例,对应UserMapper接口)
由beans.xml中的配置可知
<bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.example.mybatis.dao.UserMapper"></property> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean>
实现类为MapperFactoryBean,定义了mapperInterface和sqlSessionFactory属性。
mapperInterface要指定UserMapper接口的全路径
1) MapperFactoryBean
实现了 FactoryBean接口,真正返回的是getObject()返回的对象
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { @Override public T getObject() throws Exception { //getSqlSession()返回的是SqlSessionDaoSupport的sqlSession属性。 //MapperFactoryBean在Spring中真正返回的是getSqlSession().getMapper方法返回的Mapper接口 return getSqlSession().getMapper(this.mapperInterface); } }
2) MapperFactoryBean.afterPropertiesSet()
具体实现在DaoSupport类中
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException { //MapperFactoryBean.checkDaoConfig中实现 this.checkDaoConfig(); //Spring没有关于initDao的实现,可以自己实现 try { this.initDao(); } catch (Exception var2) { throw new BeanInitializationException("Initialization of DAO failed", var2); } } //MapperFactoryBean.checkDaoConfig protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { //主要是将接口添加进configuration configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } }
总结:
MapperFactoryBean初始化的时候就通过afterPropertiesSet()方法把Mapper接口添加进Configuration;
MapperFactoryBean.getObject()返回的也就是从Configuration中获取的Mapper接口。
MapperFactoryBean.getObject()方法类似于原生Mybatis方法
SqlSession session = sqlSessionFactory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class);
通过对Spring-Mybatis的源码分析,本质上还是Mybatis,只不过容器帮我们生产了SqlSessionFactory和SqlSession,同时把Mapper加载进Configuration,可以直接通过getBean的方式来获取Mapper