How to retrieve data from many to many relationship in HQL, with Spring and JPA,when the joinTable is not an entity? - spring

I have a similar case as this question [a link] (How do I do with HQL, many to many?)
I want to have the number of users (entity 1) for each libelle of role (entity 2). I defined a relation many to many between User and Role.
I am using Spring MVC, Hibernate, MySQL and JPA.
Entity 1: User
#Entity(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
#ManyToMany(mappedBy = "user")
private List<Role> role;
Entity 2: Role
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int ID;
private String libelle;
#ManyToMany
#JoinTable(
name = "user_role",
joinColumns = { #JoinColumn(name = "ID_role") },
inverseJoinColumns = { #JoinColumn(name = "id_user") }
)
private List<User> user;
JPA repository
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "select new map( count(u.id) as numberOfUsers,r.libelle as roleLibelle ) FROM user u join role r where r.ID =: ???? group by r.libelle")
List<Object> countByRoleList();
I am trying to figure out what =:id proposed in the question that i mentioned, have to be in my case. Instead of the "????" i tried ID, id , ID_role. All what i get is the error
"Named parameter not bound : ".
How can i solve that?

I suppose the type of your parameter is long. it's name is abc and it refer to an id.
Follow these steps:
Remove the space in front of your param .
so you will have (=:abc) not (=: abc).
Your query depends on an external parameter, uses the #param annotation for the specified param.
so you will have
".....countByRoleList(#Param("abc") long id );"
A code exemple
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
#Query(value = "select new map( count(u.id) as numberOfUsers,r.libelle as roleLibelle ) FROM user u join role r where r.ID =:abc group by r.libelle")
List<Object> countByRoleList(#Param("abc") long id);
Note: abc is a provided param . It can come from an HTML page, a function. you can also provide it manually ...

Related

spring jpa findBy column of inside entity

I have a couple of related entities in a relational DB:
#Entity
public class A {
#Id
#Column(name = "id" ...)
private UUID id;
#OneToMany(mappedBy = "a")
private Set<B> bs;
}
and
#Entity
public class B {
#Id
#Column(name = "id" ...)
private UUID id;
#ManyToOne
#JoinColumn(name = "a_id")
private A a;
}
So I have a B Repository
#Repository
public interface BRepository extends JpaRepository<B, UUID> {
List<B> findByA(A a);
}
And id like to have a query by which I obtain the all Bs with the same A. I'm used to have an a_id column in B, not the whole entity. I don't know how to manage this, nor with a JPA query nor with a NamedQuery.
Many thanks in advance!
Luis
As already answered, the proposed code should already work but it didn't... The fix that worked for me was
List<B> findByA_Id(UUID id);
Table_Field references field field to table Table.

Many to one relationship without a join table in spring

I'm trying to build the relationship between two tables using spring-data jpa. I have read many SO articles like 1, 2 but they are pretty old and don't seem to apply to my specific use case. Hence this question:
There are 2 tables user_client_scopes and scopes listed below.
user_client_scopes:
user_id (long),
client_id (string)
last_updated (timestamp)
scope_id (Foreign key to scopes table),
primary key (user_id, client_id, scope_id)
scopes:
id (int, primary key)
name (string)
A <user_id, client_id> can have multiple scopes. Similarly, the same scope can be held by many <user_id, client_id>s. Hence the many-to-many relationship. The join table (as defined by spring-data-jpa) is kind of embedded within user_client_scope table.
Here is a half-written-code:
#Entity
#Table(name = "user_client_scopes")
#RequiredArgsConstructor
#IdClass(UserClientScopesPK.class)
public class UserClientScopes implements Serializable {
#Id
#Column(name = "user_id")
private long userId;
#Id
#Column(name = "client_id")
private String clientId;
#Column(name = "last_updated")
private Timestamp lastUpdated;
#Id
#Column(name = "scope_id")
private int scopeId;
#ManyToMany // <- how to complete this definition?
private Set<Scope> scopes;
getters and setters.
Here are 2 other classes (for the sake of completion).
#Data
#RequiredArgsConstructor
public class UserClientScopesPK implements Serializable {
private long userId;
private String clientId;
private int scopeId;
}
#Entity
#Table(name = "scopes")
#RequiredArgsConstructor
public class Scope implements Serializable {
#Id
#GeneratedValue
private long id;
private String name;
}
How do I complete the user_client_scopes entity such that we can:
Find all scopes for a given <user_id, client_id>. i.e. execute the following SQL:
select user_id, client_id, scope
from scopes
join user_client_scopes ucs on ucs.scope_id = scopes.id
where ucs.user_id = ? and ucs.client_id = ?
Save new scopes for a given <user_id, client_id>. i.e. execute the following SQL:
insert into user_client_scopes (user_id, client_id, scope_id, last_updated)
select ?, ?, id, now()
from scopes
where scopes.name = ?
UPDATE 1:
Changing title to Many to one instead of Many to many relationship.
That's not a many-to-many because the association scope is mapped by the column scope_id in user_client_scopes. This means that if I take a single row in the table user_client_scopes, it will be associated to only a single row in the table scopes. Therefore, this is a many-to-one.
If the three columns <user_id, client_id, scope_id> form the key for user_client_scopes, then the mapping for the table should look like:
Entity
#Table(name = "user_client_scopes")
#RequiredArgsConstructor
#IdClass(UserClientScopesPK.class)
public class UserClientScopes implements Serializable {
#Id
#Column(name = "user_id")
private long userId;
#Id
#Column(name = "client_id")
private String clientId;
#Column(name = "last_updated")
private Timestamp lastUpdated;
#Id
#ManyToOne
#JoinedColumn(name = "scope_id")
private Scope scope;
getters and setters.
}
class UserClientScopesPK implements Serializable {
private long userId;
private String clientId;
private Scope scope;
// getters,setters, equals and hascode
}
With this mapping you can run the following HQL:
select ucs
from UserClientScopes ucs join ucs.scope
where ucs.userId = :userId and ucs.clientId = :clientId
It will return all UserClientScopes entities matching the selected pair <userId, clientId>. Each one with a different scope.
Or, if you only care about the scope:
select s
from UserClientScopes ucs join ucs.scope s
where ucs.userId = :userId and ucs.clientId = :clientId
With Spring Data JPA, it will look like this:
#Query("select s from UserClientScopes ucs join ucs.scope swhere ucs.userId = ?1 and ucs.clientId = ?2")
public List<Scope> findScopesByUserIdAndClientId(long userId, String clientId);
or
#Query("select s.name from UserClientScopes ucs join ucs.scope swhere ucs.userId = ?1 and ucs.clientId = ?2")
public List<String> findScopesNameByUserIdAndClientId(long userId, String clientId);
You can also run the insert query as native SQL (you can probably run something similar as HQL, but I don't remember the right syntax now. I will update the answer later).
One last thing, to keep track of the last updated time, you could use Spring Entity callback listener:
#Entity
...
#EntityListeners(AuditingEntityListener.class)
public class UserClientScopes implements Serializable {
#LastModifiedDate
#Column(name = "last_updated")
private Date lastUpdated;
}

How to retrieve only a specific field from child entity on #OneToOne relationship, not all fields?

When I use jpa's #OneToOne annotation, I want to get the userName field from the table, not all fields. What should I do instead?
#Setter
#Getter
#Entity
public class Menu implements Serializable {
private static final long serialVersionUID = 4462798713783196961L;
/**
* id
*/
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
#OneToOne
#JoinColumn(name = "createUserId",referencedColumnName = "userId")
private User createUser;
#Column(nullable = false)
private LocalDateTime createTime;
}
What do I need to do, can I get the userName field in the User object, but not all of it? Thank you in advance.
You can create a POJO with required fields. e.g. You only want id from Menu and userName from User:
public class CustomMenu {
private Long menuId;
private String userName;
public CustomMenu(Long menuId, String userName) {
this.menuId = menuId;
this.userName = userName;
}
// getters, setters
}
Then you can write a query with hql using the constructor in the CustomMenu with parameters new com.yourpackage.CustomMenu(m.id, m.createUser.userName) and join User entity (join m.createUser) :
TypedQuery<CustomMenu> query = entityManager.createQuery("select new com.yourpackage.CustomMenu(m.id, m.createUser.userName)"
+ "from com.yourpackage.Menu m join m.createUser", CustomMenu.class);
List<CustomMenu> menus = query.getResultList();
This generates one sql query with inner join fetching only required fields :
select menu0_.id as col_0_0_, user1_.user_name as col_1_0_ from menu menu0_ inner join user user1_ on menu0_.create_user_id=user1_.user_id

Directionality in JPQL joins for Spring Boot JPA?

Spring Boot here. I have the following two JPA entities:
#Entity
#Table(name = "accounts")
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "account_id")
private Long id;
// lots of stuff
#OneToOne(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
#JoinColumn(name = "profile_id", referencedColumnName = "profile_id")
private Profile profile; // CAN be null
// Getters, setters & ctors
}
#Entity
#Table(name = "profiles")
public class Profile {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "account_id")
private Long id;
// doesn't really matter lots of stuff in here
// Getters, setters & ctors
}
It is possible for some Accounts to have Profiles, and some will not (their Profiles will be null). I would like to create a CrudRepository impl that essentially does this query:
SELECT *
FROM profiles p
INNER JOIN accounts a
WHERE a.profile_id = null
Essentially, get me all the Profiles that are "orphaned" and are associated with any accounts here.
I'm confused as to whether I need an CrudRepository<Long,Account> impl or a CrudRepository<Long,Profile> impl and on what that impl would look like. My best pseudo-attempt thus far looks like:
public interface ProfileRepository extends CrudRepository<Profile, Long> {
#Query("FROM Account act WHERE act.profile = null")
public Set<Profile> findOrphanedProfiles();
}
Can anyone help fill in the gaps for me?
First, JPQL NULL check syntax is IS NULL, not == NULL (see JPQL docs - the link is for ObjectWeb, but applies to any JPA implementation)
Second, if you want to check for orphaned records, you definitely don't want to join in the table they're orphaned from.
Your last attempt at it,
public interface ProfileRepository extends CrudRepository<Profile, Long> {
#Query("FROM Account act WHERE act.profile = null")
public Set<Profile> findOrphanedProfiles();
}
was actually pretty close, just replace == null with is null and you should be all set.
EDIT: if you're looking for profiles that don't have accounts associated with them, EXISTS query is what you're looking for:
public interface ProfileRepository extends CrudRepository<Profile, Long> {
#Query("FROM Profile p WHERE NOT EXISTS (FROM Account a WHERE a.profile = p)")
public Set<Profile> findDisassociatedProfiles();
}
EDIT: if your Profiles maintain a list of associated accounts as a property (it's not included in the code you posted, but maybe it was omitted), you can query for disassociated profiles even shorter:
FROM Profile p WHERE p.accounts IS EMPTY

Selecting latest workout set by user

I'm trying to select the latest added workout set associated with a given user.
Users has sessions and sessions has sets. My entities are defined as below.
#Entity(name = "users") // Postgres doesn't like the table name "user"
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(mappedBy = "user")
private Set<Session> sessions;
...
#Entity
public class Session {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne
private User user;
#OneToMany(mappedBy = "session")
private Set<WorkoutSet> sets;
...
#Entity
public class WorkoutSet {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToOne
private User user;
#ManyToOne
private Session session;
private LocalDateTime timestamp = LocalDateTime.now();
...
The following sql query seems to do the trick
select w
from workout_set w
inner join session s
on w.session_id = s.id
where s.user_id = 1
order by w.timestamp DESC
limit 1
But when I'm trying to do something like the below
#Repository
public interface WorkoutSetRepository extends CrudRepository<WorkoutSet, Long> {
#Query("select w from WorkoutSet w inner join Session s on w.session_id = s.id where s.user = :user order by w.timestamp")
List<WorkoutSet> findLastSet(User user, Pageable limit);
I get...
org.springframework.data.mapping.PropertyReferenceException: No property user found for type WorkoutSet!
Any clues about how to do the query right? I'm very open to alternative ways as well because I'd rather avoid writing jpql if possible.
Try this:
#Repository
public interface WorkoutSetRepository extends CrudRepository<WorkoutSet, Long> {
#Query("select w from WorkoutSet w inner join w.session s where s.user = :user order by w.timestamp")
List<WorkoutSet> findLastSet(#Param("user") TestUser user);
}
Note the difference in the join clause. This works with hibernate 5.0.11.

Resources