4

I have single JPA entity and I would to add self join on this table. Table looks like e.g.

@Entity
@Table(name = "TABLE_A")
@IdClass(TableAPk.class)
public class TableA implements Serializable {

    private static final long serialVersionUID = 1L;


    @Id
    @Column(name = "COLUMN_1", nullable = false, length = 64)
    private String            column_1;

    @Id
    @Column(name = "COLUMN_2", nullable = false, precision = 10, scale = 2)
    private BigDecimal        column_2;


    @ManyToOne
    @JoinColumn(name = "COLUMN_1", insertable = false, updatable = false)
    //@ManyToOne(optional = true, fetch = FetchType.LAZY)
    //@JoinTable(name = "KEY_MAPPING", 
    //        joinColumns = { @JoinColumn(name = "J_COLUMN_1", referencedColumnName = "COLUMN_1", insertable = false, updatable = false) } )
    private TableA tableA;

    @OneToMany(mappedBy="tableA", fetch = FetchType.LAZY)
    private Set<TableA> tableASet;

And IdClass looks like:

public class TableAPk implements Serializable {
    // default serial version id, required for serializable classes.
    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = 1L;

    private String            column_1;

    private BigDecimal        column_2;

As per my business logic I need to add self join only on single column

final Join<TableA, TableA> joinASelf = joinX.join("tableA", JoinType.INNER);

But table has composite primary key, so more than one fields are annotated with @Id. And I get exception like:

Caused by: org.hibernate.AnnotationException: A Foreign key refering com.data.TableA from com.data.TableA has the wrong number of column. should be 2.

How do i add self join only on single column here? I am new to JPA, so please let me know if I missed anything.

Update 21 Feb 2015: I added annotation @AssociationOverride to override associations:

@ManyToOne
@AssociationOverride(name="tableA", 
        joinColumns=@JoinColumn(name="COLUMN_1"))
private TableA tableA;

Generated column name is shown as "TABLE_A_COLUMN_2". I am not able to find out why. Any clue?

V G
  • 18,822
  • 6
  • 51
  • 89
user613114
  • 2,731
  • 11
  • 47
  • 73
  • what is wrong with @JoinColumns ? – Neil Stockton Feb 20 '15 at 18:00
  • My business logic is like, in same query, I want to find all rows from TableA having specific value of column_1. – user613114 Feb 20 '15 at 18:05
  • You CAN'T add a "self-join on one column" since you need 2 columns to refer to an object of that type, to follow the object model. – Neil Stockton Feb 20 '15 at 18:15
  • Any idea guys? I have tried many options but stuck with wrong column names in final SQL query. – user613114 Feb 21 '15 at 13:16
  • why would anyone have an idea when you have said you "solved it" ? as already said, you can't just join on one column ... need both and that's where @JoinColumns comes in – Neil Stockton Feb 21 '15 at 13:18
  • Sorry Neil. I deleted my earlier post. Using AssociationOverride my JPA query looks fine, But when it tries to fetch result list, it gives exception because column name is wrong. I have updated question with all details. Do you have any reference for this scenario as I wish to add self join only on single column. My SQL query should look like: Select a1.* from table_A a1, table_A a2 where a1.column1 = a2.column1 where a2.column_2 = 'someValue'; – user613114 Feb 21 '15 at 13:25
  • 1
    No idea what you refer to with AssociationOverride ... which is intended for where you have a mappedSuperclass, or where the class is embedded, and you don't have those situations (see the JPA spec), and as you've seen that doesn't work anyway. YOU CAN'T JUST JOIN ON ONE COLUMN SINCE YOU HAVE TWO – Neil Stockton Feb 21 '15 at 14:03
  • Hmmm.. Ok so I understand using JoinColumns it is not possible :( Is there any other way to achieve solution I am looking for? I need to add join only on single column. I was wondering, if SQL allows it then there might be some way in JPA criteria to do it? – user613114 Feb 21 '15 at 14:21
  • I said why not try @JoinColumns? (with 2 columns obviously), yet you don't seem to have tried it, and now want to rule it out. I said you can't join on ONE COLUMN BECAUSE YOU HAVE TWO. – Neil Stockton Feb 21 '15 at 14:29
  • I verified generated SQL query. It applies join all all 6 columns and thus I do not get any results. So this will not work for me :( Any other hint? – user613114 Feb 21 '15 at 16:47
  • well when I have the same structure (a class with a parent-children 1-N bidir relation, and composite PK with 2 fields) I can do an innerJoin and it joins the 2 columns of the PK, as it should. This was using DataNucleus JPA. – Neil Stockton Feb 21 '15 at 18:05

1 Answers1

7
  1. The correct way would be (not what OP wants)

You could use@JoinColumns annotation (note the plural instead of the singular), thus joining the :

@Entity
@Table(name = "TABLE_A")
@IdClass(TableAPk.class)
public class TableA implements Serializable {

    private static final long serialVersionUID = 1L;


    @Id
    @Column(name = "COLUMN_1", nullable = false, length = 64)
    private String            column1;

    @Id
    @Column(name = "COLUMN_2", nullable = false, precision = 10, scale = 2)
    private BigDecimal        column2;


    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "FK_COL1", referencedColumnName="COLUMN_1"),
        @JoinColumn(name = "FK_COL2", referencedColumnName="COLUMN_2")
    })
    private TableA tableA;

    @OneToMany(mappedBy="tableA", fetch = FetchType.LAZY)
    private Set<TableA> tableASet;
  1. The solution to the problem:

Remove those @ManyToOne/@OneToMany relationships and add a single private String refCol1;. With that, write the corresponding Criteria Query for the JPQL: SELECT t1 FROM TableA t1, TableA t2 WHERE t2.refCol1 = t1.column1

With CriteriyQuqeries the solution looks like:

final CriteriaBuilder criteriaBuilder = entityManagerMds.getCriteriaBuilder();
// This Pojo is used to fetch only selected fields
final CriteriaQuery<DummyPojo> createQuery = criteriaBuilder.createQuery(DummyPojo.class);

final Root<TableX> tableX = createQuery.from(TableX.class);
final Join<TableX, TableA> joinTableA = tableX.join("tableAs", JoinType.INNER);


// Every time you want to add a self join, create new root
final Root<TableA> tableA = createQuery.from(TableA.class);
final Predicate predicateSelfJoin = criteriaBuilder.equal(joinTableA.<String>get("column_1"), tableA.<String>get("column_1"));
V G
  • 18,822
  • 6
  • 51
  • 89
  • As I mentioned in question due to my business scenario limitations, I need to add join only on single column. Not on all columns which are part of composite key. This is possible by using native query implementation. I am just wondering why not using JPA. – user613114 Feb 23 '15 at 09:26
  • I can not change DB schema. That is not in my hand. I am already using JPA 2.1. Can you please elaborate more on what shall I try? AS I am new to JPA criteria queries your hint would help me. IF it is possible for you, also kindly provide any existing reference on how it is done. – user613114 Feb 23 '15 at 09:53
  • Replace the `@ManyToOne` property with a simple `private String refCol1;` (you can also simply add it, without replacing anything) and then try the following JPQL query: `SELECT TableA t1 LEFT JOIN t2 ON t1.column1=t2.refCol1` But keep in mind: the Java classes are not correctly mapped to the DB, because from JPA point of view that is NOT a `ManyToOne` relationship (I would delete it from Java, and because you use JPA 2.1 you can always write similar queries (also Criteria Queries)). – V G Feb 23 '15 at 10:02
  • Ok I will try that. Once more question. I can not use JPQL query cause conditions and relations are added dynamically based on search criteria. Is it possible to try same thing using Query Criteria? – user613114 Feb 23 '15 at 10:08
  • Hi Andrei, I have edited your answer to mention how final solution would look like. Please wait for some time untill I am allowed to award bounty to you :) – user613114 Feb 24 '15 at 07:37