Spring JPA Specification API with custom Query and custom response Object. Is this possible? - spring-data-jpa

I have researched this for a few days but can't seem to find the right information.
Here is what I need, I have a Database, with multiple tables, I need to join a few tables together to make a sort of "search" API. I have to implement the ability to dynamically search fields (from various tables in the query), sortable, with pagination.
I have found that I cannot combine the #Query annotation with Specification API, and I looked into using the specification API to do the joins I needed but, the problem is the root must be one table/repository.
For example:
If I have a users table that has to join on addresses, phone_numbers, and preferences
the base repository will be UserResposiory and it will return the User entity model, but I need it to return a custom DTO
AccountUserDTO which contains fields from the User, Address, PhoneNumber, and Preference entities.
Would anyone know if this is possible at all??
I am at wits end here and I really want to build this the correct way.
Cheers!

You may do this way:
Build hql query as an string, depend on how the filter condition is requested, you can build the corresponding query, eg:
if (hasParam(searchName)) {
queryString = queryString + " myEntity.name = :queryName"
}
Query query = session.createQuery(queryString);
and the parameter providing
if (hasParam(searchName)) {
query.setParameter("queryName", searchName);
}
...
and execute it.
To create a customized object, the easiest way is treating the object as an array of field:
Query query = session.createQuery("select m.f1, m.f2, m.f3 from myTable m");
List managers = query.list();
Object[] manager = (Object[]) managers.get(0); //first row
System.out.println(manager[0]) //f1
System.out.println(manager[1]) //f2
System.out.println(manager[2]) //f3
There is also some other solution to select, such as
String query = "select new mypackage.myclass(m.f1, m.f2, m.f3) from myTable m";
-> And when execute the above query, it will return a list of object.
Or to be simpler, make your own view in db and map it to one entity.

Related

Trying to Write JPQL For Complex Postgres Query

I'm working on an Admin UI for an authorization server. One of the features is to display a list of who has logged in, and we are doing that by querying a database table where the currently issued refresh tokens are stored. A user can log in from multiple devices to the same application, generating multiple tokens. At the moment the requirements are NOT to break down this view by device, instead if a user has logged in at all, they are to be shown in the list. If we revoke access (one of the other requirements from this UI), then all devices will have their refresh tokens revoked.
Anyway, the main thing tripping me up is the query. I'm writing the query to pull back all the tokens for the specified user, but for each client only the most recent one is retrieved. ie, if there are 5 tokens for a given user/client combination, only the one with the most recent timestamp will be returned to the UI. I'm trying to do this entirely with JPQL in my SpringBoot/Hibernate backend, which is communicating with a Postgres database.
I can write this in SQL several different ways. Here are two forms of the query that return the same results:
select r1.*
from dev.refresh_tokens r1
join (
select r2.client_id, max(r2.timestamp) as timestamp
from dev.refresh_tokens r2
group by r2.client_id
) r3 on r1.client_id = r3.client_id and r1.timestamp = r3.timestamp
where r1.user_id = 1;
select r1.*
from dev.refresh_tokens r1
where r1.user_id = 1
and (r1.client_id, r1.timestamp) in (
select r2.client_id, max(r2.timestamp) as timestamp
from dev.refresh_tokens r2
group by r2.client_id
);
The reason I've figured out multiple ways to do the query is because I'm trying to also figure out how to translate it into JPQL. I avoid doing native queries in Hibernate as much as possible, instead relying on the DB-agnostic JPQL syntax. However, I just can't figure out how to translate this to JPQL.
I know native queries and/or putting filter logic into my Java code are both options. However, I'm hoping this is possible with a standard JPQL query.
You can use this:
select r1
from RefreshToken r1
where r1.user.id = 1
and r1.timestamp = (select max(r2.timestamp) from RefreshToken r2 where r2.user.id = r1.user.id);
Depending on your exact use case, I think this Blaze-Persistence Entity Views could come in handy here.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
public interface UserDto {
#IdMapping
Long getId();
String getName();
#Limit(limit = "1", order = "timestamp DESC")
#Mapping("tokens")
RefreshTokenDto getLatestToken();
#EntityView(RefreshToken.class)
interface RefreshTokenDto {
#IdMapping
Long getId();
String getToken();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
It will use a lateral join query behind the scenes which is the most efficient on PostgreSQL:
select u1.id, u1.name, r1.id, r1.token
from dev.user u1
left join lateral (
select *
from dev.refresh_tokens r
where r.user_id = u1.id
order by r.timestamp desc
limit 1
) r1
where r1.user_id = 1

Entity Framework query based on string stored in configuration file

i would like to know if you have any idea how i can achieve this, considering a query stored as string in the configuration file.
I tried to use SqlQuery applied to the DBSet, but the problem is that SqlQuery requires me to select all properties of the required entities in my query. If i don't consider any column, it will complain because is not able to map the query to the entities.
I don't want to select all properties of the entities i want to query.
Thanks
If you are using EF then why not use Database.ExecuteSqlCommand()? It's in the System.Data.Entity namespace.
For example:
int result = db.Database.ExecuteSqlCommand("Non SELECT SQL etc...");
Well, I ended up implementing a mechanism using reflection that basically receives a group of fields to select, and constructs dynamic objects with those fields, so when applied the query with the joins between the entities, will only bring the fields I am looking for.
So, considering Entity1, Entity2, Entity3 with the following relationship
<b>Entity1</b>{
<br/> Entity1Name, <br/> List<*Entity2*> Entity2Items, <br/> etc..
<br/>}
and
<b>Entity2</b> { <br/> Entity2Name, <br/> List<*Entity3*> Entity3Items <br/>}
I can store e.g. the following query in the configuration file, and retrieve the information:
"Entity1.Entity1Name", <br/>
"Entity1.Entity2Items.Entity2Name", <br/>
"Entity1.Entity2Items.Entity3Items.Entity3Name"
Anyway, I was just trying to see if there would be any solution out-of-the-box that would require minimal code changes.
Thank you.

Attempting to use EF/Linq to Entities for dynamic querying and CRUD operations

(as advised re-posting this question here... originally posted in msdn forum)
I am striving to write a "generic" routine for some simple CRUD operations using EF/Linq to Entities. I'm working in ASP.NET (C# or VB).
I have looked at:
Getting a reference to a dynamically selected table with "GetObjectByKey" (But I don't want anything from cache. I want data from database. Seems like not what this function is intended for).
CRM Dynamic Entities (here you can pass a tablename string to query) looked like the approach I am looking for but I don't get the idea that this CRM effort is necessarily staying current (?) and/or has much assurance for the future??
I looked at various ways of drilling thru Namespaces/Objects to get to where I could pass a TableName parameter into the oft used query syntax var query = (from c in context.C_Contacts select c); (for example) where somehow I could swap out the "C_Contacts" TEntity depending on which table I want to work with. But not finding a way to do this ??
Slightly over-simplyfing, I just want to be able to pass a tablename parameter and in some cases some associated fieldnames and values (perhaps in a generic object?) to my routine and then let that routine dynamically plug into LINQ to Entity data context/model and do some standard "select all" operations for parameter table or do a delete to parameter table based on a generic record id. I'm trying to avoid calling the various different automatically generated L2E methods based on tablename etc...instead just trying to drill into the data context and ultimately the L2E query syntax for dynamically passed table/field names.
Has anyone found any successful/efficient approaches for doing this? Any ideas, links, examples?
The DbContext object has a generic Set() method. This will give you
from c in context.Set<Contact>() select c
Here's method when starting from a string:
public void Test()
{
dynamic entity = null;
Type type = Type.GetType("Contract");
entity = Activator.CreateInstance(type);
ProcessType(entity);
}
public void ProcessType<TEntity>(TEntity instance)
where TEntity : class
{
var result =
from item in this.Set<TEntity>()
select item;
//do stuff with the result
//passing back to the caller can get more complicated
//but passing it on will be fine ...
}

Using of bulk upate via Query.executeUpdate() vs. object's set methods for multiple entities

Sometime, in he case when we have a list of entity IDs, I have to update a set of fields of a collection of related of passed IDs entities and I am wandering in which cases is better to use the standard way via 1-st: loading of all entities and 2-nd: calling related set() methods :
List<Long> ids = ....;
String par = "example";
List<User> users = USER_DAO.getUsers(ids);
for(User user : users) {
user.setField(par);
}
or the other way is via executing of a bulk update operations like :
Query query = em.createQuery("UPDATE User user SET user.field =:1? WHERE user.id IN : 2?");
int rowCount = query.executeUpdate();
Do you have some knowledge and/or investigations of that issue ?
Or the cases, when first way is better than the second way or vs. ?
All recommendations are welcome,
Thanks,
SImeon
The second way will perform better as there will be less sql's executed on the database. In your first case two sql's will be executed for each user(one for the select and one for the update). In your first case you will also take a performance hit while hibernate maps the sql to the object.

Doubt regarding JPA namedquery

I am trying to execute a namedquery
#NamedQuery(name="getEmployeeDetails",query="select e.username,e.email,e.image,e.firstname,e.lastname from Employee e where e.empid=?1")
Now when I execute this query in a EJB 3.0 Session Bean what is the object I should return.I tried returning Listits returning a Vector which creates a classcast exception.The employee table contains fields like password and other confidential details which I don't want to fetch.So I am not using select e from Employee e.
I am learning JPA can anyone help.
Below is the sample query which fetches only the required fields, but have to make such constructor for it.
Query : SELECT NEW package_name.Employee(e.username,e.email,e.image,e.firstname,e.lastname) FROM Employee e where e.empid=?1;
It will return Employee entity with selected fields & remaining will have default values.
Inspect the returned type by calling .getClass() on a returned object. I'd guess it's an array.
But this is not really a good way to use JPA. Select the whole entity and then just don't use what you don't need. It's not such a performance hit.