Spring JPA repository how to write a query - spring

I have a User class, that is identified by id, and Skills class, that has its own id field, and also references User.
public class User {
#Id
#GeneratedValue
private int id;
#JsonIgnore
#OneToOne(mappedBy = "user")
private SoftSkills softSkills;
}
the other one has
#Entity
public class SoftSkills {
#Id
#GeneratedValue
private int id;
#OneToOne
#JoinColumn
private User user;
}
Is there a simple way to write a query, implementing the JPARepository, that would search the SoftSkills class by using user.id field as a parameter and return a SoftSkills object as a result?

You can, from the documentation:
Property expressions can refer only to a direct property of the managed entity, as shown in the preceding example. At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties. Assume a Person has an Address with a ZipCode. In that case a method name of
List<Person> findByAddressZipCode(ZipCode zipCode);
creates the property traversal x.address.zipCode. The resolution algorithm starts with interpreting the entire part (AddressZipCode) as the property and checks the domain class for a property with that name (uncapitalized). If the algorithm succeeds it uses that property. If not, the algorithm splits up the source at the camel case parts from the right side into a head and a tail and tries to find the corresponding property, in our example, AddressZip and Code. If the algorithm finds a property with that head it takes the tail and continue building the tree down from there, splitting the tail up in the way just described. If the first split does not match, the algorithm move the split point to the left (Address, ZipCode) and continues.
So this will do the trick:
SoftSkills findByUserId(int id);
Reference; Spring Data JPA Documentation

Related

Cyclic dependency with JPA/Hibernate and Jackson [duplicate]

This question already has answers here:
Infinite Recursion with Jackson JSON and Hibernate JPA issue
(29 answers)
Closed 3 months ago.
I have a Spring Boot application using JPA/Hibernate in its persistence layer. The application has read-only access to a database and basically has three entities Article, Category, and Field, which have the following relationships.
Article (*) -> (1) Category (*) <-> (1) Field
That is, an Article has a Category, and a Category always belongs to a single Field, however, multiple Category instances can belong to the same Field.
The application provides two REST endpoints, which give a single Article and a single Field by their IDs, respectively. Of course, this cannot work when using Jackson for serialization due to the cyclic dependency Category <-> Field.
What I want is when I retrieve an Article, it should give me its Category including the category's Field, but not all the other Category instances that belong to the this same Field. On the other hand, when I retrieve a Field, it should give me the Field including all Category instances that belong to this Field.
How can I achieve this?
Edit:
I basically have a similar question as Jackson infinite loops many-to-one one-to-many relation
You can use interface-based projections, to only retrieve needed properties, since Spring Data allows modeling dedicated return types, to more selectively retrieve partial views of the managed aggregates.
Let's assume the entities are declared as shown below. For simplicity, only the id attribute is defined alongside association-mapping attributes.
#Entity
public class Article {
#Id
private Long id;
#ManyToOne
private Category category;
}
#Entity
public class Category {
#Id
private Long id;
#OneToMany
private Set<Article> articles;
#ManyToOne
private Field field;
}
#Entity
public class Field {
#Id
private Long id;
#OneToMany
private Set<Category> categories;
}
For the first endpoint where the Article is fetched by id, the projections should be declared as follows:
public interface ArticleDto {
Long getId();
CategoryDto1 getCategory();
interface CategoryDto1 {
Long getId();
FieldDto1 getField();
}
interface FieldDto1 {
Long getId();
}
}
The important bit here is that the properties defined here exactly
match properties in the aggregate root.
Then, the additional query method should be defined in ArticleRepository:
interface ArticleRepository extends JpaRepository<Article, Long> {
Optional<ArticleDto> findDtoById(Long id);
}
The query execution engine creates proxy instances of that interface
at runtime for each element returned and forwards calls to the exposed
methods to the target object.
Declare additional projections to retrieve properties needed for the second case:
public interface FieldDto2 {
Long getId();
Set<CategoryDto2> getCategories();
interface CategoryDto2 {
Long getId();
}
}
Lastly, define the following query method in FieldRepository:
interface FieldRepository extends JpaRepository<Field, Long> {
Optional<FieldDto2> findDtoById(Long id);
}
With this approach, the infinite recursion exception would never appear, as long as projections don't contain attributes causing recursion.

Same entity for two different aggregate

My schema will be something similar to the above picture.
I am planning to use Spring data JDBC and found that
If multiple aggregates reference the same entity, that entity can’t be part of those aggregates referencing it since it only can be part of exactly one aggregate.
Following are my questions:
How to create two different aggregates for the above without changing the DB design?
How to retrieve the Order / Vendor list alone? i.e. I don't want to traverse through the aggregate root.
How to create two different aggregates for the above without changing the DB design?
I think you simply have three Aggregates here: Order, Vendor and ProductType. A mental test that I always use is:
If A has a reference to B and I delete an A, should I automatically and without exception delete all Bs referenced by that A? If so B is part of the A Aggregate.
This doesn't seem to be true for any of the relationships in your diagram, so let's go with separate Aggregates for each entity.
This in turn makes each reference in the diagram one between different Aggregates.
As described in "Spring Data JDBC, References, and Aggregates" these must be modelled as ids in your Java code, not as Java references.
class Order {
#Id
Long orderid;
String name;
String description;
Instance created;
Long productTypeId;
}
class Vendor {
#Id
Long vid;
String name;
String description;
Instance created;
Long productTypeId;
}
class ProductType {
#Id
Long pid;
String name;
String description;
Instance created;
}
Since they are separate Aggregates each gets it's own Repository.
interface Orders extends CrudRepository<Order, Long>{
}
interface Vendors extends CrudRepository<Vendor, Long>{}
interface ProductTypes extends CrudRepository<ProductType, Long>{}
At this point I think we fulfilled your requirements. You might have to add some #Column and #Table annotations to get the exact names you want or provide a NamingStrategy.
You probably also want some kind of caching for the product types since I'd expect they see lots of reads with only few writes.
And of course you can add additional methods to the repositories, for example:
interface Orders extends CrudRepository<Order, Long>{
List<Orders> findByProductTypeId(Long productTypeId);
}

Spring data JPA self join on an entity. How do I specify the depth of recursion?

Using Spring Boot JPA, I am doing a self join on a table of "Person" with attributes id, name and parent_id. parent_id is a foreign key referencing Person.id. So, a Person will have zero or one parent. A sample of my domain class is below.
#Entity(name="person")
public class Person {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column(name="name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parent_person_id")
private Person parent;
// constructors, getters, setters, etc
}
This actually works just fine; when I query with CrudRepository.findById() for example, I get a Person object with an embedded Person object (parent), which may have another embedded Person object (grandparent), etc until I get to a Person without a parent.
My question is, how may I retrieve only a Person and their immediate parent without recursing any further (no grandparents, great-grandparents, etc)?
I imagine I could simply avoid the join, and make parent_id a plain #Column, then in the service layer do an additional query to find the parent, but I'm wondering if there is some Jpa magic that could make it easier than that.
Actually, this was not as hard as it seemed. Converting my Person entity to a dto provided me with the opportunity to simply STOP at the parent, and not recurse through the whole tree!

Replacing entire contents of spring-data Page, while maintaining paging info

Using spring-data-jpa and working on getting data out of table where there are about a dozen columns which are used in queries to find particular rows, and then a payload column of clob type which contains the actual data that is marshalled into java objects to be returned.
Entity object very roughly would be something like
#Entity
#Table(name = "Person")
public class Person {
#Column(name="PERSON_ID", length=45) #Id private String personId;
#Column(name="NAME", length=45) private String name;
#Column(name="ADDRESS", length=45) private String address;
#Column(name="PAYLOAD") #Lob private String payload;
//Bunch of other stuff
}
(Whether this approach is sensible or not is a topic for a different discussion)
The clob column causes performance to suffer on large queries ...
In an attempt to improve things a bit, I've created a separate entity object ... sans payload ...
#Entity
#Table(name = "Person")
public class NotQuiteAWholePerson {
#Column(name="PERSON_ID", length=45) #Id private String personId;
#Column(name="NAME", length=45) private String name;
#Column(name="ADDRESS", length=45) private String address;
//Bunch of other stuff
}
This gets me a page of NotQuiteAPerson ... I then query for the page of full person objects via the personIds.
The hope is that in not using the payload in the original query, which could filtering data over a good bit of the backing table, I only concern myself with the payload when I'm retrieving the current page of objects to be viewed ... a much smaller chunk.
So I'm at the point where I want to map the contents of the original returned Page of NotQuiteAWholePerson to my List of Person, while keeping all the Paging info intact, the map method however only takes a Converter which will iterate over the NotQuiteAWholePerson objects ... which doesn't quite fit what I'm trying to do.
Is there a sensible way to achieve this ?
Additional clarification for #itsallas as to why existing map() will not suffice..
PageImpl::map has
#Override
public <S> Page<S> map(Converter<? super T, ? extends S> converter) {
return new PageImpl<S>(getConvertedContent(converter), pageable, total);
}
Chunk::getConvertedContent has
protected <S> List<S> getConvertedContent(Converter<? super T, ? extends S> converter) {
Assert.notNull(converter, "Converter must not be null!");
List<S> result = new ArrayList<S>(content.size());
for (T element : this) {
result.add(converter.convert(element));
}
return result;
}
So the original List of contents is iterated through ... and a supplied convert method applied, to build a new list of contents to be inserted into the existing Pageable.
However I cannot convert a NotQuiteAWholePerson to a Person individually, as I cannot simply construct the payload... well I could, if I called out to the DB for each Person by Id in the convert... but calling out individually is not ideal from a performance perspective ...
After getting my Page of NotQuiteAWholePerson I am querying for the entire List of Person ... by Id ... in one call ... and now I am looking for a way to substitute the entire content list ... not interively, as the existing map() does, but in a simple replacement.
This particular use case would also assist where the payload, which is json, is more appropriately persisted in a NoSql datastore like Mongo ... as opposed to the sql datastore clob ...
Hope that clarifies it a bit better.
You can avoid the problem entirely with Spring Data JPA features.
The most sensible way would be to use Spring Data JPA projections, which have good extensive documentation.
For example, you would first need to ensure lazy fetching for your attribute, which you can achieve with an annotation on the attribute itself.
i.e. :
#Basic(fetch = FetchType.LAZY) #Column(name="PAYLOAD") #Lob private String payload;
or through Fetch/Load Graphs, which are neatly supported at repository-level.
You need to define this one way or another, because, as taken verbatim from the docs :
The query execution engine creates proxy instances of that interface at runtime for each element returned and forwards calls to the exposed methods to the target object.
You can then define a projection like so :
interface NotQuiteAWholePerson {
String getPersonId();
String getName();
String getAddress();
//Bunch of other stuff
}
And add a query method to your repository :
interface PersonRepository extends Repository<Person, String> {
Page<NotQuiteAWholePerson> findAll(Pageable pageable);
// or its dynamic equivalent
<T> Page<T> findAll(Pageable pageable, Class<T>);
}
Given the same pageable, a page of projections would refer back to the same entities in the same session.
If you cannot use projections for whatever reason (namely if you're using JPA < 2.1 or a version of Spring Data JPA before projections), you could define an explicit JPQL query with the columns and relationships you want, or keep the 2-entity setup. You could then map Persons and NotQuiteAWholePersons to a PersonDTO class, either manually or (preferably) using your object mapping framework of choice.
NB. : There are a variety of ways to use and setup lazy/eager relations. This covers more in detail.

Multiple Relationship classes with the same type

Using spring-data-neo4j, I want to create two classes using #RelationshipEntity(type="OWNS") to link a Person class to both a Pet and Car.
#RelationshipEntity(type="OWNS")
public class OwnsCar {
#Indexed
private String name;
#StartNode
private Person person;
#EndNode
private Car car;
}
#RelationshipEntity(type="OWNS")
public class OwnsPet {
#Indexed
private String name;
#EndNode
private Person person;
#StartNode
private Pet pet;
}
This saves to the Graph Database properly with no problems, as I can query the actual Node and Relationship and see they type, etc.
But when I attempt to use #RelatedTo(type="OWNS", elementClass=Pet.class) I either get a class cast exception, or when using lazy-initialization I get incorrect results.
#NodeEntity
public class Person {
#Indexed
private String name;
#RelatedTo(type="OWNS", direction=Direction.OUTGOING, elementClass=Pet.class)
private Set<Pet> pets;
#RelatedTo(type="OWNS", direction=Direction.OUTGOING, elementClass=Car.class)
private Set<Car> cars;
}
The result I get when I attempt to print our my person(my toString() has been omitted, but it simply calls the toString() for each field) is this:
Person [nodeId=1, name=Nick, pets=[Car [nodeId=3, name=Thunderbird]], cars=[Car [nodeId=3, name=Thunderbird]]]
Does anyone know if this can be done, should be done, is just a bug or a feature that is missing?
It seems like the problem is, that the annotation causes springDataNeo4j to priorize the relationship name. I tried the same on another sample I created. If both annotations contain
type="OWNS" it mixes both 'objects'. When I omit this information, and only use direction and type, it works for me.
Unfortunately this will lead to a problem if you are using another #RelatedTo annotation with more Pets or Cars related with another annotation. As it would not differ between "OWNS" and any other relation to a Pet-Type, the set returns all related pets (example: peter ->(HATES-Relationsip)->dogs).
If it's a bug or not, I can't tell... But for the database: There are only nodes and relations. Both are not typed, so neo4j does not know anything about your 'Pet'- or 'Car'-Class. Spring data neo4j handles this, either by indexing all nodes per type and setting a type-attribute, or using a specific graph-layout (with subreferences). Even if you would want to fetch all pets of a person with a traversal description, you would have so much more code to write, since the outgoing relations with name 'OWNS' contains two types of objects.
I would recommend using two different names. It's easier to write your custom traversals/queries later on, and its probably even faster as well, because no class-type comparison will be needed. Is there any reason, why you would need these specific names?
PS: It is possible, that not everything is 100% accurate. I don't know springdataneo4j in detail, but that's what I figured out so far.

Resources