Does Spring Data JPA support join result - spring-data-jpa

Which means I can customize result Object, and mapping it to the join result besides entity classes property
entityA{
fieldA
fieldB
}
entityB{
fieldA
fieldC
}
Result{
fieldA
fieldB
fieldC
}
#Query(select a.*,b.c from entityA a,entityB b where a.fieldA = b.fieldA)
Result customizeResult = entityARepository.nativeQuery()
How to get Result with 3 fields?

If im understand correctly you want to return custom response not your entity object itself. You can use ResponseBody annotation for your controller and Jackson objectMapper for mapping entity object fields. Assuming you have MyEntity as an Entity object something like below code will work for you. You also need to define MyCustomResponse class and define fields you want to show in response.
#RequestMapping(value = "/", method = RequestMethod.POST)
public #ResponseBody MyCustomResponse createEntity(#RequestBody String requestBody) {
ObjectMapper mapper = new ObjectMapper();
MyCustomResponse response = new MyCustomResponse()
MyEntity model = new MyEntity();
model = mapper.readValue(requestBody, MyEntity.class);
response.setSomething("");
response.setAnotherThing("");
response.setMyEntity(model);
return response;
}

Related

Spring Data Neo4j 6: findAll() operation doesn't map relationships properly

In Spring Data Neo4j 6 (6.0.1), a basic Neo4jTemplate findAll() operation with a simple relationship doesn't seem to map the relationship entity and its target even though they are part of the result set. Is this a bug or am I missing something?
Let's consider the following basic scenario:
var a = new EntityA();
var b = new EntityB();
a.entityB = b;
neo4jTemplate.save(a);
with
#Node
public class EntityA {
#Id #GeneratedValue(UUIDStringGenerator.class)
public String id;
#Relationship("HAS_ENTITY_B")
public EntityB entityB;
}
#Node
public class EntityB {
#Id #GeneratedValue(UUIDStringGenerator.class)
public String id;
}
When trying to map a result like this:
var result = neo4jTemplate.findAll("MATCH (a:EntityA)-[r:HAS_ENTITY_B]->(b:EntityB) RETURN a,r,b", EntityA.class);
Assert.notNull(result.get(0).entityB, "entityB should not be null here!");
I would expect the entityB property not to be null.
That's certainly not the expected behavior, but you should write your query this way:
MATCH (a:EntityA)-[r:HAS_ENTITY_B]->(b:EntityB) RETURN a, COLLECT(r), COLLECT(b)
For now, it looks like there is a lack of documentation and implementation regarding custom queries and relationships in SDN 6 (see this ticket and this one).

Searching by query for attributes of complex objects in Java EE

I have created the object Person, I can deleted and modify it and I can also search for Person by his name or phonenumber... but I don't know for exemple how to search for a person by his ** home address**. Here is my code:
My entity Person.java:
public class Person{
private Long id;
private String name;
#ManyToOne(cascade = CascadeType.ALL)
private Address address;
....
}
My entity Address.java
public class Address{
...
private String streetName;
...
}
And here is the most interesting function that I am trying to modify to get what I want, I would like to search for Persons who live in xxx (streetName = xxx). Here is my function getByQuery:
public List<Person> getByQuery(PersonSearchQuery searchQuery) {
Map<String, String> criteriaQuery = new HashMap<String, String>();
if (searchQuery.getName() != null)
criteriaQuery.put("name",searchQuery.getName());
TypedQuery<Person> query = this.findByQuery(criteriaQuery);
return query.getResultList();
}
The object PersonSearchQuery contains just to attributes name (String) and streetName (String) and their getters.
Function findByQuery:
public TypedQuery<T> findByQuery(Map<String, String> criteriaQuery) {
CriteriaBuilder builder = this.em.getCriteriaBuilder();
CriteriaQuery<T> criteria = builder.createQuery(this.entityClass);
Root<T> root = criteria.from(this.entityClass);
criteria.select(root);
Predicate predicate = builder.conjunction();
if (criteriaQuery.size() != 0) {
for (String key : criteriaQuery.keySet()) {
try{
predicate = builder.and(predicate, builder.equal(root.<String>get(key), criteriaQuery.get(key)));
}catch(IllegalArgumentException e){
continue;
}
}
}
criteria.where(predicate);
return this.em.createQuery(criteria);
}
So I can search for Persons by their names by I cannot search for them by streetName the problem is my function getByQuery I would like to do something like this:
if (searchQuery.getStreetName() != null)
criteriaQuery.put("Address.streetName",searchQuery.getStreetName());
The problem is I don't know how to define the key in this case. Thanks for your help
I only use CriteriaBuilder if I have several similar Entities which needs to be used/rendered in the same way, so if person is the only Entity with an Address reference I would just use JPQL, like this:
entityManager.createQuery(
"select p from Person p where p.address.streetName like :streetName", Person.class)
.setParameter("streetName", "xyz" + "%").getResultList()
The main reason I tend to avoid CriteriaBuilder, is because it has a rather steep learning curve, and you need to write a lot of code to express very simple concepts. In contrast any developer familiar with SQL can read and maintain JPQL code.
These days I always use frameworks, like DeltaSpike Data (for EE) and Spring Data, they both implements most of the basic DAO/Repository features, so If you don't mind an extra dependency (and some magic) it can save you a lot of boilerplate JPA code.

Web API OData with EntityFramework exposing only DTOs

I prefer not to expose my Entity Framework models and my controllers expose DTO style objects.
I have tried to have my controller's Get method's to be
public IQueryable<PersonDTO> Get(ODataQueryOptions<PersonDTO> query )
buy I don't know how to apply ODataQueryOptions to db.Persons so that the filtering happens in the SQL.
I ended up with
public IQueryable<PersonDTO> Get(ODataQueryOptions<PersonEF> query )
{
using (var db = new AppDbContext())
{
var result = (query.ApplyTo(db.Persons) as IQueryable<PersonEF>).Select
(c => new PersonDTO()
{
FirstName = c.FirstName,
LastName = c.LastName,
DateOfBirth = c.DateOfBirth,
Gender = c.Gender
}
);
return result.ToList().AsQueryable();
}
}
In order for this to work I have both classes added to ODataConventionModelBuilder: in my Register method.
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<PersonDTO>("PersonDTO");
builder.EntitySet<PersonEF>("Person");
How can I expose only PersonDTO and apply the ODataQueryOptions filters to EF's PersonEF?

Is there a way to transform objects that spring data repositories return?

Right now I have an entity object and a DTO. The repository returns a list of objects arrays when I do a simple example like: findById(). Is there a way to easily map the return type to be a custom DTO object rather than always return entity objects?
Example is below:
#Query("Select f.id, f.name from Food f where f.id = :id")
public List<Object[]> findById(#Param("id") String id);
My DTO object looks like:
FoodDto{
private String id;
private String name;
}
Right now I've only ever been able to get repositories to return a List< Object[] > type.
Try this.
#Query("Select new package.FoodDto(f.id, f.name) from Food f where f.id = :id")
public List<FoodDto> findById(#Param("id") String id);
Assuming class FoodDto is in package, if not you need to set the full package.
Also I assume the FoodDto have a constructor that match
public FoodDto(int id, String name){
//Variable assignation
}
I never tried in spring-jpa but that works in JPQL so I assume it will work XD

javaee 6 rest api named query result

I have a simple JEE6 rest class that gets the data from db2. I am using Jackson in ApplicationConfig class to convert the entity objects to json. It converts with the field names as the key and the value as the right hand value. So for example:
Class Entity {
String name;
String address;
}
converts to
{name:"hello", address:"world"}
The service is as follows:
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
Now I want to only return the name in json format. So I created a named query as follows in the entity class:
#NamedQuery(name = "justGetName", query = "SELECT a.name FROM Applications a")
And the service changed to
public List<T> findAll() {
return getEntityManager().createNamedQuery("justGetName").getResultList();
}
This returns the following array:
[{"first","second","third"}]
But I want to get back:
[{name:"first",name:"second",name:"third"}]
How do I write the named query so that the class field names are added to the json structure? Thank you.
You querying a list of strings from your database and this is what the service returns.
Their are multiple ways to achieve your goal.
Pure JPA
Using #JsonIgnore to tell Jackson not to serialize an attribute
class Application {
String name;
#JsonIgnore
String address;
}
Create a new Entity class that only contains the attributes you would like to share
class ApplicationName {
String name;
}
Alternatively you could introduce a separate class that only contains the attributes you would like to share and convert the results from the query into this class and return than the list of this converted values.