Spring DAO is called by the loop when the data is not updated in real time solution

  • 2020-04-01 03:26:35
  • OfStack

Before describing the problem, let's assume that the database's transactions are configured in Spring's configuration file in the following way:


 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource"/>
 </bean> 
 <tx:annotation-driven transaction-manager="transactionManager" />

There is now UserDao and SecurityService:


@Repository
 public class UserDao {
   public User getUser() {
     // query user from user table
     return queryObject("select * from user order by id desc limit 1");
   }
 }

@Service
 @Transactional
 public class SecurityService {
   @Autowired
   private UserDao userDao;
 
   public void checkUserInfo() {
     while(true) {
       User user = userDao.getUser();
       if(user != null && "Tom".equals(user.getName()) {
         System.out.println("Tom is here");
         break;
       }
     }
   }
 }

During the call to the SecurityService#checkUserInfo() method, the data retrieved through the userDao#getUser() method is unchanged, even if a new data loop with the name Tom is inserted at this time. It also doesn't help to remove the @transactional annotation from the SecurityService.
The first thing I thought about was whether the database connection pool was the problem, and the Spring one was the same. Then, the Connection object was directly called from the JdbcTemplate, and the database was operated in the original JDBC way. At this time, the data was changed in real time, so it occurred to me that the Spring transaction should be bound to the current operation thread. After checking the source code, we found in the DataSourceUtils#doGetConnection method that Spring created a Connection on each DataSource of each thread and bound to the transaction. Because the tx:annotation-driven configuration file is Transactional bound to all the Service layers (classes annotated with @service), the same Connection is bound to the same thread whether or not @transactional is used, but no transaction is performed.
After a lot of experimentation and searching for information, we finally found the perfect solution: just add the @transactional (propagation = propagation.NOT_SUPPORTED) annotation to the checkUserInfo method above. Of course, you can also get a Connection and do it manually, or you can do it using the DateUtils package.


Related articles: