Env variables in data class in Redis model annotation - java

Troubling to specify dynamic env variable in the model class #Hashkey Redis annotation.
Model:
#RedisHash("${spring.redis.namespace}:Book")
public class Book {
#Id
private String id;
private String name;
}
My application.properties file:
spring.redis.namespace=local
The resulting key is "${spring.redis.namespace}:Book" instead of local:Book
Could anyone help me with this?

Please use Keyspaces to do it. There two ways. I use one way to finish your requirement.
#Configuration
#EnableRedisRepositories(keyspaceConfiguration = MyKeyspaceConfiguration.class)
public class ApplicationConfig {
#Value("${spring.redis.namespace}:Book")
String myKey;
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {
#Override
protected Iterable<KeyspaceSettings> initialConfiguration() {
return Collections.singleton(new KeyspaceSettings(Book.class, myKey));
}
}
}

Related

Spring Boot - Apply persistence Converter Annotation based on a condition

I have below repository model class
class Model {
#Column(name="id")
private static Integer id;
#Column(name="column-to-be-converted")
#Convert(converter=Converter.class)
private static String columnToBeConverted;
#Column(name="apply-converter")
private static boolean applyConverter;
// Getters and Setters
}
Below is the Converter class
#Component
#Converter
public class PasswordConverter implements AttributeConverter<String, String> {
#Override
public String convertToDatabaseColumn(String rawData) {
// logic goes here
}
#Override
public String convertToEntityAttribute(String convertedData) {
// logic goes here
}
}
I want to apply #Convert annotation to the field columnToBeConverted only if the field applyConverter is set to true
I tried investigating if the model object can be passed to Converter Class as argument or with using #Conditional
Please suggest how can this be achieved
Thank you!

Handling multiple same properties with #ConfigurationProperties

I have Properties class with a few different beans. Values from application.yaml:
#Configuration
#Getter
#Setter
public class RabbitProperties {
private String requestExchangeName;
private String requestQueueName;
private String responseExchangeName;
private String deadLetterExchangeName;
#Bean
#ConfigurationProperties("rabbit-service.common-orders")
public RabbitProperties commonOrdersRabbitProperties() {
return new RabbitProperties();
}
#Bean
#ConfigurationProperties("rabbit-service.metrics")
public RabbitProperties metricsRabbitProperties() {
return new RabbitProperties();
}
...//more beans
}
I'm using this Configuration in another config class:
#Configuration
#RequiredArgsConstructor
public class RabbitServiceConfig {
private final RabbitProperties commonOrdersRabbitProperties;
private final RabbitProperties metricsRabbitProperties;
...//about 15 similar fields
#Bean("metricsRabbitService")
public RabbitService getMetricsRabbitService(AmqpAdmin amqpAdmin, Client rabbitClient) {
return new RabbitService(
metricsRabbitProperties.getRequestExchangeName(),
metricsRabbitProperties.getRequestQueueName(),
metricsRabbitProperties.getResponseExchangeName(),
metricsRabbitProperties.getDeadLetterExchangeName(),
rabbitClient,
amqpAdmin
);
}
#Bean("commonOrdersRabbitService")
public RabbitService getCommonOrdersRabbitService(AmqpAdmin amqpAdmin, Client rabbitClient) {
return new RabbitService(
commonOrdersRabbitProperties.getRequestExchangeName(),
commonOrdersRabbitProperties.getRequestQueueName(),
commonOrdersRabbitProperties.getResponseExchangeName(),
commonOrdersRabbitProperties.getDeadLetterExchangeName(),
rabbitClient,
amqpAdmin
);
}
...//etc
I'm adding new RabbitProperties field almost every week, so now it already has about 15-20 kinda same fields. How can I get rid of these fields and put them to Map for example? Where should I put values for this Map and initialize it? What is the proper way to use ConfigurationProperties here?
If I've understood the question properly, you could define a private final Map<String, RabbitProperties> rabbitPropertiesMap; in the RabbitServiceConfig class instead of all the fields. All the RabbitProperties will be bounded in the map by injection, with key equals to bean name.
Another different approach would be to update the implementation of RabbitProperties with something like
#ConfigurationProperties("rabbit-service")
#Value
#ConstructorBinding
public class RabbitServiceProperties {
Map<String, RabbitProperties> rabbitPropertiesMap;
#Value
static class RabbitProperties {
String requestExchangeName;
String requestQueueName;
String responseExchangeName;
String deadLetterExchangeName;
}
}
this way everything under the root rabbit-service in the application.yml with the structure described will be discovered and bound.

Want’s to Marshall different set of POJO fields based on Spring Profile

I have a POJO Deatils having following fields.
1. String Name
2. String Add
3. String Phone
.
For profile ‘x’ first two fields should marshal in XML and for other profile first and last field should marshal in XML.
Note: no field will be null for any profile.
I feel like it would be better to leave the Marshaller alone. It may be tricky to extend and customize the marshaller to fit your needs per Spring profile.
If our goal is to serialize different payloads depending on Spring profile, I'd recommend maybe a converter/factory that takes in the POJO (that has all the fields) and spits out a Data Transfer Object (DTO) for the specific profile.
Perhaps the following could work unless there is another end-goal in mind.
public class Details { ... }
// essentially a marker interface
public interface MyDto {
}
public class DtoA implements MyDto {
private String name;
private String add;
}
public class DtoB implements MyDto {
private String name;
private String phone;
}
public interface DtoConverter {
MyDto convert(Details details);
}
public class DtoConverterA implements DtoConverter { ... }
public class DtoConverterB implements DtoConverter { ... }
#Configuration
public class MyConfiguration {
#Bean
#Profile("a")
public DtoConverter dtoConverterA() {
return new DtoConverterA();
}
#Bean
#Profile("b")
public DtoConverter dtoConverterB() {
return new DtoConverterB();
}
}

How to bind a Map object containing Pojos to a yaml file?

In a spring-boot/ spring-cloud application, I would like to bind a Map object to my application.yml but I've got a "Elements ... where lef unbound error".
In my class called Ebox, I would like to bind a map called infosTenants, indentified by a string and containing values of type InfosTenant.
Below my application.yml (without the getters / setters of each classes or subclasses)
#ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties {
private Ebox ebox = new Ebox();
public ApplicationProperties() {
}
// getters/setters ...
public static class Ebox {
private String authUrl;
private Map<String, InfosTenant> infosTenants = new HashMap<>();
public Ebox() {
}
public class InfosTenant{
private String clientId="";
private String clientSecret="";
public InfosTenant() {
}
// getters/setters ...
}
}
}
In my application.yml, I defined one tenant in my tenants map, indentified by the key tenant1.
application:
ebox:
auth-url: https://oauth-server/api/oauth/token
infos-tenants:
tenant1:
client-id: myclient
client-secret: secret
But all values under infos-tenants were left unbound.
Does somebody have an idea ?
Thanks
I found my error, inner classes should be static, I forgot the static before class InfosTenant.
public static class InfosTenant{
private String clientId="";
private String clientSecret="";
public InfosTenant() {
}
// getters/setters ...
}

Spring Data MongoDB Repository with custom collection name

I am using Spring Data for MongoDB and I need to be able to configure collection at runtime.
My repository is defined as:
#Repository
public interface EventDataRepository extends MongoRepository<EventData, String> {
}
I tried this silly example:
#Document(collection = "${mongo.event.collection}")
public class EventData implements Serializable {
but mongo.event.collection did not resolve to a name as it does with a #Value annotation.
A bit more debugging and searching and I tried the following:
#Document(collection = "#{${mongo.event.collection}}")
This produced an exception:
Caused by: org.springframework.expression.spel.SpelParseException: EL1041E:(pos 1): After parsing a valid expression, there is still more data in the expression: 'lcurly({)'
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.doParseExpression(InternalSpelExpressionParser.java:129)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:60)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:32)
at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpressions(TemplateAwareExpressionParser.java:154)
at org.springframework.expression.common.TemplateAwareExpressionParser.parseTemplate(TemplateAwareExpressionParser.java:85)
Perhaps I just don't know how to quite use SPel to access values from Spring's Property Configurer.
When stepping through the code, I see that there is a way to specify collection name or even expressions, however, I am not sure which annotation should be used for this purpose or how to do it.
Thanks.
-AP_
You can solve this problem by just using SPeL:
#Document(collection = "#{environment.getProperty('mongo.event.collection')}")
public class EventData implements Serializable {
...
}
Update Spring 5.x:
Since Spring 5.x or so you need an additional # before environment:
#Document(collection = "#{#environment.getProperty('mongo.event.collection')}")
public class EventData implements Serializable {
...
}
Docs:
SpEL: 4.2 Expressions in Bean Definitions
SpEL: 4.3.12 Bean References
PropertyResolver::getProperty
So, at the end, here is a work around that did the trick. I guess I really don't know how to access data from Spring Properties Configurer using the SPeL expressions.
In my #Configuration class:
#Value("${mongo.event.collection}")
private String
mongoEventCollectionName;
#Bean
public String mongoEventCollectionName() {
return
mongoEventCollectionName;
}
On my Document:
#Document(collection = "#{mongoEventCollectionName}")
This, appears to work and properly pick up the name configured in my .properties file, however, I am still not sure why I could not just access the value with $ as I do in the #Value annotation.
define your entity class like
#Document(collection = "${EventDataRepository.getCollectionName()}")
public class EventData implements Serializable {
Define a custom repository interface with getter and setter methods for "collectionName"
public interface EventDataRepositoryCustom {
String getCollectionName();
void setCollectionName(String collectionName);
}
provide implementation class for custom repository with "collectionName" implementation
public class EventDataRepositoryImpl implements EventDataRepositoryCustom{
private static String collectionName = "myCollection";
#Override
public String getCollectionName() {
return collectionName;
}
#Override
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
}
Add EventDataRepositoryImpl to the extends list of your repository interface in this it would look like
#Repository
public interface EventDataRepository extends MongoRepository<EventData, String>, EventDataRepositoryImpl {
}
Now in your Service class where you are using the MongoRepository set the collection name, it would look like
#Autowired
EventDataRepository repository ;
repository.setCollectionName("collectionName");
Entity Class
#Document // remove the parameters from here
public class EscalationCase
{
}
Configuration class
public class MongoDBConfiguration {
private final Logger logger = LoggerFactory.getLogger(MongoDBConfiguration.class);
#Value("${sfdc.mongodb.collection}") //taking collection name from properties file
private String collectionName;
#Bean
public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MongoMappingContext context) {
MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), context);
converter.setTypeMapper(new DefaultMongoTypeMapper(null));
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);
if (!mongoTemplate.collectionExists(collectionName)) {
mongoTemplate.createCollection(collectionName); // adding the collection name here
}
return mongoTemplate;
}
}

Categories

Resources