I have the following MS SQL Query that works perfectly.
select u.id, u.username, r2.authority, em.hrt02_first_name, em.hrt02_last_name from users as u
inner join group_members gm
on u.id = gm.user_id
inner join groups g
on gm.group_id = g.id
inner join group_authorities ga
on ga.group_id = g.id
inner join roles r2
on ga.role_id = r2.id
inner join hrt02_employee_name em
on em.id = u.id
where u.username = 'john'
The output is as follows
+----+----------+------------+------------------+-----------------+
| id | username | authority | hrt02_first_name | hrt02_last_name |
+----+----------+------------+------------------+-----------------+
| 1 | john | ROLE_ADMIN | fname | lname |
+----+----------+------------+------------------+-----------------+
| 1 | john | ROLE_USER | fname | lname |
+----+----------+------------+------------------+-----------------+
But When I tried to convert it to Hibernate Query or `#Query(..., nativeQuery=true) it throws exception. (Failed to Lazy Initialize and Path expected for join).
This is the My Schema Design
#Entity
public class Users {
// id, username omitted
#ManyToMany
#JoinTable(name="group_members", joinColumns=#JoinColumn(name="user_id", referencedColumnName="id"), inverseJoinColumns=#JoinColumn(name="group_id", referencedColumnName="id"))
private List<Groups> groups;
}
#Entity
public class Groups {
// id omitted
#ManyToMany
#JoinTable(name="group_authorities", joinColumns=#JoinColumn(name="group_id", referencedColumnName="id"), inverseJoinColumns=#JoinColumn(name="role_id", referencedColumnName="id"))
private List<Roles> roles;
}
#Entity
public class Roles {
// id omitted, authority
}
#Entity
public class Hrt02EmployeeName {
// id, firstname, lastname omitted
}
Update 1 - Queries I've tried,
All examples here throws an error, but if you written them in native query and run them, they works as expected. So, It's probably me not knowing how to convert it from nativeQuery to Hibernate Query.
public interface UsersRepository extends JpaRepository<Users, Long> {
#Query(value =
"select * from users as u" +
" inner join group_members gm" +
" on u.id = gm.user_id" +
" inner join groups g" +
" on gm.group_id = g.id" +
" inner join group_authorities ga" +
" on ga.group_id = g.id" +
" inner join roles r2" +
" on ga.role_id = r2.id" +
" inner join hrt02_employee_name em" +
" on em.id = u.id" +
" where u.username = :qryusername", nativeQuery = true)
public Users findRoleByUsername(#Param("qryusername") String username);
#Query("select distinct u.username, r2.authority from Users as u " +
" inner join group_members gm " +
" on u.id = gm.user_id " +
" inner join Groups g " +
" on gm.group_id = g.id " +
" inner join group_authorities ga " +
" on ga.group_id = g.id " +
" inner join Roles r2 " +
" on ga.role_id = r2.id" +
" inner join hrt02_employee_name em" +
" on em.id = u.id" +
" where u.username = :username")
public Users findRoleByUsername(#Param("username") String username);
#Query(value =
"select u from Users u" +
" inner join GroupMembers gm" +
" on u.id = gm.user_id" +
" inner join Groups g" +
" on gm.group_id = g.id" +
" inner join GroupAuthorities ga" +
" on ga.group_id = g.id" +
" inner join Roles r2" +
" on ga.role_id = r2.id" +
" inner join hrt02_employee_name em" +
" on em.id = u.id" +
" where u.username = :username")
public Users findRoleByUsername(#Param("username") String username);
}
Since you want to cast it to User, you would have to construct it, not u.username, r2.authority. Secondly you need to fetch what you're using to avoid LazyInitializationException:
#Query("select distinct u from Users u " +
" left join fetch u.groups g "
" left join fetch g.roles r " +
" ... "
" where u.username = :username")
public Users findRoleByUsername(#Param("username") String username);
It's the beginning, because your entity Role has no mapping. You would have to write the same way other entities where the dots are.
Related
I'm using JpaRepository and I'm creating a #Query in my repository:
this is my query:
#Query( "SELECT SUM(p.prima) as prima, p.producto as producto, p.tipoProducto as tipoProducto, p.compania as compania, p.cliente as cliente, p.vendedor as vendedor " +
"FROM Poliza p " +
"JOIN Producto pr ON p.producto=pr " +
"JOIN TipoProducto tp ON p.tipoProducto=tp " +
"JOIN Compania c ON p.compania=c " +
"JOIN Cliente cl ON p.cliente=cl " +
"LEFT JOIN Vendedor v ON p.vendedor=v " +
"WHERE p.comienzo >=?1 " +
"AND p.comienzo <= ?2 " +
"GROUP BY p.producto")
and I realize that I only get the rows where "Vendedor" is present.
I used the spring.jpa.show-sql=true property to check what was going on and I realize that the query is creating an inner join for each property in the SELECT STATEMENT
inner join producto producto1_ on (poliza0_.producto=producto1_.id)
inner join tipo_producto tipoproduc2_ on (poliza0_.tipo_producto=tipoproduc2_.id)
inner join compania compania3_ on (poliza0_.compania=compania3_.id)
inner join cliente cliente4_ on (poliza0_.cliente=cliente4_.id)
inner join vendedor vendedor5_ on (poliza0_.vendedor=vendedor5_.id)
join producto producto6_ on poliza0_.producto=producto6_.id
join tipo_producto tipoproduc7_ on poliza0_.tipo_producto=tipoproduc7_.id
join compania compania8_ on poliza0_.compania=compania8_.id
join cliente cliente9_ on poliza0_.cliente=cliente9_.id
left join vendedor vendedor10_ on poliza0_.vendedor=vendedor10_.id
As you can see in the first part I have an inner join from Vendedor which makes the query wrong.
How should I create my query to get the expected result?
if understand clearly
#Query( "SELECT SUM(p.prima) as prima, p.producto as producto, p.tipoProducto as tipoProducto, p.compania as compania, p.cliente as cliente, p.vendedor as vendedor " +
"FROM Poliza p " +
"JOIN Producto pr ON p.producto=pr " +
"JOIN TipoProducto tp ON p.tipoProducto=tp " +
"JOIN Compania c ON p.compania=c " +
"JOIN Cliente cl ON p.cliente=cl " +
"LEFT JOIN Vendedor v ON p.vendedor=v " +
"WHERE p.comienzo >=?1 " +
"AND p.comienzo <= ?2 " +
"GROUP BY p.producto")
i think you can using v instead p.vendedor as vendedor in select field.
If I read correctly, you are defining your own query. So, you can edit the JOIN in your query and write LEFT JOIN instead.
Am I right on your intentions?
I have an update query on hibernate on this table
class PackEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String status;
private String oldStatus;
#ManyToOne
#JoinColumn(name = "order_id")
private OrderEntity order;
...
}
And on OrderEntity I have there another relationship to another table when I have machine names.
On the JPA repository, I have the query. Basically first I search by machine and status and then I want to update the old status to put the current value of the status field and in status to put the new status. This is it:
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PackEntity p " +
"SET p.oldStatus= p.status, p.status = ?3 " +
"WHERE p.id IN " +
" ( SELECT p2" +
" FROM PackEntity p2" +
" JOIN p2.order " +
" JOIN p2.order.machine" +
" WHERE p2.order.machine.name = ?1 AND p2.status = ?2)")
List<PackEntity > updateAllWithStatusByMachineNameAndStatus(String machineName, String status, String newStatus);
Now I'm having this error .QueryExecutionRequestException: Not supported for DML operations [UPDATE com.pxprox.entities.PackEntity with root cause ...
Why not create a method that does this for you? Initializing the entity and updating everything, changes will be flushed automatically at the end of the transaction. You can have a look at updating entity with spring-data-jpa
It should basically be something like:
#Autowired
private PackEntityRepository packEntityRepository;
public void updatePackEntity(PackEntity newPE) {
PackEntity packEntity = packEntityRepository.findById(newPE.getId());
packEntity.setOldStatus = packEntity.getStatus();
packEntity.setStatus = newPE.getStatus();
packEntityRepository.save(packEntity);
}
The return type of the method is wrong and also the query should be a little adapted. Use the following:
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PackEntity p " +
"SET p.oldStatus = p.status, p.status = ?3 " +
"WHERE EXISTS " +
" ( SELECT 1" +
" FROM PackEntity p2" +
" JOIN p2.order o " +
" JOIN o.machine m" +
" WHERE m.name = ?1 AND p2.status = ?2 AND p2.id = p.id)")
void updateAllWithStatusByMachineNameAndStatus(String machineName, String status, String newStatus);
or even better
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PackEntity p " +
"SET p.oldStatus = p.status, p.status = ?3 " +
"WHERE p.status = ?2 AND EXISTS " +
" ( SELECT 1" +
" FROM p.order o " +
" JOIN o.machine m" +
" WHERE m.name = ?1)")
void updateAllWithStatusByMachineNameAndStatus(String machineName, String status, String newStatus);
I have the following model:
public class BaseModel {
List<DataA> lazyCollectionA;
List<DataB> lazyCollectionB;
}
public class DataA {
OtherEntity otherEntity;
}
public class OtherEntity {
List<DataC> lazyCollectionC;
}
When I visit a particular page I need to use all this data. This is creating a performance select n+1 problem.
I already partly solved the issue by eagerly fetching the collections using:
List<BaseModel> result = entityManager.createQuery(
"select m from BaseModel m " +
"left join fetch m.lazyCollectionA " +
"where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();
result = entityManager.createQuery(
"select m from BaseModel m " +
"left join fetch m.lazyCollectionB " +
"where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();
Note that I had to perform 2 queries instead of only 1 because otherwise I would get a MultipleBagFetchException.
However, I'm having problems eagerly loading lazyCollectionA.otherEntity.lazyCollectionC. I tried several variations of the query to try to eagerly fetch the results, but when otherEntity.lazyCollectionC is accessed, the select n+1 problem keeps surfacing.
I think this should work, but unfortunately it is not:
entityManager.createQuery(
"select a from BaseModel m " +
"left join m.lazyCollectionA a " +
"left join fetch a.otherEntity o " +
"left join fetch o.lazyCollectionC " +
"where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();
Do you have any ideas why this is not working?
Also, I don't fully understand how my first 2 queries to load lazyCollectionA and lazyCollectionB are working. I mean, since they are loaded at different times, I would expect that only the last query would have the loaded instances. Is it because hibernate is caching the results and therefore it does not need to query the database again?
Thanks for any help you can provide!
I assume that all connections between your models are #OneToMany. In this case you could try simething like this:
#Autowired
private EntityManager em;
#Transactional
public List<BaseModel> getAllByThreeQueries() {
List<Long> ids = Arrays.asList(1L);
List<BaseModel> first = em.createQuery(
"select distinct m from BaseModel m " +
"left join fetch m.lazyCollectionB " +
"where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();
List<BaseModel> second = em.createQuery(
"select distinct m from BaseModel m " +
"left join fetch m.lazyCollectionA a " +
"left join fetch a.otherEntity o " +
"where m in (:models) ", BaseModel.class)
.setParameter("models", first)
.getResultList();
em.createQuery("select distinct a from BaseModel m " +
"left join m.lazyCollectionA a " +
"left join fetch a.otherEntity o " +
"left join fetch o.lazyCollectionC " +
"where m in (:models) ", DataA.class)
.setParameter("models", second)
.getResultList();
return second;
}
Full code
Do you have any ideas why this is not working?
entityManager.createQuery(
"select a from BaseModel m " +
"left join m.lazyCollectionA a " +
"left join fetch a.otherEntity o " +
"left join fetch o.lazyCollectionC " +
"where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();
Because you get a MultipleBagFetchException in this case. You need to do one more request.
I have working query in PostgreSQL:
select s.id, s.seat_number as available_seat, s.row_number as available_row, rm.room_name as screening_room
from seats s
join rooms rm on rm.id=s.room_id
left join (
select r.seat_id from reserved_seats r
join reservations res on res.id=r.reservation_id AND res.screening_id = 3 ) res on res.seat_id=s.id
where res.seat_id is null AND s.room_id=3
ORDER BY s.id;
But I make mistakes translating it into the JPA query language.
Can I use nested SELECTs in JPQL?
The answer is to use native query:
#Query(value =
"SELECT s.id seatId, s.seat_number availableSeat, " +
"s.row_number availableRow, rm.name screeningRoom \n" +
"FROM seats s\n" +
"JOIN rooms rm on rm.id=s.room_id\n" +
" LEFT JOIN (\n" +
" SELECT r.seat_id FROM reserved_seats r\n" +
" JOIN reservations res ON res.id=r.reservation_id " +
" AND res.screening_id = :screeningId) res ON res.seat_id=s.id\n" +
"WHERE res.seat_id IS NULL AND s.room_id=:roomId AND s.row_number=:rowNumber\n" +
"ORDER BY s.id;", nativeQuery = true)
The answer is to use native query:
#Query(value =
"SELECT s.id seatId, s.seat_number availableSeat, " +
"s.row_number availableRow, rm.name screeningRoom \n" +
"FROM seats s\n" +
"JOIN rooms rm on rm.id=s.room_id\n" +
" LEFT JOIN (\n" +
" SELECT r.seat_id FROM reserved_seats r\n" +
" JOIN reservations res ON res.id=r.reservation_id " +
" AND res.screening_id = :screeningId) res ON res.seat_id=s.id\n" +
"WHERE res.seat_id IS NULL AND s.room_id=:roomId AND s.row_number=:rowNumber\n" +
"ORDER BY s.id;", nativeQuery = true)
I have a NHibernate Linq query and I need to translate it to HQL:
var result = Conexion.Session.Query<Person>();
result = result.Where(p => p.addresses.Any(a => a.City.Name == "mycity"));
Returns the people who have at least one adress in the city "MyCity"
How I can translate the "any" clause to "HQL sintax"?
Thanks.
To replicate the SQL generated by:
var result = Conexion.Session.Query<Person>();
result = result.Where(p => p.addresses.Any(a => a.City.Name == "mycity"));
we have to do it like this:
var hql = "SELECT p FROM Person p WHERE EXISTS "
+ "(SELECT a FROM Address a "
+ " LEFT JOIN a.City c "
+ " WHERE p = a.Person "
+ " AND c.Name = 'mycity') ";
mycity could be even parameter:
var hql = "SELECT p FROM Person p WHERE EXISTS "
+ "(SELECT a FROM Address a "
+ " LEFT JOIN a.City c "
+ " WHERE p = a.Person "
+ " AND c.Name = :mycity) ";
var result = Conexion.Session.CreateQuery(hql)
.SetParameter("mycity", "...")
.List<Person()
That will work, in case, that Address has a back reference to Person, because that is part of condition WHERE p = a.Person
In case, that mapping contains only way from Person to Address, hql must be like this:
var hql = "SELECT p FROM Person p WHERE EXISTS "
+ "(SELECT p2 FROM Person p2 "
+ " LEFT JOIN p2.addresses a "
+ " LEFT JOIN a.City c "
+ " WHERE p = p2 "
+ " AND c.Name = :mycity) ";
That will work even if the referenc from address to person is missing
This is the HQL equivalent:
select p
from Person p
join p.addresses a
join a.city c
where c.name = :cityName
The any is simply resolved by the one-to-many join where condition.
If there are multiple addresses within the same City, to remove Person entity duplicates, you need to use distinct:
select distinct p
from Person p
join p.addresses a
join a.city c
where c.name = :cityName
or
select distinct p
from Address a
join fetch a.person p
join a.city c
where c.name = :cityName