Spring RedisTemplate : use same key with multiple RedisTemplate to store different values - spring

I am new to Redis and want to implement it with my existing spring application.
My question is to use different redisTemplate with same keys to store different types of values.
For e.g.
I have redisTemplate1 and redisTemplate2 beans defined in spring, like.
<bean id="redisTemplate1" class ="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref ="connectionFactory" />
<bean id="redisTemplate2" class ="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref ="connectionFactory" />
In java file to my service, I have created two different data structure using these two redis templates.
#Autowired
#Qualifier(value = "redisTemplate1")
private RedisTemplate<String, Student> redisTemplate1;
#Autowired
#Qualifier(value = "redisTemplate2")
private RedisTemplate<String, Address> redisTemplate2;
And, using following pattern to store data.
redisTemplate1.opsForHash().put("KEY1", student.getId(), student);
redisTemplate2.opsForHash().put("KEY1", address.getId(), address);
The case is, I have primary keys starting with 1 for each table. So 1 is there a primary key of Student as well as of Address.
I am using line below to get Student back from data-store.
(Student) redisTemplate1.opsForHash().get("KEY1", 1);
But, unfortunately it generates an exception.
java.lang.ClassCastException: com.redis.model.Address cannot be cast to com.redis.model.Student
So, my questions are,
Is it possible to use multiple redis templates ?
If yes, can I use same key (unique for each template) to store different types of data and access the same data stored using that template and key ?
If not, what are the alternative to perform same operation ?
Thanks in advance.

Actually Redis is a key/value store and if you use the same key for the same store you just override the old value with new one. And it doesn't matter how much RedisTemplates (or even connectionFactorys) you have, if the real Redis server is the same.
Now how to help you with your task:
You should have different kyes for different domain objects: e.g. students, addresses.
Since you are going to store domain objects with their own keys it looks like Map value is for you. I mean under key students a map of Students should be stored ,and the same for Addresses.
However you, actually, do it, but you use the same key for both domains.
So, the answer for you: that's because you are using the same Redis from both RedisTemplates.

Related

Does OpenCSV support write to CSV from bean with a map [duplicate]

I'm using opencsv for read/write csv files using opencsv annotations.
My bean is having fields not just primitives, but a java HashMap as well.
Now what i want to do is
public class MyBean {
#CsvBindByName(column = "ID")
private int id;
#CsvBindByName(column = "PROPERTIES")
private Map<String, String> sampleMap = new HashMap<String, String>();
TO
ID, property1, property2...
1, value1, value2.....
I'd like to get this working in both read/write.
as i understand, the default MappingStrategy doesn't work in this case. Also Creating Custom MappingStrategy doesn't makes sense for HashMap field. because we don't know the complete field list until we iterate all the map.
Another way to get column names is that just read one bean from the list of beans. And get access to HashMap then create the header.(Hashmap keys are fixed across beans in my case)
MappingStrategy only concerned about Class level meta data. Like fields etc.
public static Field[] getAllFields(Class<?> cls) {
List allFieldsList = getAllFieldsList(cls);
return (Field[])allFieldsList.toArray(new Field[allFieldsList.size()]);
}
getting access to the real data for creating csv header doesn't look like a natural way to do.
Any advice on how to solve this?
Please point me out to any other libraries out there that can do read/write into beans having Map field.
Cheers!
Sadly openCSV does not support this. A quick google showed me that SuperCSV comes close but it puts everything in a map whereas you want to control what goes in the map and what does not. There may be others out there but most require a one to one between the fields in the object and the columns in the csv file.
This was something that I wanted to develop for years and contribute because the company I currently work for has need for that but they do not want to pay me to develop it and I have higher priorities for openCSV when free time is available.
A possible but limited workaround would be to create what I would call a Data Transfer Object. Have a custom object that has all the values you would have and have it return the object of the type you want (or a translator that will convert the DTO that has all fields to the object you want with the map and some fields). The problem with this solution is that it forces you to know in advance what are all possible entries you have in the map.

Can MongoTemplate provide automatic translation?

I have a simple persistent pojo like:
public class Peristent {
private String unsafe;
}
I use Spring Data mongoTemplate to persist and fetch the above object. I also need to encrypt the Persistent.unsafe variable and store a complex representation of that in backend, everytime I try to save Persistent object.
Can I annotate Persistent, or provide some sort of hooks where I can make the aforementioned translations without me having to do that in the Pojo code manually. This has to happen automatically during mongoTemplate.insert.
Spring Data currently only support Type based conversions. There is an issue for supporting property based conversion, which you might want to track.
Therefore annotating won't work. What you could do is, create use a separate class for the property, which just wraps the String and register a custom converter for that type. See http://docs.spring.io/spring-data/data-mongo/docs/1.10.4.RELEASE/reference/html/#mongo.custom-converters for details, how to do that.

Spring Ldap - multipe base names

I am trying to use spring LDAP /ODM to receive some attributes from LDAP. Is there a way to configure multiple base names in
<ldap:context-source
url="${ldap.url}"
base="${ldap.base}" // here ..is there a prop that will take an array of base names
username="${userdn}"
password="${password}" />
<ldap:ldap-template id="ldapTemplate" />
or in
#Entry(objectClasses = { "person"} base={..CAN I GIVE MULTIPLE BASENames here..})
public class LdapUser {
#Id
private Name dn;
//..
}
The app I am developing has users defined under one OU and internal TESTERs defined in another ou in our AD. So I am trying to see if I can use the same LDAP entry class for looking up everyone.
The ContextSource base is intended to specify the base of all operations on the ContextSource, and is typically set to the domain controller DN.
You can use ODM without specifying a base on the #Entry (or using a base DN higher up in the tree), but in that case you will typically use the #DnAttribute annotation in order to have the framework automatically build DNs for you (mainly needed when persisting entries back to LDAP).
If we assume your users are in the following structure:
dc=example,dc=com,ou=USERS
dc=example,dc=com,ou=TESTERS
Now, if you specify base dc=example,dc=com on the ContextSource you can have ODM handle this automatically as described briefly below:
#Entry(objectclasses={"person"})
public class Person {
#Id
private Name dn;
#DnAttribute(name="ou", index=0)
#Transient // Indicates that this is not an attribute on the entry
private String userType;
#Attribute(name="cn")
private String name;
// More attributes here
}
The above will handle automatic mapping of LDAP entries to and from the Person class. Now, if you want to find all persons, do:
List<Person> allPersons = ldapTemplate.findAll(Person.class);
If you want to find all testers you would do:
List<Person> testers = ldapTemplate.find(
query().base("ou=TESTERS"),
Person.class);
I am not very familiar with Spring LDAP but (IIRC) LDAP itself can only search from a single node (base). So, looking at the documentation, you might have to do a search from the organization (o=xx) with an LDAPQueryBuilder, adding conditions for the ous. See the javadocs.
No expert here, mind you.
With XML config at least, you can wire an LdapTemplate instance. One suggestion might be to make a new implementation called something like DelegatingLdapTemplate that gets injected with two regular templates (one per basename) and then delegates to them appropriately (or just calls one, then the other if the first one return 0 results), and use this in place of a normal template instance. This of course makes sense only if your use case really warrants this behavior (e.g. if you never know where to search for the user and have to check both locations). Otherwise, just make two separate beans.

Spring Data for Redis: when dealing with values, does it only works with StringRedisTemplate?

When dealing with strings with Spring Data for Redis, I noticed the following behavior:
Given the code below:
template.opsForValue().set("person", "value");
it only works when I'm using an instance of StringRedisTemplate, and not when I'm using an instance of RedisTemplate. Should'nt it work with RedisTemplate since it aggregates all the operations for all the Redis data types?
StringRedisTemplate is just a specialization of RedisTemplate, so you should be able to use RedisTemplate directly if you prefer, provided that you set all the key/value serializers as is done in the constructor of StringRedisTemplate.
Perhaps you could provide more information about your configuration and what error you are getting? Also, why is StringRedisTemplate not a desirable option for your use case?

How to handle a large set of data using Spring Data Repositories?

I have a large table that I'd like to access via a Spring Data Repository.
Currently, I'm trying to extend the PagingAndSortingRepository interface but it seems I can only define methods that return lists, eg.:
public interface MyRepository extends
PagingAndSortingRepository<MyEntity, Integer>
{
#Query(value="SELECT * ...")
List<MyEntity> myQuery(Pageable p);
}
On the other hand, the findAll() method that comes with PagingAndSortingRepository returns an Iterable (and I suppose that the data is not loaded into memory).
Is it possible to define custom queries that also return Iterable and/or don't load all the data into memory at once?
Are there any alternatives for handling large tables?
We have the classical consulting answer here: it depends. As the implementation of the method is store specific, we depend on the underlying store API. In case of JPA there's no chance to provide streaming access as ….getResultList() returns a List. Hence we also expose the List to the client as especially JPA developers might be used to working with lists. So for JPA the only option is using the pagination API.
For a store like Neo4j we support the streaming access as the repositories return Iterable on CRUD methods as well as on the execution of finder methods.
The implementation of findAll() simply loads the entire list of all entities into memory. Its Iterable return type doesn't imply that it implements some sort of database level cursor handling.
On the other hand your custom myQuery(Pageable) method will only load one page worth of entities, because the generated implementation honours its Pageable parameter. You can declare its return type either as Page or List. In the latter case you still receive the same (restricted) number of entities, but not the metadata that a Page would additionally carry.
So you basically did the right thing to avoid loading all entities into memory in your custom query.
Please review the related documentation here.
I think what you are looking for is Spring Data JPA Stream. It brings a significant performance boost to data fetching particularly in databases with millions of record. In your case you have several options which you can consider
Pull all data once in memory
Use pagination and read pages each time
Use something like Apache Spark
Streaming data using Spring Data JPA
In order to make Spring Data JPA Stream to work, we need to modify our MyRepository to return Stream<MyEntity> like this:
public interface MyRepository extends PagingAndSortingRepository<MyEntity, Integer> {
#QueryHints(value = {
#QueryHint(name = HINT_CACHEABLE, value = "false"),
#QueryHint(name = READ_ONLY, value = "true")
})
#Query(value="SELECT * ...")
Stream<MyEntity> myQuery();
}
In this example, we disable second level caching and hint Hibernate that the entities will be read only. If your requirement is different, make sure to change those settings accordingly for your requirements.

Resources