报错如下:
代码如下:
Controller
import com.zwh.service.impl.TimeOutService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/timeout") @Slf4j public class DemoController { @Autowired private TimeOutService timeOutService; @GetMapping("/jdbc") public void queryPage() { timeOutService.testAnnotationTransactionalTimeOutWithJdbcTemplate(); } }
service
import com.zwh.dao.StudentDaoWithJdbcTemplate; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.Resource; @Service public class TimeOutService { @Resource private StudentDaoWithJdbcTemplate studentDaoWithJdbcTemplate; @Autowired private TransactionTemplate transactionTemplate; @Transactional(value = "transactionManager", timeout = 2) public void testAnnotationTransactionalTimeOutWithJdbcTemplate() { try { Thread.sleep(3000); } catch (Exception e) { } studentDaoWithJdbcTemplate.update(); } }
dao
使用JDBC定义一个dao
import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.StatementCallback; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.sql.SQLException; import java.sql.Statement; @Component public class StudentDaoWithJdbcTemplate { @Resource private JdbcTemplate jdbcTemplate; public void update() { jdbcTemplate.execute(new StatementCallback<Integer>() { public Integer doInStatement(Statement stmt) throws SQLException, DataAccessException { stmt.execute("update test set name='zhangsan3' where id = 2;"); return 1; } }); } }
注意:如果选择DataSourceTransactionManager,事务内所有的sql操作必须通过JdbcTemplate执行才能使timeout设置正常工作,通过myBatis执行的sql操作将无法应用超时设置。由上分析,使用@Transactional的timeout属性时,需要使用JDBCtemplate实现dao,而不能使用Mybatis。
数据库表test如下:
使用postman访问:http://localhost:8090/api/timeout/jdbc
原因分析:
我们平时在使用spring框架开发项目时,喜欢用@Transactional
注解声明事务。如上面的:
@Transactional(value = "transactionManager", timeout = 2)
只需在需要使用事务的方法上,使用@Transactional
注解声明一下,该方法通过AOP就自动拥有了事务的功能。
没错,这种做法给我们带来了极大的便利,开发效率更高了。
但也给我们带来了很多隐患,比如大事务的问题。我们一起看看下面的这段代码:
@Transactional(rollbackFor = Throwable.class) public void updateUser(User user) { User oldUser = userMapper.getUserById(user.getId()); if(null != oldUser) { userMapper.update(user); } else { userMapper.insert(user); } sendMq(user); }
这段代码中getUserById方法和sendMq方法,在这个案例中无需使用事务,只有update或insert方法才需要事务。
所以上面这段代码的事务太大了,是整个方法级别的事务。假如sendMq方法是一个非常耗时的操作,则可能会导致整个updateUser方法的事务超时,从而出现大事务问题。
timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
那么,如何解决这个问题呢?
答:可以使用TransactionTemplate
的编程式事务优化代码。
我们目的时时间执行时间过长不会被中止。所以:就不需要设置@Transactional了,因为@Transaction设置的目的是为了在一个事务执行时间超过某一个阈值时抛出异常。
import com.zwh.dao.StudentDaoWithJdbcTemplate; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.Resource; @Service public class TimeOutService { @Resource private StudentDaoWithJdbcTemplate studentDaoWithJdbcTemplate; @Autowired private TransactionTemplate transactionTemplate;
public void testAnnotationTransactionalTimeOutWithJdbcTemplate() { try { Thread.sleep(3000); } catch (Exception e) { } transactionTemplate.execute(new TransactionCallbackWithoutResult() { @SneakyThrows protected void doInTransactionWithoutResult(TransactionStatus status) { try { studentDaoWithJdbcTemplate.update(); } catch (Exception e) { status.setRollbackOnly(); throw e; } } }); } }
注意:这里没有了@Transactional(value = "transactionManager", timeout = 2)注解