Multiple datasources with Jsr310JpaConverters not work - java-8

When there is a LocalDateTime attribute in Entity, serialization fails when using the repository interface query, and when a single data source is working.
When I only use the following configuration in application.java, it does not work:
#EntityScan (
BasePackageClasses = {Application.class, Jsr310JpaConverters.class}
)
When I do not use the above configuration, the LocalDateTime property is directly configured with the following configuration, it works:
#Convert (converter = Jsr310JpaConverters.LocalDateConverter.class)
Can anyone help me?
Note: I found a question related to this one, here.

Related

Spring #AliasFor not working for #Profile annotation

Environment: Kotlin 1.5.30, SpringBoot 2.5.4(Spring 5.3.9)
Background & Issue
I'm trying to create a composed annotation to simplify similar template annotation codes. The annotation class is like this:
#Profile("default") //NOTE 1: use a placeholder, see the investigations below
annotation class ProfileAware(
#get: AliasFor(annotation = Profile::class, attribute = "value")
val profiles: Array<String>,
//properties delegated for other annotations
)
expected usage:
#Component
#ProfileAware(profiles = ["dev"])
class TheBean {
init {
LoggerFactory.getLogger("TheBean").info("TheBean: I'm registered")
}
}
in application.yaml:
spring:
profiles:
active: dev
But after the application starts, TheBean is not registered as expected.
Investigation & Try
First I've search in github spring repository, and found this: Regression: custom composed #Profile annotation without runtime retention no longer supported with component scanning. So I tried to add #Retention(AnnotationRetention.RUNTIME) on #ProfileAware, but no effect.
Tried to remove ("default") value from meta annotation (and, whether add the default value to profiles attribute or not), but got java.lang.IllegalArgumentException: Must specify at least one profile.
Tried to remove #Profile("default") from meta annotation, but got AnnotationConfigurationException: #AliasFor declaration on attribute 'profile' in annotation ... which is not meta-present.
(Important) Try to use #Profile("dev") directly on bean instead of ProfileAware , works as expected.
(Important) Try to change the value on meta annotaion as "dev", it works, but obviously it is hardcoded and not match my need.
Is there something I did wrong? Or is it possible to create composed annotation with dynamic #Profile value?
Thanks for your reading and help.
#Profile is looked up by org.springframework.context.annotation.ProfileCondition whose matches(...) method uses org.springframework.core.type.AnnotatedTypeMetadata.getAllAnnotationAttributes(String) to look up the #Profile annotations, and the Javadoc for that method states the following (bold emphasis added by me).
Retrieve all attributes of all annotations of the given type, if any (i.e. if defined on the underlying element, as direct annotation or meta-annotation). Note that this variant does not take attribute overrides into account.
Thus, you currently cannot use #Profile with #AliasFor for annotation attribute overrides.

How does spring boot #Value("${somevalue}") annotation work?

I have some #Value annotation in spring-boot project. To Simplify, I have few classes: a restcontroller, service (annotated with #Service) and a pojo.
In each of these classes, I declared a variable as below:
#Value("${somevalue}")
private String somevalueVariable
In the controller class, the value is getting populated as defined in the application.properties. So no problem here.
In the service class, the value is showing up as null. This is my issue, how should i fix it to get the value from the application.properties
In the pojo, the value is showing up as null, I am thinking this is expected behaviour as spring does not manage this class.
Try this:
import org.springframework.beans.factory.annotation.Value;
#Value("#{${somevalue}}")
private String somevalueVariable
ideally service class should have #Service anotation over it, either you missed that or this class is not scanned by spring context, so please add ComponentScan anotation for service class package over main class to scan classes uner this package -
#SpringBootApplication
#ComponentScan({"com.in28minutes.springboot"})
public class Application
It uses Spring Expression Language (SpEL):
https://docs.spring.io/spring-integration/docs/5.3.0.RELEASE/reference/html/spel.html
Also there is 2 #Value : org.springframework.beans.factory.annotation.Value and lombok.Value;
Make sure you are using the right one.
To get value from property try this:
#Value("${systemValue}")
private String systemValue;
For more information I find this useful:
https://www.baeldung.com/spring-value-annotation

ReactiveCrudRepository supply with index name

I am using spring-data with reactive support for Elasticsearch:
#Repository
public interface UserDocumentRepository extends ReactiveCrudRepository<UserDocument, UUID> {
}
For now, my UserDocument is annotated with #Document(indexName = "user-*") It is working properly for searching (as I am using ES supplied from Kafka-connect, my service will not create new documents).
My problem is that I have multiple environments dev/test when I need to parametrize index name for each cluster (using the same elastic search, with different index names).
So for dev, I need dev-user-* and for the test, I need test-user-*. I can use ReactiveElasticsearchTemplate where you can supply indexName, but how to do it with ReactiveCrudRepository?
You can use a SpEL expression in your #Document annotation. Check this question and my answer there about the syntax.
Edit:
Just a couple of examples how to build the index name dynamically:
If you have a configuration from the application.properties named env-name:
#Document(indexName = "#{#environment.getProperty('env-name')}-index-*")
I you have a bean named environmentProvider with a getEnv() method:
#Document(indexName = "#{#environmentProvider.getEnv()}-index-*")

QueryException when using Spring Data Rest with EclipseLink on Multi-Tenant System

I am using Spring data rest and EclipseLink to create a multi-tenant single table application.
But I am not able to create an Repository where I can call on custom QueryParameters.
My Kid.class
#Entity
#Table(name="kid")
#Multitenant
public class Kid {
#Id
private Long id;
#Column(name = "tenant_id")
private String tenant_id;
#Column(name = "mother_id")
private Long motherId;
//more attributes, constructor, getter and setter
}
My KidRepository
#RepositoryRestResource
public interface KidRepository extends PagingAndSortingRepository<Kid, Long>, QuerydslPredicateExecutor<Kid> {}
When I call localhost/kids I get the following exception:
Exception [EclipseLink-6174] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a):
org.eclipse.persistence.exceptions.QueryException\r\nException Description: No value was provided for the session property [eclipselink.tenant-id].
This exception is possible when using additional criteria or tenant discriminator columns without specifying the associated contextual property.
These properties must be set through EntityManager, EntityManagerFactory or persistence unit properties.
If using native EclipseLink, these properties should be set directly on the session.
When I remove the #Multitenant annotation on my entity, everything works fine. So it has definitively something to do with EclipseLink.
When I don't extend from the QuerydslPredicateExecutor it works too. But then I have to implement all findBy* by myself. And even doing so, it breaks again. Changing my KidsRepository to:
#RepositoryRestResource
public interface KidRepository extends PagingAndSortingRepository<Kid, Long> {
Collection<Kid> findByMotherId(#Param("motherId") Long motherId);
}
When I now call localhost/kids/search/findByMotherId?motherId=1 I get the same exception as above.
I used this tutorial to set up EcpliseLink with JPA: https://blog.marcnuri.com/spring-data-jpa-eclipselink-configuring-spring-boot-to-use-eclipselink-as-the-jpa-provider/, meaning the PlatformTransactionManager, the createJpaVendorAdapter and the getVendorProperties are overwritten.
The tenant-id comes with a jwt and everything works fine as long as I don't use QuerydslPredicateExecutor, which is mandatory for the use case.
Turns out, that the wrong JpaTransactionManager is used we I rely on the QuerydslPredicateExecutor. I couldn't find out, which one is created, but having multiple breakpoints inside the EclipseLink Framework code, non of them were hit. This is true for both, using the QuerydslPredicateExecutor or using the custom findby method.
I have googled a lot and tried to override some of the basic EclipseLink methods, but non of that worked. I am running out of options.
Does anyone has any idea how to fix or work around this?
I was looking for a solution for the same issue; what finally helped was adding the Spring's #Transactional annotation to either Repository or any place from where this custom query is called. (It even works with javax.transactional.) We had the #Transactional annotation on most of our services so the issue was not obvious and its occurrence seemed rather accidental.
More detailed explanation about using #Transactional on Repository is here: How to use #Transactional with Spring Data?.

Spring Data MongoDB: Specifying a hint on a Spring Data Repository find method

I am implementing a Spring Data Repository and having my repository extend the MongoRepository. I am looking for a way to specify a hint on my findBy methods so I can be control. I have seen several times when a non-optimal index would be picked as the winning plan.
This is what my repository looks like right now:
public interface AccountRepository extends MongoRepository<Account, ObjectId> {
#Meta(maxExcecutionTime = 60000L, comment = "Comment" )
public List<Account> findByUserIdAndBrandId(Long userId, Long brandId);
}
I researched a bunch and found that the JPARepository from spring data supports the #QueryHint annotation but I do not believe that annotation is supported for MongoDb. Is there a similar annotation I can specify on top of my findBy method to specify the hint?
MongoTemplate allows to specify a hint, however, I have a ton of findBy methods and I would hate to add an implementation underneath just to specify a hint.

Resources