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

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.

Related

JPA Specification Select returns all columns instead of specific

I am using JPA Specification , need to select specific columns only.
This is the code:
Specification<Item> spec = (root, query, builder) -> {
query.select(root.get("Id"));
Predicate predicate = builder.equal(root.get("Id"), "12345");
return predicate;
};
In log I see that all columns from Item Entity are selected from database.
Is it a bug?
 usage:
interface:
public interface Repo extends PagingAndSortingRepository<Item,String>, JpaSpecificationExecutor<Item> {
}
call:
repo.findAll(spec );
JpaSpecificationExecutor is specifically defined to return the entity type. I suspect it is ignoring the .select(root.get("Id")).
Normally you would use Specifications if you have an extremely dynamic set of conditions you are querying by. If you have just a few parameters you need to search by, I would use a derived query, or a named query.
public interface Repo extends PagingAndSortingRepository<Item,String>, JpaSpecificationExecutor<Item> {
#Query("Select i.id from Item i where name=:name")
Long getIdforName(String name);
}

Spring Data JPA Select Distinct dynamic columns based on list of string input

I want to select columns based on my input (List < String >)
#Entity
#Table
public class Product {
#Id
private Long id;
private String name;
private String price;
}
I understand that to select distinct specific columns, I can use #Query("SELECT DISTINCT name FROM TABLE")
However, I want to give users the flexibility to select the columns they want. e.g. List < String > columns = Arrays.asList(["name", "price"]). This will then select distinct from both name and price columns.
For this create a custom method implementation and use one of the following (or of the many variants thereof)
construct a SQL or JPQL query using String concatenation (be careful not to introduce SQL injection vulnerabilities).
use some kind of criteria API like
JPA Criteria API
Querydsl
JOOQ

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

Can Spring JPA projections have Collections?

I have a Customer entity from which I only want to select a few fields and their associated CustomerAddresses. I've defined a Spring Data JPA projection interface as follows:
public interface CustomerWithAddresses {
Integer getId();
String getFirstName();
String getLastName();
String getBrandCode();
String getCustomerNumber();
Set<CustomerAddress> getCustomerAddresses();
}
But from my Repository method:
CustomerWithAddresses findCustomerWithAddressesById(#Param("id") Integer id);
I keep getting NonUniqueResultException for Customers with multiple CustomerAddresses. Do projections have to have a flat structure, i.e. they don't support Collections the same way true Entities do?
you have Set<CustomerAddress> getCustomerAddresses(); it's X-to-Many relation. When spring data do select for CustomerWithAddresses it does join , in result set N-records (N - amount of CustomerAddress for CustomerWithAddresses with id = id). You can check if it you change CustomerWithAddresses to List of CustomerWithAddresses .
List<CustomerWithAddresses> findCustomerWithAddressesById(#Param("id") Integer id);
when you use entity sping data gropu multiply result into one element , gouped it by id as id it's unique identifier.
you can do :
1) add into CustomerWithAddresses interface
#Value("#{target.id}")
Integer getId();
and use your query
2) use #Query
#Query("select adr from CustomerWithAddressesEntity adr where adr.id=:id")
CustomerWithAddresses findCustomerWithAddressesById(#Param("id") Integer id);

How to create association one to many and many to one between two entities in gorm?

I'm new in Grails. I have a problem with generation association many to one and one to many between two tables. I'm using postgresql database.
Employee.groovy
class Employee {
String firstName
String lastName
int hoursLimit
Contact contact
Account account
Unit unit
char isBoss
static hasMany = [positionTypes:PositionType, employeePlans: EmployeePlan]
}
EmployeePlan.groovy
class EmployeePlan {
AcademicYear academicYear
HourType hourType
int hours
float weightOfSubject
Employee employee
static belongsTo = [SubjectPlan]
}
I'd like to have access from employee to list of employeePlans and access from EmployeePlan to Employee instance. Unfortunately GORM generates only two tables Employee and EmployeePlan with employee_id. I don't have third table which should have two columns employee_id and employee_plan_id. Could you help me ?
I think your setup is correct, as you write from Employee class you can access to a collection of EmployeePlan (take care, that if you don't explicitly define EmployeePlan like a List, it will be a Set by default) and from EmployeePlan you can access Employee.
If you need List, you can define it like that:
class Employee {
String firstName
String lastName
int hoursLimit
Contact contact
Account account
Unit unit
char isBoss
//explicitly define List
List<EmployeePlan> employeePlans
static hasMany = [positionTypes:PositionType, employeePlans: EmployeePlan]
}
But back to your question. You'd like to have join table between Employee and employeePlan, but why? Its not necessary, since you have bidirectional mapping with sets (unordered), grails will not create a join table. Can you explain why do you need it? In grails the references will be auto-populated, so I don't see any issue here.
If need to preserve order of employeePlans, then define it as List, shown above, and grails will create a join table with corresponding indexes.
have you read the ref-doc? it gives you the answer immediately:
class Person {
String firstName
static hasMany = [addresses: Address]
static mapping = {
table 'people'
firstName column: 'First_Name'
addresses joinTable: [name: 'Person_Addresses',
key: 'Person_Id',
column: 'Address_Id']
}
}