Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to solve the conflict between @ Transactive and @ DS dynamic data source annotations in java

2025-03-26 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the knowledge about "how to solve the annotation conflict between @ Transactive and @ DS dynamic data sources in java". Many people will encounter this dilemma in the operation of actual cases, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Catalogue

@ Transactive conflicts with @ DS dynamic data source annotations

Background

@ Transactional execute the process

Solution method

Dynamic data source switching failed

The problem of switching failure of dynamic data sources caused by transaction @ Transactional annotations

Background of conflict between @ Transactive and @ DS dynamic data source annotations

When writing a project not long ago, there was a requirement to insert data into three libraries and three tables. In the same method, the company uses baomidou's @ DS annotation to configure dynamic data sources. This is the background, and then I manipulated these three tables in a service method, and I added the @ Transactional annotation, because the method was a save method, so I added transactions.

The pseudo code is as follows:

Spring: datasource: dynamic: primary: A datasource: A: url:.. Driver-class-name: com.mysql.jdbc.Driver username: root password: B: url:.. Driver-class-name: com.mysql.jdbc.Driver username: root password: C: url:.. Driver-class-name: com.mysql.jdbc.Driver username: root password:@Mapper@DS ("A") public interface AMapper {@ Insert ("insert into a...") Void save ();} @ Mapper@DS ("B") public interface BMapper {@ Insert ("insert into b...") Void save ();} @ Mapper@DS ("C") public interface CMapper {@ Insert ("insert into c...") Void save ();} public class aService {@ Autowiredprivate aMapper, bMapper, cMapper @ Transactional public void save () {aMapper.save (); bMapper.save (); # error cMapper.save ();}}

When the development completed the self-test with postman, it was found that the line bMapper.save () reported an error and the error content: table b could not be found. If @ Transactional is commented out, the code runs normally and the data is successfully stored. We clearly added @ DS annotations to all three mapper to switch data sources, so why not add @ Transactional?

@ Transactional execute the process

The @ Transactional annotation is added to the save method, and the Spring transaction takes effect. At this point, Spring TransactionInterceptor intercepts the method through AOP and creates a transaction. If you create a transaction, you will inevitably get the data source. Then TransactionInterceptor creates the transaction using Spring DataSourceTransactionManager and binds the transaction information to the current thread through ThreadLocal.

The transaction information includes the Connection connection corresponding to the transaction. That means that the Connection has already been created before the query operation of OrderMapper is reached. Moreover, because the transaction information is bound to the current thread, when the OrderMapper needs to obtain the Connection in the query operation, it directly gets the Connection bound by the current thread instead of the Connection corresponding to the DataSource corresponding to the @ DS annotation added by OrderMapper.

OK, so we can now focus on how DataSourceTransactionManager gets DataSource and thus Connection. For each DataSourceTransactionManager database transaction manager, the DataSource data source that it needs to manage is passed in when it is created. When using dynamic-datasource-spring-boot-starter, it creates a DynamicRoutingDataSource and passes it into DataSourceTransactionManager.

DynamicRoutingDataSource is responsible for managing multiple data sources that we configure. For example, in this example, three data sources a, b, and c are managed, and a data source is used by default. In the current scenario, DynamicRoutingDataSource needs to get the data source name based on @ DS to get the corresponding DataSource. As a result, since we did not add the @ DS annotation to the Service method, it has to return the default data source, that is, a. As a result, an exception occurred that the table could not be found.

We learned above that because @ Transactional creates the transaction and gets the data source, because we have no @ DS annotation on the service method, we take the default data source, and after that, the transaction information is bound to the current thread through threadLocal, and the transaction information includes the connection connection, which means that when entering the service method, the current transaction binds the data source a when running to bMapper.save (). Because connection already exists, the data source is still a, so you can't find the table in b library at this time.

Solution method

Divide the three storage operations into three separate methods, all with @ Transactional and @ DS annotations (on service). Ps: when calling bMapper.save () after completing aMapper.save (), be sure to set @ Transactional to Propagation.REQUIRES_NEW, so that when another transaction method is called, TransactionInterceptor will suspend the original transaction and temporarily unbind the original transaction information from the current thread.

Pps:

When this is used in one transaction method to call another transaction method, @ DS also works because this calls not a transaction object and does not open the transaction. If you want to know more about it, you can see the article I posted earlier / / www.yisu.com/article/222082.htm.

Dynamic data source switching failure caused by transaction @ Transactional annotation

Not much BB, just go to the code:

Public class DataSourceKey {/ * user data source * / public final static String USER = "userDataSource"; / * report data source * / public final static String REPORT = "reportDataSource"; / * Collection of all data sources * / final static List SOURCES = ImmutableList.of (USER, REPORT) / * find the data source according to the package name, and the prefixes of multiple data sources cannot be the same. Example: user-> userDataSource * * @ param pack package name * @ return data source name * / public static String getDataSourceKey (String pack) {return SOURCES.stream () .filter (s-> s.startsWith (pack)) .findFirst () .orElse (USER);} @ Component@Aspect@Order (- 1) @ Slf4jpublic class DynamicDataSourceAspect {@ Pointcut ("execution (* com.in.g.data.mapper..*.* (..)") Public void dataSourcePointcut () {} @ Before ("dataSourcePointcut ()") public void doBefore (JoinPoint point) throws Throwable {log.debug ("start switching data sources.") ; Package pack = point.getSignature (). GetDeclaringType (). GetPackage (); String str = StringUtils.substringAfterLast (pack.getName (), "."); String dataSourceKey = DataSourceKey.getDataSourceKey (str); DynamicDataSourceHolder.set (dataSourceKey); log.debug ("data source switched successfully, current data source: {}", dataSourceKey) } @ After ("dataSourcePointcut ()") public void doAfterReturning () throws Throwable {DynamicDataSourceHolder.clear ();}} / * dynamic data source holder * / public class DynamicDataSourceHolder {public static ThreadLocal keyHolder = new ThreadLocal (); public static void clear () {keyHolder.remove ();} public static void set (String key) {keyHolder.set (key) } public static String get () {return keyHolder.get ();}} / * dynamic data source configuration * / public class DynamicRoutingDataSource extends AbstractRoutingDataSource {@ Override protected Object determineCurrentLookupKey () {return DynamicDataSourceHolder.get () }} / / correct code-- lazy here, directly controller calls Dao layer @ GetMapping ("/ testdb") public String testDateSources () {/ / narrow the scope of the transaction add (); rptFieldMapper.selectxxxx (); return "sss";} @ Transactional (rollbackFor = Exception.class) public void add () {userMapper.insertXXXX (xxxx) } / / incorrect code. @ Transactional annotation will cause data source switching to fail @ GetMapping ("/ testdb") @ Transactional (rollbackFor = Exception.class) public String testDateSources () {userMapper.insertXXXX (xxxx); rptFieldMapper.selectXXXX (); return "sss" } the content of "how to resolve the conflict between @ Transactive and @ DS dynamic data source comments in java" is introduced here. Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 280

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report