JPA count related entities without joining them - java

I have two entities:
#Entity
class X {
#Id
int id;
}
#Entity
class Y {
#Id
int id;
#ManyToOne
#JoinColumn(name = "x_id")
X x;
}
I would like to count distinct values of x_id in y table. I've tried:
select count(distinct Y.x) from Y;
It works but in sql i get join to x table which is uneccesery:
select count(distinct x.id) from y, x where y.x_id = x.id;
This join is unnecessary and quite costly for me. Is there any way to avoid it without native query?

You may try with select count(distinct Y.x.id) from Y (T.x.id instead of Y.x). I am not sure, but intelligent JPA implementation should find out that only id is necessary and would not add the join.
Alternative is to add a int field to Y with a read-only mapping to x_id column:
#Entity
class Y {
#Id
int id;
#ManyToOne
#JoinColumn(name = "x_id")
X x;
#Column(name = "x_id", insertable = false, updatable = false, nullable = false)
int xId;
}
And the your query would be simply select count(distinct Y.xId) from Y

For count inside the JPA repository, you can even use:
Suppose there are two entities: EntityA and EntityB. If EntityA has any relation with EntityB then you can use count in your
#Entity
#Table(name = "entity_a")
public class EntityA {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ea_id")
private Long eaId;
#ManyToOne
#JoinColumn(name = "eb_id")
private EntityB entityB;
...
}
And another EntityB
#Entity
#Table(name = "entity_b")
public class EntityB {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "eb_id")
private Long ebId;
...
}
For this, you can use the following method in your JPARepository of EntityA for getting the count. Remember _ is the replacement of . for method signature in the repository.
int countByEntityB_EbId(long ebId);

Related

Spring data jpa operation between table views

in my spring data application i have two TABLE VIEW mapped:
the first view
#Entity
#Immutable
#Table(name="VD_CONT")
#NamedQuery(name="VdContr.findAll", query="SELECT d FROM VdContr d")
public class VdContr {
#Id
#Column(name="CONTR_ID")
private Long id;
#Column(name="CF")
private String cf;
#OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="vdcontr")
private List<VdArr> vdArr;
}
and the second view
#Entity
#Immutable
#Table(name="VD_ARR")
#NamedQuery(name="VdArr.findAll", query="SELECT v FROM VdArr v")
public class VdArr {
#Id
#Column(name="ARR_ID")
private Long id;
#Column(name="FK_CONTR_ID")
private Long fkContrId;
#ManyToOne(fetch=FetchType.LAZY)
public VdContr vdcontr;
}
If i put a relationship "OneToMany" and "ManyToOne" (1, first view : many, second view), i receive errors.
My question is: is it possibile create a relationship between two table view?
you need to add a #JoinColumn to VdContr.
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "vdcontr_id", nullable = false)
In general, views are mapped in the same way as tables.
By looking at your classes, the problem is that Hibernate cannot find the correct join column. You need to specify it.
Also, in your VdArr you should delete the fkContrId, because hibernate will need to use this column to map the VdContr relationship.
By looking at your code, the join column is FK_CONTR_ID, so you need to specify it by using #JoinColumn.
#Entity
#Immutable
#Table(name = "VD_ARR")
#NamedQuery(name = "VdArr.findAll", query = "SELECT v FROM VdArr v")
public class VdArr {
#Id
#Column(name = "ARR_ID")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_CONTR_ID")
public VdContr vdcontr;
}

JPAQuery join intermediate many to many table

I'm using QueryDSL JPA, and want to do a join between two tables. I found many similar questions here already, but my case is different from all of them by one little detail. Here is a simplified version of my classes:
#Entity(name = "TABLE_A")
public class TableA {
#Id
#Column(name = "ID_A", nullable = false)
private Long idA;
}
#Entity(name = "TABLE_B")
public class TableB {
#Id
#Column(name = "ID_B", nullable = false)
private Long idB;
}
#Entity(name = "TABLE_C")
public class TableC {
#Id
#Column(name = "ID_C", nullable = false)
private Long idC;
#JoinColumn(name = "ID_A", referencedColumnName = "ID_A")
#ManyToOne
private TableA tableA;
#JoinColumn(name = "ID_B", referencedColumnName = "ID_B")
#ManyToOne
private TableB tableB;
}
Now what I want to do is join Tables A, C and B, to find the Bs which are linked to A. I know this seems like a useless step between, why not add a relation from A to B directly. In my case this is needed, these are just example classes to illustrate.
I tried this:
QTbTableA tableA = QTbTableA.tableA;
QTbTableB tableC = QTbTableC.tableC;
JPAQuery query = new JPAQuery(entityManager).from(tableA);
query.leftJoin(tableA, tableC.tableA);
The join throws an Exception because tableC.tableA is not a root path, only a property. But how do I join these tables correctly then?
Thanks in advance!
If you want to keep your current impl, you could start from TableC and then join the other tables:
query.from(tableC)
.innerJoin(tableC.tableA, tableA)
.innerJoin(tableC.tableB, tableB)
.where(tableA.idA.eq(myId)
.list(tableB);

hibernate one to one join using primary key not working

I have 2 entities in hibernate A and B. Here is the relevant code.
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#OneToOne(mappedBy = "a", cascade = CascadeType.ALL)
private B b;
}
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#Column(name = "a_id")
#GeneratedValue(generator = "gen")
#GenericGenerator(name = "gen", strategy = "foreign", parameters = #Parameter(name = "property", value = "a"))
private Integer aId;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#PrimaryKeyJoinColumn
private A a;
}
I did the same as mentioned in the below mentioned link
one to one mapping using primary key join column
However, when I do the following hql query,
"from A a left join a.b"
the join is taken on the following condition
a.id = b.id
although what I desire is the following condition
a.id = b.aId
You must use #JoinColumn(name = "a_id") instead of #PrimaryKeyJoinColumn.
By the way, you can't define two fields on the same column. However, if you need to do so you must make one of them not insertable and not updateable like this:
#JoinColumn(name = "a_id", insertable = false, updatable = false)
You have given the reference of class A to field a in class B.
#OneToOne(fetch = FetchType.LAZY, optional = false)
#PrimaryKeyJoinColumn
private A a;
And class B to field b in class A
#OneToOne(mappedBy="a", cascade=CascadeType.ALL)
private B b;
so by default Hibernate create a join query with referenced field. So hibernate by default perform the join on a.id = b.id.
But i think you can create your own query and use native query for performing join with a.id = b.aId.

Hibernate trying to persist same object twice

I have 3 classes corresponding to 3 tables V, D and P. D had a FK to V (FK_V) and is join using OneToMany relationship. Also their exits a 4th table V_D_P which has the relationship of these V, D and P.
Following is what the data model for these looks like:
#Entity
#Table(name = "V")
public class V {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private Long id;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name = "FK_V", referencedColumnName="Id", nullable = false)
private Set<D> d;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name = "FK_V", referencedColumnName="Id", nullable = false)
private Set<V_D_P> vdp;
//Getters Setters etc.
}
#Entity
#Table(name = "V_D_P")
public class V_D_P {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private Long id;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "FK_D", nullable = false)
private D d;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "FK_P", nullable = false)
private P p;
//Getters Setters etc.
}
#Entity
#Table(name = "D")
public class D {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private Long id;
//Getters Setters etc.
}
#Entity
#Table(name = "P")
public class P {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private Long id;
//Getters Setters etc.
}
Now I want to persist V, D and P along with their relationship. I am
V v = new V();
D d = new D();
Set<D> dSet = new HashSet<>();
dSet.add(d);
v.setD(dSet); // Adding d to V ....(1)
P p = new P();
V_D_P vdp = new V_D_P();
vdp.setD(d); // Adding d to V_D_P ....(2)
vdp.setP(p);
Set<V_D_P> vdpSet = new HashSet<V_D_P>();
vdpSet.add(vdp);
v.setVdp(vdpSet);
entityManager.persist(v);
Now you can see the I am adding the same d object twice. Once to P and once to V_D_P. However since these are the same objects, only once of them should persist. However based on the hibernate logs I see that hibernate is trying to insert 2 different objects.
I also see the following exception: ORA-00001: unique constraint
Is there a way to let hibernate that these are the same objects and to persis them only once ?
Objects are uniquely identified by id. Therefore they have to have the same id.
Either provide the id before,
or persist once and refresh d object before saving the other (so it has the id set).
Try save() instead of persist(). As noted in this post, persist doesn't necessarily update the ID of the object immediately. That means that hibernate may encounter your 'd' object twice as is traverses the object graph. Both times it might say "the id is null, I need to insert a new one!".
Apparently this behavior isn't well defined, so it may not happen in all cases.
Do you really need the VDP.d field: can't you just use a bi-directional (mapped) relation between V and V(D)P (V(D)P would have a v field) and just navigate V(D)P.v.d?

JPA Query - JOIN Clause

I have two tables
Table A
Table B
Table A contains
Column X
Column Y
Column Z
Column W
Tables B Contains
Column P
Column Q
Column R
Column W
Column W is common in both the tables.
and their entities
First Entity
#Entity
#Table(name = "A")
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class TableA extends AbstractBaseEntity {
#Id
#NotNull
#Column(name = "X")
private Long sampleId1;
#Id
#NotNull
#Column(name = "Y")
private Long sampleId2;
#Id
#NotNull
#Column(name = "Z")
private Date sampleDate3;
#ManyToOne(targetEntity = TableB.class)
#JoinColumn(name = "W")
private TableB tableB;
...
getter
setter
....
}
Second Entity
#Entity
#Table(name = "TableB")
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class TableB extends AbstractBaseEntity {
#Id
#NotNull
#Column(name = "W")
private Long sampleId4;
#Id
#NotNull
#Column(name = "P")
private Long sampleId1;
#Id
#NotNull
#Column(name = "Q")
private Long sampleId2;
#Id
#NotNull
#Column(name = "R")
private Long sampleId3;
...
getter
setter
....
}
I have an Interface where all the queries are written
Public interface sqlquery{
String query1 = "from TableA ORDER BY" +
sampleDate3 asc;";
String query2= "from TableB";
}
Right now i am fetching all data separately in these query,
I need some help in writing a new single query where data should be shown on the basis of same ID i.e SampleId4(column W)
using JOIN clause and where.
and store the data in result list.
Your mapping doesn't make much sense. If all the columns in table B are part of the ID, that probably means that you might have several rows in it which have the same value in column W. So, if a row in table A has this shared value, it actually references all these rows of table B. So you don't have a ManyToOne association between TableA and TableB.
Regarding your query, since the mapping is wrong in the first place, I don't see how I could write it. End even if it was OK, you should tell us what the query should return, because it's far from being clear.

Categories