SpringBoot 整合Mybatis + 事物管理
事物管理
- A是原子性(atomicity);B是一致性(consistency),事务执行前后涉及到的相关数据保持一致;C是隔离性(isolation),事务正确提交之前结果不应被其他事务所见;D是持久性(durability)
在设计service层的时候,应该合理的抽象出方法包含的内容。
然后将方法用@Trasactional注解注释,默认的话在抛出Exception.class异常的时候,就会触发方法中所有数据库操作回滚,当然这指的是增、删、改。
- 使用@EnableTransactionManagement 开启事物
@SpringBootApplication
@EnableTransactionManagement//开启事物
@MapperScan("com.skysoft.material.dao")// MyBatis Mapper接口文件所在包路径
public class MaterialSystemApplication {
public static void main(String[] args) {
SpringApplication.run(MaterialSystemApplication.class, args);
}
}
- 当然,@Transational方法是可以带参数的,具体的参数解释如下:
属性 |
类型 |
描述 |
---|---|---|
value |
String |
可选的限定描述符,指定使用的事务管理器 |
propagation |
enum: Propagation |
可选的事务传播行为设置 |
isolation |
enum: Isolation |
可选的事务隔离级别设置 |
readOnly |
boolean |
读写或只读事务,默认读写 |
timeout |
int (in seconds granularity) |
事务超时时间设置 |
rollbackFor |
Class对象数组,必须继承自Throwable |
导致事务回滚的异常类数组 |
rollbackForClassName |
类名数组,必须继承自Throwable |
导致事务回滚的异常类名字数组 |
noRollbackFor |
Class对象数组,必须继承自Throwable |
不会导致事务回滚的异常类数组 |
noRollbackForClassName |
类名数组,必须继承自Throwable |
不会导致事务回滚的异常类名字数组 |
事务隔离级别
- 1.Isolation.DEFAULT 这是默认值,表示使用底层数据存储的默认隔离级别。所有其他级别对应于JDBC隔离级别。
- 2.Isolation.READ_UNCOMMITTED 该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
- 3.Isolation.READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
- 4.Isolation.REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
- 5.Isolation.SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务属性
- 1、PROPAGATION.REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
- 2、PROPAGATION.SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
- 3、PROPAGATION.MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
- 4、PROPAGATION.REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
- 5、PROPAGATION.NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- 6、PROPAGATION.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- 7、PROPAGATION.NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
事务的只读属性 readOnly
- 事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。
rollbackFor 属性
- 默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者 Error,则 Spring 将回滚事务;除此之外,Spring 不会回滚事务。
- 如果在事务中抛出其他类型的异常,并期望 Spring 能够回滚事务,可以指定 rollbackFor。
@Transactional(propagation=Propagation.REQUIRED,rollbackFor= MyException.class)
注解
@Mapper
public interface UserMapper {
// 获取主键
@Insert("INSERT INTO user(name,password) VALUES (#{name}, #{password}) ")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int delete(@Param("id") Integer id);
@Update("UPDATE user SET name = #{name}, password = #{password} WHERE id = #{id}")
int update(User user);
@Select("SELECT id, name, password FROM user WHERE id = #{id}")
@Results(id = "userMap", value = { @Result(column = "id", property = "id", javaType = Integer.class),
@Result(column = "name", property = "name", javaType = String.class),
@Result(column = "password", property = "password", javaType = String.class) })
User findById(Integer id);
@Select("SELECT * FROM user")
@ResultMap("userMap")
List<User> fingAll();
}
- 复杂查询(动态sql):
@Mapper
public interface UserMapper {
// 动态生成sql
@SelectProvider(type = UserMapperProvider.class, method = "findByNameLike")
List<User> findByNameLike(String name);
//多参使用map
@SelectProvider(type = UserMapperProvider.class, method = "findByNameAndPassword")
List<User> findByNameAndPassword(String name, String password);
@InsertProvider(type = UserMapperProvider.class, method = "insert")
int insertUser(User user);
@DeleteProvider(type = UserMapperProvider.class, method = "delete")
int deleteUser(Integer id);
}
- mappperProvider对象:
public class UserMapperProvider {
// 动态生成sql
public String findByName(String name) {
String sql = "SELECT * FROM user";
if (StringUtils.isEmpty(name)) {
return sql;
}
sql += " WHERE name LIKE '%" + name + "%'";
return sql;
}
// 使用工具类来准备相同的 SQL 语句
public String findByNameLike(String name) {
return new SQL() {
{
SELECT("id, name, password");
FROM("user");
WHERE("name LIKE '%" + name + "%'");
}
}.toString();
}
public String findByNameAndPassword(Map<String, Object> map) {
String name = (String) map.get("param1");
String password = (String) map.get("param2");
return new SQL() {
{
SELECT("id, name, password");
FROM("user");
WHERE("name = " + name);
AND();
WHERE("password = " + password);
}
}.toString();
}
public String update(User user) {
return new SQL() {
{
if (!StringUtils.isEmpty(user.getId())) {
UPDATE("user");
if (!StringUtils.isEmpty(user.getName())) {
SET("name = #{name}");
}
if (user.getPassword() != null) {
SET("password = #{password}");
}
WHERE("id = #{id}");
}
}
}.toString();
}
public String insert(User user) {
return new SQL() {
{
INSERT_INTO("user");
VALUES("name", "#{name}");
VALUES("password", "#{password}");
}
}.toString();
}
public String delete(Integer id) {
return new SQL() {
{
DELETE_FROM("user");
WHERE("id = #{id}");
}
}.toString();
}
}
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">
<!-- 指定工作空间,要与接口名相同,源代码没有去看,猜测应该是通过"这里的namespace.下边方法的id"来定位方法的 -->
<mapper namespace="cn.sunxyz.mapper.UserMapper">
<!-- 若不需要自动返回主键,将useGeneratedKeys="true" keyProperty="id"去掉即可(当然如果不需要自动返回主键,直接用注解即可) -->
<insert id="insertUserXml" parameterType="User" keyProperty="id" useGeneratedKeys="true">
<![CDATA[
INSERT INTO USER
(
name,
password
)
VALUES
(
#{name, jdbcType=VARCHAR},
#{password, jdbcType=VARCHAR}
)
]]>
</insert>
</mapper>