SpringMVC中的事务和异常

异常和Error

异常主要分为两大类:

  • 受检异常(Checked Exception):继承自 Exception 类,但不继承自 RuntimeException

    编译器要求必须处理这些异常,通常通过 try-catch 块或在方法签名中声明 throws

    示例:IOExceptionSQLExceptionClassNotFoundException 等。

  • 未受检异常(Unchecked Exception):继承自 RuntimeException 的类。编译器不要求必须处理这些异常,但可选择性地捕获。

    示例:NullPointerExceptionArrayIndexOutOfBoundsExceptionIllegalArgumentException 等。

Error 类

  • ErrorThrowable 的一个子类,表示 JVM 所无法处理的严重问题,通常用于表示系统级别的错误。
  • 常见的Error类型包括:
    • OutOfMemoryError:当 JVM 无法分配内存时抛出。
    • StackOverflowError:当线程的调用栈超过限制时抛出。
    • InternalError:表示 JVM 内部发生了错误。

Spring的事务机制

Spring通过异常进行事务回滚的机制:

  1. Spring 的默认事务机制,当出现unchecked异常Error时候回滚,checked异常的时候不会回滚;

  2. 当有try catch后捕获了异常,事务不会回滚,如果需要回滚, 要catchthrow new RuntimeException 让事务回滚;

默认事务会在RuntimeExceptionError的情况下回滚。

设置@Transactional(rollbackFor = Exception.class)时,所有的异常Error都会回滚。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyService {

@Transactional
public void performTransactionalOperation() {
// 抛出运行时异常,事务将回滚
throw new RuntimeException("This will trigger a rollback.");

// 抛出Error,事务也会回滚
// throw new Error("This will also trigger a rollback.");
}
}

处理方式

在需要处理事务的地方添加上@Transactional注解

在 Spring 中,如果你使用 @Transactional 注解而不显式设置 rollbackFor 属性,

默认行为是:

  • 只对运行时异常(RuntimeException)和错误(Error)进行回滚

    这意味着在事务方法中抛出任何子类为 RuntimeException 的异常。

    例如 NullPointerExceptionIllegalArgumentExceptionDataIntegrityViolationException,将导致事务回滚。

  • 对于检查性异常(Checked Exception),如 IOException 或者自定义的检查性异常,事务将不会回滚。

    除非你明确指定 rollbackFor 属性来让这些异常导致回滚。

如果添加@Transactional(rollbackFor = Exception.class) 的情况下,所有异常都会回滚,如 IOException 或者自定义的检查性异常。

方式1

因为要保证当前方法具有返回值,在前端界面展示操作状态。

所以我们service层在添加了try catch日志打印后,抛出的runtime类异常需要在controller层进行捕获,捕获之后,在catch中编写操作失败后返回值的信息。

1
2
3
4
5
6
7
8
9
10
11
12
@Service
public class UserService {
@Transactional(rollbackFor = Exception.class)
public void createUser(User user) {
try {
userDao.save(user);
userCapabilityQuotaDao.save(capabilityQuota);
} catch (Exception e) {
throw new RuntimeException();
}
}
}

当然这样也可以,因为指定了回滚的是所有异常的父类,所以所有异常都会回滚。

1
2
3
4
5
6
7
8
@Service
public class UserService {
@Transactional(rollbackFor = Exception.class)
public void createUser(User user) {
userDao.save(user);
userCapabilityQuotaDao.save(capabilityQuota);
}
}

方式2

service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常了

1
2
3
4
5
6
7
8
9
10
11
12
@Service
public class UserService {
@Transactional
public void createUser(User user) {
try {
userDao.save(user);
userCapabilityQuotaDao.save(capabilityQuota);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}

方式3

不依赖于Spring的异常捕获机制进行事务回滚,通过手动的session.rollback进行异常捕获后回滚事务也可。

这种方式太麻烦不推荐

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
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyService {

@Autowired
private SessionFactory sessionFactory;

public void performTransactionalOperation() {
Session session = null;
Transaction transaction = null;

try {
session = sessionFactory.openSession();
transaction = session.beginTransaction();

// 执行一些数据库操作
// 例如保存一个实体
MyEntity entity = new MyEntity();
entity.setName("Test");
session.save(entity);

// 故意抛出一个异常,触发回滚
if (true) { // 这里是个示例条件
throw new RuntimeException("Simulated Exception for Rollback");
}

// 提交事务
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback(); // 回滚事务
}
e.printStackTrace(); // 处理异常
} finally {
if (session != null) {
session.close(); // 关闭会话
}
}
}
}