Populating Collections using Spring Data Projections - spring-data

I'm struggling trying to figure out how to use nested projects / populating the many side. I have a User entity that has a ManyToMany relationship with Brand. I've created the following Projection interfaces...
interface UserView {
#Value("#{target.id}")
fun getId(): Long
fun getFirstName(): String
fun getLastName(): String
fun getBrands(): MutableSet<BrandView>
}
interface BrandView {
fun getId(): Long
fun getName(): String
fun getDescription(): String
}
In my repository, I have the following #Query
#Query("select u.id, u.firstName, u.lastName, b.id, b.name, b.description from User u left join u.brands b where u.id = :id")
fun findByIdUsingProjectionDto(#Param("id") id: Long): UserView
The SQL that gets generated is correct and will return appropriate data, however, even though the UserView is populated with data, getBrands() returns null. I'm unsure how to get the query to also populate the brands collection.

Related

Spring jpa returning specific column without nativeQuery

I trying to select from database specific column only
public interface ItemRepository extends PagingAndSortingRepository<Item,String> {
#Query("SELECT i.id FROM Item i where i.id =?1")
Optional<Item> getit(String id);
}
But instead of returning Object Item it returns String (the id value).
How Can I return the object? (I can't add any constructors to it)
p.s. if I use nativeQuery it does work.
This query in JQL can be simpler.
#Query("SELECT i FROM Item i where i.id =?1")
Optional<Item> getit(String id);
Or even
#Query("FROM Item where id =?1")
Optional<Item> getit(String id);
However, since you are already using JPA, you don't need to define any method with a #Query annotation.
findById is already defined.
Optional<Item> itemOpt = itemRepository.findById(id);

Is it possible to return custom Java objects combining multiple aggregates in Spring Data JDBC?

I have multiple aggregate classes, such as Request, Scribe, Candidate, and Exam.
Sample schema:
Request (id, scribe_id, candidate_id, exam_id, status)
Scribe (id, name)
Candidate (id, name)
Exam (id, name, schedule)
As you can see, Request table has references to Scribe, Candidate, and Exam tables.
For one of the requirements, I need to return all requests based on a condition by including all the corresponding details of scribe, candidate, and exam.
For this, the query in my repository class will be similar to the following:
SELECT r.id, r.status, c.name, s.name,
e.schedule, e.name
FROM request r
JOIN candidate c ON r.candidate=c.id
JOIN scribe s ON r.scribe=s.id
JOIN exam e ON r.exam=e.id
WHERE <some-condition>
Now, is there a way to map the result of this query directly to a custom Java object and return the same in Spring Data JDBC?
I believe another alternative is to use the Spring JDBC template.
Curious, any out-of-the-box support from Spring Data JDBC?
Thanks.
I am able to return custom Java object by setting rowMapperClass value of org.springframework.data.jdbc.repository.query.Query annotation. For this need to define RowMapper for custom Java object.
Changes look similar to the following:
public class RequestResourceRowMapper implements RowMapper<RequestResource> {
#Override
public RequestResource mapRow(ResultSet resultSet, int rowNumber) throws SQLException { ... }
}
In repository class, need to set rowMapper value.
#Query(value = """
SELECT r.id, r.status, c.name, s.name,
e.schedule, e.name
FROM request r
JOIN candidate c ON r.candidate=c.id
JOIN scribe s ON r.scribe=s.id
JOIN exam e ON r.exam=e.id
WHERE <some-condition>
""",
rowMapperClass = RequestResourceRowMapper.class)
List<RequestResource> searchRequestResources(...);
This could have even been possible without using a custom row mapper as well, but in that case, you will have to assign different names to the columns across the tables. You could have defined a simple class and defined all the fields in there and for mapping the java fields with the corresponding columns in the table, you could have used the #Column attributes example:
public class RequestData {
#Column("id")
private Integer requestId;
#Column("scribe_id")
private String scribeId;
#Column("candidate_id")
private Integer candidateId;
#Column("scribe_name")
private String scribeName;
#Column("candidate_name")
private String candidateName;
#Column("exam_name")
private String examName;
#Column("exam_schedule")
private String examSchedule;
}
However, for such case, you need to have different column names across the schema's which might not be possible in your case as you have same column names in multiple schemas.

How to avoiding AND condition if parameter is null in Spring Data JPA query

I am trying to get the result of one query using Spring Data JPA. Here I am sending some parameter and receiving result according to that.
My repository query is,
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname AND p.pname = :pname")
List<Users> findByUsername(#Param("uname") String uname , #Param("pname") String pname );
And calling from controller like the following,
#RequestMapping(value = "/joinResult", method = RequestMethod.GET)
public List<Users> joinResultShow()
{
return (List<Users>) userRepo.findByUsername("test_user","testRole");
}
Here we can see that if I am passing some value then only checking according to that parameter. Here I need to modify my query like if parameter is null, then not need to use AND condition in query.
How can I modify this query for avoiding AND condition if parameter is null? I am new to Spring Data JPA world.
Here are some possible options for you
1. Create multiple methods in your repository like
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname AND p.pname = :pname")
List<Users> findByusernamewithRole(#Param("uname") String uname , #Param("pname") String pname );
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname")
List<Users> findByUsernameWithoutRole(#Param("uname") String uname);
Write a custom respository and use EntityManager. With this you can create a dynamic queries based on your input using CriteriaBuilder and use this criteria in querying.
Last and the most preferred option in case of dynamic inputs(like you have) is Querydsl.
Some articles about querydsl
http://www.baeldung.com/querydsl-with-jpa-tutorial
http://www.querydsl.com/static/querydsl/latest/reference/html/ch02.html

Get an object list from an HQL query with joint

I have two tables: Customers and Commands, it's a #OneToMany relation from Client to Commands, one client have many Commands.
the table commands contain : idCommand, date, nameCommande, idCustomer.
the table customers contain: idCustomer, nameClient, email.
All the JPA and EJB are set up and I can easily get a list of Commands or Clients using an HQL query in a managed bean and list them in a JSP using this code.
public List<Commande> selectAllCommandes() {
List<Commande> commandes = em.createQuery("select c from Commande c").getResultList();
return commandes;
}
public List<Customer> selectAllCustomers() {
List<Customer> customers = em.createQuery("select cu from Customer cu").getResultList();
return customers;
}
How do I join the two tables with the idCustomer column in a way to show the name of client instead his id? I've used this HQL query
SELECT c.date, c.name Commande, cu.nameClient FROM Commande AS c, Customer AS cu WHERE cu.idCustomer = c.idCustomer
But I have no idea about the List<> type that I need to use to get the result/
If you map the reverse relation in the Commande entity ...
public class Commande {
...
#ManyToOne(mappedBy="commande")
private Client client;
// getter and setter ...
}
(Here, mappedBy is getting the name of the #OneToMany property set up at the
other side of the relationship)
Then after executing your query SELECT c FROM Commande c you would get a list of Commande objects, and for each one of them you could get the name of the client using: thisCommande.getClient().getName().

JPA OneToMany relations and performace

I have two entities: parent Customer and child Order.
Each Customer has 1,000,000 Orders for example, so it is not needed in any given time to load a Customer with all Orders but I want to have this ability to make join query on these two entities in JPA.
So because of this, I must create #OneToMany relationship for making join queries.
My question is: how to get query without making joinColumn because even in Lazy mode it is possible to load 1,000,000 objects!
I just want to get query on these object with where restrictions like native join.
If you don't want the #OneToMany relationship implicitly set in your Customer class than you don't have to. You can execute JPQL queries (in very precise manner) without the marked relationship.
Assume you have:
#Entity
public class Customer {
// all Customer-related fields WITHOUT #OneToMany relationship with Order
}
#Entity
public class Order {
#ManyToOne
private Customer owner;
}
Then if you want to get all Orders for particular Customer you can execute a simple JPQL query like that:
// Customer customer = ...
// EntityManager em = ...
String jpql = "SELECT o FROM Order o WHERE o.owner = :customer";
TypedQuery<Order> query = em.createQuery(jpql, Order.class);
query.setParameter("customer", customer);
List<Order> orders = query.getResultList();
In this way you can execute the code only when you're really sure you want to fetch Customer's orders.
I hope I've understood your problem correctly.
EclipseLink has support for QueryKeys, that allow you to define fields or relationships for querying that are not mapped. Currently there in no annotation support for query keys, but you can define them using the API and a DescriptorCustomizer.
Also you do not need the OneToMany to query on it, just use the inverse ManyToOne to query,
i.e.
Select distinct c from Customer c, Order o where o.customer = c and o.item = :item
Or,
Select distinct o.customer from Order o join o.customer c where o.customer = c and o.item = :item