Spring与MyBatis的集成
要在Spring中使用MyBatis,需要在Spring的配置文件中定义一些类
- SqlSessionFactoryBean:为整合应用提供SqlSession对象资源。
- SqlSessionTemplate:负责管理MyBatis的SqlSession,调用SQL映射语句,实现对数据库的访问。
- MapperFactoryBean:根据指定Mapper接口生成Bean实例。
- MapperScannerConfigurer:根据指定包批量扫描Mapper接口并生成实例。
Spring和MyBatis的大致整合步骤
- 首先在Spring的配置文件中配置数据库连接(此处用druid的数据库连接池)
<!--1.配置数据库连接池 dataSource就是druid数据库连接池;也可以引入外部数据源文件-->
<!-- 如果是引入外部连接数据库的配置文件需要注意修改property 标签的value属性
<context:property-placeholder location="database.properties"/>
<property name="password" value="${password}"></property> -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/smbms?relaxAutoCommit=true&zeroDateTimeBehavior=convertToNull"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
- 配置SqlSessionFactoryBean对象,这里需要注意的是:在Spring的配置文件中可以引入MyBatis的配置文件,也可以在Spring的配置文件中配置MyBatis配置文件所能配置。但是两者不建议同时使用,要么引入外部配置文件,要么在Spring配置文件中配置一些规则。
<!--2.配置SqlSessionFactoryBean对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--指定MyBatis的总配置文件的位置;不能和configuration属性一起使用,要么引入外部配置文件,要么在spring的配置文件中配置-->
<!--<property name="configLocation" value="classpath:mybatis-config.xml"></property>-->
<!--注册Mapper.xml文件(和在MyBatis的总配置文件中做注册效果一样)-->
<property name="mapperLocations">
<list>
<value>classpath:com/bd/dao/*.xml</value>
</list>
</property>
<!--类型别名-->
<property name="typeAliasesPackage" value="com.bd.pojo"/>
<!--总配置文件-->
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<!--开启驼峰命名规则-->
<property name="mapUnderscoreToCamelCase" value="true"/>
<!--开启最高权限自动映射-->
<property name="autoMappingBehavior" value="FULL"/>
</bean>
</property>
</bean>
- 为DAO层的接口映射相应的Mapper.xml文件(其中value值根据自己的情况决定是否加*号 ")
<!--递归扫描基准包下所有接口,若它们在SQL映射文件中定义过,则动态注册为MapperFactoryBean,如此即可批量产生映射器实现类,即和mapper.xml文件进行映射-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bd.dao" />
</bean>
- 另外需要开启注解扫描
<!--spring扫描项目中的注解,指定需要扫描的基类包,多个包可用逗号隔开-->
<context:component-scan base-package="com.bd"/>
- DAO层的接口和配置文件正常编写
public interface UserMapper {
/**
* 查询全部用户
* @return 返回用户集合
*/
public List<User> selectAll();
}
- 对应的Mapper.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.bd.dao.UserMapper">
<!--查询全部用户-->
<select id="selectAll" resultType="user">
select * from user
</select>
</mapper>
- 编写service层的接口和实现类(通过注解的方式)
@Component
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> selectAll() {
return userMapper.selectAll();
}
}
- 测试(有两种测试方法)
- 测试一:正常的通过getBean创建对象,调用方法
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService us = context.getBean("userServiceImpl", UserService.class);
us.selectAll().forEach(System.out::println);
userService.selectAll().forEach(System.out::println);
}
- 测试二:通过注解的方式创建对象和指定配置文件的路径,类上边的两个注解联合使用。@RunWith标注当前类是一个运行类,不是测试类(需要注意junit4的包版本不能太低)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestSpring {
@Autowired
private UserService userService;
@Test
public void test01(){
userService.selectAll().forEach(System.out::println);
}
}
小结:MapperScannerConfigurer 与@Autowired注解或@Resource注解配合使用,自动创建映射器实现并注入给业务组件,能够最大限度地减少DAO组件与业务组件的编码和配置工作。
Spring提供了声明式事务支持
- 事务配置步骤
- 导入tx和aop命名空间
- 定义事务管理器Bean,并为其注入数据源Bean
- 通过<tx:advice>配置事务增强,绑定事务管理器并针对不同方法定义事务规则
- 配置切面,将事务增强与方法切入点组合
事务的属性
-
propagation:事务传播机制
- REQUIRED(默认值):指定当前方法必须放在一个事务中,如果当前没有可运行的事务环境,则新建一个事务。
- REQUIRES_NEW 、MANDATORY、NESTED:指定当前方法会创建一个新的事务,如果当前运行环境有事务,则挂起该事务,直到新的事务提交,则继续执行被挂起的事务。
- SUPPORTS:如果当前运行环境有可用事务,则将当前方法加入到正在运行的事务中,如果没有可运行的事务,则当前方法以非事务(不要事务)的方式运行。
- NOT_SUPPORTED、NEVER:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。
-
ACID数据库的四个隔离级别
- A:原子性:一个事务中的一组SQL语句,要么全部执行成功,要么全部执行失败。
- C:一致性:事务在完成前和完成后,所有的数据保持一致的状态。
- I:隔离性:一个事务的执行不能被其他事务所影响。
- D:持久性:一个事务一旦提交,事物的操作便永久性的保存在数据库中。
-
关于事务隔离级别的三个名词(这是由于事务并发而引起的问题)
- 脏读:一个事务还没有提交,另外一个事务读取到了他修改后的数据。
- 幻读:先进行了一个查询,另外一个事务向表中又添加了新的数据,所以再次查询,查询结果和之前不一致。
- 不可重复读:在同一个事务中,执行两条同样的SQL语句,但是查询的结果不同。
-
isolation:事务隔离等级
- DEFAULT(默认值):使用数据库默认的事务隔离级别。
- READ_COMMITTED:提交读,较为安全,造成不可重复读。
- READ_UNCOMMITTED:未提交读,造成脏读。
- REPEATABLE_READ:可重复读,使用for udpate加查询锁,造成幻读。
- SERIALIZABLE:串行读,可以解决脏读、不可重复读、幻读,效率非常低。
事务属性 2
- timeout:事务超时时间,允许事务运行的最长时间,以秒为单位。默认值为-1,表示不超时。
- read-only:事务是否为只读,默认值为false。
- rollback-for:设定能够触发回滚的异常类型。
- Spring默认只在抛出runtime exception时才标识事务回滚。
- 可以通过全限定类名指定需要回滚事务的异常,多个类名用逗号隔开。
- no-rollback-for:设定不触发回滚的异常类型。
- Spring默认checked Exception不会触发事务回滚。
- 可以通过全限定类名指定不需回滚事务的异常,多个类名用英文逗号隔开。
配置声明式事务的步骤
- 在Spring的配置文件中添加事务管理
<!--事务管理;其中ref属性值是数据库连接池的唯一标识id-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
- 配置事务增强,给不同前缀的方法添加不同的事务类型
<!--事务增强/可通过注解实现更简单方便-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="select*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="insert*" rollback-for="RuntimeException"/>
</tx:attributes>
</tx:advice>
- 配置切面
<aop:config>
<aop:pointcut id="myPoint" expression="execution(* com.bd.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint" />
</aop:config>
以上是通过配置文件添加事务,使用起来配置繁琐,不够灵活!
通过注解的方式声明事务
- 在Spring的配置文件中添加事务管理并添加对注解配置的事务的支持(可以省略二、三步骤)。
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
- 然后再需要添加事务的方法上添加@Transactional注解。
@Transactional(rollbackFor = {RuntimeException.class})
@Override
public void insertUser(User user) {
userMapper.insertUser(user);
}
-
@Transactional使用的位置
- 接口实现类或接口实现方法上,而不是接口类中。
- 此方法必须是public修饰的,否则不会生效。
-
@Transactional注解的属性
属性 | 类型 | 说明 |
---|---|---|
propagation | 枚举型:Propagation | 可选的传播性设置。使用举例:@Transactional(propagation=Propagation.REQUIRES_NEW) |
isolation | 枚举型:Isolation | 可选的隔离性级别。使用举例:@Transactional(isolation=Isolation.READ_COMMITTED) |
readOnly | 布尔型 | 是否为只读型事务。使用举例:@Transactional(readOnly=true) |
readOnlytimeout | int型(以秒为单位) | 事务超时。使用举例:Transactional(timeout=10) |
rollbackFor | 一组 Class 类的实例,必须是Throwable的子类 | 一组异常类,遇到时 必须 回滚。使用举例:@Transactional(rollbackFor={SQLException.class}),多个异常用逗号隔开 |
rollbackForClassName | 一组 Class 类的名字,必须是Throwable的子类 | 一组异常类名,遇到时 必须 回滚。使用举例:@Transactional(rollbackForClassName={“SQLException”}),多个异常用逗号隔开 |
noRollbackFor | 一组 Class 类的实例,必须是Throwable的子类 | 一组异常类,遇到时 必须不 回滚 |
noRollbackForClassName | 一组 Class 类的名字,必须是Throwable的子类 | 一组异常类名,遇到时 必须不 回滚 |