先来挂个“眼科”,Spring 与 Mybatis 框架整合 分为 5 步:
- 创建项目,引入JAR包
- 编写Spring配置文件applicationContext.xml;
- 编写Mybatis核心配置mybatis-config.xml;
- 引入db.properties(数据库配置文件),并在Spring的配置文件中引入(注意,此处不在Mybatis核心配置中引入了,两个框架整合后,在Spring配置中注入数据源!!!)
- 引入log4j.properties(用于在控制台检查执行的SQL语句)
接下来,一步一个脚印,做大做强(哦,也不对,这5步也就是做成,做大做强还远呐。。。。。。。):
第一步 引入JAR包(PS:JAR包真多。。。。贴个图放最后了)
第二步,编写Spring配置文件(进行数据源注入,事务配置,关联Mybatis。重要性不言而喻!)
第三步 编写Myabtis框架核心配置(主要是关联映射文件,Mybatis起作用的关键。也很重要!)
第四步 引入db.properties数据库配置文件(这一步属于锦上添花,可以使内部配置外部化,将数据库配置信息从Spring配置文件中分离出来,方便以后项目部署、维护)
第五步 引入log4j.properties:主要是方便开发人员在单元测试数据库操作时Debug使用的,可以在控制台检查执行的SQL语句,快速定位bug(这个文件目前没有什么灵魂了,这么写就可以)
好了,到了这Spring 与 Mybatis 框架整合完了
下面就是使用了
具体使用方式呢,这里讨论两种吧:
第一种 传统开发方式主要在DAO层(也就是MVC三层架构的Model层)
这种方法呢,主要是用 mybatis-spring 这个jar包中的SqlSessionTemplate类或者是SqlSessionDaoSupport类来实现的。这里来说说SqlSessionDaoSupport类,它继承自DaoSupport类(说它继承谁干啥。。。),在开发时主要作为DAO的基类来使用,通过SqlSessionDaoSupport类的getSqlSession方法来获取SqlSession,有了SqlSession就可以“为所欲为”了。
上代码!:
package com.ssm.test; // 自定义的单元测试包
import org.junit.jupiter.api.Test; // 引入的支持单元测试的包
import org.springframework.context.ApplicationContext; // Spring 的容器 ApplicationContext
import org.springframework.context.support.ClassPathXmlApplicationContext; // 用来加载Spring 容器的包
import com.ssm.dao.CustomerDao; // 测试的目标类
import com.ssm.po.Customer; // 数据库表的映射实体 POJO类
/**
* DAO测试类
*/
public class DaoTest {
@Test
public void findCustomerByIdDaoTest(){
ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml");
// 根据容器中Bean的id来获取指定的Bean
CustomerDao customerDao = (CustomerDao) act.getBean("customerDao");
// 这种获取bean也可以
// CustomerDao customerDao = act.getBean(CustomerDao.class);
Customer customer = customerDao.findCustomerById(1);
System.out.println(customer);
}
}
然后来看看 customerDao.findCustomerById 这个方法在干啥呢(精髓到了):
package com.ssm.dao.impl;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import com.ssm.dao.CustomerDao;
import com.ssm.po.Customer;
// 看到没!!! 继承了SqlSessionDaoSupport类了!!!
public class CustomerDaoImpl extends SqlSessionDaoSupport implements CustomerDao {
// 通过id查询客户
public Customer findCustomerById(Integer id) {
// 看到没!!!利用SqlSessionDaoSupport类的getSQLSession方法获取SqlSession了!!!
return this.getSqlSession().selectOne("com.ssm.po"
+ ".CustomerMapper.findCustomerById", id);
}
}
看到 …….selectOone("……") 找到熟悉的感觉了吧,就是去Myabtis的映射文件中找SQL 去了,并利用 SqlSession 去操作数据库了
感觉还想看看Mybatis的映射文件???可以!看一看吧:
<?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.ssm.po.CustomerMapper">
<!--根据id查询客户信息 -->
<!--这不在这呢!!!-->
<select id="findCustomerById" parameterType="Integer"
resultType="customer">
select * from t_customer where id = #{id}
</select>
<!--添加客户信息 -->
<insert id="addCustomer" parameterType="customer">
insert into t_customer(username,jobs,phone)
values(#{username},#{jobs},#{phone})
</insert>
</mapper>
好了,这就是传统的基于 mybatis-spring 包中的SqlSessionDaoSupport类 在Spring 与 Mybatis 整合后,操作数据库的最简单的样子;这个办法叫传统方法,当然还有不传统的方法:
第二种 基于Mapper接口方式的开发
(1)基于MapperFactoryBean的整合开发:
核心思想,是根据Mapper接口生成Mapper对象的类,使用前需要在Spring的配置文件中配置一下:
直接上代码,怕看晕了,上图!(Spring配置文件:applicationContext.xml):
这是在Spring配置文件中注入的Myabtis核心配置文件中关联的映射文件(CustomerMapper.xml):
<?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.ssm.mapper.CustomerMapper">
<!--根据id查询客户信息 -->
<select id="findCustomerById" parameterType="Integer"
resultType="customer">
select * from t_customer where id = #{id}
</select>
</mapper>
还是简单解释一下Spring的配置文件:
mapperInterface:用于指定接口,注意:这个指定的接口的名称要与Mybatis核心配置中关联的映射文件的名称严格一致!!!,比如这里指定了CustomerMapper接口,那么映射文件名就必须是CustomerMapper.xml(坑);
SqlSessionFactory:用于指定一个SqlSessionFactory(这里是配置好数据源和Mybatis核心配置的一个bean);
SqlSessionTemplate:用于指定SqlSessionTemplate(也是生产SqlSession的),注意:如果此时同时配置了SqlSessionFactory,只会用SqlSessionTemplate。
可能有人好奇 在mapperInterface属性注入的接口CustomerMapper 是啥样的,好的。
package com.ssm.mapper;
import com.ssm.po.Customer;
public interface CustomerMapper {
// 通过id查询客户
public Customer findCustomerById(Integer id);
// 添加客户
public void addCustomer(Customer customer);
}
最后这是 单元测试的 代码:
package com.ssm.test;
import com.ssm.mapper.CustomerMapper;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ssm.po.Customer;
@Test
public void findCustomerByIdMapperTest(){
ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml");
CustomerMapper customerMapper = act.getBean(CustomerMapper.class);
// 与传统的主要区别 在这里 不用再指定映射文件全路径了!!!
Customer customer = customerMapper.findCustomerById(1);
System.out.println(customer);
}
}
好了,基于MapperFactoryBean的开发就这样了。会有个感觉,这种方式有点儿“黑箱”,实际运行时,也很容易报错,暴露几个坑就会豁然开朗了:
第一个坑:Myabtis核心配置文件中关联的映射文件 的命名空间 一定 要与Spring配置中 mapperInterface 指定的接口路径严格一致!!!,这里就必须是 “com.ssm.mapper.CustomerMapper”
第二个坑:Spring的配置文件中 MapperFactoryBean 的bean下的 子元素 mapperInterface 指定的接口的名称要与Mybatis核心配置中关联的映射文件的名称严格一致!!!,比如这里指定了CustomerMapper接口,那么映射文件名就必须是CustomerMapper.xml
第三个坑:下面的单元测试 customerMapper.findCustomerById(1) 调用的方法名要与Myabtis关联的映射文件 写的什么select update insert delete ……的 id 要严格一致,否则找不到就报错了啊!;
第四个坑:就是参数类型了。首先, customerMapper.findCustomerById(1) 这个方法的传入参数数据类型,要与映射文件中 写的什么select update insert delete ……的 parameterType属性 指定的数据类型一致;且 customerMapper.findCustomerById(1) 的返回类型要与 映射文件中 每个sql的resultType 指定的数据类型一致!!!
(2)基于MapperScannerConfigurer的整合开发:
对于Spring框架的配置,很多人应该有一个认识了,就是一开始肯定会在Spring配置文件中以 bean 的方式 注入,然后获取使用;然后随着项目扩展会让Spring配置文件无法直视,然后就开始了向注解开发方式的过渡,所以 基于MapperScannerConfigurer 的方式就是,避免Spring配置文件太臃肿 将 基于MapperFactoryBean 开发方式 转为注解开发方式。
这是Spring配置文件applicationContext.xml文件的改变(与基于 MapperFactoryBean的开发相比,也就只有这里有变化):
解释一下:
basePackage:指定映射接口文件所在的包路径,需要扫描多个包时,使用分号或逗号隔开就行。此时会扫描指定的包及其子包中所有文件;
除此之外,还有几个这里没用的属性(一般指定basePackage就可以了,了解就行):
annotationClass:指定了要扫描的注解名称,只有被注解标识的类才会被配置为映射器。
sqlSessionFactoryBeanName:指定在Spring中定义的SqlSessionFactory的Bean名称。
sqlSessionTemplateBeanName:指定在Spring中定义的SqlSessionTemplate的Bean名称。如果定义此属性,则sqlSessionFactoryBeanName将不起作用。
markerInterface:指定创建映射器的接口。
好了,基于MapperScannerConfigurer 的开发也完事了。
附加:
在上述的Spring的配置中,其实已经配置了事务管理,可以测试一下:
单元测试:
package com.ssm.test;
import com.ssm.po.Customer;
import com.ssm.service.CustomerService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试事务
*/
public class TransactionTest {
public static void main(String[] args) {
ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml");
CustomerService customerService = act.getBean(CustomerService.class);
Customer customer = new Customer();
customer.setUsername("zhangsan");
customer.setJobs("manager");
customer.setPhone("13233334444");
customerService.addCustomer(customer);
}
}
单元测试调用的Service层的CustomerServiceImpl 类:
package com.ssm.service.impl;
import com.ssm.mapper.CustomerMapper;
import com.ssm.po.Customer;
import com.ssm.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class CustomerServiceImpl implements CustomerService {
//注解注入CustomerMapper
@Autowired
private CustomerMapper customerMapper;
//添加客户
public void addCustomer(Customer customer) {
this.customerMapper.addCustomer(customer);
int i=1/0; //模拟添加操作后系统突然出现的异常问题
}
}
若Spring事务配置正确,将不会把该条数据插入到数据库中。
综上,来讨论一下,传统方法 与 非传统方法 优缺点:
传统的基于SqlSessionDaoSupport类 开发,可以实现几乎所有功能 了,但是这种方式,会产生大量的冗余代码,随着项目的扩展,只要对自己有要求的同胞们会觉得越来越恶心,一开始的动脑子的活慢慢变成体力活了,对后期维护,就更加恶心了,且出错率越来越高,指不定在改的时候,就遗忘了一处,就需要重新打包了。。。。
附件1:项目的pom.xml(说明一下引入的众多依赖包们):