Let's say I have an entity
public class Person {
private String id;
private String firstname;
private String lastname;
private Set<Car> ownedCars;
}
Is there a way I can use query by example to find any person named James having both a Ferrari and Lamborghini?
If I use:
Person p = new Person();
p.setName("James");
p.getOwnedCars.addCar(new Car("Lamborgnihi"));
p.getOwnedCars.addCar(new Car("Ferrari"));
Example<Person> exampleOfPerson = Example.of(p);
List<Person> foundPersons = personRepository.finaAll(exampleOfPerson);
it seems it queries only on person's attributes and ignores any child collections.
You can use a query method for that. Let's say your Car has a property name that can be "Lamborghini" or "Ferrari"
interface PersonRepository extends JpaRepository<Person, String> {
List<Person> findByOwnedCarsNameIn(Collection<String> names);
}
Then you use it like this:
personRepository.findByOwnedCarsNameIn(Arrays.asList("Ferrari","Lamborghini"));
Some gotchas:
The method parameter can take any subclass of Collection, or an array.
The property names on Person and Car must match the method signature and the parameter name as shown above for spring to know how to generate the query, i.e. Person must have a property called "cars", and Car must have a property called "name".
I used JpaRepository, but this works with any of the Repository interfaces provided with spring data JPA
Related
Is it possible to use JPA derived methods and query by example at the same time?
Let's imagine i have two entities like this:
#Entity
#Data
public class Person {
#Id
#GeneratedValue
Long id
String name;
String surname;
#OneToMany
List<Dog> dogs;
}
#Entity
#Data
public class Dog{
#Id
#GeneratedValue
Long id
String name;
}
I'd like to be able to do something like this (just an example):
Person p = new Person ();
p.setName("Mario");
personRepository.findDistinctByDogsIsNotNull(Example.of(p));
The Example.of(p) only works if i do findAll, but it doesn't work if i define inside the repository a method like this
private interface PersonRepository extends JpaRepository<Person, Long>{
List<Person> findDistinctByDogsIsNotNull(Example<Person> example)
}
The error it gives me is something like this:
Failed to create query for method public abstract java.util.List dev.cele.test.repository.PersonRepository.findDistinctByDogIsNotNull(org.springframework.data.domain.Example)! At least 1 parameter(s) provided but only 0 parameter(s) present in query.
So my question is: is it possible to do a query by example in a JPA derived query method?
And if it's not possible how can i create some sort of parametrizable query that also has a predetermined condition?
My Repository Method Query cannot parse a property name that comes after the OrderBy, but it can if it follows the findBy or findAllBy. The attribute in my entity that is giving me issues is zIndex
Entity Class
#Entity
public class DisplayLayer
{
#Id
#Column(name="DISPLAY_LAYER_ID")
private Long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="DISPLAY_CONTAINER_ID")
private DisplayContainer displayContainer;
#Column(name="NAME")
private String name;
#Column(name="Z_INDEX")
private Long zIndex;
#Column(name="DESCRIPTION")
private String description;
// Getters & Setters
}
Repository Class
public interface DisplayLayerRepository extends BaseRepository<DisplayLayer, Long>
{
// This one method query works just fine
public List<DisplayLayer> findByZIndex(Long pZIndex);
// These two throw the same error:
// Unable to locate Attribute with the the given name [ZIndex]
//
// public List<DisplayLayer> findAllByOrderByZIndexAsc();
// public List<DisplayLayer> findByDisplayContainerIdOrderByZIndexAsc(Long pDisplayLayerId);
}
So in my first method query, when OrderBy is not used, it correctly parses it as [zIndex], but when it follows the OrderBy for some reason it capitalizes the z: [ZIndex].
Is this a known issue, or am I doing something wrong?
EDIT
What makes me think there might be a bug with the parser is that if I update zIndex to IndexZ in my entity and then change my query methods from OrderByZIndex to OrderByIndexZ everything works.
This solution is not ideal though as the database table is Z_INDEX and that has a specific meaning when talking about layering of graphics.
Yes there is a bug but there is a simple workaround, please try :
displayLayerRepository.findAll(Sort.by(Sort.Order.desc("zIndex").ignoreCase()));
I suppose DisplayLayerRepository extends JpaRepository.
It's similar when you use parameters in your findBy method. Define in your repository empty method :
public List<DisplayLayer> findByDisplayContainerId(Long pDisplayLayerId, Sort sort);
Then you can call it by :
displayLayerRepository.findByDisplayContainerId(1L, Sort.by(Sort.Order.desc("zIndex").ignoreCase()));
You can use JpaSort in query. As in your case, it looks something like this:
Sort sort = JpaSort.unsafe(Sort.Direction.ASC, "zIndex");
List<DisplayLayer> zIndexes = displayLayerRepository.findByZIndex(pZIndex, sort);
and your query in Repository will look like this:
public List<DisplayLayer> findByZIndex(Long pZIndex, Sort sort);
I have an Entity "Person" which has following properties,
Id
Name
F-Name
Age
Address
When I call a repository function findAll() on Person, it returns a list of Persons.
List<Person> list = somefuntionToConvertIterableToList(personRepository.findAll());
this list has multiple objects of Person Type.
Person ...... Id1,Name1, F-Name1, Age1, Address1
Person .......IdN,NameN, F-NameN, AgeN, AddressN
I need to remove "Id" from all Persons, what should I do?
I know we can use "remove" to delete an element of list, but how to delete a property with in an element?
I think you need used : #JsonIgnore in Id look like:
#JsonIgnore
Field you want remove in reponse.
Or you can create DTO and put all field you want return look like :
public class PersonDTO {
//all field you want return
}
You can of course set id null or add ignores in serialization but maybe you do not want to load id at all. Usually you then would use DTO or Tuple to decide what fields to populate. So not first to populate all and then remove unneeded (I have not used your Person but just one simplified example class).
Tuple query in your repository would simply be like (JPQL):
#Query("SELECT te.name AS name, te.created as created FROM TestEntity te")
List<Tuple> findAllTuple();
This would need then do extra work to have tuple to correspond original entity when serialized. So preferably with a DTO, like:
// This class would be exactly as your Person but without that id
#AllArgsConstructor // you need the constructor for new in jpql
public class TestEntityDto {
private String name;
private LocalDateTime created;
}
while the query in your repository would be like:
#Query("SELECT NEW org.example.data.entity.dto.TestEntityDto(te.name, te.created) FROM TestEntity te")
List<TestEntityDto> findAllDto();
I have two entities: Book and Category and a repository for both. In the controller, I have set up the methods correctly as such:
#RequestMapping(value="/books", method = RequestMethod.GET)
#CrossOrigin
public #ResponseBody List<Book> bookListRest() {
return (List<Book>) bookRepository.findAll();
}
This obviously shows all books and every field in the entity that isn't #JsonIgnore'd. The problem is, I need to have:
One page with Book data (book name, author name, isbn..) without category
One page with Category data (Category name) without books
One page with Everything (book data along with categories where they belong in)
How can one accomplish this?
I somehow need to in a way ignore #jsonignore on some occasions. Should I make a new entity that extends say, Question and also make a repository for that? Surely that can't be the correct way to do this.
As khalid Ahmed Said you can use costum dtos or you can add Filters to ignore specific fields in Jackson. First, we need to define the filter on the java object:
#JsonFilter("myFilterBook")
public class Book{
...
}
#JsonFilter("myFilterCategory")
public class Category{
...
}
Before you return your ResponseBody you try to use ObjectMapper (Jackson):
The case of one page with Book data (book name, author name, isbn..) without category:
ObjectMapper mapper = new ObjectMapper();
SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter
.serializeAllExcept("category");
FilterProvider filters = new SimpleFilterProvider()
.addFilter("myFilterBook", theFilter);
String dtoAsString = mapper.writer(filters).writeValueAsString(book);
You can do the same think by putting what you want o ignore for the other example.
And for more details to ignore field during marshalling with jackson you can check here
What about using DTOs data transfer objects
you can create multiple DTOs to use them in the response of your API
DTO is a pojo class that customize the returning data from your entity
public class BookWithoutCategoryDTO {
private String name;
private String authorName;
.....
/// and make setters and getters for them
}
public class BookWithCategoryDTO {
private String name;
private String authorName;
private String category;
.....
/// and make setters and getters for them
}
and create your custom mapper to convert from Book to BookDTO
I have the following class!
public class Task{
ObjectId id;
String title;
String description;
/* Getters and Setters removed for brevity */
}
and I have the following mongoRepository class, very simple :
public interface TaskRepository extends MongoRepository<Task, String> {
}
As you can see, I have not yet tried to extend this class - What would I want to do here if I want to have a find method, where I could just hand it a list of Ids, and get my list of corresponding tasks back?
The CrudRepository which MongoRepository extends has a findAll method, which takes an Itereable<ID>
I think that is exactly what you are looking for.
Note that it is renamed to findAllById in the latest Milestone releases.
You can create a custom query method that searches for an array of _id values:
#Query(value = "{ '_id' : {'$in' : ?0 } }", fields = "{ 'description': 0 }")
Iterable<Task> findAllThin(Iterable<String> ids);
(in this case it returns the fields id and title only)
#Neil Lunn brought me to the answer.