NamedQuery selectively define attributes - jpa

I have namedQuery in Entity class defined as
#NamedQuery(name = "Emp.findAll",
query = " select new test.entity.Emp(o.empNo, o.salary, o.project) from Emp o ")
Constructor
public Emp(String empNo, String salary, Project project) {
this.empNo = empNo;
this.salary= salary;
this.project = project;
}
and generated SQL is
SELECT t0.emp_no, t0.salary, t1.project_id, t1.project_name, t1.project_desc
FROM EMP t0, PROJECTS t1 WHERE (t1.project_id (+) = t0.project_id)
In namedQuery how do I selectively declare projectId and projectName instead of all attributes from Project class? I wouldn't like to display all attributes of Project class in namedQuery.
How can I achieve this?
Update 1
public Emp(String empNo, String salary, Long projectId, String projectName) {
Project pr = new Project();
this.empNo = empNo;
this.salary= salary;
pr.setProjectId = projectId;
pr.setProjectName = projectName;
}

Try this (and update the constructor accordingly)
#NamedQuery(name = "Emp.findAll",
query = " select new test.entity.Emp(o.empNo, o.salary, p.projectId, p.projectName) from Emp o inner join o.project p ")
Constructor will be something like this
public Emp(String empNo, String salary, Long projectId, String projectName) {
this.empNo = empNo;
this.salary= salary;
Project pr = new Project();
pr.setProjectId(projectId);
pr.setProjectName(projectName);
this.project = pr;
}

Related

How to use string_agg with PostgreSQL in #Query annotation without nativeQuery flag?

I need to remove the nativeQuery flag from the #Query annotation.
The table structure may change in the future, and code without nativeQuery will make it easier to maintain later.
I have a class Parent that is linked to class Child with the #ManyToMany annotation.
Class Child has a field pseudonym that is a value of the String type.
The result of the query needs to be sorted by the String value from class Child, which I have to sort and then concatenate into one String value.
The query without the nativeQuery flag in the #Query annotation works if I do not add an additional sort in the string_agg function:
order by string_agg(c.pseudonym, ',')
If I add additional required sorting as below, an exception occurs
order by string_agg(c.pseudonym, ',' order by c.pseudonym)
org.hibernate.hql.internal.ast.QuerySyntaxException: expecting CLOSE, found 'order' near line 1, column ...
#Entity
#Getter
#Setter
#Table(name = "parent")
public class Parent {
#Id
private Long id;
private String name;
#ManyToMany
#JoinTable(
name = "parent_child_link",
joinColumns = {#JoinColumn(name = "parent_id")},
inverseJoinColumns = {#JoinColumn(name = "child_id")}
)
#OrderBy("pseudonym ASC")
private List<Child> childs = new ArrayList<>();
}
#Entity
#Getter
#Setter
#Table(name = "child")
public class Child {
#Id
private Long id;
private String pseudonym;
#ManyToMany(mappedBy = "childs")
private Set<Parent> parents = new HashSet<>();
}
public interface ParentRepository extends JpaRepository<Parent, Long> {
#Query(nativeQuery = true, value =
"select p.*" +
" from parent p" +
" left join parent_child_link link on p.id = link.parent_id" +
" left join child c on link.child_id = c.id" +
" where p.name = :name" +
" group by (p.id)" +
" order by string_agg(c.pseudonym, ',' order by c.pseudonym)")
Page<Parent> find(#Param("name") String name, Pageable pageable);
}
Please try with nested query:
select p.*
from (
select p, string_agg(c.pseudonym, ',' order by c.pseudonym) ord
from parent p
left join parent_child_link link on p.id = link.parent_id
left join child c on link.child_id = c.id
where p.name = :name
group by (p.id)
) inn(p, ord)
order by ord
or:
select p.*
from(
select p, c.pseudonym
from parent p
left join parent_child_link link on p.id = link.parent_id
left join child c on link.child_id = c.id
where p.name = :name
order by p, pseudonym
) inn(p, pseudonym)
group by p.id
order by string_agg(pseudonym, ',')

Eclipselink problem with subquery in where clause

I have the following structure: (I am using eclipselink 2.6.0)
#Entity
public class A {
#Id
private double id;
...
#OneToMany
private List<B> b;
#OneToMany(mappedBy = "a")
private List<C> c;
...
}
#Entity
public class B {
#Id
private double id;
private boolean hidden;
}
#Entity
public class C {
#Id
private double id;
private String label;
private String value;
#ManyToOne
private A a;
}
I need to find A that are not hidden, and has s1 = value1, s2 = value 2, this is the implementation using criteria:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<A> cq = cb.createQuery(A.class);
Root<A> root = cq.from(A.class);
Join<A, B> property = root.join(A_.b, JoinType.LEFT);
String currentUser = ctx.getCallerPrincipal().getName();
property.on(cb.equal(property.get(B_.creator), currentUser));
C af = new C("value1", "value2");
Subquery<B> sq = cq.subquery(B.class);
Root<C> sqRoot = sq.from(C.class);
sq.where(cb.and(cb.equal(sqRoot.get(C_.A), root),
cb.equal(sqRoot.get(C_.label), af.getLabel()),
cb.like(sqRoot.get(C_.value), "%" + af.getValue() + "%")));
sq.select(sqRoot);
cq.where(cb.and(
cb.exists(sq),
cb.or(cb.isNull(property.get(B_.id)),
cb.equal(property.get(B_.hidden), false))));
cq.select(root).distinct(true);
The generated query is:
SELECT DISTINCT t1.ID
FROM B t0, A t1
WHERE (EXISTS (SELECT 1 FROM C t2 WHERE (((t2.A_ID = t1.ID) AND (t2.LABEL = "value1")) AND t2.VALUE LIKE "%value2%")) AND ((t0.ID IS NULL) OR (t0.HIDDEN = false)))
As you can see, the provider ignores the left Join totally as if it doesn't exist! It doesn't do any join between tow tables A and B..
Note that in the JPA JSR, the query should be correct. It also worked as a named query. But I can't use it here since I have lots of changing conditions...
The query should be:
SELECT DISTINCT t1.ID
FROM A t1 LEFT JOIN B t0 ON t1.ID=t0.A_ID AND t0.creator="currentUserValue"
WHERE (EXISTS (SELECT 1 FROM C t2 WHERE (((t2.A_ID = t1.ID) AND (t2.LABEL = "value1")) AND t2.VALUE LIKE "%value2%")) AND ((t0.ID IS NULL) OR (t0.HIDDEN = false)))
I tried to change eclipseLink to the 2.7.7 release...The query changed a bit, but it still wrong, with no left join between A and B.
I appreciate any help with this problem.

JPA Native Query to map One to Many

I have a complex Oracle query which for simplicity's sake looks like this;
SQL> SELECT d.id AS dept_id,
2 d.name AS dept_name,
3 e.id AS emp_id,
4 e.name AS emp_name,
5 e.dept_id AS emp_dept_id
6 FROM drs2_dept d, drs2_emp e
7 WHERE d.id = e.dept_id (+)
8 /
DEPT_ID DEPT_NAME EMP_ID EMP_NAME EMP_DEPT_ID
---------- ------------------- ---------- -------------- -----------
1 SALES 101 JOHN 1
1 SALES 102 JANE 1
2 ADMIN
My Department class is;
#SqlResultSetMapping(
name = "Department.employeeMapping",
classes = {
#ConstructorResult(
targetClass = Department.class,
columns = {
#ColumnResult(name = "DEPT_ID", type = Integer.class),
#ColumnResult(name = "DEPT_NAME")
}
),
#ConstructorResult(
targetClass = Employee.class,
columns = {
#ColumnResult(name = "EMP_ID", type = Integer.class),
#ColumnResult(name = "EMP_NAME"),
#ColumnResult(name = "EMP_DEPT_ID", type = Integer.class)
}
)
}
)
#NamedNativeQuery(
name = "Department.findAllEmployees",
query = "SELECT d.id AS dept_id, " +
" d.name AS dept_name, " +
" e.id AS emp_id, " +
" e.name AS emp_name " +
" e.dept_id AS emp_dept_id, " +
"FROM drs2_dept d, drs2_emp e " +
"WHERE d.id = e.dept_id (+)",
resultSetMapping = "Department.employeeMapping"
)
#Entity
public class Department {
#Id // JPA will not start without it.
Integer id;
String name;
#OneToMany // JPA will not start without it.
List<Employee> employees = new ArrayList<>();
public Department(Integer id, String name) {
this.id = id;
this.name = name;
}
public Department() {}
// getters and setters
}
My Employee class is;
#Entity
public class Employee {
#Id Integer id;
Integer departmentId;
String name;
public Employee(Integer id, String name, Integer departmentId) {
this.id = id;
this.name = name;
this.departmentId = departmentId;
}
public Employee() {}
// getters and setters
}
Because I am using #ConstructorResult I am able to get the data, but it still in a flat structure, that is to say a List<Object[]> with three entries, each containing [Department, Employee]. So I have to do the following to move the Employee records within their respective Department;
#Component
public class DepartmentDAO {
#PersistenceContext EntityManager entityManager;
public Collection<Department> getAllDepartments() {
Query query = entityManager.createNamedQuery("Department.findAllEmployees");
Map<Integer, Department> map = new HashMap<>();
List<Object[]> list = query.getResultList();
for (Object[] tuple: list) {
Department d = (Department) tuple[0];
if (! map.containsKey(d.getId())) {
map.put(d.getId(), d);
}
d = map.get(d.getId());
Employee e = (Employee) tuple[1];
if (e.getId() != null) {
d.getEmployees().add(e);
}
}
return map.values();
}
}
Whenever I add any additional properties to the #OneToMany I seem to get spurious SQL generated in the Hibernate logs which is incorrect (i.e. non-existent column or table names), but as I stated at the start of this question, I want the native SQL only - I don't want Hibernate to figure out what I am trying to do.
Is there any way to get JPA/Hibernate to put the Employee objects into the Department's list for me?
(
As a sub-note, I have seen this question asked here, but either never answered or answered back in 2011, by which time JPA and Hibernate may have progressed.
I should also add that elsewhere in my project I already have Department and Employee fully mapped for CrudRepository use with #Table and #Column, however their #OneToMany definitions do not depict what I am doing in the above query, hence their absence in my example code.
)
This query does not have any clause that forces it to be implemented with a native.
In fact it is considered a bad practice.
Try this:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Departament> cq = cb.createQuery(Departament.class);
Root<Departament> rootDepartament = cq.from(Departament.class);
Join<Departament,Employee> joinEmployee = rootDepartament.join(Departament_.employees,JoinType.Left);
cq.select(rootDepartament);
List<Departament> result = entityManager.createQuery(cq).getResultList();

JPA inheritance issue, why the generated query is different between wildfly10 and jboss7

I am migrating a project from jboss7 to wildfly10. The strange thing is the generated query in jboss is different in wildfly10, that causes the tables structure have to be changed, but it is not expected.
public class BaseAnnotation implements Serializable {
private static final long serialVersionUID = 6636704943305921427L;
}
#Entity
#Table(name="one")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class oneBaseAnnotation extends BaseAnnotation {
#Id
#GeneratedValue(generator = "baseAnnotationSequencer")
#SequenceGenerator(name = "baseAnnotationSequencer", sequenceName = "BASEANNOTATION_SEQ")
private Long id;
private String annotationType;
.....
}
#Entity
public class TwoStructureAnnotation extends oneBaseAnnotation {
private static final long serialVersionUID = -5838272604038154615L;
#OneToMany
#JoinTable(name= "CSA_CS")
private List<TwoStructure> twoStructures = new ArrayList<TwoStructure>();
public TwoStructureAnnotation() {
setAnnotationType("Two Structure");
}
.....
}
public class..... {
protected List<T> createQuery(int first, int pageSize,
List<SortMeta> multiSortMeta, Map<String, String> filters,
String joinField) {
// Setup
CriteriaBuilder cb = getObjectEntityManager().getCriteriaBuilder();
CriteriaQuery<T> criteria = (CriteriaQuery<T>) cb.createQuery();
Root<A> annotationRoot = criteria.from(TwoStructureAnnotation.class);
ListJoin<A, T> joinRoot = annotationRoot.joinList("twosStructures");
Predicate restrictions = cb.conjunction();
// Filter
filters.putAll(this.getBaseFilter());
restrictions = cb.and(restrictions,
createGlobalFilter(filters, joinRoot, cb));
restrictions = cb.and(restrictions,
cb.equal(annotationRoot, annotation));
...
// Query creation
criteria.where(restrictions);
criteria.select(joinRoot);
// Restrict Returns
TypedQuery<T> returnQuery = getObjectEntityManager().createQuery(
criteria);
returnQuery.setFirstResult(first);
returnQuery.setMaxResults(pageSize);
List<T> results = returnQuery.getResultList();
....}
The query below, the different that the key in the inner join on table CSA_CS. I have no idea why, please suggest me, thank you.
--in Jboss7
select * from
( select
crystalstr2_.id as id1_43_,
crystalstr2_.pdbEntry_id as pdbEntry_id3_43_,
crystalstr2_.title as title2_43_
from
ONE crystalstr0_
inner join
CSA_CS crystalstr1_
on crystalstr0_.id=crystalstr1_.ONE_id
inner join
TwoStructure crystalstr2_
on crystalstr1_.crystalStructures_id=crystalstr2_.id
where
crystalstr0_.DTYPE='TwoStructureAnnotation'
and 1=1
and 1=1
and crystalstr0_.id=? )
where
rownum <= ?
---In wildfly10
select
*
from
( select
crystalstr2_.id as id1_36_,
crystalstr2_.pdbEntry_id as pdbEntry_id3_36_,
crystalstr2_.title as title2_36_
from
ONE crystalstr0_
inner join
CSA_CS crystalstr1_
on crystalstr0_.id=crystalstr1_.TWOStructureAnnotation_id
inner join
TwoStructure crystalstr2_
on crystalstr1_.crystalStructures_id=crystalstr2_.id
where
crystalstr0_.DTYPE='TwoStructureAnnotation'
and 1=1
and 1=1
and crystalstr0_.id=? )
where
rownum <= ?
Tables:
table-TWOSTRUCTURE
ID
TITLE
table-CSA_CS
ONE_ID
CRYSTALSTRUCTURES_ID
table-ONE
DTYPE
ID
ANNOTATIONTYPE
JBoss7 ships hibernate 4.x and wildfly 10 ships hibernate 5. In hibernate 5, Oracle is implemented 'inner join'. If you use Oracle10gDialect, then
Oracle10gDialect added support for ANSI joins. Subclasses (e.g. Oracle12cDialect) inherit this functionality.

JPQL query using WHERE column = int

I'm new to using JPQL/JPA and I somehow can't get this working:
public List<Bil> hentBiler(int kontor) {
List<Bil> biler = new ArrayList<Bil>();
TypedQuery<Bil> query = em.createQuery("SELECT o FROM Bil o WHERE o.kontornr = ?1", Bil.class);
query.setParameter(1, kontor);
}
The kontornr column in the database is an integer.
The error i recieve is this:
Exception Description: Problem compiling [SELECT o FROM Bil o WHERE o.kontornr = ?1].
[26, 36] The state field path 'o.kontornr' cannot be resolved to a valid type.
#Entity
public class Bil {
#Id
private String regNr;
private String merke;
private String modell;
private String farge;
private char gruppe;
private boolean ledig;
private int kontorNr;
The field is declared as
private int kontorNr;
The query is
SELECT o FROM Bil o WHERE o.kontornr = ?1
Java is case sensitive. kontronr != kontorNr.