CriteriaBuilder.or returns no values if entity mapping is empty - spring-data-jpa

I have a spring project with two entities.
public class Parent {
private integer id;
private String name;
private List<Child> children
}
public class Child {
private integer id;
private integer parentId;
private String name;
}
I am attempting to use the Hibernate criteriaBuilder to construct a query that will return all parents who has a name, or has a child with a given name.
criteriaBuilder.or(
criteriaBuilder.like(parent.join("children").get("name"),
"%" + query.getValue() + "%"),
criteriaBuilder.like(parent.get("name"),
"%" + query.getValue() + "%"));
The query works with no problems if a parent has children but will return no results if the parent has no children.
I've tried adding additional an additional predicate that will only check the parent's name if they are childless.
I have completely run out of ideas, any help will be greatly appreciated.

Related

JPA Criteria API join on 3 tables and some null elements

I have one parent entity that has two child entities as attributes.
I want to select all elements from the parent entity that have EITHER a childOne with a given parameter as personal attribute OR childTwo with that same given parameter as personal attribute.
Here are my three classes simplified:
The Parent Object:
#Entity
public class ParentObject {
#Id
private int id;
private int fkChildOne;
private int fkChildTwo;
#ManyToOne
#JoinColumn(name = "fk_child_one_id", referencedColumnName =
"child_one_id")
private ChildOne childOne;
#ManyToOne
#JoinColumn(name = "fk_child_one_id", referencedColumnName =
"child_one_id")
private ChildTwo childTwo;
// getters and setters
}
The Child One Object:
#Entity
public class ChildOne {
#Id
private int childOneId;
private String nameChildOne;
#OneToMany
#JoinColumn(name = "fk_child_one_id")
private List<ParentObject> parents;
// getters and setters
}
The Child Two Object:
#Entity
public class ChildTwo {
#Id
private int childOneId;
private String nameChildTwo;
#OneToMany
#JoinColumn(name = "fk_child_two_id")
private List<ParentObject> parents;
// getters and setters
}
The Specs Class:
public static Specification<ParentObject> checkName(String name) {
return Specifications.where(
(root, query, builder) -> {
final Join<ParentObject, ChildOne> joinchildOne =
root.join("childOne");
final Join<ParentObject, ChildTwo > joinchildTwo =
root.join("childTwo");
return builder.or(
builder.equal(joinchildOne .get("nameChildOne"), name),
builder.equal(joinchildTwo .get("nameChildTwo"), name)
);
}
);
}
When this spec is called in my service, I get no results. However, if I comment out one of the two joins and the corresponding Predicate in my builder.or method, then I get some results but they obviously don't match what I'm looking for, which is to select every ParentObject that have either ChildOne with that parameter or ChildTwo with that paramater.
Any clue what's wrong with the code ?
Finally got the solution : to fetch all the corresponding results, I had to add the type of the join which would be left join, since I wanted to fetch all ParentObjects regardless of owning childOne or ChildTwo objects.
final Join<ParentObject, ChildOne> joinchildOne =
root.join("childOne", JoinType.LEFT);
final Join<ParentObject, ChildTwo > joinchildTwo =
root.join("childTwo", JoinType.LEFT);
Great, now you have to choose if you need to join or fetch.To optimize the query and the memory, you should establish the relations as Lazy (#ManyToMany (fetch = FetchType.LAZY)), so you will only bring the objects that you demand.
The main difference is that Join defines the crossing of tables in a variable and allows you to use it, to extract certain fields in the select clause, for example, on the other hand, fetch makes it feed all the objects of that property. On your example,
a select from parent with join of children (once the relation is set to lazy) would only bring initialized objects of type parent, however if you perform a fetch, it would bring the parent and child objects initialized.
Another modification I would make is to change the type of the identifier to non-primitive, so that it accepts null values, necessary for insertion using sequences

Spring Data Repository - composite key & don't need to save entity

I'm working with a third party database that is read-only. I have the following Spring Data repository:
public interface FolderRepository extends Repository<Folder, Integer> {
#Query(value = "SELECT f.folderId, a.fileId, a.fileName, " //selecting other columns in my app
+ "FROM User.functionSys(:binData) f "
+ "LEFT JOIN User.vFile a "
+ "ON f.fileId = a.fileId "
+ "group by f.folderId, a.fileId, a.fileName",
nativeQuery = true)
List<Folder> getFolders(#Param("binData") byte[] binData);
}
Folder id and file id form an unique key. So, my folder entity looks like this:
#Entity
#Data
public class Folder implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private FolderFileKey id;
private String fileName;
// several other fields in my app
}
#Embeddable
#Data
class FolderFileKey implements Serializable {
private static final long serialVersionUID = 2L;
private Integer folderId;
private Integer fileId;
}
The problem is that I really want a list of File objects where each File object contains a list of Folders (the same file id may be in several different folders):
#Data
public class FileDto {
private Integer fileId;
private String fileName;
private List<Folder> folders;
}
#Data
public class FolderDto {
private Integer folderId;
private String folderName;
}
I know that I could write a service to transform a Folder entity into the FileDto and FolderDto but is there a way to use Spring Data projections or rewrite the entities to achieve the structure wanted in the Dtos?
Update
User.functionSys(:binData) is a table-valued function (so it returns a table).

Spring Data JPA Projection nested list projection interface

I have a question about usage of nested list projection interface. I have two entity (Parent and child) (they have Unidirectional association)
Parent =>
#Table(name = "parent")
#Entity
public class ParentEntity {
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
// other fields........
}
Child =>
#Table(name = "child")
#Entity
public class ChildEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NonNull
private String name;
#NonNull
#ManyToOne(fetch = FetchType.LAZY)
private ParentEntity parent;
// other fields........
}
I have two projection interface for select specific columns.
ParentProjection =>
public interface ParentProjection {
String getName();
Set<ChildProjection> getChild();
}
ChildProjection =>
public interface ChildProjection {
String getId();
String getName();
}
I want to take list of ParentProjection which includes with list of ChildProjection.
Repository query like that =>
#Query("select p.name as name, c as child from ParentEntity p left join ChildEntity as c on p.id = c.parent.id")
List<ParentProjection> getParentProjectionList();
This query works, but it selects all columns of ChildEntity, and map only id, name propeties to ChildProjection. (generated query selects all columns, but i want to select only id and name columns)
How can i select only id and name columns (select specific columns for nested list projection interface) and map to ChildProjection fields (using with #Query) ?
Note: I don't need to use class type projection.
You need to add the OneToMany relation to ParentEntity and annotate with Lazy.
Hope it helps (i have tried this).

How to query for entities by their string collection values LIKE

I have the following entity:
#Entity
public class SystemLogEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private long creationTime;
private String thread;
private int severity;
#Lob
private String message;
#ElementCollection(fetch = FetchType.EAGER)
#Lob
private List<String> stacktrace;
...
}
My Respository implements JpaSpecificationExecutor, which allows me to use Specifications to filter my db requests:
#Repository
public interface SystemLogRepository extends JpaRepository<SystemLogEntity, Long>, JpaSpecificationExecutor<SystemLogEntity> {
public List<SystemLogEntity> findAll(Specification spec);
}
For the simple field of the SystemLogEntity this works fine, the Predicate are straight forward.
Also if I filter for an exact item in a collection, the Predicate are still straight forward (in).
But how can I filter my SystemLogEntity after a stack trace collection item which is LIKE a given value?
In other words, I would e.g. like to filter SystemLogEntity after the term NullpointerException. Is this even possible with Predicate?
I hope this will work:
Specification<SystemLogEntity> stacktraceLike(String stacktrace) {
return (root, query, cb) -> cb.like(root.join("stacktrace"), "%" + stacktrace + "%");
}
More examples...

JPA or Play Framework list from query joining 2 tables

Mainly I work with JSF so am totally new to this annotation subject
If anyone can help
I wanna a list from this query
SELECT f.CODE ,f.NAME || '-' || e.NAME
FROM FS.ELIGIBLE e RIGHT
OUTER JOIN FS.FINANCIAL_SUPPORT f ON e.CODE = f.CODE ;
The query above retrieves a list from 2 tables and concatenating the name field from both tables!!
How can i do this in JPA or in play with another query supported by Play Framework ???
Have a read of the Play Framework documentation, specifically the part about JPA and your Domain Model.
You can access the entity manager at any time by calling
EntityManager entityManager = JPA.em();
Using this you can create any query that you want, even a "Native" Query. For example:
List<Object> results = JPA.em().createNativeQuery(
"SELECT f.CODE ,f.NAME || '-' || e.NAME "+
"FROM FS.ELIGIBLE e RIGHT "+
"OUTER JOIN FS.FINANCIAL_SUPPORT f ON e.CODE = f.CODE").getResultList()
JPA is not like relational database system which you can do your queries like join, left join or outer joins it is a mapping technology of objects. You can also do the same fetch just like those RDBMS counterparts but with a different approach.
What you have to do is make an Object then relate your second Object to your first Object, that is the proper way to relate 2 or more Objects. The Objects I'm talking about is your Table. See my example below:
Table1: Items.java
...
// do your imports here ...
// do your annotations here like
#Entity
#Table(name="Items")
public class Items implements Serializable {
private String id;
private String itemno;
private String description;
private Set<Vendors> vendors; //this is the 2nd table (1:n relationship)
...
// don't forget your constructor
// in your setter and getter for Vendors do the ff:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name="id")
public Set<Vendors> getVendors() {
return vendors;
}
public void setVendors(Set<Vendors> vendors) {
this.vendors = vendors;
}
...
}
Table2: Vendors.java
#Entity
#Table(name="Vendors")
public class Vendors implements Serializable {
private long id;
private String company;
private String contact;
private String sequence;
...
public Vendors() { }
...
// in your setter & getter
#Id
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
...
}
Now on your query, just do a regular select as in the ff:
public void makeQuery(String seq) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(...);
EntityManager em = emf.createEntityManager();
TypedQuery<Items> query = em.createQuery("
SELECT i, j.contact, j.company, j.sequence FROM Items i LEFT OUTER JOIN i.vendors j WHERE i.vendors.sequence = :seq
ORDER BY i.id", Items.class);
List<Items> items = query.setParameter("sequence", seq).getResultList();
...
}
Now you can refer to your 2nd table Vendors by using items.vendors.company ... and so on.
One disadvantage with this one is that, JPA make its related objects in the form of Set, see the declaration in Table1 (Items) - private Set vendors. Since it was a Set, the sequence is not in order it was received, unlike using List.
Hope this will help ...
Regards, Nelson Deogracias