I try to create query setAll , because a entity with top isValid and I want all value to false before I use saveAll for udpade my database. I use this methods synchronize my database with a batch 6000-7000 lines actually.
import org.springframework.data.repository.CrudRepository;
public interface DomRepository extends CrudRepository<Dom, String> {
public Domaine findDomByName(String dom);
public List<Dom> findAll();
public void setIsValidAll(boolean isValid);
}
import javax.persistence.*;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.annotation.CreatedDate;
#Entity
#DynamicUpdate
public class Dom{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
// more attribute
private boolean isValid;
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property 'setIsValidAll' found for type 'Dom'!
It's not entirely clear what you want to achieve based on the description you have provided but if I get this right, you would like to create a repository when that will effectively update the value of the isValid field for all Dom objects. If that is indeed the case then you could simply create a custom repository method that will accept your flag and will make use of a simple HQL or JPQL to update all records in the database. For example something like this could work for this case:
#Modifying
#Query("UPDATE Dom SET isValid = :valid")
void updateValidAll(boolean valid);
If on the other hand you want to perform the update in memory on the fetched entities and then perform a saveAll you could do something like this:
#Transactional
public void updateAllRecords2(boolean isValid) {
var records = repository.findAll()
.stream()
.peek(dom -> dom.setValid(isValid))
.toList();
repository.saveAll(records);
}
Note that the latter is not very optimal as it uses peek which should not be used on operations that have side-effects (i.e mutations). On top of that will will perform an update query for every single entity that is present, meaning N queries for N entities.
Related
I'm using Spring boot 2.7.0
And have the next entities in simple:
#Getter
#Setter
#Entity
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Version
private Long version;
private String name;
}
#Getter
#Setter
#Entity
public class Event {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
Account account;
private String message;
}
and jpa repositories:
#Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
}
#Repository
public interface EventRepository extends JpaRepository<Event, Long> {
Page<Event> findAllByAccount(Account account, Pageable pageable);
}
In short I call
eventRepository.findAllByAccount(accountRepository.findById(1), PageRequest.of(1,10));
Problem is every call of last code increases the version field of Account by 1. So question is why? I don't call any update or save method.
And additionally the result of this behaviour is calling of method needs
#Transactional(readonly=false)
Otherwise if I write readonly=true that throws cannot execute UPDATE in a read-only transaction
ADDED:
full code of usage:
#Transactional
public Page<Event> events(Long accountId, int page) {
return eventRepository.findByAccount(findById(accountId), PageRequest.of(page, PAGE_SIZE));
}
#GetMapping("/events")
public List<EventResponse> listEvents(#RequestParam(value = "max", defaultValue = "0") int page) {
return eventService.events(1L, page).stream().map(EventResponse::of).toList();
}
It looks like hibernate is deriving lockMode type as either of WRITE or OPTIMISTIC_FORCE_INCREMENT or PESSIMISTIC_FORCE_INCREMENT based on isolation level of your database. As per reference hibernate decides this pessimistic locking by its own based on database you use.
As per doc, if lockmode type is either of what I mentioned above, Version will get automatically incremented even if you haven't changed anything i.e. even if you haven't do any update or save.
Please check database isolation level & based on that you might get an idea about this.
Edit: as you explicitly setting lockmode as write so my answer validates that because of WRITE mode, your version got incremented automatically.
The problem should be related in the code which is using the result of the find.
If you're modifying entities under a transaction they're going to be modified at the end of the method, when Spring in this case is going to close the transaction. In this part when transaction ends, the JPA provider (for example hibernate) aligns the relative entity record into the database with the 'java entity object' by an update.
I'm sorry. After trim all my code to the posted and debug I found my mistake:
In the begin I was retrieving Account in another method by .lock(Long) method instead of .findById(Long)
lock method is below:
#Lock(LockModeType.WRITE)
#Query("from Account where id = :id")
public Optional<Account> lock(Long id);
I'm experimenting with Spring Data REST and so far it's going relatively well. I'm able to query and manipulate the entities, and I have reached a point where I'd like to filter the retrieved data by a variable number of parameters. For this purpose I've been reading and decided on QueryDSL which is integrated nicely with Spring, and it works (almost) flawlessly when using fields from the entities.
However, my filtering form contains some parameters which have no direct mapping to the entity, leading to this question. For the sake of brevity, I'll be using an over-simplified example, hence my using of a persons's age instead of birth-date & etc.
Supposing we have the following Person entity:
#Data
#NoArgsConstructor
#Entity
public class Person {
#Id
#GeneratedValue
private UUID id;
private String name;
private String lastName;
private Integer age;
}
... and the appropriate repo
#RepositoryRestResource
public interface PersonRepository extends CrudRepository<Person, UUID>, QuerydslPredicateExecutor<Person>, QuerydslBinderCustomizer<QPerson> {
#RestResource
Page<Person> findAll(#QuerydslPredicate Predicate predicate, Pageable pageable);
#Override
default void customize(QuerydslBindings bindings, QPerson person) {
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
}
}
... one can access and filter persons by name or last name (case insensitive) via http://<server>/persons?name=whatever, so far so good.
Next step, I would like to see only the people that are "pensionable", let's say over 65 years old, so the URL would look like http://<server>/persons?pensionable=true. However, pensionable is not an attribute in the Person entity, so adding it as a request param doesn't do anything.
I've been trying to figure out how this can be achieved or if this is currently a limitation of the framework(s), but my searches haven't been successful so far. Eventually via trial and error, I've come up with something that seems to work but feels more like a hack:
Create a different PersonExtendedFilter bean (not entity) which includes the extra/arbitrary params:
#Data
#NoArgsConstructor
public class PersonExtendedFilter{
private Boolean pensionable;
}
... create a BooleanPath using the above, and use it to define a binding inside the repo's customize method:
#Override
default void customize(QuerydslBindings bindings, QPerson person) {
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
BooleanPath pensionable = new PathBuilder<>(PersonExtendedFilter.class, "personExtendedFilter").getBoolean("pensionable");
bindings.bind(pensionable).first((path, value) -> new BooleanBuilder().and(value ? person.age.gt(65) : person.age.loe(65)));
}
Bottom line, I'm wondering whether there is an elegant way of doing this or if I missing something, be it from a logical POV, a RTFM one, or something else.
I am in a situation where I have to store data belonging to multiple entities in a single collection. But when I query then back, I dont want unwanted records in my result. How can we achieve this using spring? Below is what I have done so far.
1. I give same collection name in entity as shown below.
#Document(collection = "livingThings")
#Data
public class AnimalEntity {
//contains id, type, bla, bla
}
#Document(collection = "livingThings")
#Data
public class HumanEntity {
//contains id, gender, address
}
2. I create independent mongoRepository interfaces
public interface AnimalRepository implements MongoRepository<AnimalEntity, String> {
}
public interface HumanRepository implements MongoRepository<HumanEntity, String> {
}
3. And the problem is
when I do animalRepo.findAll or humanRepo.findAll, I get all records available in the collection.
4. What I expect
animalRepo.findAll returns only those records where document structure is same as AnimalEntity.
Thank you very much for your time and patience to attend this query.
MongoDB automatically adds _class field to entities in a collection. Even though it is not the best solution, you can try this:
#Query("_class:your package name here.AnimalEntity")
public AnimalEntity findAllAnimals();
So I have looked at various tutorials about JPA with Spring Data and this has been done different on many occasions and I am no quite sure what the correct approach is.
Assume there is the follwing entity:
package stackoverflowTest.dao;
import javax.persistence.*;
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
public Customer(String name) {
this.name = name;
}
public Customer() {
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
We also have a DTO which is retrieved in the service layer and then handed to the controller/client side.
package stackoverflowTest.dto;
public class CustomerDto {
private long id;
private String name;
public CustomerDto(long id, String name) {
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
So now assume the Customer wants to change his name in the webui - then there will be some controller action, where there will be the updated DTO with the old ID and the new name.
Now I have to save this updated DTO to the database.
Unluckily currently there is no way to update an existing customer (except than deleting the entry in the DB and creating a new Cusomter with a new auto-generated id)
However as this is not feasible (especially considering such an entity could have hundreds of relations potentially) - so there come 2 straight forward solutions to my mind:
make a setter for the id in the Customer class - and thus allow setting of the id and then save the Customer object via the corresponding repository.
or
add the id field to the constructor and whenever you want to update a customer you always create a new object with the old id, but the new values for the other fields (in this case only the name)
So my question is wether there is a general rule how to do this?
Any maybe what the drawbacks of the 2 methods I explained are?
Even better then #Tanjim Rahman answer you can using Spring Data JPA use the method T getOne(ID id)
Customer customerToUpdate = customerRepository.getOne(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);
Is's better because getOne(ID id) gets you only a reference (proxy) object and does not fetch it from the DB. On this reference you can set what you want and on save() it will do just an SQL UPDATE statement like you expect it. In comparsion when you call find() like in #Tanjim Rahmans answer spring data JPA will do an SQL SELECT to physically fetch the entity from the DB, which you dont need, when you are just updating.
In Spring Data you simply define an update query if you have the ID
#Repository
public interface CustomerRepository extends JpaRepository<Customer , Long> {
#Query("update Customer c set c.name = :name WHERE c.id = :customerId")
void setCustomerName(#Param("customerId") Long id, #Param("name") String name);
}
Some solutions claim to use Spring data and do JPA oldschool (even in a manner with lost updates) instead.
Simple JPA update..
Customer customer = em.find(id, Customer.class); //Consider em as JPA EntityManager
customer.setName(customerDto.getName);
em.merge(customer);
This is more an object initialzation question more than a jpa question, both methods work and you can have both of them at the same time , usually if the data member value is ready before the instantiation you use the constructor parameters, if this value could be updated after the instantiation you should have a setter.
If you need to work with DTOs rather than entities directly then you should retrieve the existing Customer instance and map the updated fields from the DTO to that.
Customer entity = //load from DB
//map fields from DTO to entity
So now assume the Customer wants to change his name in the webui -
then there will be some controller action, where there will be the
updated DTO with the old ID and the new name.
Normally, you have the following workflow:
User requests his data from server and obtains them in UI;
User corrects his data and sends it back to server with already present ID;
On server you obtain DTO with updated data by user, find it in DB by ID (otherwise throw exception) and transform DTO -> Entity with all given data, foreign keys, etc...
Then you just merge it, or if using Spring Data invoke save(), which in turn will merge it (see this thread);
P.S. This operation will inevitably issue 2 queries: select and update. Again, 2 queries, even if you wanna update a single field. However, if you utilize Hibernate's proprietary #DynamicUpdate annotation on top of entity class, it will help you not to include into update statement all the fields, but only those that actually changed.
P.S. If you do not wanna pay for first select statement and prefer to use Spring Data's #Modifying query, be prepared to lose L2C cache region related to modifiable entity; even worse situation with native update queries (see this thread) and also of course be prepared to write those queries manually, test them and support them in the future.
I have encountered this issue!
Luckily, I determine 2 ways and understand some things but the rest is not clear.
Hope someone discuss or support if you know.
Use RepositoryExtendJPA.save(entity). Example:
List<Person> person = this.PersonRepository.findById(0)
person.setName("Neo");
This.PersonReository.save(person);
this block code updated new name for record which has id = 0;
Use #Transactional from javax or spring framework. Let put #Transactional upon your class or specified function, both are ok. I read at somewhere that this annotation do a "commit" action at the end your function flow. So, every things you modified at entity would be updated to database.
There is a method in JpaRepository
getOne
It is deprecated at the moment in favor of
getById
So correct approach would be
Customer customerToUpdate = customerRepository.getById(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);
I have the following
#Entity
public class Restaurant{
#ManyToOne
private City c;
// more
}
#Entity
public class City{
private String name;
// more
}
I also have a repository
public interface RestaurantRepository extends JPARepository<Restaurant, Long> {
// something to put here
}
===EDIT====
I have the list of ALL cities, but only some of them are associated to restaurants.
I need to write a method in such respository to extract all cities that are referred by a Restaurant.
In SQL I would just do the following:
SELECT CITY.id, CITY.name FROM CITY WHERE ID NOT IN (SELECT DISTINCT(city_id) FROM RESTAURANT)
Is there a way to obtain the same result using the name conventions as of http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html ?
Thanks.
As I do not see any support for the IS [NOT] EMPTY operator listed in Spring Data's documentation around query creation strategy, you can try manually defining a JPQL query:
public interface CityRepository extends JPARepository<City, Long> {
#Query(value="SELECT c FROM CITY c WHERE c.restaurants IS EMPTY")
List<City> findCitiesWithNoRestaurants();
}
which I'm guessing will translate to the native SQL you gave as an example.
Otherwise looks like you would need to find a list of cities with Restraunts and then pass that to a method which used the NotIn pattern. This will obviously be less performant than the above.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation