Convert Spring's Order to JPA Order from Pageable - jpa

Our frontend is designed to send Pageable object with Spring's Order which contains attribute name.
Sample Entity:
#Entity
#Table
public class Foo {
private String userName;
#Embedded
private Bar bar;
}
#Embeddable
public class Bar {
private String value;
}
We receive pageable with order in repository and parse JPA's Order from that pageable object like this:
pageable.getSort().get().forEach(springOrder ->
jpaOrders.add(getBuilder().asc(root.get(springOrder .getProperty())))
);
We need do it this way because we are building queries via criteria api.
When we receive in repository attribute of Foo, everything is fine, e.g. "userName". But when we receive attribute of embeddable entity, e.g. "bar.value" we got exception
Unable to locate Attribute with the the given name [bar.value] on this ManagedType
Can you tell me how to create JPA's Order object from attribute which define attribute of embeddable entities? Thank you in advice

For these property paths you will have to create joins and apply the Order using the proper join alias and the last part of the property path.
For more details, I'd recommend looking into the code of Spring Data JPA.
Find all references to the Order class and start reading some code.

Have you tried using org.springframework.data.jpa.repository.query.QueryUtils#toOrders ?

Related

Simple Tagging Implementation with Spring Data JPA/Rest

I am trying to come up with a way of implementing tags for my entity that works well for me and need some help in the process. Let me write down some requirements I have in mind:
Firstly, I would like tags to show in entities as a list of strings like this:
{
"tags": ["foo", "bar"]
}
Secondly, I need to be able to retrieve a set of available tags across all entities so that users can easily choose from existing tags.
The 2nd requirement could be achieved by creating a Tag entity with the value of the Tag as the #Id. But that would make the tags property in my entity a relation that requires an extra GET operation to fetch. I could work with a getter method that resolves all the Tags and returns only a list of strings, but I see two disadvantages in that: 1. The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case. 2. The process of creating an entity requires to create all the Tags via a /tags endpoint first. That seem rather complicated for such a simple thing.
Also, I think I read somewhere that you shouldn't create a repository for an entity that isn't standalone. Would I create a Tag and only a Tag at any point in time? Nope.
I could store the tags as an #ElementCollection in my entity. In this case I don't know how to fulfill the 2nd requirement, though.
#ElementCollection
private Set<String> tags;
I made a simple test via EntityManager but it looks like I cannot query things that are not an #Entity in a result set.
#RestController
#RequestMapping("/tagList")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class TagListController implements RepresentationModelProcessor<RepositoryLinksResource> {
#PersistenceContext
private final #NonNull EntityManager entityManager;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<EntityModel<TagList>> get() {
System.out.println(entityManager.createQuery("SELECT t.tags FROM Training t").getFirstResult());
EntityModel<TagList> model = EntityModel.of(new TagList(Set.of("foo", "bar")));
model.add(linkTo(methodOn(TagListController.class).get()).withSelfRel());
return ResponseEntity.ok(model);
}
}
org.hibernate.QueryException: not an entity
Does anyone know a smart way?
The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case
This is precisely the issue with using entities as REST resource representations. They work fine until it turns out the internal representation (entity) does not match the external representation (the missing DTO).
However, it would probably make most sense performance-wise to simply use an #ElementCollection like you mentioned, because you then don't have the double join with a join table for the many-to-many association (you could also use a one-to-many association where the parent entity and the tag value are both part of the #Id to avoid a join table, but I'm not sure it's convenient to work with. Probably better to just put a UNIQUE(parent_id, TAG) constraint on the collection table, if you need it). Regarding the not an entity error, you would need to use a native query. Assuming that you have #ElementCollection #CollectionTable(name = "TAGS") #Column(name = "TAG") on tags, then SELECT DISTINCT(TAG) FROM TAGS should do the job.
(as a side note, the DISTINCT part of the query will surely introduce some performance penalty, but I would assume the result of that query is a good candidate for caching)

How are fields set on an entity by Spring Data MongoDB?

I have a MongoRepository class
public interface UserRepository extends MongoRepository<User, Long> {
User findById(Long id);
}
and my Entity pojo looks like this
#Document(collection = "user")
class User {
Long id;
String name;
Department department;
…
}
When I call the findBy method, a User object is returned. I want to know how does Spring Data MongoDB converts DBObject to Java object. I was under the impression that Spring Data MongoDB uses some sort of mapper (Jackson?) under the hood which would call setters/constructors method of the java(Entity) class based on the field names in the class or #Field Annotation. But to my surprise, the setters are never invoked. Only the default constructor is invoked.
Then how does the fields are set? The reason I am asking is if the setters are called, it would give me an option to set some other fields may be.
Thanks
Spring Data defaults to field access as accessor methods can contain additional logic that we don't want to trigger by accident. If that's what you actually want though, you can switch to property access by annotating your class with #AccessType(Type.PROPERTY).
Spring has a entity converter at the subsequent layer below it. It uses reflection to read the type of field, variables and signature. The conversion logic is generic for all data repositories. You can read about the same here
You can also introduce a custom converter be it yours or jackson, an example of it is here
Take a look at MappingMongoConverter class - it has the logic which does all this.

Spring JPA findBy accent

I am developing a spring-data-jpa application.
I 've ridden the repository with findBy but does not work when I look content with an accent. Does anyone know why?
I am using the following:
Page<Dades> findByNomcomercialIgnoreCaseContaining (#Param ("nomcom") String nomcom, Pageable pageable);
The database is Oracle.
Thanks for your interest.
It's because ignoreCase does not deal with accents. It only compares both String after uppercasing them. See the documentation for more informations.
I don't know any simple solution to ignore accents with Spring Jpa. You coud either :
Remove this parameter in your query, and filter the result in Java afterward (comparing Strings after doing org.apache.commons.lang3.StringUtils.stripAccents).
Use Spring Jpa Specifications. Again, it provides only functionslike upper, like, ... so you have to write your own, depending on what database you are using.
#Param is not required. if you have any query i.e if you are using #Query then we need use #param to pass the specific value releated to query.Below you can check how to use #Query and #Param
#Query("SELECT t.title FROM Todo t where t.id = :id")
String findTitleById(#Param("id") Long id);
if you are not added these annotation use in your repository layer
#Transactional
#Repository
Check another thing i.e
#Transactional
#Repository
public interface IXyzRepository extends CrudRepository<ABC,Integer>{
//some methods you have
}
whether the primary id of class ABC is of type Integer

Is it possible to move named queries of an entity to another class

I have an entity named Client, And it has some named queries and native named queries. What I want to do is, I want to move this named queries to another class. For that I would like to extend the Client entity by another class ClientQuery. And move all named,native queries to that class. Is it possible to do so?
Client CLASS
#XmlRootElement(name = "CLIENT_DETAILS")
#XmlAccessorType(XmlAccessType.FIELD)
#Entity
#NamedQueries({
#NamedQuery(name = Client.GET_CLIENT_BYLANGID,
query = "select T from Client T where T.clientPK.langId=:langId")
})
public class Client implements Serializable {
public static final String GET_CLIENT_BYLANGID = "Client.getClientByLangId";
As I understand, you want to know whether it correct to move the #NamedQuery out of the Entityclass to a non-entity class.
I have quickly checked the specification and did not see any restrictions about that. Additionally I have tried to put in an mapping.xml an <named-query> element outside of the <entity> element and it is xml-valid, so it is legal.
If you move the #NamedQuery in a class that is not an entity that won´t work as hibernate only scan the classes that are entities. You will find something like NamedQuery not found, also it is a good practice to have them in the entity that has more reference to them.

Spring Data JPA finder for dynamic fields as Map

My requirement is to have few custom fields in the domain objects. These fields may vary as per the clients.
We are using Spring Data JPA to execute finders. Spring data implicitly provides finders for static fields of the domain and can also handle finders for the fields in the object graph.
I want to know if there is a way to find data on the custom fields? Can someone suggest me a strategy to achieve the same. Below is the sample of my domain class.
public class Employee{
private String name;
private String age;
private Map customeFields; (May vary as per client)
}
I was thinking of overriding QueryLookupStrategy and create my CustomJpaQuery on lines of PartTreeJpaQuery to achieve it. Is there any better approach? Does spring data jpa provides an easy mechanism to override query creation mechanism?
If you are using hibernate (not sure about other JPA implementations) you may add methods with #Query annotations like this:
#Query("select e from Employee as e where e.customeFields[:key] = :value")
List<Employee> findSomeHow(#Param("key") String key, #Param("value") String value)