Relationships between JPQL, Java (and the Oracle DB) - java

I am confused about the above relationship.
My Oracle DB Tables:
xd_Users: user_id (pk), client_id (fk), name, doj, email, dol
xd_Managers: manager_id (pk), user_id (fk)
Corresponding Java Entities User and Manager relate to the above two tables respectively. Manager and User are separate, not related by inheritance, and have fields that correspond to the DB tables.
In my application, a Manager has to be a User.
I am writing the a(n as yet unfinished) method (in a class called PersistService) to retrieve a list of users who are managers.
public static ArrayList<User> getManagersForClient(Client client) {
Long clientId = client.getClientId();
EntityManager em = getEntityManager();
String sqlQuery = "SELECT u FROM XD_USERS u, XD_MANAGERS m WHERE u.CLIENT_ID = :clientId";
TypedQuery<User> query = em.createQuery(sqlQuery, User.class);
query = query.setParameter("clientId", clientId);
ArrayList<User> clientUsers = (ArrayList<User>) query.getResultList();
for (User user : clientUsers) {
}
return clientUsers;
}
The pseudo-sql query I constructed was (:client_id at the end is just the java variable, hence the pseudo-sql):
select * from users u join managers m on u.user_id = m.user_id where u.client_id = :client_id;
I am having trouble converting this to a valid JPQL query. I don't understand how to think about solving this. In particular, the relationship between the identification variable, the single-valued relationship field, the collection-valued relationship field and the persistent field is very confusing. And I am even more confused by this post. Please help!

If you have coded the related entities correctly then your sql select query is something like this in jpql (according to the structure of related tables you gave):
select u.userId,u.name,u.doj,u.email, u.dol, m.managerId
from User u
join u.manager m
where u.clientId = :client_id;

Related

Dyamic table name for JPQL / Hibernate query

I've a database with many thousands of tables that have been (and continue to be) created with a naming strategy - one table per calendar day:
data_2010_01_01
data_2010_01_02
...
data_2020_01_01
All tables contain sensor data from the same system in the same shape. So a single entity (lets call it SensorRecord) will absolutely map to all tables.
I'd imagined something like this would work:
#Query(nativeQuery = true, value = "SELECT * FROM \"?1\"")
Collection<SensorRecord> findSensorDataForDate(String tableName);
But it does not, and reading around the topic seems to suggest I am on the wrong path. Most posts on dynamic naming seem to state explicitly that you need one entity per table, but generating thousands of duplicate entities also seems wrong.
How can I use JPA (JPQL?) to work with this data where the table name follows a naming convention and can be changed as part of the query?
Parameters are only allowed in the where clause.
You can create custom repository method returns collection of SensorRecord dto. No need to map so many entities. You should get List<Object []> as query result and manually create dto objects.
#Autowired
EntityManager entityManager;
public List<SensorRecord> findSensorDataForDate(LocalDate date) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy_MM_dd");
String tableName = "data_" + date.format(formatter);
Query query = entityManager.createNativeQuery(
"select t.first_column, t.second_column from " + tableName + " t");
List<Object[]> queryResults = query.getResultList();
List<SensorRecord> sensorRecords = new ArrayList<>();
for (Object[] row : queryResults) {
SensorRecord record = new SensorRecord();
record.setFirstParameter((Integer) row[0]);
record.setSecondParameter((String) row[1]);
sensorRecords.add(record);
}
return sensorRecords;
}
Could it be just syntax error?
This has worked for me:
#Query(value = "select * from job where job.locked = 1 and job.user = ?1", nativeQuery = true)
public List<JobDAO> getJobsForUser(#Param("user") String user);

How to determine if two lists share any objects with Hibernate

I have this example SQL query that I created that tries to find all REPORTs that have an associated REPORT_PERMISSION object with one of the USER_GROUPs that the current user also has. So there are many REPORT_PERMISSION objects that tie a report to a group, and a user can have many groups, just one of those have to match up to allow the user to view the report. I'm just not sure how to write this in HQL
SELECT * FROM REPORT r
JOIN REPORT_PERMISSION rp
on r.id = rp.report_id and rp.user_group_id in
(SELECT l.user_group_id FROM USER_GROUP_LINK l where l.user_id = 2)
where r.type = 'GENERAL';
it should be something like :
Query reportQuery = entityManager.createQuery
("select distinct rep from Report rep
join rep.reportPermissions per
join per.userGroups gr
join gr.users u
where u.id = :userId and rep.type = 'GENERAL'");
reportQuery.setParameter("userId" , user.getId());
example mapping (names that are used in hql , and fileds name in mappings ) :
rep.reportPermissions -- Collection < ReportPermission> reportPermissions in Report;
per.userGroups -- Collection < UserGroup> userGroups in ReportPermission;
gr.users -- Collection < User> users in UserGroup;
u.id -- #Id Long id; in user class

How to create mapping of composed object with Hibernate when using a complex sqlquery?

I am trying to use the below query with Hibernate's session.createSQLQuery.
The Entity object corresponding to user has an attribute called address.
The address object is created out of 5 fields from table 'user'.
If I do not use an SQLQuery it gets filled auto-magically.
However without the SQLQuery I can't get all the info I would get from the desired joins shown below.
The user entity object also attributes like accessPlan which I am filling up using
.addEntity("accessPlan", AccessPlan.class)
Query:
SELECT
user.*,
ap.*,
country.*,
auth.*,
GROUP_CONCAT(coup.code SEPARATOR ' ') coupons
FROM
user
INNER JOIN access_plan ap ON (user.access_plan = ap.id)
INNER JOIN country ON (user.country=country.code)
LEFT JOIN user_auth auth ON (user.id = auth.userid)
LEFT JOIN (
SELECT
trans.user_id,coupon.code
FROM
payments_transaction AS trans
INNER JOIN payments_coupon coupon ON (trans.payments_coupon_id=coupon.id)
) coup ON (user.id=coup.user_id)
GROUP BY user.id;
What can be the easiest way to fill up the composed address object while using the SQLQuery?
OR
Is there a way to avoid using SQLQuery for a query like this?
Please check below example from the section 'Returning multiple entities'
String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class).list()
In your case, cat = user, mother = address... somewhat like that.
I do not have anything to try out at the moment but I guess this will help.

How to retrieve a member object of a class using Hibernate?

Using following code I can successfully retrieve address fields of a user, to do that I need to define all its fields using Projection. Imagine address has 100 fields, in this case I have to define all of them.
I am wondering if I can return just address object of customer without defining all its fields in Proposition?
I know I can retrieve id of address and use that to retrieve its object, but I am wondering if there is ano other method rather than this or defining all its fields.
Hibernate
.....
Criteria cre = session.createCriteria(User.class, "user")
.createAlias("user.address", "addr");
cre.add(Restrictions.eq("user.id", ID));
ProjectionList pl = Projections.projectionList();
pl.add(Projections.property("addr.id").as("id"));
pl.add(Projections.property("addr.unit").as("unit"));
.......
cre.setProjection(pl);
Address address = (Address) cre.list().get(0);
I used the following as well but it runs into error (could not resolve property: addr of: com.myProject.User)
pl.add(Projections.property("addr").as("address"));
Java
#Entity
public Class User {
#Id
#GeneratedValue
private long id;
#OneToOne
private Address address;
...
}
Use JPQL/HQL:
select a from User u join u.address a where u.id = :userId
The Criteria API is more limited than JPQL, and can't select any other entity than the root entity. It shouldn't be used if the query doesn't have to be dynamically composed. Of course, if the association is bidirectional, you can simply use
select a from Address a where a.user.id = :userId
or its equivalent Criteria:
Criteria c = session.createCriteria(Address.class, "a");
c.createAlias("a.user", "u");
c.add(Restrictions.eq("u.id", userId));
If the result you pull in from a query will match the fields of a DAO you have defined. I would just type-cast the result from an hql or native SQL query.
Select *
From Address a
where a.id = :userid
Address addrObject = (Address) query.uniqueResult();
Do like this
Criteria criteria = session.createCriteria(User.class, "user")
.createAlias("user.address", "addr")
.add(Restrictions.eq("user.id", userId))
.setProjection(Projections.property("addr"));
Address address = (Address) criteria.list().get(0);
Couple of options:
use lazy="false" for Address object. If you have to use lazy=true for some reason, you can run this query in a separate session and override the lazy behavior in that session.
Use the database specific query to get a list of field names and then dynamically generate Projections by looping through the field names.
For example,
In mysql
SHOW COLUMNS FROM Address
In postgres
SELECT * FROM information_schema.columns
WHERE table_schema = your_schema
AND table_name = your_table
I hope this helps.

How do I port query with join across multiple tables from JDOQL to HQL

I am porting an application for KodoJDO to Hibernate.
I have a query that goes across 4 tables in the db, and 3 objects in the java code.
In English the query is Find the users that have entitlements in system X.
my JDOQL where clause called on the User object was
where entitlements.contains(ent) && (upper( ent.system.id ) = 'EVPN')
some sql that does the query is:
select unique(u.id)
from USER u, USERENTITLEMENT ue, ENTITLEMENT e, SYSTEM s
where u.id = ue.userid
and ue.entitlementid = e.id
and e.systemid = s.id
and s.id = 'evpn'
My best guess for HQL gives me an exception
org.hibernate.hql.ast.QuerySyntaxException: unexpected AST node: ( [select user from com.ebig.entity.User as user, com.ebig.entity.Entitlement as ent, com.ebig.entity.System as sys where entitlements.contains(ent) and ent.system = sys and sys.id = 'evpn']
the db is structured like this:
User
id
UserEntitlement
userid
entitlementid
Entitlement
id
systemid
System
id
the java code is structured as below:
class User
{
String id;
Set<Entitlement> entitlements;
}
class Entitlement
{
String id;
System system;
}
class System
{
String id;
}
Update My final query that works
hqlQuery = "select distinct user from User as user "+
"inner join user.entitlements as entitlement inner join entitlement.system as system "+
"where system.id = 'evpn' AND mod(user.flags, 2) = 0 AND source = 1";
Yes I know I should use parameters, but I have a great many problems to solve, and will post pone that code for another day.
Another variation with an implicit inner join for entitlement to system
hqlQuery = "select distinct user from User as user "+
"inner join user.entitlements as entitlement "+
"where entitlement.system.id = 'evpn' AND mod(user.flags, 2) = 0 AND source = 1";
You should use joins :
select distinct u.id from User u
inner join u.entitlements as entitlement
inner join entitlement.system as system
where system.id = :evpn
where :evpn is a named parameter that you have to bind.
You must think in terms of objects and relationships between objects when doing HQL, and not in terms of tables, foreign keys and join tables.

Categories

Resources