Can the Spring AbstractRoutingDataSource work without a default DataSource? - spring

I am trying to implement a multi-tenant DataSource in Spring using AbstractRoutingDataSource. I don't want any default DataSource because it is not a valid scenario in my case. During startup, determineCurrentLookupKey() is being called (I don't know from where). My context is not yet loaded at this point so determineCurrentLookupKey() returns a null value and I get this exception:
Cannot determine target DataSource for lookup key [null]
Is there a way around this? Thanks.

I faced the same issue and adding the spring.jpa.properties.javax.persistence.validation.mode=none didn't work for me...
And actually, I'm not sure that this is a really good solution to disable these validations...
But I found another solution!
setLenientFallback(boolean)
public void setLenientFallback(boolean lenientFallback)
Specify whether to apply a lenient fallback to the default DataSource if no specific DataSource could be found for the current lookup key.
Default is "true", accepting lookup keys without a corresponding entry in the target DataSource map - simply falling back to the default DataSource in that case.
Switch this flag to "false" if you would prefer the fallback to only apply if the lookup key was null. Lookup keys without a DataSource entry will then lead to an IllegalStateException.
probably this is what you're looking for?
But if the lookup key will be null the default DataSource still will be called...
UPD:
If you want completely get rid of setting the default DataSource you need to remove spring.jpa.hibernate.ddl-auto=update from your application.yaml, coz this is the reason why Spring trying to get this connection during the context initialization. This worked for me as well :)

Related

Spring Boot & Vault: incomplete context initialization issue

I've faced an issue that on rare occasions (it might take dozens of restarts) Spring doesn't initialize all properties correctly.
I define the bean of CbKafkaConsumerConfig (my custom bean) type and check its state in the thread that was created by a method that is marked as #EventListener(ApplicationReadyEvent.class), so I expect it to be completely initialized by this point. However, this is what I see:
Values that I expected to be filled are left with placeholders.
Here's how they are defined in application.properties file. (And I've checked the spelling - it's correct, otherwise it would fail every time, not occasionally)
config-bean-prefix.msg-topics=${cb.kafka.tc-topic}
config-bean-prefix.unexpected-error-topic=${cb.kafka.unexpected-errors-topic}
These properties are defined in Vault and I expected them to be fetched and set with the power of Spring Cloud Vault. Here you can see that Vault is present as a property source AND that these properties are populated there.
At the same time, in the context there are other beans of the same type CbKafkaConsumerConfig that are referring to these properties and yet it resolved fine for them.
Here's how the bean is defined
#Bean({"myBean"})
#ConfigurationProperties(
prefix = "config-bean-prefix"
)
public CbKafkaConsumerConfig myBeanConsumer() {
return new CbKafkaConsumerConfig();
}
And the bean itself:
#Data
public class CbKafkaConsumerConfig extends CbKafkaBaseConfig {
#NotNull
#Size(
min = 1
)
private Collection<String> msgTopics;
#NotNull
private String unExpectedErrorTopic;
}
We're using Spring Boot 2.2.x however this issue is also present for Spring Boot 2.1.x.
It's not specific for this type of beans, other might fail as well while being correctly set in Vault. What could be the reason of such unpredictable behavior and what I should look into?
Turns out by default spring cloud vault is not simply fetching properties on start, every so often it's updating them. While updating, there's a short time window when properties were already deleted from property source in the context, but not filled with the new ones and it might actually happen during context initialization (super questionable behavior in my opinion) causing some beans being corrupted.
If you don't want properties to be updated in runtime just set spring.cloud.vault.config.lifecycle.enabled to false

Kotlin Spring boot #Value annotation process

#Value("\${datasource.host}")
private val host: String = ""
I wrote the following code in KOTLIN and it worked fine.
I don't understand how the host was injected into the host.
In my knowledge, the value should not be injected because the host variable is val.
How does this code work?
Short answer: Spring is magical!
For a Kotlin property, val doesn't necessarily mean that the property is constant.  (It's not an exact equivalent of Java final here.)  It simply means that there's a get() method but no set() method.
That leaves open the possibility for the value to change some other way.  (For example, the property could have a custom getter which returned different values.)
I'm not sure quite how Spring works its magic; it may be able to set the property's backing field directly, or it may create a hidden subclass which can.  In any case, it's perfectly capable of setting val properties.  (You can also see this in Hibernate.)

Difference between net.sf.ehcache and org.ehcache?

What is the difference between net.sf.ehcache and org.ehcache?
The current version of net.sf.ehcache is 2.10.5 whereas same for org.ehcache is 3.5.2.
Spring uses net.sf.ehcache's CacheManager, and org.ehcache's CacheManager isn't compatible for same.
Is there any specific reason for this? Please explain.
As you can verify on the page http://www.ehcache.org/downloads/, Ehcache 3 is using the package prefix org.ehcache and Ehcache 2 is using the package prefix net.sf.ehcache. That's it.
There are different in many levels. With ehcache 3.x, Element is not there anymore. One should directly put the key and value in the Cache therefore you can provide types when you create cache:
Cache<Long, String> myCache = cacheManager.getCache("myCache", Long.class, String.class);
And consequently when retrieving the value, you avoid the hassle of getObjectValue instead you just treat Cache like a ConcurrentMap. So you won't get NullPointerException if the key doesn't exist, so you won't need check for cache.get(cacheKey) != null
cache.get(cacheKey);
The way to instantiate CacheManager has also changed. You won't getInstance so it is not singleton anymore. Instead you get a builder, which is way nicer, especially that you can provide it with configuration parameters inline:
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("preConfigured",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.heap(100))
.build())
.build(true);

How to list resolvedDataSources from AbstractRoutingDataSource?

I implemented Dynamic DataSource Routing using Spring Boot (JavaConfig) to add and switch new DataSources in runtime.
I implemented AbstractRoutingDataSource and I need access to all resolvedDataSources that is a private property. How can I do it?
I actually don't know why that field has not been made protected to let implementing classes access the data sources set. Regarding your questions two options come into my mind.
Option 1:
Copy the code of AbstractRoutingDataSource into a class of your own. Then you can expose the resolvedDataSources simply by a getter. This should work as long as the configuration relies on the interface AbstractDataSource and not AbstractRoutingDataSource.
Option 2
Pick the brute force way by accessing the field via Reflection API

Spring Data JPA save() throws NPE

I wrote a web service with spring boot using spring data jpa for persistence.
The webservice has some static objects (in Singleton Bean) that regulary needs to be backed up to my database.
Sometimes! (This sucks...I dont' really know what happens) when I call
ObjectType updated = myRepository.save(existingObject)
I get an java.lang.NullPointerException - without usable stacktrace as the method doing this is running via #Scheduled.
I tried debugging and existingObject seems to be absolutely fine. The error only occurs, when existingObject is actually NOT a new object (i.e. when id != 0)
P.S. I am using Spring Boot therefore not really using EntityManager. I only use the #Autowired myRepository.
I'm seeing something similar happening. During save, it seems the object is re-fetched from DB (perhaps to see which fields were altered?) but a ManyToOne relationship is not loaded (even though the FetchType is explicitly set to EAGER).
For some reason, a compareTo is called on the relationship. The related object isn't null, but it only has its ID filled in (presumably because that was available in the object that was fetched from the DB). All other fields are null.
When the compareTo then does its stuff, a NullPointerException follows.
As to the actual solution, I don't know yet, as I would have expected the FetchType EAGER to make sure the relationship is loaded. Hopefully this helps someone to further find the root cause.
(I would have added this as a comment as it doesn't actually answer the question, but StackOverflow won't let me due to insufficient reputation...)
You haven't provided enough information. IF that line is where the NullPointerException is occurring, then the only possibilities are that myRepository is null, or existingObject is null. However, it's possible the NullPointerException is happening as a result of something in the save. Wrap the code in a try catch, and log the exception stacktrace to file.
If needed, checkout the logging customization notes here:
http://projects.spring.io/spring-boot/docs/spring-boot/README.html

Resources