Can Spring JPA projections have Collections? - spring-data-jpa

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);

Related

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.

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

spring boot JPA returns null object when it finds a null attribute eg date is null

I wanted to load an object from the database that contains more
attributes, one of which is null (e.g : date), using spring boot JPA
that returns a null object because it found the date is null despite
the object exists in the database. Please who can who can tell me how I solve this problem
#Entity #IdClass(Exemple.class) #Table(name= "table") public class
Exemple implements Serializable{
#Id #Column(columnDefinition = "id_activite") private Integer idActivite;
//the Problem is here
#Column(columnDefinition = "date_debut_prevue") #Nullale private Calendar dateDebutPrevue;
//Other attribute and Id
#Id ...
// Getters && setters
}
//Query Repository
#Query("SELECT ar FROM table ar where ar.idActivity = ?1 ")
List<Activitie> findAllPositionByIdActivitie(Integer idActivitie);
Your query is a native query and not a JPQL query.
The query must use the Entity name in the FROM clause not the table name.
#Query("SELECT ar FROM Exemple ar where ar.idActivity = ?1 ")
List<Activitie> findAllPositionByIdActivitie(Integer idActivitie);

How to get db column value from Mybatis for sharding?

I use Mybatis to access db, and some tables is sharding by id with hash algorithm.
I want to write a Mybatis intecepter to change table name automatic, it need to get the sharding column value.
Table Entity:
#Data
#TableName("m_user")
public class User {
#TableId(type = IdType.AUTO)
private Integer id;
private String name;
private Integer age;
}
UserMapper sql:
#Select("select * from m_user where id = #{id2} and name = #{name2};")
List<User> selectByIdAndName(Integer id2, String name2);
I use boundSql.getParameterObject() and boundSql.getParameterMappings() to check, but I can not make sure whether the sharding column id is in sql and then get the value of the sharding column.
ParameterMappings values and ParameterObject values are here:
parameter mapping:ParameterMapping{property='id2', mode=IN, javaType=class java.lang.Object, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}
parameter mapping:ParameterMapping{property='name2', mode=IN, javaType=class java.lang.Object, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}
params:{id2=1, param1=1, name2=name1, param2=name1}
The parameters are Mapper function parameters, but I need sharding column id and value, program can only get id2 or param1.
How to get the db column and value from Mybatis?

Select Map<Integer, Integer> using mybatis

I want to select Map for each record. Result class looks like
public class Mapping {
private String name;
private Map<Integer, Integer>;
}
SQL table has only three columns namely name, id, partner_id.
How can I create Map of Id to Partner Id for each name using mybatis?
you can use sqlSession.selectForMap, and give mybatis the column which will be processed as the key of map , such as name. then it will return
Map<String,Map<String,Object>>
as a result, but the value is Map, key is the column name, you need to transform it .