How to handle null value in One to many relation by using Criteria Builder.
I am using criteria builder from my API to get the result.
The problem which I am facing is When I am passing the any text which is available in student_group table I am getting the result. When I am giving null so my lastRollResult is null in student table I am getting result but when I am passing combination of both text and null I am not getting result.
My APIs are like this
http://localhost:8080/student?query=grade:5,lastRollResult%40SUCCESS%23FAILURE
In this case I am able to get the data
http://localhost:8080/student?query=grade:5,lastRollResult%null
In this case I am not able to get the data
Now here is what I am trying and how my code looks like.
My controller .
Controller class :
@RequestMapping(method = RequestMethod.GET)
public Page<StudentDTO> index(MySpecificationsBuilder builder,
@PageableDefault(value = 25, page = 0) Pageable pageable) {
Specification<Student> spec = builder.build();
Page<Student> stu = studentService.findAll(spec, pageable);
/** here some modification on retuen */
}
You can notice in Controller I am building MySpecificationsBuilder where all the criteria is getting set. Here is brief about MySpecificationsBuilder
public class MySpecificationsBuilder extends BaseSpecification<Student> {
public MySpecificationsBuilder(final SearchCriteria criteria) {
super(criteria);
}
@Override
protected Expression<String> getPath(SearchCriteria criteria, Root<Student> root) {
/** some other conditions */
if (criteria.getKey().equals("lastRollResult")) {
if (!"null".contains(criteria.getValue())) {
Join results = root.join("lastRoll", JoinType.INNER); // In case of text (JoinType.INNER is enum of Inner.
return results.get("result");
}else{
return root.get("lastRoll”); // in case of null
}
}
return root.get(criteria.getKey());
}
/** below toPredicate methods */
}
In MySpecificationsBuilder I am doing some other calculation as resultConstant and toPredicate the resiults.
Here as you can see in case of text I am doing inner join but in case of null I am directly getting the data. So That is the reason I am getting this response. How to fix this values.
Here is my tables details.
@Entity
@Data
@DiscriminatorFormula("case when entity_type is null then ‘Student’ else entity_type end")
@DiscriminatorValue("Student")
public class Student extends AbstractStudent {
@ManyToOne
@Fetch(value = FetchMode.SELECT)
@JoinColumn(name = "created_by_id", insertable = false, updatable = false)
@EqualsAndHashCode.Exclude
@ToString.Exclude
@NotFound(action = NotFoundAction.IGNORE)
User createdBy;
@Transient
private String name;
@Transient
private List<String> tags;
}
And AbstractStudent
@RequiresAudit
@Log4j2
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Data
@Entity
@Table(name = "student_group")
@DiscriminatorColumn(name = "entity_type", discriminatorType = DiscriminatorType.STRING)
public class AbstractStudent extends BaseModel {
@OneToMany(mappedBy = "student", fetch = FetchType.LAZY)
@EqualsAndHashCode.Exclude
@ToString.Exclude
List<StudentMapping> studentMappings;
@OneToMany(mappedBy = "student", fetch = FetchType.LAZY)
@EqualsAndHashCode.Exclude
@ToString.Exclude
List<StudentDataMapping> studentDataMapping;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@AuditColumn
@Column(name = "last_roll_id")
private Long lastRollId;
@OneToMany(mappedBy = "student", fetch = FetchType.LAZY)
@EqualsAndHashCode.student
@ToString.Exclude
private Set<TagUse> tagUses;
@ManyToOne
@Fetch(value = FetchMode.SELECT)
@JoinColumn(name = "last_roll_id", referencedColumnName = "id", insertable = false, updatable = false)
@EqualsAndHashCode.Exclude
@ToString.Exclude
@NotFound(action = NotFoundAction.IGNORE)
private Result lastRoll;
/** some other column defination */
}
In My Abstract column you can I have one to one mapping.
Is there any solution like group or any where clause or any other thing by which I can club these two parameter to get result for null as well as text.
It's not clear how do you want to handle the relationship in the query results. If you want to return students data regardless of the existence of the related entity, I think you should try with left join:
Maybe you want to achieve something like this:
EDIT:
If this solution is working for you, it means that you are implementing a search for the entity
Student, based in certain criteria that may or may not include the related entityResult. To achieve that you can apply aLEFTjoin to the criteria dynamically, based on the existing criteria inputs.You could return always the join results, and still, it will work just fine, and you can apply all the predicates for both entities:
However, the generated SQL query will be less performant in some cases. If you handle join inclusion only when it's required you will get better queries and better performance, however, you will need to apply the predicates related to
Resultentity dynamically too: