Ruoyi框架学习--事务

本文介绍了Ruoyi框架中的事务管理,包括事务的概念、如何使用事务、常见坑点及解决方案。在用户新增场景中需要事务以防止脏数据,而登录和操作日志则不需要。还讲解了Ruoyi框架下事务的自定义使用方法,以及如何通过注解实现操作日志的记录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、什么是事务?

事务(一)——什么是事务,为什么会有事务,事务是做什么的?_蓝狐乐队的博客-CSDN博客_什么是事务

2、怎么使用事务,以及相对应的坑点:

事务管理

新建的Spring Boot项目中,一般都会引用spring-boot-starter或者spring-boot-starter-web,而这两个起步依赖中都已经包含了对于spring-boot-starter-jdbcspring-boot-starter-data-jpa的依赖。 当我们使用了这两个依赖的时候,框架会自动默认分别注入DataSourceTransactionManagerJpaTransactionManager。 所以我们不需要任何额外配置就可以用@Transactional注解进行事务的使用。

提示

@Transactional注解只能应用到public可见度的方法上,可以被应用于接口定义和接口方法,方法会覆盖类上面声明的事务。

例如用户新增需要插入用户表、用户与岗位关联表、用户与角色关联表,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作, 这样可以防止出现脏数据,就可以使用事务让它实现回退。
做法非常简单,我们只需要在方法或类添加@Transactional注解即可。

@Transactional
public int insertUser(User user)
{
	// 新增用户信息
	int rows = userMapper.insertUser(user);
	// 新增用户岗位关联
	insertUserPost(user);
	// 新增用户与角色管理
	insertUserRole(user);
	return rows;
}
  • 常见坑点1:遇到检查异常时,事务开启,也无法回滚。 例如下面这段代码,用户依旧增加成功,并没有因为后面遇到检查异常而回滚!!

@Transactional
public int insertUser(User user) throws Exception
{
	// 新增用户信息
	int rows = userMapper.insertUser(user);
	// 新增用户岗位关联
	insertUserPost(user);
	// 新增用户与角色管理
	insertUserRole(user);
	// 模拟抛出SQLException异常
	boolean flag = true;
	if (flag)
	{
		throw new SQLException("发生异常了..");
	}
	return rows;
}

原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用 rollbackFor属性明确指定异常。
例如下面这样,就可以正常回滚:

@Transactional(rollbackFor = Exception.class)
public int insertUser(User user) throws Exception
{
	// 新增用户信息
	int rows = userMapper.insertUser(user);
	// 新增用户岗位关联
	insertUserPost(user);
	// 新增用户与角色管理
	insertUserRole(user);
	// 模拟抛出SQLException异常
	boolean flag = true;
	if (flag)
	{
		throw new SQLException("发生异常了..");
	}
	return rows;
}

  • 常见坑点2: 在业务层捕捉异常后,发现事务不生效。 这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。
    例如:下面这段代码直接导致用户新增的事务回滚没有生效。

@Transactional
public int insertUser(User user) throws Exception
{
	// 新增用户信息
	int rows = userMapper.insertUser(user);
	// 新增用户岗位关联
	insertUserPost(user);
	// 新增用户与角色管理
	insertUserRole(user);
	// 模拟抛出SQLException异常
	boolean flag = true;
	if (flag)
	{
		try
		{
			// 谨慎:尽量不要在业务层捕捉异常并处理
			throw new SQLException("发生异常了..");
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	return rows;
}

推荐做法:在业务层统一抛出异常,然后在控制层统一处理。



 

@Transactional
public int insertUser(User user) throws Exception
{
	// 新增用户信息
	int rows = userMapper.insertUser(user);
	// 新增用户岗位关联
	insertUserPost(user);
	// 新增用户与角色管理
	insertUserRole(user);
	// 模拟抛出SQLException异常
	boolean flag = true;
	if (flag)
	{
		throw new RuntimeException("发生异常了..");
	}
	return rows;
}

Transactional注解的常用属性表:

属性说明
propagation事务的传播行为,默认值为 REQUIRED。
isolation事务的隔离度,默认值采用 DEFAULT
timeout事务的超时时间,默认值为-1,不超时。如果设置了超时时间(单位秒),那么如果超过该时间限制了但事务还没有完成,则自动回滚事务。
read-only指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。{xxx1.class, xxx2.class,……}
noRollbackFor抛出 no-rollback-for 指定的异常类型,不回滚事务。{xxx1.class, xxx2.class,……}
....

提示

事务的传播机制是指如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。 即:在执行一个@Transactinal注解标注的方法时,开启了事务;当该方法还在执行中时,另一个人也触发了该方法;那么此时怎么算事务呢,这时就可以通过事务的传播机制来指定处理方式。

TransactionDefinition传播行为的常量:

常量含义
TransactionDefinition.PROPAGATION_REQUIRED如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED

如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

在Ruoyi框架下为什么增加用户需要添加事务 而 登录/操作日志不需要添加事务?

因为添加用户需要核实用户信息和用户权限等等问题,只有都确保正确了才可以形成一段数据传入数据库。

但是登录/操作日志不论成功与否,成功了没事失败了抛出异常并且都要记录在数据库中,他们没有一定要操作正确的需求,所以不需要添加事务。

Ruoyi框架下事务操作的具体用法(已封装):

在实际开发中,对于某些关键业务,我们通常需要记录该操作的内容,一个操作调一次记录方法,每次还得去收集参数等等,会造成大量代码重复。 我们希望代码中只有业务相关的操作,在项目中使用注解来完成此项功能。

在需要被记录日志的controller方法上添加@Log注解,使用方法如下:

@Log(title = "用户管理", businessType = BusinessType.INSERT)
public AjaxResult addSave(...)
{
    return success(...);
}

#注解参数说明

参数类型默认值描述
titleString操作模块
businessTypeBusinessTypeOTHER操作功能(OTHER其他、INSERT新增、UPDATE修改、DELETE删除、GRANT授权、EXPORT导出、IMPORT导入、FORCE强退、GENCODE生成代码、CLEAN清空数据)
operatorTypeOperatorTypeMANAGE操作人类别(OTHER其他、MANAGE后台用户、MOBILE手机端用户)
isSaveRequestDatabooleantrue是否保存请求的参数
isSaveResponseDatabooleantrue是否保存响应的参数

#自定义操作功能

1、在BusinessType中新增业务操作类型如:

/**
 * 测试
 */
TEST,

2、在sys_dict_data字典数据表中初始化操作业务类型

insert into sys_dict_data values(25, 10, '测试',     '10', 'sys_oper_type',       '',   'primary', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '测试操作');

3、在Controller中使用注解

@Log(title = "测试标题", businessType = BusinessType.TEST)
public AjaxResult test(...)
{
    return success(...);
}

操作日志记录逻辑实现代码LogAspect.java(opens new window)
登录系统(系统管理-操作日志)可以查询操作日志列表和详细信息。

### 若依框架整合ClickHouse数据库 #### 添加依赖 为了在基于若依框架的应用中使用 ClickHouse 数据库,在 `pom.xml` 文件中添加 ClickHouse 的 JDBC 依赖是必要的操作[^1]。 ```xml <dependency> <groupId>ru.yandex.clickhouse</groupId> <artifactId>clickhouse-jdbc</artifactId> <version>${clickhouse.version}</version> </dependency> ``` #### 创建配置类 针对 ClickHouse 进行数据源配置,通过自定义 DataSource Bean 来实现。由于 RuoYi 默认采用 Druid 作为连接池管理工具,因此也需要引入相应的支持并调整其参数适应 ClickHouse 特性。 ```java @Configuration public class DataSourceConfig { @Bean(name = "clickHouseDataSource") public DataSource clickHouseDataSource() { HikariDataSource dataSource = new HikariDataSource(); dataSource.setDriverClassName("ru.yandex.clickhouse.ClickHouseDriver"); dataSource.setJdbcUrl("jdbc:clickhouse://localhost:8123/default"); // 替换成实际地址 dataSource.setUsername("default"); dataSource.setPassword(""); return dataSource; } } ``` #### 定义实体映射 考虑到 ClickHouse 对大小写敏感以及特定引擎的要求[^2][^3],当设计表结构时需特别注意字段命名规则,并确保指定了合适的存储引擎(如 MergeTree)。对于主键设置,则依据业务需求决定是否必需存在唯一索引列。 ```sql CREATE TABLE IF NOT EXISTS user_log ( id UInt64, username String, action_time DateTime DEFAULT now(), description FixedString(255) ) ENGINE = MergeTree() ORDER BY (id); ``` #### 编写 Repository 接口 利用 Spring Data 提供的功能简化 CRUD 操作接口编写工作量的同时保持灵活性。这里假设已经完成了上述提到的数据源注入过程。 ```java @Repository public interface UserLogRepository extends JpaRepository<UserLog, Long>, JpaSpecificationExecutor<UserLog> {} ``` #### Service 层逻辑处理 最后一步是在服务层调用 repository 方法完成增删改查等基本功能开发;同时也可以在此基础上封装更复杂的业务流程控制逻辑。 ```java @Service @Slf4j @Transactional(readOnly = true) public class UserServiceImpl implements IUserService { private final UserLogRepository logRepo; public UserServiceImpl(UserLogRepository logRepo){ this.logRepo = logRepo; } @Override @Transactional public void saveUserAction(String userName, String desc){ try{ UserLog ulog = new UserLog(userName,desc); logRepo.saveAndFlush(ulog); }catch(Exception e){ log.error(e.getMessage(),e); } } } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值