Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue when using @Transactional #16

Closed
fabienfleureau opened this issue Oct 17, 2023 · 7 comments
Closed

Issue when using @Transactional #16

fabienfleureau opened this issue Oct 17, 2023 · 7 comments
Assignees

Comments

@fabienfleureau
Copy link

fabienfleureau commented Oct 17, 2023

Describe the bug
When we use the @transactional annotation, it fails at runtime saying there are two TransactionManager available (NoUniqueBeanDefinitionException)

Is the bug at startup before you perform any action?
No, it's at runtime when going to a @transactional annotated method

Stacktrace
No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,internalTransactionManager

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,internalTransactionManager
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1299)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:484)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:506)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:345)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:751)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)
	at myMethodWithTransactionalAnnotation```
@aileftech
Copy link
Owner

aileftech commented Oct 18, 2023

Hi fabien,

This happens because we register an internal transaction manager for the secondary data source.
I think the issue should go away if you specify which transaction manager to use, like:

@Transactional("transactionManager") // This is the "default" one, registered by your application

I'd ask you to try this and see if it goes away. I'm not sure how to apply this setting globally/by default in order not to have to change all @Transactional, but if this solution works I can investigate.

EDIT: Another solution could be to use the @Primary annotation on the default transaction manager, but since usually the transaction manager is not defined in the code but instantiated automatically by Spring I'm not sure how to apply that yet (but if you defined the transactionManager bean this should be applicable).

@aileftech aileftech self-assigned this Oct 18, 2023
@fabienfleureau
Copy link
Author

fabienfleureau commented Oct 18, 2023

Hello, indeed it works like this, but I would be nice that integrating dbadmin would not require any modification in existing bean configuration. (I have a lot of existing @transactional, I won't modify all of them)
As a workaround I did anotate with @primary annotation

    @Bean
    @Primary
    @ConditionalOnMissingBean(TransactionManager::class)
    fun transactionManager(
        transactionManagerCustomizers: ObjectProvider<TransactionManagerCustomizers>
    ): PlatformTransactionManager {
        return JpaTransactionManager()
            .apply { transactionManagerCustomizers.ifAvailable {
                it.customize(this)
            }
        }
    }

@aileftech
Copy link
Owner

Yes, indeed it's ideal to not require any modification. But I think at least adding @Primary is required. Are you aware of any other possibility I can look into to avoid having to add that too?

@aileftech
Copy link
Owner

aileftech commented Oct 19, 2023

I've read a little bit more about this. Ideally I would need something like a @Secondary/@NotPrimary annotation, in order to make the other bean @Primary "by default" without having the user specify it.

From my limited knowledge such an annotation doesn't exist. These issues might be related so I'm saving them for future reference:

spring-projects/spring-framework#26528 (comment)

spring-projects/spring-framework#26241

@fabienfleureau
Copy link
Author

fabienfleureau commented Oct 20, 2023

There are way to instantiate the JPARepository programmatically without the needs of beans. Be doing there will be no conflicts with the main application beans.
Also one possibility to could be not to use JPA at all for the internal processes and use jdbc related (you only have less than 5 different requests to write)

@aileftech
Copy link
Owner

The previous version was using JdbcTemplate indeed, but this brings many problems because table/field names can vary a lot with annotations, naming strategies, etc... and it's hard to get a solid result since you have to specify the field names "manually" if writing queries.

There might be a way to get the correct names by querying some Spring class, but I then decided to rewrite using JPA and solve all of this at once.

I will look into instantiating them without making them beans.

@aileftech
Copy link
Owner

aileftech commented Oct 26, 2023

This should be fixed with this latest commit on the dev branch. If you can confirm it as well on your side it would be great, @fabienfleureau ! (You should be able to remove the @Primary annotation and it should keep working)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants