Organizing data searches using Spring Data Key-Value Repositories

And so in Spring there is KeyValueOperations, which is similar to the Spring Data repository, but it operates on the concept of Key-Value and places the data in a HashMap structure (I wrote about Spring Data repositories here ). The objects can be of any type, the main thing is that the key is specified.
public class Status {
@org.springframework.data.annotation.Id
private long statusId;
private String code;
private String name;
....
Here the key is statusId, and the full path of the annotation is specifically indicated, in the future I will use JPA Entity, and there is also an Id there, but already related to the database.
KeyValueOperations has similar methods as in Spring Data repositories
interface KeyValueOperations {
T insert(T objectToInsert);
void update(Object objectToUpdate);
void delete(Class type);
T findById(Object id, Class type);
Iterable find(KeyValueQuery query, Class type);
.... др.
So you can specify java KeyValueOperations configuration for Spring bean
@SpringBootApplication
public class DemoSpringDataApplication {
@Bean
public KeyValueOperations keyValueTemplate() {
return new KeyValueTemplate(keyValueAdapter());
}
@Bean
public KeyValueAdapter keyValueAdapter() {
return new MapKeyValueAdapter(ConcurrentHashMap.class);
}
The dictionary storage class ConcurrentHashMap is specified here.
And since I will work with JPA Entity dictionaries, I will connect two of them to this project.
This is a dictionary of “Status” and “Card”
@Entity
public class Status {
@org.springframework.data.annotation.Id
private long statusId;
private String code;
private String name;
@Id
@Column(name = "STATUS_ID")
public long getStatusId() {
return statusId;
}
....
@Entity
public class Card {
@org.springframework.data.annotation.Id
private long cardId;
private String code;
private String name;
@Id
@Column(name = "CARD_ID")
public long getCardId() {
return cardId;
}
...
These are standard entities that correspond to tables in the database, I draw attention to two Id annotations for each entity, one for JPA, the other for KeyValueOperations
The structure of dictionaries is similar, an example of one of them
create table STATUS
(
status_id NUMBER not null,
code VARCHAR2(20) not null,
name VARCHAR2(50) not null
);
-- Create/Recreate primary, unique and foreign key constraints
alter table STATUS add constraint STATUS_PK primary key (STATUS_ID)
Spring Data repositories for them:
@Repository
public interface CardCrudRepository extends CrudRepository {
}
@Repository
public interface StatusCrudRepository extends CrudRepository {
}
And here is the DictionaryProvider example itself where we connect Spring Data repositories and KeyValueOperations
@Service
public class DictionaryProvider {
private static Logger logger = LoggerFactory.getLogger(DictionaryProvider.class);
private Map repositoryMap = new HashMap<>();
@Autowired
private KeyValueOperations keyValueTemplate;
@Autowired
private StatusCrudRepository statusRepository;
@Autowired
private CardCrudRepository cardRepository;
@PostConstruct
public void post() {
repositoryMap.put(Status.class, statusRepository);
repositoryMap.put(Card.class, cardRepository);
}
public Optional dictionaryById(Class clazz, long id) {
Optional optDictionary = keyValueTemplate.findById(id, clazz);
if (optDictionary.isPresent()) {
logger.info("Dictionary {} found in keyValueTemplate", optDictionary.get());
return optDictionary;
}
CrudRepository crudRepository = repositoryMap.get(clazz);
optDictionary = crudRepository.findById(id);
keyValueTemplate.insert(optDictionary.get());
logger.info("Dictionary {} insert in keyValueTemplate", optDictionary.get());
return optDictionary;
}
....
Auto injects are installed in it for repositories and for KeyValueOperations, and then simple logic (here, without checking for null, etc.), we look in the keyValueTemplate dictionary, if there is, then we return, otherwise we extract from the database via crudRepository and place in keyValueTemplate, and give out.
But if all this would be limited only to a key search, then there is probably nothing special. And so KeyValueOperations has a wide range of CRUD operations, and requests. Here's an example of a search in the same keyValueTemplate, but already by Code using the KeyValueQuery query.
public Optional dictionaryByCode(Class clazz, String code) {
KeyValueQuery query = new KeyValueQuery<>(String.format("code == '%s'", code));
Iterable iterable = keyValueTemplate.find(query, clazz);
Iterator iterator = iterable.iterator();
if (iterator.hasNext()) {
return Optional.of(iterator.next());
}
return Optional.empty();
}
And it’s understandable, if earlier I searched by ID and the object got into keyValueTemplate, then searching by the code of the same object will return it already from keyValueTemplate, there will be no access to the database. Spring Expression Language is used to describe the request.
Test Examples:
ID Search
private void find() {
Optional status = dictionaryProvider.dictionaryById(Status.class, 1L);
Assert.assertTrue(status.isPresent());
Optional card = dictionaryProvider.dictionaryById(Card.class, 100L);
Assert.assertTrue(card.isPresent());
}
Search by Code
private void findByCode() {
Optional card = dictionaryProvider.dictionaryByCode(Card.class, "VISA");
Assert.assertTrue(card.isPresent());
}
You can get lists of data through
Iterable find(KeyValueQuery query, Class type); You can specify sorting in the request
query.setSort(Sort.by(DESC, "name"));Materials:
spring-data keyvalue
github project