I'm using the Java spring framework for security. My pre-existing table layout differs from spring's expected, but I'm allowed to specify a custom authorities-by-username query on the jdbc-user-service. The problem is that this query expects only a single parameter (?) in the SQL statement. Basically, if the user exists in the users table at all, they should get 'ROLE_USER'. If they exist in the auth table as 'S', they should get 'ROLE_USER', 'ROLE_LICENSEE' and 'ROLE_SYSADMIN'. If they exist in the auth table as 'L', they should get both 'ROLE_LICENSEE', and 'ROLE_USER'.
SELECT U.email AS 'username', 'ROLE_USER' AS 'authority' FROM users U WHERE U.email=**?**
UNION
SELECT U.email AS 'username', 'ROLE_LICENSEE' AS 'authority'
FROM users U, auth_sys A
WHERE U.user_id=A.user_id AND A.auth_type IN ('L', 'S') AND U.email=**?**
UNION
SELECT U.email AS 'username', 'ROLE_ADMIN' AS 'authority'
FROM users U, auth_sys A
WHERE U.user_id=A.user_id AND A.auth_type='S' AND U.email=**?**;
My question is how can I reduce this from 3 (?)'s down to 1?
Since you are already using Spring Framework, you can use the Spring Framework to make your JDBC calls as well. Spring Framework has a NamedParameterJdbcTemplate that allows you to name your JDBC parameters. So, you can create a named parameter called :emailAddress for example, and use the same parameter three times in the SQL, but pass it in only once into the template.
It sounds like you won't be able to do that; since it sounds like you want a separate row for each user if they're in multiple roles.
If you can get by with only one row returned per user; and can assume that any Admin is also a licensee (evident by your where clause), you should be able to use a case statement and a left join.
Something like:
SELECT u.Email,
CASE WHEN a.user_id is null then 'Role_User'
WHEN a.auth_type = 'S' then 'Role_Admin'
WHEN a.auth_type = 'L' then 'Role_Licensee' end as 'authority'
FROM users u LEFT JOIN auth_sys a on u.user_id = a.user_id
WHERE u.email = **?**
Granted; i'm not completely familiar with the table structure, so it might require a little tweaking; but that should be enough to get you started
As far as I know is not possible. You have to use three query with union.
Related
I have a constant set of users that I want to filter out and apply to each query to look at what they are doing on an app. I have been poking and probing around here to get a better sense of how to do this, but it is still unclear to me. I am a newbie with SQL statements and JAVA. Any help, particularly explanation is highly welcomed.
I have been trying to figure it out using these two articles to no avail: 1 & 2; these examples show how you can create a query and use it in another query, which is what I am trying to do.
Someone mentions a wrap (see ref. 1) which is what i attempt to do:
Sure, wrap your two set operations in outer queries with selects that include fixed columns called Source, i.e. SELECT
'horrible_query_1' AS Source, * and SELECT 'ugly_query_2' AS Source,
*.
That way your UNION will give you all the columns from your query plus the source identifier.
My question: is it possible to repurpose these queries into separate queries without having to do joins with the variable? I don't understand how that works (I'm not sure I am saying it right either).
This is the User group and filter I want to repurpose across all queries:
<select id="get_num_x_users" resultClass="java.lang.Integer">
select count(distinct user_id) from positions sp
join position_definitions sd on sp.position_id = sd.id
where sd.position like '%catcher%'
or lower(sd.position) like '%pitcher%'
or lower(sd.position) like '%dh%';
</select>
Repurpose into this query (among a few others):
<select id="get_u_counts" resultClass="java.lang.Integer">
SELECT COUNT(DISTINCT u.id)
FROM searches s
JOIN users u
ON s.user_id = u.id
WHERE search_date > DATE_SUB(curdate(), INTERVAL 3 MONTH)
AND LENGTH(search_term) > 0
AND search_term NOT LIKE ' LIC: SPEC: %'
AND u.type != 'coach';
</select>
My attempt (That does not work when I am in mysql database):
with get_x_users as (
select count(distinct user_id) from positions sp
join position_definitions sd on sp.position_id = sd.id
where sd.position like '%catcher%'
or lower(sd.position) like '%pitcher%'
or lower(sd.position) like '%dh%';),
(SELECT COUNT(get_x_users)
FROM searches s
JOIN users u
ON s.user_id = u.id
AND get_x_users
WHERE search_date > DATE_SUB(curdate(), INTERVAL 3 MONTH)
AND LENGTH(search_term) > 0
AND u.type != 'coach');
In SQL we can join tables together using a common field. For instance, if you have a user_id field in two different tables you can match the records using that common field. You can also create a join that gives you all the records in one table and only those that match in the second table, that's what LEFT JOIN does.
Using this principle, you do a little reverse logic and create a query which gives you all the records from your search query (ExcludeQuery) and join it to the users whom you want to exclude. This will give you a list of records with and without matches to the excluded users. What you do then is use a where to only include the records that haven't matched an excluded user, WHERE ExcludeQuery.user_id IS NULL.
SELECT s.*
FROM searches s
JOIN users u
ON s.user_id = u.id
WHERE search_date > DATE_SUB(curdate(), INTERVAL 3 MONTH)
AND LENGTH(search_term) > 0 AND u.type != 'coach'
LEFT JOIN
(select user_id from positions sp
join position_definitions sd on sp.position_id = sd.id
where sd.position like '%catcher%'
or lower(sd.position) like '%pitcher%'
or lower(sd.position) like '%dh%') AS ExcludeQuery
ON ExcludeQuery.user_id=s.user_id
WHERE ExcludeQuery.user_id IS NULL
I am migrating a Struts2 Web Application from Hibernate V3 to Hibernate 4.3.5.
In Hibernate V3 we used SQL statements like the following, and they worked well :
select u.id as id,
u.userId as userId,
sum(u.totalSearchedFields) as total,
u.date as date,
u.status as status
from user u;
After migrating to Hibernate V4.3.5, the above SQL statement shows the error:
org.hibernate.QueryException: , expected in SELECT
We made it work by removing the aliases from the SQL statement, like this:
select u.id,
u.userId,
sum(u.totalSearchedFields),
u.date,
u.status
from user u;
Does anyone recognize the cause for this? How to fix this problem without removing the aliases?
Appearently, according to the Hibernate 4 developer guide, the aliases should be used in an "inversed" way from what you did before:
SELECT <PROPERTY_NAME> AS <ALIAS_TABLE.PROPERTY_NAME>
FROM TABLE AS <ALIAS_TABLE>
So try changing this:
select u.id as id,
u.userId as userId,
sum(u.totalSearchedFields) as total,
u.date as date,
u.status as status
from user u;
to this:
select id as {u.id},
userId as {u.userId},
sum(totalSearchedFields) as total,
date as {u.date},
status as {u.status}
from user u;
The approach seems a bit different from the SQL's one, because this aliases are intended to be used by Hibernate to prevent multiple columns with the same name to conflict, while in SQL they were used to generate an user-friendly ResultSet.
A long shot BTW, hope that helps.
I was always taught to use IDs in my code to refer to records into the database.
But let's take the case we have same roles in the table Role. Now I want to query only the records related to the role Player:
ID ROLE
1 Admin
2 Organizer
3 Player
I don't know in my code the ID of Player, but I want to retrieve all the players, so with Hibernate I wrote:
String queryString = "from User u where u.role.role = ?";
Query queryObject = getSession().createQuery(queryString);
queryObject.setParameter(0, "player");
return queryObject.list();
As you can see I wrote "player" in the code. I think this is not the best way and I should use an ID instead. But I don't know the ID and it may change depending on the server on which I run the application. A second problem with my solution is that "player" can be capitalized into the database and this may be changed over time.
So, what should be the solution to all these problems? Is there any way to use the ID instead? Or any other way to improve this code?
In this case it seems that role should be an enum and your query would look something like:
queryObject.setParameter(0, Role.PLAYER);
Also, you might take a look at the criteria API which will help you create more type-safe queries that are more robust vs. refactoring.
You should create a enum class like this.
public enum Role {
Admin(1),
Organizer(2),
Player(3);
}
And change your code to
String queryString = "from User u where u.id= ?";
Query queryObject = getSession().createQuery(queryString);
queryObject .setParameter(0, Role.PLAYER);
return queryObject.list();
You can do using create a mapping table like ,
UserRoleMapping
ID - Incremental,
UserId - Refers to user table,
RoleId - Refers to role table
As one user can have more than one role so it will satisfy that thing too.
to Get the roles using query
select role.id from userrolemapping urm innerjoin on user u.id = urm.id where u.id = ?
using IDs or string/vachar etc. lookup is all dependent on the data that you have in the database. Some organization keep the id static and some keep the name/description static. So, make sure you have good understanding of your DB data and what will stay static . However, if the role is static you can use HQL ignore case like the example I provided for you below (I'm not adding information about the ID static path because others have already provided information about it for and don't want to duplicate it ).
--note you can take the percentages out if you only want "player"
String queryString = "from User u where lower( u.role.role ) like lower('%"+ ? +"%')";
Query queryObject = getSession().createQuery(queryString);
queryObject.setParameter(0, "player");
return queryObject.list();
Hi I'm trying to select records from one table which doesn't have records in connected many-to-many table with specific values.
I will explain on sample tables:
documentation:
id_documentation
irrelevant_data
user:
id_user
irrelevant_data
documentation_user:
id_documentation
id_user
role
What I want to achieve is to select every single documentation which doesn't have user in specific role. Any ideas?
The main problem is that I'm using java's CriteriaBuilder to create query so using subqueries is impossible (I think).
You can add restrictions on your left join using: createAlias(java.lang.String, java.lang.String, int, org.hibernate.criterion.Criterion) method, see API.
Check this answer for an example on how to use the left join with a criteria.
Main problem does not exist - Criteria API do have SubQuery. Query itself selects instances of User and uses not in construct to limit results based to subquery. Subquery selects all users that are connected to document with specific role via DocumentationUser.
Try something like this (code not tested):
CriteriaQuery<Documentation> cq = cb.createQuery(Documentation.class);
Root<Documentation> u = cq.from(Documentation.class);
Subquery<Integer> sq = cq.subquery(Integer.class);
Root<User> su = sq.from(User.class);
sq.select(su.get("id_user"));
Join<User, DocumentationUser> du = su.join("documentationUserCollection");
sq.where(cb.equals(du.get("role"), "mySpecificRole"));
cq.where(cb.not(cb.in(u.get("id_user")).value(sq)));
See also this useful answer on SO.
I am new to the Hibernate and HQL. I want to write an update query in HQL, whose SQL equivalent is as follows:
update patient set
`last_name` = "new_last",
`first_name` = "new_first"
where id = (select doctor_id from doctor
where clinic_id = 22 and city = 'abc_city');
doctor_id is PK for doctor and is FK and PK in patient. There is one-to-one mapping.
The corresponding Java classes are Patient (with fields lastName, firstName, doctorId) and Doctor (with fields doctorId).
Can anyone please tell what will be the HQL equivalent of the above SQL query?
Thanks a lot.
String update = "update Patient p set p.last_name = :new_last, p.first_name = :new_first where p.id = some (select doctor.id from Doctor doctor where doctor.clinic_id = 22 and city = 'abc_city')";
You can work out how to phrase hql queries if you check the specification. You can find a section about subqueries there.
I don't think you need HQL (I know, you ask that explicitly, but since you say you're new to Hibernate, let me offer a Hibernate-style alternative). I am not a favor of HQL, because you are still dealing with strings, which can become hard to maintain, just like SQL, and you loose type safety.
Instead, use Hibernate criteria queries and methods to query your data. Depending on your class mapping, you could do something like this:
List patients = session.CreateCriteria(typeof(Patient.class))
.createAlias("doctor", "dr")
.add(Restrictions.Eq("dr.clinic_id", 22))
.add(Restrictions.Eq("dr.city", "abc_city"))
.list();
// go through the patients and set the properties something like this:
for(Patient p : patients)
{
p.lastName = "new lastname";
p.firstName = "new firstname";
}
Some people argue that using CreateCriteria is difficult. It takes a little getting used to, true, but it has the advantage of type safety and complexities can easily be hidden behind generic classes. Google for "Hibernate java GetByProperty" and you see what I mean.
update Patient set last_name = :new_last , first_name = :new_first where patient.id = some(select doctor_id from Doctor as doctor where clinic_id = 22 and city = abc_city)
There is a significant difference between executing update with select and actually fetching the records to the client, updating them and posting them back:
UPDATE x SET a=:a WHERE b in (SELECT ...)
works in the database, no data is transferred to the client.
list=CreateCriteria().add(Restriction).list();
brings all the records to be updated to the client, updates them, then posts them back to the database, probably with one UPDATE per record.
Using UPDATE is much, much faster than using criteria (think thousands of times).
Since the question title can be interpreted generally as "How to use nested selects in hibernate", and the HQL syntax restricts nested selects only to be in the select- and the where-clause, I would like to add here the possibility to use native SQL as well. In Oracle - for instance - you may also use a nested select in the from-clause.
Following query with two nested inner selects cannot be expressed by HQL:
select ext, count(ext)
from (
select substr(s, nullif( instr(s,'.', -1) +1, 1) ) as ext
from (
select b.FILE_NAME as s from ATTACHMENT_B b
union select att.FILE_NAME as s from ATTACHEMENT_FOR_MAIL att
)
)
GROUP BY ext
order by ext;
(which counts, BTW, the occurences of each distinct file name extension in two different tables).
You can use such an sql string as native sql like this:
#Autowired
private SessionFactory sessionFactory;
String sql = ...
SQLQuery qry = sessionFactory.getCurrentSession().createSQLQuery(sql);
// provide an appropriate ResultTransformer
return qry.list();