Redis - Why details are saving both as HASH and SET using Spring Data Redis? - spring

I am new to Redis and developing Spring Boot + Spring Data Redis example. I am using CrudRepository, Example and ExampleMatchers API to do the searching from the Redis Key value store DB.
Now when I simply run my code, I saw that persons data saved as SET and HASH as well. Is this correct ? What's the use of saving the Person details both as SET and HASH
Showing all my code
public enum Gender {
MALE, FEMALE {
#Override
public String toString() {
return "Superwoman";
}
}
}
Species.java
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Species {
#Indexed
private String name;
}
Person.java
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
#RedisHash("persons")
public class Person {
#Id
private String id;
#Indexed
private String firstname;
private String lastname;
#Indexed
private Gender gender;
private List<String> nicknames;
#Indexed
private Integer age;
private Map<String, String> physicalAttributes;
#Reference
private Person relative;
private Species species;
}
PersonRepository.java
public interface PersonRepository extends CrudRepository<Person, String>, QueryByExampleExecutor<Person> {
}
RedisExampleDemoApplication.java
#SpringBootApplication
public class RedisExampleDemoApplication implements CommandLineRunner{
RedisMappingContext mappingContext = new RedisMappingContext();
ExampleQueryMapper mapper = new ExampleQueryMapper(mappingContext, new PathIndexResolver(mappingContext));
#Autowired
private PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(RedisExampleDemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Person person = Person.builder().firstname("Walter").gender(Gender.MALE).age(50).build();
Person person1 = Person.builder().firstname("Savani").gender(Gender.FEMALE).age(35).build();
personRepository.save(person);
personRepository.save(person1);
// [firstname:Walter, gender:MALE, age:50]
RedisOperationChain operationChain = mapper.getMappedExample(Example.of(person, ExampleMatcher.matchingAny()));
System.out.println(operationChain.getOrSismember());
System.out.println("----------------------------------------------");
Person p = Person.builder().lastname("Foo").build();
RedisOperationChain roc = mapper.getMappedExample(Example.of(p));
System.out.println(" == "+roc.getOrSismember());
System.out.println("-- "+roc.getSismember());
}
}

May be it is late to answer now , the reason that SET is visible is because of the secondary Index. I.e in your example First name is annotated as Indexed. Redis consider this as secondary index which is default a SET.

Related

Get records for last 3 days via Spring JPA Repository

I have an entity which contains field date.
#Entity
#Table(name="messages", schema = "users")
...
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "author")
private String author;
#Column(name = "tags")
private String tags;
#Column(name = "message_date")
private LocalDate date;
#Override
public String toString() {
...
}
}
#Repository
public interface MessageRepository extends JpaRepository<Message, Long> {
Message findByMessageId(Long id);
}
I'm using Spring Data JPA with repository. I want to get all messages from database for last 3 days (field date). How can I do it with Spring JPA?
#Query(...?)
List<Message> findBy...?
I suggest to split the logic from the actual queries. A service could handle all the intermediate things, e.g.:
#Service
public class MessageService {
private final MessageRepository repository;
#Autowired
public MessageService(MessageRepository repository) {
this.repository = repository;
}
List<Message> getLastThreeDays() {
// subtract 3 days from today
LocalDate threeDaysAgoDate = LocalDate.now().minusDays(3);
return this.repository.findAllWithDateAfter(threeDaysAgoDate);
}
}
and your repository stays nice and clean:
#Repository
public interface MessageRepository extends JpaRepository<Message, Long> {
Optional<Message> findByMessageId(Long id);
#Query("select m from Message m where date >= :threeDaysAgoDate")
List<Message> findAllWithDateAfter(#Param("threeDaysAgoDate") LocalDate threeDaysAgoDate);
}

I don't know why the double values are displayed in postman. Is the my code correct?

This is my Book class:
#Entity
#Table(name="book")
public class Book {
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#ManyToOne(targetEntity=Category.class,cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinColumn(name="CategoryId")
public Category category;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(length=10)
private int book_id;
#Column(length=128)
private String title;
#Column(length=64)
private String author;
#Column(length=200)
private String description;
#Column(length=10)
private int ISBN;
#Column(length=10)
private float price;
private Date published_Date;
#Lob
#Column
#Basic(fetch = FetchType.LAZY)
private byte[] icon;
//getter and setter
}
This is my Category class:
#Entity
#Table(name="category1")
public class Category {
#Id
#Column(length=12)
#GeneratedValue(strategy=GenerationType.AUTO)
public int CategoryId;
#Column(length=50)
public String CategoryName;
//#JsonBackReference
#OneToMany(mappedBy="category")
private List<Book> books = new ArrayList<Book>();
//getter and setter
}
The relationship between them is one to many.
This is my Category Service class
#Service
#Transactional
public class AdminServiceImpl implements AdminService {
#Autowired
private CategoryDao dao;
#Autowired
private BookDao dao1;
#Override
public List<Category> getAllCategory(){
return dao.findAll();
}
}
My Controller class
#RestController
#RequestMapping("/bookstore")
public class CategoryController {
#Autowired
private AdminService service;
#GetMapping("/GetAllCategory")
private ResponseEntity<List<Category>> getAllCategory() {
List<Category> catlist = service.getAllCategory();
return new ResponseEntity<List<Category>>(catlist, new HttpHeaders(), HttpStatus.OK);
}
}
My category table already has data.When i try to display them it is showing double values.
Displaying values using Postman
The Category table in the Database: Database table
Jackson's ObjectMapper uses the Java bean pattern and it expects the following
public class Foo {
public Object bar;
public Object getBar() {...}
public void setBar(Object bar) {...}
}
The getters and setters start with get and set, respectively, followed by the corresponding field name with its first letter capitalized.
Change
CategoryId to categoryId (first letter lowercase)
and
CategoryName to categoryName

Spring boot MongoDb complex query

I have been learning myself MongoDB implementation in Spring Boot.
However, I came into a problem with complex queries.
I cannot find any right solution for how to implement complex queries to MongoDB from Spring boot.
I am querying the database with MongoRepository interface implementation.
Let's say that I have three collections:
Person - 1 Person can have many Pets.
Pet - 1 Pet can have 1 PetToy and 1 Person who owns him.
PetToy - 1 PetToy can belong to 1 Pet.
POJO classes are bellow
What do I want to achieve?
I want to make a query, which would be returned me a Person, whose Pet has a Toy (PetToy) with the name "Teddy".
I could not have found a way how to do it. Furthermore, is it the best practice to even use such complex queries, or is it better to write more of little ones in MongoDB?
POJOs:
#Document
#Data
#ToString
public class Person {
#Id
private String id;
private String firstname;
private String lastname;
private int age;
#DBRef
private Pet pet;
}
#Document
#Data
#ToString
public class Pet {
#Id
private String id;
private String name;
private int age;
#DBRef
private List<PetToy> toys;
}
#Document
#Data
#ToString
public class PetToy {
#Id
private String id;
private String name;
}
I have tried to use MongoRepositories; however, I was not able to make the complex query.
How can one write such a query to a MongoDB from Spring Boot?
Thank you very much in advance.
If you can use embedded attributes, the class model should be:
#Document
#Data
#Builder
public class Person {
#Id
private String id;
private String firstName;
private String lastName;
private int age;
private List<Pet> pets;
}
#Data
#Builder
public class Pet {
private String name;
private int age;
private List<PetToy> toys;
}
#Data
#Builder
public class PetToy {
private String name;
}
The repository with the method that achieves what you want:
public interface PersonRepository extends MongoRepository<Person, String> {
List<Person> getByPetsToysName(String name);
}
The getByPetsToysName method basically navigate between Person's attributes Person->pets->toys->name. More info here.
An example
#Configuration
#EnableMongoRepositories
public class TestMongo implements CommandLineRunner {
private final PersonRepository repository;
public TestMongo(PersonRepository repository) {
this.repository = repository;
}
#Override
public void run(String... args) throws Exception {
repository.save(Person.builder()
.firstName("John")
.lastName("Doe")
.age(20)
.pets(Stream.of(Pet.builder()
.name("Ursa")
.age(1)
.toys(Stream.of(PetToy.builder()
.name("Teddy")
.build())
.collect(Collectors.toList()))
.build())
.collect(Collectors.toList()))
.build());
repository.save(Person.builder()
.firstName("Phillip")
.lastName("Larson")
.age(21)
.pets(Stream.of(Pet.builder()
.name("Bella")
.age(5)
.toys(Stream.of(PetToy.builder()
.name("Lolo")
.build())
.collect(Collectors.toList()))
.build())
.collect(Collectors.toList()))
.build());
List<Person> persons = repository.getByPetsToysName("Teddy");
System.out.println(persons.size());
List<Person> persons1 = repository.getByPetsToysName("Lolo");
System.out.println(persons1.size());
}
}
Logs:
find using query: { "pets.toys.name" : "Teddy" } fields: Document{{}} for class: class Person in collection: person
If you want more complex queries you can to take a look at the Spring Data MongoDB docs.

Spring Data JPA Redis : Cannot write custom method based query

I have configured Spring Data JPA with Redis and using RedisRepositories with provides methods like find(), findAll() etc. All these methods seem to be working just fine, but I am not able to write my custom method like.
RedisEntity findByGenderAndGrade(String gender, String grade);
RedisEntity is a simple POJO Entity class. If you want any more info, please let me know in messages.
Following is my entity:
#Data
#RedisHash("test1")
public class RedisEntity implements Serializable {
#Id
#GeneratedValue
private String id;
private String name;
private String gender;
private Integer grade;
}
Repository:
#Repository
public interface TestRepository extends JpaRepository<RedisEntity, String> {
List<RedisEntity> findAllByGender(String gender);
List<RedisEntity> findAllByGrade(Integer grade);
}
Service/Controller:
#Override
public List<RedisEntity> getById(String id) {
return testRepository.findById(id); //returns data perfectly.
}
#Override
public List<RedisEntity> getAllByGender(String gender) {
return testRepository.findAllByGender(gender); //returns []
}
#Override
public void saveEntity(RedisEntity redisEntity) {
testRepository.save(redisEntity); // saves it in redis perfectly.
}
Also,
findByGender and findAllByGender both give [], although I can see data in my redis database and save it as well.
As requested by FrançoisDupire,
#Configuration
public class RedisConfig {
#Autowired
private DeploymentProperties deploymentProperties;
private static Logger logger = LoggerFactory.getLogger(RedisConfig.class);
#Bean
JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration("localhost", 6379);
redisStandaloneConfiguration.setPassword(RedisPassword.of("root"));
return new JedisConnectionFactory(redisStandaloneConfiguration);
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
Also, I had referred this article: Baeldung article on Spring data redis
As mentioned by #JoshJ and verified by myself and others,
The solution to the problem is:
Adding #Indexed annotation
to all those columns/fields which need to be used with all finds.
#Data
#RedisHash("EmployeeDetails")
public class RedisEntity {
#Id
private String employeeId;
private String firstName;
private String lastName;
#Indexed
private String gender;
#Indexed
private String grade;
}
We have the Spring Data Redis Library which provides the scope to write the custom method.Attaching Sample code.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>
Entity Definition
#Data
#RedisHash("EmployeeDetails")
public class RedisEntity {
#Id
private String employeeId;
private String firstName;
private String lastName;
private String gender;
private String grade;
}
Repository Definition
#Repository
public interface RedisEntityRepository extends CrudRepository<RedisEntity, String>{
List<RedisEntity> findAllByGenderAndGrade(String gender, String grade);
}
Implementation
#Component
public class RedisEntityImpl implements RedisEntityService {
#Autowired
private RedisEntityRepository redisEntityRepository;
#Override
public List<RedisEntity> getAllByGenderAndGrade(String gender, String grade) {
return redisEntityRepository.findAllByGenderAndGrade(gender,grade);
}
}
Properties
spring.cache.type = redis
spring.redis.host = localhost
spring.redis.port = 6379

Spring Data postgresql 10 insertion does not work

I am working on spring boot application with RestController, Service a Repository and an Entity.
My problem is when I call the web service to save my data in the data base, it seems it works fine and there is no exception thrown but when I check my data base I find that the table was created but I find no data saved. and here is what I get in the output(for each element in my list):
Hibernate:
insert
into
table_name
(columnOne, columnTwo)
values
(?, ?)
Here is my code:
RestController:
#RestController
#RequestMapping(path = "/api/")
public class myController {
#Autowired
private MyService myService;
#PostMapping(path="/inject/{year}")
public void myControllerMethod(#PathParam("year") Year year) {
this.myService.myServiceMethod(year);
}
}
Service:
#Service
public class MyService {
#Autowired
MyRepository myRepository;
public void myServiceMethod(Year year) {
List<MyEntity> myEntityList = this.parseMyEntityList(year);
this.myRepository.save(myEntityList)
}
}
Repository:
#Repository
public interface MyRepository extends CrudRepository<MyEntity, Long>, JpaSpecificationExecutor<InseeLibelle> {
}
Entity:
#Entity
#Table(name = "table_name", indexes = {
#Index(name = "columnOne_idx", columnList = "columnOne"),
#Index(name = "columneTwo_idx", columnList = "columnTwo"),
})
public class MyEntity{
#JsonIgnore
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long columnId;
#Column
private Integer columnOne;
#Column
private String columnTwo;
public Integer getColumnOne() {
return columnOne;
}
public void setColumnOne(Integer columnOne) {
this.columneOne = colmunOne;
}
public String getColumnTwo() {
return columnTwo;
}
public void setColumnTwo(String columnTwo) {
this.columnTwo = columnTwo;
}
}
I tried to add this line in the repository but it does not work too:
<S extends MyEntity> Iterable<S> save(Iterable<S> entities) ;
Perhaps the problem is with the pgAdmin (like my case), it does not show the data but they exist in the database, try findAll method in the repository or check them with select * directly.

Resources