EclipseLink inheritance queries: Avoid joining - java

Right now, In a short example, if i have two entities Person and Applicant and i want to write a query that gets all applicants in my database, i end up getting referenced queries with the following set up. I am using Single_Table_Inheritance strategy (fyi)
public class Person {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "schoolID")
private School school;
}
The Teacher object with a reference to another entity.
public class Applicant extends Person {
#OneToMany(targetEntity = ApplicantSchool.class, cascade = { javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REMOVE }, mappedBy = "applicant")
private Set<ApplicantSchool> schools = new HashSet<ApplicantSchool>();
}
Query attempt that jumps and attempts to query for the school relationship in Person
public List<Applicant> getAllApplicants(){
EntityManager entityManager = factory.createEntityManager();
#SuppressWarnings("unchecked")
List<Applicant> applicants = entityManager.createQuery("Select a from Applicant a LEFT JOIN FETCH a.schools WHERE a.active = :active ")
.setParameter("active", true).setHint("eclipselink.refresh", "true")
.getResultList();
//List<Applicant> applicants = Applicant.findAllApplicants();
System.out.println("The long query getting applicants");
entityManager.close();
return applicants;
}
If i try to change my query to use nested joining, i get an error saying relationship not recognized. I tried to query like so:
entityManager.createQuery("Select a from Applicant a LEFT JOIN FETCH a.schools WHERE LEFT JOIN FETCH a.person.school a.active = :active ")
UPDATED
After removing the keyword "left join fetch" my new query:
List<Applicant> applicants = entityManager.createQuery("Select a from Applicant a WHERE a.active = :active ")
.setParameter("active", true)
.getResultList();
The only query i agree with that it first creates is this one
SELECT personID, TYPE, DATEADDED, FIRSTNAME, LASTNAME, MIDDLENAME, ACTIVE, BIRTHDAY, EMAILADDRESS, ETHNICITY, GENDER, HISPANIC, IMAGEPATH, MARITAL, NATIVELANGUAGE, PRIMARYTELEPHONE, RELIGIOUSAFFILIATION, SECONDARYTELEPHONE, version, addressID, schoolID, MAJOR FROM PERSON WHERE ((ACTIVE = ?) AND (TYPE = ?))
bind => [true, AP]
Returns this stack trace and the following sql's selects are created:
[EL Fine]: sql: 2012-07-13 17:25:52.297--ServerSession(1529073996)--Connection(726700617)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT schoolID, ACTIVE, ADMISSIONSEMAILADDRESS, ADMISSIONSPHONE, CODE, description, HELPGENERALEMAILADDRESS, NAME, PRIMARYPHONE, version, addressID FROM SCHOOL WHERE (schoolID = ?)
bind => [1]
[EL Fine]: sql: 2012-07-13 17:25:52.305--ServerSession(1529073996)--Connection(1615948530)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT id, ACTIVE, CODE, DESCRIPTION, NAME, version, SCHOOLDEPARTMENT_schoolID FROM DEPARTMENT WHERE (SCHOOLDEPARTMENT_schoolID = ?)
bind => [1]
[EL Fine]: sql: 2012-07-13 17:25:52.308--ServerSession(1529073996)--Connection(893810654)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT programID, ACTIVE, CODE, description, NAME, PROGRAMTYPE, REQUIREDCREDITS, version, SCHOOL_schoolID FROM PROGRAM WHERE (SCHOOL_schoolID = ?)
bind => [1]
[EL Fine]: sql: 2012-07-13 17:25:52.31--ServerSession(1529073996)--Connection(399107363)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT personID, TYPE, DATEADDED, FIRSTNAME, LASTNAME, MIDDLENAME, ACTIVE, BIRTHDAY, EMAILADDRESS, ETHNICITY, GENDER, HISPANIC, IMAGEPATH, MARITAL, NATIVELANGUAGE, PRIMARYTELEPHONE, RELIGIOUSAFFILIATION, SECONDARYTELEPHONE, version, addressID, schoolID, MAJOR, studentId FROM PERSON WHERE (schoolID = ?)
bind => [1]
[EL Fine]: sql: 2012-07-13 17:25:52.314--ServerSession(1529073996)--Connection(464642021)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT registrationId, SEMESTERTYPE, version, YEAR, semesterCourseId, personID FROM REGISTRATION WHERE (personID = ?)
bind => [501]
[EL Fine]: sql: 2012-07-13 17:25:52.322--ServerSession(1529073996)--Connection(395757277)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT registrationId, SEMESTERTYPE, version, YEAR, semesterCourseId, personID FROM REGISTRATION WHERE (personID = ?)
bind => [552]
[EL Fine]: sql: 2012-07-13 17:25:52.327--ServerSession(1529073996)--Connection(328677993)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT registrationId, SEMESTERTYPE, version, YEAR, semesterCourseId, personID FROM REGISTRATION WHERE (personID = ?)
bind => [602]
[EL Fine]: sql: 2012-07-13 17:25:52.358--ServerSession(1529073996)--Connection(873312397)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT addressID, CITY, COUNTRY, STATE_US, STREETADDRESS, STREETADDRESS2, version, ZIPCODE FROM ADDRESS WHERE (addressID = ?)
bind => [2]
[EL Fine]: sql: 2012-07-13 17:25:52.365--ServerSession(1529073996)--Connection(509650638)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT applicantSchoolID, CREDITSCOMPLETED, FROMMONTH, FROMYEAR, GPA, NAME, SCHOOLTYPE, TOMONTH, TOYEAR, version, APPLICANT_personID FROM APPLICANTSCHOOL WHERE (APPLICANT_personID = ?)
bind => [151]
[EL Fine]: sql: 2012-07-13 17:25:52.385--ServerSession(1529073996)--Connection(17334753)--Thread(Thread["http-bio-8080"-exec-18,5,main])--SELECT applicantSchoolID, CREDITSCOMPLETED, FROMMONTH, FROMYEAR, GPA, NAME, SCHOOLTYPE, TOMONTH, TOYEAR, version, APPLICANT_personID FROM APPLICANTSCHOOL WHERE (APPLICANT_personID = ?)
bind => [51]

You cannot nest join in JPA, but you shouldn't need to when querying on Applicant.
JPQL "Select a from Applicant a LEFT JOIN FETCH a.schools LEFT JOIN FETCH a.school where a.active = :active " should work, since Appliant IS a Person and so has the school relationship. Nesting is only if Applicant has a relation to person and you needed to fetch join both person and school.
If that is the case, EclipseLink allows batch reading or joins through query hints described here:
http://wiki.eclipse.org/EclipseLink/Examples/JPA/QueryOptimization
You can also specify batching/fetch joins at the mapping level using EclipseLink annotations described here:
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_batchfetch.htm#CHDCCIDA
and
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_joinfetch.htm#CEGFFHHA
Batch reading will result in 2 queries, while fetch joins in 1.
For the query described above, you could also use:
em.createQuery("Select a from Applicant a WHERE a.active = :active ")
.setParameter("active", true)
.setQueryHint("eclipselink.left-join-fetch", "a.schools");
.setQueryHint("eclipselink.left-join-fetch", "a.school");
.getResultList();

Related

I am new to springboot and need to insert json data in oracle table and avoid duplicate inserts

I am able to insert the json data in oracle table using intermediate table from springboot
I have a table abc-
ID first_name last_name cust_ID Active_Ind last_upd_dt
1 abc pqr 101 Y 01-Apr-2021
2 aaa bbb 102 Y 05-Feb-2021
I need to make sure-
If the new json data has the above existing value, do not update table abc, keep it as is and if it has new record only then insert. And if the oracle table record is not present in new json data, then change the ACTIVE_IND to 'N'
I tried the below query to insert value where not exists from intermediate table 'test':
insert into abc
(ID,
first_name,
last_name,
cust_ID,
active_ind,
last_upd_dt)
select
abc_seq.nextval,
first_name,
last_name,
cust_ID,
active_ind,
last_upd_dt
from test t
where not exists(
select null from abc a
where a.fist_name = t.first_name
and a.cust_ID = t.cust_ID);
This works fine in Oracle developer, but when I try the below query in springboot, it somehow inserts duplicates, not sure why it is happening, I have used prepared statement for the index.
insert into abc
(ID,
first_name,
last_name,
cust_ID,
active_ind,
last_upd_dt)
select
abc_seq.nextval,
?,
?,
?,
?,
?
from test t
where not exists(
select null from abc a
where a.fist_name = t.first_name
and a.cust_ID = t.cust_ID);```
I have tried merge queries as well, but none of them worked for me.
If you are using spring boot I'm assuming you have mapped entities and you are using JPA.
Once you mapped the entity
#Entity
#Table(name = "abc")
public class ABC {
#Id
private Long id;
#Column(name = "name")
private String name;
...
}
Then I also assume you created a Repository (if you are using the Repo pattern)
public interface ABCRepository extends JpaRepository<ABC, Long> {
//leave it blank If you don't need any particular method for querying
}
In this case you can use the findOne JPA method passing the Example of the object that you want the result to match. If it's just the ID then simply use the findByID. Anyways, you can now create yourself a saveIfExists(Example example)
public ABC saveIfNotExists(Example<ABC> example) {
return abcRepo.findOne(example).orElse(()-> {
return abcRepo.save(example);
};
}

How to check before insert?

I have a table with 6 columns.
1.ID (number)(PK)
2.USER_ID (number)
3.ROLE_ID (number)
4.CREATED_TS (date)
5.CREATED_BY (VARCHAR2)
6.ACTIVE_IND (VARCHAR2)
I am inserting data via a POST REST API Call in that table. I am firing a query in Java Repository.
public UserRoles save(final UserRoles u) {
final String insertQuery = "INSERT INTO VEB_USER_ROLES(ID, USER_ID, ROLE_ID, CREATED_TS, CREATED_BY, ACTIVE_IND) " +
"VALUES(VEB_USER_ROLES_SEQ.NEXTVAL, ?, ?, ?, 'PROD_MASTER', 'Y')";
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(insertQuery, new String[]{"ID"});
ps.setLong(1, u.getUserId());
ps.setLong(2, u.getRoleId());
ps.setDate(3, new java.sql.Date(System.currentTimeMillis()));
return ps;
}
}, keyHolder);
return u;
}
The query is working fine and the data is been inserted.I just want to add a check if same UserID and RoleID exists it should not insert.
for eg in the DB I have Userid '1' and roleId '1' and I send the same values ie userid '1' and role id '1' it should not get inserted.
Can anyone guide me regarding this.
Create a UNIQUE constraint on the two columns: USER_ID and ROLE_ID
ALTER TABLE yourtablename ADD CONSTRAINT uq_yourtablename UNIQUE(column1, column2);
Examples: Status on Insertion-
-- succeeds:
INSERT yourtablename(USER_ID, ROLE_ID) VALUES(1, 22);
-- succeeds:
INSERT yourtablename(USER_ID, ROLE_ID) VALUES(2, 23);
-- fails:
INSERT yourtablename(USER_ID, ROLE_ID) VALUES(1, 23);
If its not possible to ALTER the DB structure, you can check this also by your code:
- Before inserting new record check USER_ID & ROLE_ID exists in DB.
Use a select query & check whether a similar record exists.
Its just a idea, please make it working for you, if required:
$exists = SELECT COUNT(*) no_of_similar_records FROM yourTableName WHERE USER_ID = $USER_ID AND ROLE_ID = $ROLE_ID
If it has a value the do not INSERT again.
So here we do not need a DB update on structure.
This may help you..
Couple of ways to handle this
Add unique constraint on the table on columns Userid, Roleid or,
Do a SELECT before trying to insert to check if they already exists or,
Use merge into insert when NOT MATCHED

Joining db table

I have 2 db tables:
courses
id|name|teacher_id
teachers
id|first_name|last_name|email
I want to show id, name from courses table and first_name, last_name from teachers table.
I used full join method:
select name,
first_name,
last_name
from courses
full join teachers on teachers.id = courses.teacher_id;
And I'm getting the error below :
Unknown column 'courses.teachers_id' in 'on clause'
You don't have FULL JOINS on MySQL, but achieve this as below:
In case you intend to do FULL OUTER JOIN:
select name, first_name, last_name from courses left join teachers on teachers.id = courses.teacher_id;
union all
select name, first_name, last_name from courses right join teachers on teachers.id = courses.teacher_id;
For INNER JOIN
select name, first_name, last_name from courses left join teachers on teachers.id = courses.teacher_id;
Try this one:
select name, first_name, last_name from courses
inner join
teachers on teachers.id = courses.teacher_id;

SQL: Invalid column name eventhough column is there?

I have the following Spring Data Query:
#Query(value = "select * from person where person_id =?! and family_id not in (select related_person_id from relationships where related_family_id = ?1)", native query = true)
Person getPerson(String personId);
I am getting the error:
Caused by: java.sql.SQLException: Invalid column name
However, I know that all my column names for the two tables in my query are correct, what could ne causing this?
i don't know the structure of your data but your spring data query has many typos and errors, the standard query method should be:
#Query(value = "select * from person where person_id =?1 and family_id not in (select related_person_id from relationships where related_family_id = ?2)", nativeQuery = true);
Person findByPersonIdAndRelatedFamilyId(String personId, String relatedFamilyId);
also check your inner select query -I don't know the relation between family_id and related_person_id- but it should return a family_id column or an aliased column as family_id may be thats why you're receiving such error ..

Get multiple entities from hibernate sql join

I have 4 table:
Orders(orderID, orderDate, orderItem (OneToMany))
OrderItem(id, order(manyToOne), book (manyToOne), quantity)
Book (id, title, cost)
User(id, username, password)
Here is my query in SQL:
String sql = "SELECT orders.id, book.title, orderitem.quantity
FROM orderitem INNER JOIN book ON book.id = orderitem.book_id INNER JOIN orders ON orders.id = orderitem.orders_id WHERE user_id = 1;
(user_id is the foreign key of User in Orders table)
(orders_id is the foreign key of Orders in OrderItem table)
List<OrderItem> orderBookInfo = (List<OrderItem>) session.createSQLQuery(sql); // returns List<Object[]> why?!
This query result comes from joining of 3 tables (Book, Order, OderItem)
And this is the result in table:
Question is how can i assign each result's column to it's corresponding properties?
For example:
orderBookInfo.order.id = (first location of orderBookInfo)
orderBookInfo.book.title = (second location of orderBookInfo)
You need to execute an Entity query instead. Assuming you already mapped the entities properly, this is how the HQL query would look like:
SELECT o
FROM orderitem oi
JOIN FETCH oi.book
JOIN FETCH oi.orders
JOIN FETCH oi.user u
WHERE u.id = 1;

Categories