主要内容
本文主要介绍mybatis和spring的集成,spring和springmvc的集成在之前shiro集成的时候已经介绍过,这里不再赘述。
mybatis和spring集成
两者的集成需要额外添加mybatis-spring依赖库。使用普通的mybatis api操作数据库的时候,我们通常需要自己加载配置文件,而在集成spring的时候,一些操作将会被SqlSessionFactoryBean
接管,例如原先在xml文件中配置的选项,现在都可以通过这个类进行配置,它是mybatis-spring库中提供的一个工厂Bean。
配置SqlSessionFactoryBean
只需要为SqlSessionFactoryBean
注入一个数据源,它就可以正常工作,下面是最简单的配置:
1 2 3 4 5 6 7 8 9 10 |
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///shiro?useSSL=true"/> <property name="username" value="root"/> <property name="password" value=""/> </bean> |
当然,这个时候并不能进行任何数据库操作,这个配置相当于代码中的创建SqlSessionFactory
,也就是说并没有打开任何session,一般地,在spring环境中,我们都不会直接操作SqlSession
,而是通过mybatis-spring库提供的两个类来间接使用SqlSession的功能,这两个类分别是:SqlSessionTemplate
和SqlSessionDaoSupport
,我们不需要同时使用两者,任选其一即可。
使用SqlSessionTemplate
从SqlSessionTemplate的源码来看,它只是一个SqlSession接口的实现,所以,通过这个类,我们可以操作SqlSession的各种select方法,还有getMapper方法。
在使用SqlSessionTemplate之前还需要引入mapper配置文件,不过,既然我们已经和spring集成,就不再以xml配置文件的方式引入mapper,mybatis-spring库提供了MapperFactoryBean类,专门用来引入Mapper接口。
配置MapperFactoryBean
MapperFactoryBean的作用是把用户自定义的Mapper注册到当前的上下文环境中。
1 2 3 4 |
<bean id="staffMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="cn.sharpcode.mapper.StaffMapper"/> <property name="sqlSessionFactory" ref="sqlSessionFactoryBean"/> </bean> |
这样,就可以正式使用SqlSessionTemplate查询数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@RequestMapping(value = "hello") public String hello(Model model){ //使用select方式查询数据库 Staff staff = sqlSessionTemplate.selectOne("cn.sharpcode.mapper.StaffMapper.selectStaff", 1); //使用mapper方式查询数据库 StaffMapper mapper = sqlSessionTemplate.getMapper(StaffMapper.class); Staff staff2 = mapper.selectStaff(2); Map<String, Staff> staffMap = new HashMap<>(); staffMap.put("staff", staff); staffMap.put("staff2", staff2); model.addAllAttributes(staffMap); return "hello"; } |
使用MapperScannerConfigurer自动扫描Mapper接口
MapperFactoryBean的缺点是,每添加一个Mapper,就要在配置文件中手动引入,如果Mapper太多,工作量就会十分大。幸运的是,可以使用MapperScannerConfigurer自动扫描添加所有的Mapper:
1 2 3 4 |
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="cn.sharpcode"/> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> </bean> |
添加了MapperScannerConfigurer后,MapperFactoryBean就不再需要了。
使用SqlSessionDaoSupport
SqlSessionDaoSupport为我们提供了一个访问SqlSessionTemplate对象的快捷方式,它内部包含的SqlSession对象是new SqlSessionTemplate()
创建的。不过,SqlSessionDaoSupport类是抽象的,我们不能直接在spring的配置文件中创建这个对象,如果要使用它,还得继承它创建自己的Dao类:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package cn.sharpcode.dao; import cn.sharpcode.mapper.StaffMapper; import cn.sharpcode.po.Staff; import org.mybatis.spring.support.SqlSessionDaoSupport; public class StaffDao extends SqlSessionDaoSupport { public Staff selectStaff(Integer id){ StaffMapper mapper = getSqlSession().getMapper(StaffMapper.class); return mapper.selectStaff(id); } } |
然后,就可以在spring配置文件中创建这个Bean,不过,SqlSessionDaoSupport依赖SqlSessionTemplate或者SqlSessionFactory,所以要注入这两个属性其中之一。
1 2 3 |
<bean id="staffDao" class="cn.sharpcode.dao.StaffDao"> <property name="sqlSessionTemplate" ref="sqlSessionTemplate" /> </bean> |
最后,可以在控制器中测试这个Dao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Controller public class IndexController { private StaffDao staffDao; @Autowired public void setStaffDao(StaffDao staffDao){ this.staffDao = staffDao; } @RequestMapping(value = "hello") public String hello(Model model) { Staff staff = staffDao.selectStaff(1); model.addAttribute("staff", staff); return "hello"; } } |
使用事务
mybatis和spring集成之后最简单的开启事务方法是使用基于注解的事务管理。
配置事务管理器
DataSourceTransactionManger类提供了平台无关的事务管理能力
1 2 3 |
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> |
配置注解驱动
通过@Transactional注解标记的类或者方法会自动实现事务,前提是spring配置了注解驱动:
1 |
<tx:annotation-driven transaction-manager="txManager" /> |
在方法中使用注解标记
通过@Transactional注解自动实现事务控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@Transactional public int insertStaff(Staff staff) { //插入职务信息 TitleMapper titleMapper = getSqlSession().getMapper(TitleMapper.class); Title title = new Title(); title.setName("CAO"); titleMapper.insertTitle(title); //插入员工资料 StaffMapper staffMapper = getSqlSession().getMapper(StaffMapper.class); int affectedRow; try{ affectedRow = staffMapper.insertStaff(staff); } catch (Exception e){ throw new RuntimeException(e); } return affectedRow; } |
如果事务配置成功,如果职务信息成功插入,但员工信息插入失败的话,插入成功的职务信息就会被回滚。
注意: 要事务生效,被Transactional标记的方法必须抛出RuntimeException异常,其次,数据库表引擎必须是innoDB类型。当创建数据库表的时候,如果没有指定引擎,默认使用的是myIsam,它是不支持事务的。
相关代码
StaffMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package cn.sharpcode.mapper; import cn.sharpcode.po.Staff; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; public interface StaffMapper { @Select("SELECT * FROM staff WHERE id = #{id}") Staff selectStaff(@Param("id") Integer staffId); @Insert(value = {"INSERT INTO staff VALUES (#{id}, #{name})"}) int insertStaff(Staff staff); } |
TitleMapper
1 2 3 4 5 6 7 8 9 |
package cn.sharpcode.mapper; import cn.sharpcode.po.Title; import org.apache.ibatis.annotations.Insert; public interface TitleMapper { @Insert("INSERT INTO title VALUES (NULL, #{name})") int insertTitle(Title title); } |
StaffDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package cn.sharpcode.dao; import cn.sharpcode.mapper.StaffMapper; import cn.sharpcode.mapper.TitleMapper; import cn.sharpcode.po.Staff; import cn.sharpcode.po.Title; import org.mybatis.spring.support.SqlSessionDaoSupport; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @Repository public class StaffDao extends SqlSessionDaoSupport { public Staff selectStaff(Integer id){ StaffMapper staffMapper = getSqlSession().getMapper(StaffMapper.class); return staffMapper.selectStaff(id); } @Transactional public int insertStaff(Staff staff) { //插入职务信息 TitleMapper titleMapper = getSqlSession().getMapper(TitleMapper.class); Title title = new Title(); title.setName("CAO"); titleMapper.insertTitle(title); //插入员工资料 StaffMapper staffMapper = getSqlSession().getMapper(StaffMapper.class); int affectedRow; try { affectedRow = staffMapper.insertStaff(staff); } catch (Exception e) { throw new RuntimeException(e); } return affectedRow; } } |
Staff和Title
这两个Bean都只包含id和name属性和它们的get、set方法
applicationContext.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<?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:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:component-scan base-package="cn.sharpcode"/> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///shiro?useSSL=true"/> <property name="username" value="root"/> <property name="password" value=""/> </bean> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"/> </bean> <bean id="staffDao" class="cn.sharpcode.dao.StaffDao"> <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="cn.sharpcode"/> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> </bean> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".jsp"/> </bean> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="txManager"/> </beans> |