How to update many item in mongodb via MongoRepository? - spring

I got many items in mongodb with the structure like this
#Document
#Data
public class Config{
#Id
private String id;
private Date createDate;
private Date updateDate;
private Boolean active;
private String memo;
}
Where the field active indicates whether the current config is active.
When any item needs to be actived (setting field active to true), the value active hold by the rest items should be false. And the code I suppose shoud be like this:
List<Config> configs = configRepo.findAll();
configs.forEach(e->e.setActive(e.getId().equals("TARGET_CONFIG_ID")));
configRepo.saveAll(configs);
The code above is slow as redundant IO operation exists. We can execute the code like this if the database were Mysql:
#Query("UPDATE CONFIG SET ACTIVE = ID == ?0")
Is any sub-class or method in MongoRepository can operate similar and update all items within one IO?

Related

Not able to search data in redis cache using spring crud repository by passing list of values for a property of the model saved in cache

We have model class saved in Redis as mentioned below:-
#Data
#NoArgsConstructor
#AllArgsConstructor
#RedisHash("book")
public class Book implements Serializable {
private static final long serialVersionUID = 2208852329346517265L;
#Id
private Integer bookID;
#Indexed
private String title;
#Indexed
private String authors;
private String averageRating;
private String isbn;
private String languageCode;
private String ratingsCount;
private BigDecimal price;
}
We have title and authors as our indexed property.
Now we wanted to search all the records from Redis by passing title and a list of authors using the spring crud repository as mentioned below.
public interface BookSpringRepository extends CrudRepository<Book, String> {
List<Book> findAllByTitleAndAuthors(String title, List<String> authors);
}
Service layer:-
#Override
public Optional<List<Book>> searchBooksByTitleAndAuthorNames(String title, List<String>
autherNames) {
return Optional.ofNullable(bookSpringRepository.findAllByTitleAndAuthors(title,
autherNames));
}
Here we are getting below exception
Unable to fetch data from Spring data Redis cache using List of Integer or
String.
Getting error while fetching - "Resolved
[org.springframework.core.convert.ConversionFailedException: Failed to convert from type
[java.lang.String] to type [byte] for value 'Ronak';
nested exception is java.lang.NumberFormatException: For input string: "Ronak"]."
We would not want to convert the list of string/integer to byte as it is a time-consuming process and as we tried took so much amount of time. Also when the results are retrieved we will again have to convert back to normal integer or string values.
The other option is to loop through the list and pass a single value at a time to the Redis crud repository and this time Redis crud repository is happy but that will be a loop call to Redis and network latency.
We cannot add ID attributes on authors' property as these can be duplicate records.
Does the spring crud repository support the LIKE query in search that way we can create a unique id having these authors' names and make put ID annotation on that new derived property to search the records using spring crud repository using LIKE or contains kind of query.
Any suggestions here are highly appreciated!!
Try to add serialization to your redis key and value. This might help :
https://medium.com/#betul5634/redis-serialization-with-spring-redis-data-lettuce-codec-1a1d2bc73d26

Advantage of assigning the returned savedEntity in Spring Data

I see in most of the coders save data(using spring data) as:
savedEntity = repo.save(savedEntity);
Long id = savedEntity.getId();
I am confused about why most of them assign back the returned value to the saved Entity while the following code also works exact(I have tested myself):
repo.save(savedEntity);
Long id = savedEntity.getId();
Did I miss some benefit of assigning back?
for example, let the entity be:
#Entity
public class SavedEntity {
#Id
private int id;
private String name;
//getter, setters, all arg-constructor, and no-arg constructor
}
Consider the object of SavedEntity is
SavedEntity entity = new SavedEntity(1,"abcd");
now for your first question,
SavedUser entity1 = repo.save(entity);
Long id = entity1.getId();
this entity1 object is the return object getting from the database, which means the above entity is saved in the database succesfully.
for the Second Question,
repo.save(entity);
Long id = entity.getId();//which you got it from SavedEntity entity = new SavedEntity(1,"abcd");
here the value of id is the integer you mentioned in place of id(the raw value).
Most of the time the id (primary key) is generated automatically while storing the entity to the database using strategies like AUTO, Sequence etc. So as to fetch those id's or autogenerated primary key values we assign back the saved entity.
For example:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
}
In this case you'll not pass the id externally but it will create a value for it automatically while storing the data to DB.

Bulk data to find exists or not : Spring Data JPA

I get an Post request that would give me a List<PersonApi> Objects
class PersonApi {
private String name;
private String age;
private String pincode ;
}
And I have an Entity Object named Person
#Entity
#Table(name = "person_master")
public class Person{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#Column(name = "name")
String name;
#Column(name = "age")
String age;
#Column(name = "pincode ")
String pincode ;
}
My record from Post request would look something like this (pseudocode representation of the data below)
[
"Arun","33","09876gh"
"James","34","8765468"
]
I need to do a bulk-validation using Spring JPA.. Give the List<PersonApi> and get a True or False based on the condition that all the entries in the PersonApi objects list should be there in the database.
How to do this ?
The selected answer is not a right one. (not always right)
You are selecting the whole database to check for existence. Unless your use case is very special, i.e. table is very small, this will kill the performance.
The proper way may start from issuing repository.existsById(id) for each Person, if you never delete the persons, you can even apply some caching on top of it.
exists
Pseudo Code:
List<PersonApi> personsApiList = ...; //from request
List<Person> result = personRepository.findAll();
in your service class you can access your repository to fetch all database entities and check if your list of personapi's is completeley available.
boolean allEntriesExist = result.stream().allMatch(person -> personsApiList.contains(createPersonApiFromPerson(person)));
public PersonApi createPersonApiFromPerson(Person person){
return new PersonApi(person.getName(), person.getAge(), person.getPincode());
}

Spring data mongodb #DBRef list

I am trying to have a list in a model using #DBRef but I can't get it to work.
This is my User model:
#Data
#Document
public class User {
#Id
#JsonSerialize(using = ToStringSerializer.class)
private ObjectId id;
#Indexed(unique = true)
#NotBlank
private String email;
#NotBlank
private String name;
#NotBlank
private String password;
#DBRef
private List<Server> servers;
}
Server model:
#Data
#Document
public class Server {
#Id
#JsonSerialize(using = ToStringSerializer.class)
private ObjectId id;
#NotBlank
private String name;
#NotBlank
private String host;
}
The structure is very simple, every user can have multiple servers. But when I add servers to the user the server is created, but the servers array contains one null entry("servers" : [ null ]). So the server isn't added to the user. This is how I create a server and add it to an user:
#PostMapping
public Mono create(#Valid #RequestBody Server server, Mono<Authentication> authentication) {
return this.serverRepository.save(server).then(authentication.flatMap(value -> {
User user = (User) value.getDetails();
user.getServers().add(server);
return userRepository.save(user);
})).map(value -> server);
}
So I simply create and save a server, add the server the user and then save the user. But it doesn't work. I keep having an array with one null entry.
I've seen this page: http://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb. But it is for saving the child document, not for linking it. Also it is for a single document, not for an array or list.
Why is my list not being saved correctly?
All my libraries are coming from spring boot version 2.0.0.M6.
UPDATE
When removing #DBRef from the user's servers property the servers are getting saved, but they of course get double created, in the server collection and in every user.servers. So the error has something to do with references.
After some googling I found the answer...
https://jira.spring.io/browse/DATAMONGO-1583
https://jira.spring.io/browse/DATAMONGO-1584
Reactive mongo doesn't support this.
Actually there is a way to resolve DbRefs without to using the blocking driver. Yes - the references are resolved in a blocking fashion, but does not require a second connection. In order to achieve this we have to write our own DbRefResolver: NbDbRefResolver.java. In the provided resolver there is a flag: RESOLVE_DB_REFS_BY_ID_ONLY. If is switched on will not going to resolve the DbRefs from the database, but instead will resolve them to fake objects with id only. It is up to implementation to fill the references later in non-blocking fashion.
If the flag RESOLVE_DB_REFS_BY_ID_ONLY is set to false it will eagerly resolve the references by using the non-blocking driver, but will block the execution until the references are resolved.
Here is how to register the DbRefResolver in the app: DbConfig.kt
Files attached are provided here: https://jira.spring.io/browse/DATAMONGO-1584
Me did it like that for roles :
#Unwrapped(onEmpty = Unwrapped.OnEmpty.USE_NULL)
private Collection<Role> roles;
you can check the doc (2021) here : https://spring.io/blog/2021/04/20/what-s-new-in-spring-data-2021-0

How can I include or exclude a record according to a boolean parameter using Spring Data JPA?

I am not so into Spring Data JPA and I have the following doubt about how to implement a simple query.
I have this AccomodationMedia entity class mapping the accomodation_media on my database:
#Entity
#Table(name = "accomodation_media")
public class AccomodationMedia {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "id_accomodation")
private Long idAccomodation;
#Column(name = "is_master")
private boolean isMaster;
#Lob
#Column(name = "media")
private byte[] media;
private String description;
private Date time_stamp;
public AccomodationMedia() {
}
...............................................................
...............................................................
...............................................................
// GETTER AND SETTER METHODS
...............................................................
...............................................................
...............................................................
}
The instance of this class represents the photo associated to an accomodation (an hotel)
So as you can see in the prvious code snippet I have this field :
#Column(name = "id_accomodation")
private Long idAccomodation;
that contains the id of an accomodation (the id of an hotel on my database).
I also have this boolean field that specify if an image is the master image or not:
#Column(name = "is_master")
private boolean isMaster;
So, at this time, in my repository class I have this method that should return all the images associated to a specific hotel:
#Repository
public interface AccomodationMediaDAO extends JpaRepository<AccomodationMedia, Long> {
List<AccomodationMedia> findByIdAccomodation(Long accomodationId);
}
I want to modify this method passing also the boolean parameter that specify if have to be returned also the master image or only the images that are not master.
So I tryied doing in this way:
List<AccomodationMedia> findByIdAccomodationAndIsMaster(Long accomodationId, boolean isMaster);
but this is not correct because setting to true the isMaster parameter it will return only the master image (because it is first selecting all the Accomodation having a specific accomodation ID and then the one that have the isMaster field setted as true).
So, how can I correctly create this query that use the isMaster boolean parameter to include or exclude the AccomodationMedia instance that represent my master image?
I know that I can use also native SQL or HQL to do it but I prefer do it using the "query creation from method names"
I don't have how to test this, but essentially your final query should be:
id_accomodation = ?1 AND (is_master = ?2 OR is_master = false)
So I would try the following method signature:
findByIdAccomodationAndIsMasterOrIsMasterFalse(Long accomodationId, boolean isMaster);
I would go with two methods one for isMaster true, while second for false value like this:
List<AccomodationMedia> findByIdAccomodationAndIsMasterFalse(Long accomodationId);
List<AccomodationMedia> findByIdAccomodationAndIsMasterTrue(Long accomodationId);
Change your acommodation id as accomodationId instead of idAccomodation. When you write findByIdAccomodationAndIsMaster spring confusing findByIdAccomodationAndIsMaster
Try this this convention
#Column(name = "accomodation_id")
private Long accomodationId;

Resources