I have a query with a self join that looks like this,
select t1., t2. from table t1
left outer join table t2 on t2.LFT < t1.LFT
and t2.RGT > t1.RGT
AND t2.REG_CODE_PAR = ‘ALL’
AND t1.STATUS_CODE = ‘A’
AND t2.STATUS_CODE = ‘A’
I’m using @NamedNativeQuery with a result set mapping to get the result.
@NamedNativeQuery(
name="findTree",
query="..... the query above",
resultSetMapping = "regionT")
With the following result set mapping
@SqlResultSetMapping(name = "regionT" , entities ={
@EntityResult(
entityClass = Tree.class
fields = {
@FieldResult(name = "regCode", column = "REG_CODE")
@FieldResult(name = "rgt", column = "RGT"),
@FieldResult(name = "lft", column = "LFT"),
@FieldResult(name = "name", column = "NAME"),
@FieldResult(name = "regCodePar", column = "REG_CODE_PAR"),
@FieldResult(name = "statusCode", column = "STATUS_CODE")
}
),
@EntityResult(
entityClass = TreeSelf.class
fields = {
@FieldResult(name = "regCode1", column = "REG_CODE")
@FieldResult(name = "rgt1", column = "RGT"),
@FieldResult(name = "lft1", column = "LFT"),
@FieldResult(name = "name1", column = "NAME"),
@FieldResult(name = "regCodePar1", column = "REG_CODE_PAR"),
@FieldResult(name = "statusCode1", column = "STATUS_CODE")
}
)
})
The entity class contains looks like this.
@NamedNativeQuery(...)
@SqlResultSetMapping(...)
@Entity
@Table(name = "table")
public class Tree implements Serializable {
@Id
@Column(name = "REG_CODE")
private String regCode; ... ..getters and setters...}
When I run the query using em.createQuery(“findTree”), I get the exact same object in both
the 1st and 2nd elements of the returned object array.
Even if I create a class called TreeSelf that is identical to Tree and use it as the 2nd
EntityResult instead of having 2 EntityResults using the same entityClass, I get the same
result.
Can someone point out what’s wrong with the configuration?
Let’s see if I understand your question. You’re expecting to capture two
Treeentities from each native query result row. The first entity should be formed fromt1‘s columns. The second entity should be formed fromt2‘s columns. Contrary to expectation, you actually receive two instances formed fromt1. No instances fromt2appear. You made a doppelganger Entity forTreecalledTreeSelfwhile debugging, butTreeSelfis ultimately unnecessary and you want to get rid of it. Stop me if any of that was wrong.I think the problem is due to ambiguous column names. Each column name in the native query appears twice, once from
t1and once fromt2. The result mapper seems to be arbitrarily picking the first occurrence of each ambiguous column name for bothTreeentities. I’m surprised that works at all. I would have expected an SQLException complaining about column reference ambiguity.Also, are you sure you want a left outer join? What if no match is found for a
t1row? It will be paired with all NULL int2‘s columns. Then you have a null-valuedTreeentity. I think. I don’t even know what the result mapper would do in that case. Perhaps you want an inner join?Consider translating this native query into a JPQL query. (JPA Criteria API is just as well, but I find it more cumbersome for examples.) Here’s a JPQL version of the native query:
N.B.: This changes the join semantics to inner instead of left outer.
Here’s a sketch of code that could run this query:
In case you are stuck with the native query for whatever reason, here’s how you can work around the column name ambiguity. Instead of starting the native query like
select t1.*, t2.*, alias each column withAS. TheSELECTclause would resemble this:The
columnattribute in eachFieldResultmust change accordingly. So thecolumnattributes under the firstEntityResultshould all start witht1_and the second’s should all start witht2_.I’d humbly recommend deleting the native query and sql result mapper and using JPA Query Language or Criteria API, if you can find a way.
Update: As confirmed in your comments, a useful answer to your question must preserve left (outer) join semantics. Unfortunately, JPQL and the Criteria API don’t support complex left join conditions. There is no way to qualify a JPQL left join with an explicit
ONcondition.To my knowledege, the only way to do a left outer join under the spec is by traversing an entity relationship. The JPA implementation then generates an
ONcondition that tests identity equality. The relevant spec bits are 4.4.5 “Joins” and 4.4.5.2 “Left Outer Joins”.To satisfy this constraint, each
Treeyou want to left-join to its ultimate parent must have an additional column storing the ultimate parent’s id. You might be able to cheat around this constraint in a variety of ways (views?). But the path of least resistance seems to be modifying the native query to use aliased arguments, deleting TreeSelf, and updating the result mapper accordingly. Cleverer solutions welcome, though…