Happy moment
Venomous snakes and pythons are discussing who the most efficient way of hunting.
Viper: I only need to bite each other, and over time, it will gradually lose its ability to move and eventually die.
Python sneered: That has to wait for the effective time, I just need to entangle the other party, it can immediately kill it.
The viper is furious: you are entangled in it, you are cheap!
Python: Didn't you kiss it too?
Review
Looking at the title of the article, I don't know if you can think of what the specific problem is. If you are a little ignorant, that's right! (If you are not ignorant, this article will not be meaningful, hehe)
Before pointing out what the specific problem is, let ’s review some content first
Spring transaction principle
I believe everyone can say something about this. Spring transactions are a specific application of Spring AOP. The bottom layer depends on dynamic proxies.
The general process is similar to the following
Call the target object through the proxy object, and there are transaction-related enhanced processing in the proxy object
For specific details, please refer to the following article
Spring transaction source code analysis
Remember an online question → Where did the business go
Spring dynamic data source principle
Principle decryption → Spring AOP implements dynamic data sources (read and write separation). The underlying principles have been introduced in detail. The process is roughly as follows
Spring AOP → put our specified lookupKey into ThreadLocal
ThreadLocal → shared lookupKey within the thread
DynamicDataSource → encapsulate multiple data sources, and dynamically select specific data sources based on the lookupKey in ThreadLocal
What's the problem
Since transactions and dynamic data sources are specific applications of Spring AOP, there is a sequence of proxies
Either
Either
Let's see what is the difference between the two
Transaction first, dynamic data source last
At this time, the pre-enhanced processing of the transaction will be effective, so where does the Connection that started the transaction come from? It must be from DynamicDataSource, because it is what we configure for the transaction manager
@Bean public PlatformTransactionManager transactionManager (@Qualifier ("dynamicDataSource" ) DataSource dynamicDataSource) { // Configure transaction management, add @Transactional annotation to the method header when using transactions to return new DataSourceTransactionManager (dynamicDataSource); }
Since it is a Connection obtained from DynamicDataSource, when DynamicDataSource obtains Connection according to lookupKey, will it be obtained from masterDataSource data source or slaveDataSource data source? Because the lookupKey has not been bound to the current thread at this time, the DynamicDataSource will be obtained from the default data source, and the default data source we configured is slaveDataSource
/ ** * Get the data source of the current thread * @return * / public static DataSourceType getDataSourceType () { return HOLDER.get () == null ? DataSourceType.SLAVE: HOLDER.get (); }
To put it bluntly, the dynamic data source does not take effect on the transaction at this time, and the transaction always obtains the Connection from the default data source without dynamic effects.
Talk is cheap. Show me the code, let ’s see if it ’s really as above
192.168.0.112 is our slave library, corresponding to our default data source slaveDataSource
Dynamic data source first, transaction last
At this time, the pre-enhancement of the dynamic data source will be executed first, and the lookupKey required by the DynamicDataSource will be bound to the current thread before the transaction. Then when the transaction obtains the Connection from the DynamicDataSource, it can dynamically select masterDataSource or slaveDataSource according to the lookupKey of the current thread
There is no problem in this case
Solve the problem
Summarize the question: how to ensure that the dynamic data source in the transaction also has a dynamic effect, that is, how to ensure that the pre-enhancement of the dynamic data source precedes the transaction
We know that Spring AOP can specify the order, as long as we display the AOP that specifies the dynamic data source before the AOP of the transaction; how to specify the order, the common way is to implement the Order interface, or use @Order annotation, the order value Smaller, the first to execute, so we only need to ensure that the Order value of the dynamic data source is less than the Order value of the transaction
Let ’s first take a look at the default value of the Order of the transaction, in the EnableTransactionManagement annotation
/** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}. */ int order() default Ordered.LOWEST_PRECEDENCE;
The default is the lowest level (the value is very large), then we only need to ensure that the Order of the dynamic data source is smaller than this value, we take 1
@Component @Slf4j @Order(1) public class DynamicDataSourceAspect {
We are here to see if it is really feasible
It is no longer the default slaveDataSource, but the masterDataSource we specified (specified by @MasterSlave (MASTER))
At this point, I believe you have figured out what the problem is and how to solve it
What, don't you understand? Come here, I promise not to kill you
to sum up
1. Not only dynamic data sources and transactions, as long as multiple AOPs are involved, there may be a sequence problem, which is worth everyone's attention
2. Related constraints
The master database performs INSERT UPDATE DELETE operations, and there may be some SELECT operations (the master-slave synchronization has a delay)
Perform only SELECT operations from the database
The default data source is best set as the master data source to prevent careless execution of the update operation to the slave database; the reason why the landlord is set as the slave data source is to consider that most database operations are queries, which can reduce the amount of code; how to choose , You need to decide based on the actual situation