[Spring boot ]Error in native query with join spring data jpa - java

I have a many to many relationship in spring boot with User and Role. I have three tables in database (Role_utilisateur, users_roles and utilisateurs). In my repository, ireturn List of my entite called RoleEntite which have two attributes (nom and id).
#Query(value = "SELECT ROLE_UTILISATEUR.NOM, ROLE_UTILISATEUR.ROLE_ID " + "FROM ROLE_UTILISATEUR "
+ "INNER JOIN USERS_ROLES on users_roles.role_id = ROLE_UTILISATEUR.ROLE_ID "
+ "INNER JOIN UTILISATEURS on utilisateurs.utilisateur_id = USERS_ROLES.UTILISATEUR_ID "
+ "WHERE UTILISATEURS.UTILISATEUR_ID = :userId", nativeQuery = true)
List<RoleEntite> findUsersRolesNative(#Param("userId") Long userId);
When i call my function in
private List<GrantedAuthority> getGrantedAuthorities(UserEntite user) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
List<RoleEntite> roleList = userRepository.findUsersRolesNative(user.getId());
for (RoleEntite roleEntite : roleList) {
authorities.add(new SimpleGrantedAuthority(roleEntite.getNom()));
}
return authorities;
}
I get this error in my java eclipse
org.springframework.security.authentication.InternalAuthenticationServiceException: Failed to convert from type [java.lang.Object[]] to type [com.id.firstSpringBoot.entite.RoleEntite] for value '{ADMIN, 1}'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [com.id.firstSpringBoot.entite.RoleEntite]
How can i resolve this error and get the name ADMIN in my function getGrantedAuthorities can do this

The problem is you're selecting two columns and returning a list of entities. You need to either select the entity in your query or return the list of two values in a collection.
To return entities you would need to convert your query to JPQL. The following is a JPQL representation of your native query.
I've had to do some guessing for the joins (I assume you have some JPA related entities):
#Query(value = "SELECT RoleEntite r "
+ "INNER JOIN UserRole r.user u "
+ "INNER JOIN Utilisateurs u.Utilisateur x "
+ "WHERE x.UTILISATEUR_ID = :userId")
List<RoleEntite> findUsersRolesNative(#Param("userId") Long userId);
If you go the native query route:
#Query(value = "SELECT ROLE_UTILISATEUR.NOM, ROLE_UTILISATEUR.ROLE_ID " + "FROM ROLE_UTILISATEUR "
+ "INNER JOIN USERS_ROLES on users_roles.role_id = ROLE_UTILISATEUR.ROLE_ID "
+ "INNER JOIN UTILISATEURS on utilisateurs.utilisateur_id = USERS_ROLES.UTILISATEUR_ID "
+ "WHERE UTILISATEURS.UTILISATEUR_ID = :userId", nativeQuery = true)
List<Object[]> findUsersRolesNative(#Param("userId") Long userId);
The returned list should yield:
for (Object[] obj : list) {
nom = obj[0];
roleId = obj[1];
// ... do what ever you want with the values
}

Related

Native SQL query removes backslashs [duplicate]

Currently I have such piece of code, which doesn't work, since I have to add schema name before each table in a query(like DEV.DASHBOARDS_METADATA):
public interface DashboardMetadataDao extends CrudRepository<DashboardMetadata, Integer> {
#Query("SELECT D FROM DASHBOARDS_METADATA D " +
"INNER JOIN FAC_DASHBOARDS_LINK DL ON D.ID = DL.DASHBOARD_ID " +
"INNER JOIN FIRMS F ON DL.FAC_ID = F.FAC_UNIT_ID " +
"INNER JOIN USERS U ON U.FIRM_ID = F.FIRM_ID WHERE LOWER(U.USERID) = LOWER(:userid)")
public Set<DashboardMetadata> findByUserId(#Param("userid") String userId);
}
The problem is that schema name differs from database to database (DEV/QA/PROD). Normally I use component's method which prepend schema's name to each table during query generation. How can do this using annotations?
Thanks!
Hibernate has a variable that can be used in native queries to get schema name called: {h-schema}
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/query/native/Native.html#sql-global-catalog-schema You can just put {h-schema}table in your query. I think you have to convert it to a native query.
Below Code Snippet worked for me
#Query(
value = "select *\n" +
"from {h-schema}TABLE-A r left join {h-schema}TABLE-B p on r.ID=p.ID\n" +
"left join {h-schema}TABLE-C pe on p.ID=pe.ID\n" +
"left join {h-schema}TABLE-D e on pe.ENT_ID=e.ENT_ID\n" +
"where r.role_type='Provisioning'",
nativeQuery = true)

Eagerly load nested association using HQL

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.

Write sql native query with left join and pagination in hibernate (springboot) [duplicate]

This question already has answers here:
Jpa namedquery with left join fetch
(2 answers)
Closed 5 years ago.
I'm using spring data JPA and I want to write a SQL query in my repository.
I have a following SQL query (notice the LEFT JOIN):
SELECT * FROM institution LEFT JOIN
(select * from building_institutions where building_institutions.building_id = 1) as reserved_institutions
ON reserved_institutions.institutions_user_id = institution.user_id
WHERE reserved_institutions.institutions_user_id is null;
and i want to execute it in my InstitutionRepository which is as follows:
#Repository
public interface InstitutionRepository extends JpaRepository<Institution, Long>, PagingAndSortingRepository<Institution,Long> {
// doesn't work
//#Query("SELECT b.institutions as bi FROM Building b left join Institution i WHERE bi.building_id not in :id")
Page<Institution> findPotentialInstitutionsByBuildingId(#Param("id") Collection<Long> id, Pageable pageable);
// doesn't work
#Query(
value = "SELECT * FROM kits_nwt.institution LEFT JOIN\n" +
"(SELECT * FROM kits_nwt.building_institutions WHERE kits_nwt.building_institutions.building_id = ?1) AS reserved_institutions\n" +
"ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id\n" +
"WHERE reserved_institutions.institutions_user_id IS null ORDER BY ?#{#pageable}",
nativeQuery = true)
Page<Institution> findPotentialInstitutionsByBuildingId(Long userId, Pageable pageable);
}
So, I want to get all institutions which are not in building with certain ID (which I will send as a parameter).
Here is my current DB data:
Institutions:
Building institutions:
What I want: (in this query, the id is set on 1, for presentation purposes)
I have looked at many SO questions and answers (such as this one) but I haven't been able to figure out the solution.
So, how do I write this query so that I get what I want?
Edit 1:
#KevinAnderson Currently, I'm trying with:
#Query(
value = "SELECT username, password, description, location, title, user_id FROM (institution INNER JOIN user ON institution.user_id = user.id) LEFT JOIN\n" +
"(SELECT * FROM building_institutions WHERE building_institutions.building_id = 1) AS reserved_institutions\n" +
"ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id\n" +
"WHERE reserved_institutions.institutions_user_id IS null ORDER BY ?#{#pageable}",
nativeQuery = true)
Page<Institution> findPotentialInstitutionsByBuildingId(Long userId, Pageable pageable);
And I'm getting this exception:
MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE) FROM building_institutions WHERE building_institutions.building_id = 1) A' at line 2
Edit 2:
#StanislavL here it is:
The problem is that your query gets me the institution with ID 25 because it is in both building with ID 1 and building with ID 2. When you do a JOIN, you have 2 rows ON institution ID being 25 in both institution table and building_institutions table. Then, your WHERE condition removes one from those two rows and I get one row where instituiton ID is 25, and I don't want what.
Here is an image for the above:
Edit 3 - this question is not a duplicate because of the following:
My query is with pagination (I added "with pagination" to the question title)
I'm not using #NamedQuery but #Query
My mistake was that I didn't write countQuery parameter to the #Query annotation
I solved it...
The query needs to look like this:
#Query(
value = "SELECT * FROM \n" +
"(institution INNER JOIN user ON institution.user_id = user.id) \n" +
"LEFT JOIN \n" +
"(SELECT * FROM \n" +
"building_institutions \n" +
"WHERE building_id = :userId)\n" +
" AS reserved_institutions \n" +
"ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id \n" +
" where reserved_institutions.institutions_user_id IS null \n"
+ "ORDER BY ?#{#pageable}"
,
countQuery = "SELECT count(*) FROM \n" +
"(institution INNER JOIN user ON institution.user_id = user.id) \n" +
"LEFT JOIN \n" +
"(SELECT * FROM \n" +
"building_institutions \n" +
"WHERE building_id =:userId)\n" +
" AS reserved_institutions \n" +
"ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id \n" +
"where reserved_institutions.institutions_user_id IS null \n" +
"ORDER BY ?#{#pageable}",
nativeQuery = true)
Page<Institution> findPotentialInstitutionsByBuildingId(#Param("userId") Long userId, Pageable pageable);
I was getting the error at line 4 near WHERE, when my query looked like this:
#Query(
value = "SELECT username, password, description, location, title, user_id FROM (institution INNER JOIN user ON institution.user_id = user.id) LEFT JOIN\n" +
"(SELECT * FROM building_institutions WHERE building_institutions.building_id = 1) AS reserved_institutions\n" +
"ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id\n" +
"WHERE reserved_institutions.institutions_user_id IS null ORDER BY ?#{#pageable}",
nativeQuery = true)
Page<Institution> findPotentialInstitutionsByBuildingId(Long userId, Pageable pageable);
and that was because I didn't add the countQuery parameter to the #Query annotation.
Big thanks to all of you who tried to help.
I hope that I save someone else many hours of misery.
Cheers! :)

How to add schema name to Query annotation in Spring Data JPA

Currently I have such piece of code, which doesn't work, since I have to add schema name before each table in a query(like DEV.DASHBOARDS_METADATA):
public interface DashboardMetadataDao extends CrudRepository<DashboardMetadata, Integer> {
#Query("SELECT D FROM DASHBOARDS_METADATA D " +
"INNER JOIN FAC_DASHBOARDS_LINK DL ON D.ID = DL.DASHBOARD_ID " +
"INNER JOIN FIRMS F ON DL.FAC_ID = F.FAC_UNIT_ID " +
"INNER JOIN USERS U ON U.FIRM_ID = F.FIRM_ID WHERE LOWER(U.USERID) = LOWER(:userid)")
public Set<DashboardMetadata> findByUserId(#Param("userid") String userId);
}
The problem is that schema name differs from database to database (DEV/QA/PROD). Normally I use component's method which prepend schema's name to each table during query generation. How can do this using annotations?
Thanks!
Hibernate has a variable that can be used in native queries to get schema name called: {h-schema}
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/query/native/Native.html#sql-global-catalog-schema You can just put {h-schema}table in your query. I think you have to convert it to a native query.
Below Code Snippet worked for me
#Query(
value = "select *\n" +
"from {h-schema}TABLE-A r left join {h-schema}TABLE-B p on r.ID=p.ID\n" +
"left join {h-schema}TABLE-C pe on p.ID=pe.ID\n" +
"left join {h-schema}TABLE-D e on pe.ENT_ID=e.ENT_ID\n" +
"where r.role_type='Provisioning'",
nativeQuery = true)

Spring Data JPA: How not to repeat myself in countQueries?

I am using Spring Data JPA repositories (1.7.2) and I am typically facing the following scenario:
entities have lazy-loaded collections
those collections are sometimes eagerly fetched (via JPAQL fetch join)
repositories often return Page<Foo> instead of List<Foo>
I need to provide countQuery to every #Query that uses fetch joins on a repository that returns a Page. This issue has been discussed in this StackOverflow question
My typical repository method looks like this:
#Query(value = "SELECT e FROM Employee e LEFT JOIN FETCH e.addresses a " +
"WHERE e.company.id = :companyId " +
"AND e.deleted = false " +
"AND e.primaryAddress.deleted = false " +
"ORDER BY e.id, a.id",
countQuery="SELECT count(e) FROM Employee e WHERE e.companyId = :companyId AND e.deleted = false AND e.primaryAddress.deleted = false"
)
Page<Employee> findAllEmployeesWithAddressesForCompany(#Param("companyId") long companyId, Pageable pageable);
Obviously, it's not very DRY. You can tell that I am repeating all of the conditions in both value and countQuery parameters. How do I stay DRY here?
You could do something like this
public interface MyRepository extends JpaRepository {
public static final String WHERE_PART = "e.companyId = :companyId AND e.deleted = false AND e.primaryAddress.deleted = false ";
#Query(value = "SELECT e FROM Employee e LEFT JOIN FETCH e.addresses a " +
"WHERE " + MyRepository.WHERE_PART
"ORDER BY e.id, a.id",
countQuery="SELECT count(e) FROM Employee e WHERE " + MyRepository.WHERE_PART
)
Page<Employee> findAllEmployeesWithAddressesForCompany(#Param("companyId") long companyId, Pageable pageable);

Categories

Resources