How does Spring JPA derive queries? - java

I am wondering how does Spring JPA derive queries from methods. As an example, if I was to type
interface CarRepo extends CrudRepository<Car, Long>{ findByCarMake(Make make) }
my query would be automatically derived from the method and would be something as "SELECT * from Car WHERE carMake = xxxxx"
I do understand this concepts but I would like to understand how it works behind the scenes. So, how does it actually derive a query from the method name?
I am aiming at creating a similar thing to suit our needs for a NestJs project so in Typescript not Java and also for an..."unorthodox" database which does not have such support out of the box( Neo4J).
I ll be very grateful to whom can and will help me.

Spring Data JPA derives the queries from the method names in your repository.
There are certain keywords that are reserved by Spring. One the one hand, there are query subject keywords like findBy, existsBy, countBy, etc. which influence the return type of the method. On the other hand, there are operators like and, or, isIn, between, etc. that are applied to the actual query logic.
You start your query with a query subject keyword like findBy and then the fields of your entity (and optionally operators and more fields). You can even have nested fields like findByProviderName where your entity has a field provider which has a field name. If you define an invalid property or property path (e.g. findByProviderNamw), your Spring Boot application would fail on startup. You can find more about defining query methods in the official spring reference.

Spring Data using part tree JPA queries, than map them into SQL query by pre-defined parts.

Related

How query method of Spring Data JPA generate query?

When a method is created under a specific rule of Spring Data JPA, a method that calls the corresponding query is created.
For example,
public interface CustomerJpaRepository implements JpaRepository<Customer, Long>{
public List<Customer> findByName(String name);
}
findByName() generate the query similar to one below.
select * from Customer where name = name;
I am curious about this principle. To be precise, I'm curious about the code that parses this method and turns it into a query.
I looked at the code of the SimpleJpaRepository class that implements JpaRepository, but could not find a clue. (Of course, there is a possibility that I did not find it).
In summary, when a method consisting of specific words is declared in JpaRepository, I am curious about the code that actually executes this method internally. More specifically, I'd like to see the code that makes this method works.
If there is no code to do this internally (I personally doubt it's possible...), I want to know how it is implemented in detail, if there is a link or material that explains the principle or internal process, please share related references.
The parsing logic for creating queries from spring-data repository method names is currently mainly declared in the package org.springframework.data.repository.query.parser.
Basically, a repository method name string is parsed into a PartTree, which contains Parts representing defined abstract query criteria.
The PartTree can then be used to create a more specific query object, e.g. with a JpaQueryCreator, or a RedisQueryCreator, depending on the type of repository.
I recommend you to check this Query Creation spring doc
It explains the rules of how the method convert into a query.

Is it possible to create a QueryDSL Predicate with a String of raw SQL?

I have a Java, GraphQL, Hibernate, PostgreSQL, QueryDSL application that queries a very large PostgreSQL table with over 275 columns.
I've created a GraphQL schema with the 25 most popular columns as query-able fields. I'd like to add a generic "field" input type that consists of a name (the db column name + "_" + operation (like gte, gt, contains, etc.) and a value (the value the user is searching for).
So when the user (in GraphiQL) enters something like (field:{name:"age_gt", value:"50"}) as a search input to the GraphQL query, I can come up with: "age > 50".
All that works fine, but when it's time to create the Predicate and add it to the whole query ( booleanBuilder.and(new Predicate) ), I cannot figure out how to create a Predicate that just contains a raw String of SQL ("age > 50").
I've created several Predicates the "right" way using my entity POJO tied to Hibernate and the jpa generated "Q" object. But I need the ability to add one or more Predicates that are just a String of SQL. I'm not even sure if the ability exists, the documentation for QueryDSL Predicates is non-existent.
I'm thinking PredicateOperation() might be the answer, but again, no documentation and I cannot find any examples online.
My apologies for not posting code, all my stuff is behind a firewall on a different network so there's no cut and paste to my internet machine.
In Hibernate its possible to inject arbitrary SQL using custom functions or the FUNCTION-function (introduced in JPA 2.1). In QueryDSL its possible to inject arbitrary JPQL/HQL through TemplateExpressions. Combined you get:
Expressions.numberTemplate("FUNCTION('SUM', {0}), x)
However, age > 50 as expression is probably valid JPQL as well, so one can just write:
Expressions.numberTemplate("SUM(age)")
Either way, its probably best to create a visitor that traverses the GraphQL query and creates the proper expression in QueryDSL, as TemplateExpressions are prone to SQL injection.

Java find and compare members by string (maybe JPA feature?)

I've got alot of beans with attributes, which are derived from database tables with JPA. The users shall be able to enter any column name and a value as a string, and the app shall automatically find the correct member in the one of the beans.
I must use JPA, otherwise I would use some JDBC meta data to put all columns and values into a normal map. Is something like this possible with JPA? It only has to work from database to beans, I don't want to persist changes.
If this doesn't work, can I somehow analyze member names programmatically at runtime?
The EntityManagerFactory has a getMetamodel() method, which returns its MetaModel. From this MetaModel, you may ask for the MetaModel of every entity class, and discover all its attributes, their types, etc.
In case the JPA part doesn't work, you can access class members (fields, methods) of your class as follows:
Field[] fs = YouClass.class.getDeclaredFields();
Details for accessing different members are on this link

How to select property from an entity in a hibernate relation

I have an entity class set up in Java, with a many-to-many relationship to another class. However, rather than selecting the entire entity collection, I'd like to select only a property from the child entities. The reason for doing this is that it will lower the amount of data being loaded into the system as I don't always need the entire entity depending on my view.
This is what I have so far:
#Entity
public class Disposition {
...
#ManyToMany
private List<Project> projects;
...
}
This works fine and retrieves a list of Project instances. However, I don't want to get all the Projects for the Disposition; I only want to retrieve Project.name.
The only solution I've been able to come up with so far is using the #Formula annotation but I'd like to avoid this if possible since it requires writing native SQL instead of HQL.
This view is read-only so I don't expect any changes to the data to be persisted.
you can use hql to only get the child's name. It would look something like
"select p.name from Project p where p.parent_id = ?"
you would have to tailor the variable names in that, and use a parameterized query to replace the ? with the id of the parent.
It is common to have tailored DAO methods for exactly this sort of situation.
This is where object relational mapping cannot help you anymore. But you can use the Query API which allows to query arbitrary objects by HQL, not SQL. Isn't #Formula using HQL, too?
It is not Hibernate, but the ebean project could interrest you. Ebean is an ORM project using the JPA annotations and allowing the lazy (partial) loading of objects.
In your example, getting only project names would result in this code:
List<Project> projects = Ebean.find(Project.class)
.select("name") // Only name properties are loaded
.where().eq("disposition", yourDisposition)
.findList();
Then, if you try to get project owner (or every other property), theses properties will be lazy loaded by Ebean.
Check out org.hibernate.criterion.Projections. Given a Criteria you can simply do the following:
criteria.setProjection(Projections.property("name"));

How to create case-insensitive field on Java POJO. Looking for a good design.

So I'm using GigaSpaces XAP with Hibernate plugin to load my DB table into the their grid cache, unfortunately for querying the grid they do not support directly case insensitive searches.
They offered 2 solutions:
1- Use LIKE query (same as SQL) which is slow (Wont even bother with this)
2- Create a separate property of the fields I want to have case insensitive.
So now my choices are:
1- Create an extra column in DB to have case-insensitive field (Not in this life time)
2- Create a custom data loader plugin for XAP such that the moment the data is loaded the field is stored in a "ToLower" properperty. (Supported but will leave as last resort)
I'm definitely not going with #1 and I will leave the custom data loader as a last result. So my idea is to...
class Person {
String firstName
String firstNameLower
public void setFirstName(String firstName) {
this.firstName = firstName
this.firstNameLower = firstName.toLowerCase(...);
}
}
Will this work with Hibernate? As the GigaSpace dataloader uses Hibernate and my pojo is pretty much an Entity class. I'm using Hibernate XML mapping not annotations. The "ToLower" fields will not be mapped. Does Hibernate call setXXX() on all fields that are mapped or does it do some fancy code replacement under the hood and doesn't call the setXXX() methods.
I'm also thinking an annotation would be good use here but not sure how to implement them or even if it's possible for this case.
hibernate does use proxy objects, but the data is still stored in your object and hibernate will use the getter/setter implementations you provide, so your solution should work if you want to set the lowercase version, although I'm not clear why you need a separate instance variable to store the lowercase version.
this article goes over the basics of proxies.
For anyone interested.
Simply create a "tolower()" method of the field and add the index annotation on that and you can map your pojo to hibernate the usual way.
If you configure Hibernate to use the fields accessor method (and not directly access the fields) its default behavior is to use all the setXXX() method.
I think in your case the best solution would be (if Hibernate is only used for GigaSpace) to customise the way Hibernate load the data, by defining a custom type using standrd StringType to load the data then converting it to lower case...
Meanwhile, if your system requires lower case data, can't you make sure all strings entered into the database are lower case ?

Categories