Spring specification avoid unexpected CROSS JOIN - spring

I have 2 entities:
1 is the Org Structure (with a self-join refers to its parent org structure)
2 is the Device (has FK from OrgStructure)
public class OrgStructure{
#Id
private Long id;
#ManyToOne
private OrgStructure parentOrgStructure;
}
public class Device {
#Id
private Long id;
#ManyToOne
private OrgStructure hospitalRoom
}
I have 1 specification which generates a CROSS JOIN query. My question is how to avoid unnecessary CROSS JOIN like this
criteriaBuilder.equal(root.get(Device_.orgStructure).get(OrgStructure_.parentOrgStructure).get(OrganizationStructure_.id), *param goes here*)
The generated query:
SELECT
*
FROM
devices device0_
CROSS JOIN org_structures organizati1_
WHERE
device0_.hospital_room_id = organizati1_.id_organization_structure
AND organizati1_.organization_structure_id =?

please change :
root.get(Device_.orgStructure).get(OrgStructure_.parentOrgStructure)
to
root.join(Device_.orgStructure).join(OrgStructure_.parentOrgStructure)

Related

JPA #OneToMany but with only one rekord

I have a table in the database that has a #OneToMany link to another table, JPA in standard form will return me the values from the other table as a list, however I would like to get the records as :
SELECT * FROM a LEFT JOIN b ON b.a_id = a.id
so if there are 2 records in the table "b" then I should get a list of 2 elements, not a one element list with a list inside that has values from table "b". Additionally, I will point out that I care to implement this by the function "Page findAll(#Nullable Specification spec, Pageable pageable);".
Example entity:
#Entity
public class A {
private Long id;
#OneToMany
private List<B> b;
soo i like to look like this
#Entity
public class A {
private Long id;
#OneToOne
private B b;
But when theres more that one B i will get second rekord instede of error.
What can I do to achieve this?

Join 2 tables using Spring JPA

I have two entities(Invoice and InvoiceItems) without adding any relationship.
Invoice
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invoiceID;
#Column(name="code")
private String code;
//other columns
}
Invoice Items
public class InvoiceItems {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invItemID;
#Column(name="invoice_id")
private Integer invoiceId;
//other columns
}
Can I join these entities and get data without adding relationship using JPA?
If it isn't possible how to join 2 entities using JPQL or Native query?
If your data is valid then using native query you can do that
#Query(nativeQuery = true, "select * from Invoice i join InvoiceItems im on i.id = im.invoice_id")
public List<Invoice> findData();
But that is not a good way join without relation using JPA.
Yes, you can join these entities and get data without adding relationship using JPA, but it's a little bit losing the purpose of using JPA.
You need to create a java class first, which will be the returning data object from the DB. After that you can use entityManager's createNamedQuery method to get the result.
createNamedQuery(String sqlString, ResultClass.Class)
sqlString may be something like:
SELECT INV.INVOICE_ID
INV.CODE
INV_ITEMS.INV_ITEM_ID
FROM INVOICE INV
JOIN INVOICE_ITEMS INV_ITEMS
ON INV.INVOICE_ID = INV_ITEMS.INVOICE_ID;
And the corresponding ResultClass:
public class ResultClass {
private Long invoiceID;
private String code;
private Long invItemID;
// other columns
}
Or you can even use RowMapper to map the object all by yourself for more flexibility by using JdbcTemplate with query() method.

How to do JPA join in one to many situations?

I have two entities Outlet and products having one to many relationship as follows-
#Entity
public class Outlet{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
Long id;
#Fetch (FetchMode.SELECT)
#OneToMany(mappedBy = "outletProduct",fetch = FetchType.LAZY)
List<Product> products;
and
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
Boolean isActive;
#ManyToOne
#JoinColumn(name = "outletId")
Outlet outletProduct;
Now i want Outlets where products isactive is true.
I am writing follwing JPQL for-
#Query("Select op From Outlet op join op.products pro where pro.isActive=1 order by op.id")
List<Outlet> findAllByVarietiesPriceTimePeriodIn( );
But i am still getting outlets with prodcuts isactive false.
Please suggest.
I want to achieve result which will come from this sql query-
SELECT * FROM outlet join product on product.outlet_id= outlet.id where product.is_active=1 ;
I want Outlet where it has at least one Product but i dont want Products which is isactive false. Right now I am getting all Products in the List
It's not recommended, but will work using JOIN FETCH:
Select op From Outlet op join fetch op.products pro where pro.isActive = 1 order by op.id

one-way one-to-many throws Hibernate Cannot add or update a child row: a foreign key constraint fails

I have an application that teaches the user how to play various card games. The data model that gets persisted consists of a TrainingSession with a uni-directional one-to-many relationship with the Hands.
[EDIT] To clarify, a Hand has no existence outside the context of a TrainingSession (i.e they are created/destroyed when the TrainingSession is). Following the principals of Data Driven Design, the TrainingSession is treated as an aggregate root and therefore a single spring-data CrudRepository is used (i.e., no repository is created for Hand)
When I try to save a TrainingSession using a CrudRepository, I get: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (blackjack.hand, CONSTRAINT FKrpuxac6b80xc7rc98vt1euc3n FOREIGN KEY (id) REFERENCES training_session (tsid))
My problem is the 'save(trainingSession)' operation via the CrudRepository instance. What I don't understand is why the error message states that FOREIGN KEY (id) REFERENCES training_session (tsid)). That seems to be the cause of the problem but I cant figure out why this is the case or how to fix it. The relationship is uni-directional and nothing in the Hand class refers to the TrainingSession.
The code, minus all the getters and setters, is:
#Entity
public class TrainingSession {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer tsid;
private String strategy;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="id")
private List<Hand> hands;
private int userId;
protected TrainingSession() {
}
public TrainingSession(int userId, Strategy strategy, List<Hand> hands) {
this.strategy = strategy.getClass().getSimpleName();
this.hands = hands;
this.userId = userId;
}
while Hand is
#Entity // This tells Hibernate to make a table out of this class
public class Hand {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private int p1;
private String p1s;
private int p2;
private String p2s;
private int d1;
private String d1s;
private int trials;
private int score;
public Hand() {
}
You need to save your TrainingSession and Hand objects first before saving the adding the hand objects to TrainingSession.
TrainingSession ts1 = new TrainingSession();
trainingSessionManager.save(ts1);
Hand hand1 = new Hand();
handManager.save(hand1);
Hand hand2 = new Hand();
handManager.save(hand2);
ts1.gethands().add(hand1);
ts1.gethands().add(hand2)
trainingSessionManager.save(ts1);
If you check your database you will find 3 tables TrainingSession, Hand and TrainingSession_Hand, The TrainingSession_Hand table references to both TrainingSession and Hand both. Therefore you need to save TrainingSession and hand before saving the relationship.
Found the problem. I was assuming that when spring-data set up the DB tables, it was able to figure out and set up the uni-directional 1-to-many relationship. Apparently that isn't the case. When I configure the relationship as bi-directional everything seems to work.
To fix things I:
removed from TrainingSession the #joincolumn annotation for hands
in Hands I added a TrainingSession field with a #ManyToOne annotation:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "tsid", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private TrainingSession tsession;
I also added in the Hand class the getter/setter for tsession
I can now do a save of the entire aggregate construct using only a TrainingSessionRepository.

spring data jpa findAll() not working properly

I am having below classes
#Entity
#Table(name = "USR_E_GROUPS")
public class GroupEntity {
#Id
#Column(name = "UIDUSERGROUP")
#GenericGenerator(name = "generator", strategy = "uuid2")
#GeneratedValue(generator = "generator")
private String id;
.........
#OneToMany(mappedBy = "group", cascade = CascadeType.PERSIST)
private List<UserGroupEntity> users;
same is for UserGroupEntity
now if I use groupRepoository.findAll()
It's is firing select query for every Group and inside different select query for UserGroupEntity. so it's taking too much time.
I want to make it to fire select with join so it will be a single query.
This is probably an n + 1 issue.
From the docs
By default, Hibernate3 uses lazy select fetching for collections and
lazy proxy fetching for single-valued associations. These defaults
make sense for most associations in the majority of applications.
By default the children are fetched lazily. Use JOIN FETCH to get the result in a single query.
In your GroupRepoository
#Query("SELECT g FROM GroupEntity g JOIN FETCH g.users gu")
List<GroupEntity> findAllEager();

Resources