Selecting latest workout set by user - spring

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.

Related

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

Spring JPA - Is there a good way for inner join between entities or the only way is JPQL?

I have the following Entities:
#Entity
public class Organisation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
// ...
}
#Entity
public class Section{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
// ...
#ManyToOne
#JoinColumn(name = "organisation_id", nullable = false)
private Organisation organisation;
}
#Entity
public class SubSection {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
// ...
#ManyToOne
#JoinColumn(name = "section_id")
private Section section;
}
Now I want to find all SubSections by Organisation Id. Right now I am doing it using JPQL at the SubSectionRepository as above:
public interface SubSectionRepository extends JpaRepository<SubSection, Long>{
#Query(value = "SELECT ss.* FROM sub_section as ss INNER JOIN section as s ON ss.section_id = s.id WHERE s.organisation_id = ?1", nativeQuery = true)
List<SubSection> findByOrganisation(Long organisationId);
}
Is there any way that I can make INNER JOIN using the JpaRepository interface?
Did you try this way
subsectionRepository.findBySectionOrganizationId(long organizationId)
Update your method to
public interface SubSectionRepository extends JpaRepository<SubSection, Long> {
List<SubSection> findBySectionOrganisationId(Long organisationId);
}
Hibernate will generate something like this.
select subsec.*,
from sub_section subsec
left outer join section sec on subsection.section_id=sec.id
left outer join organisation org on section.organisation_id=org.id
where org.id=?
Don't worry about the left join term. It is actually a inner join because you have a where condition with id = on the most right table. Because of that, it effectively becomes a inner join. I.e if there is no record on the right table for that record, it will be ignored.

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

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 ...

findBy not working with inherited properties

I have the following model and repository:
#Entity
#Table(name = "db_user", uniqueConstraints = { #UniqueConstraint(columnNames = "email") })
public class User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_user")
#SequenceGenerator(name = "seq_user", sequenceName = "seq_user")
#Column(name = "id")
private Long id;
// ...
}
#Entity
#Table(name = "movie")
public class Movie extends AbstractItem {
// Id column inherited from AbstractItem
// ...
}
#Entity
#Table(name = "movie_user")
public class MovieOwnership extends AbstractOwnership {
#ManyToOne
private Movie movie;
// ...
}
#MappedSuperclass
public abstract class AbstractOwnership{
#Id
#SequenceGenerator(name = "seq_default", sequenceName = "seq_default")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_default")
#Column(name = "id")
private Long id;
#ManyToOne
private User owner;
// ...
}
public interface MovieOwnershipRepository extends QueryDslJpaRepository<MovieOwnership, Long> {
List<MovieOwnership> findByOwnerId(Long ownerId);
MovieOwnership findByOwnerIdAndMovie(Long ownerId, Movie movieId);
List<MovieOwnership> findByOwnerIdAndMovieIdIn(Long ownerId, Set<Long> movieIds);
}
I'm trying to use Spring's findBy requests to fetch MovieOwnerships by owner or movie, using the id field of both entities. I'm able to work directly with the owner's id, but using MovieId in my requests seems broken (I can use the whole Movie object though). In the code above, the first two findBy are fine but the last one throws this exception:
Caused by: java.lang.IllegalArgumentException: Unable to locate
Attribute with the the given name [movieId] on this ManagedType
[carrm.app.data.AbstractOwnership]
It compiles if I try with another property from Movie (like findByMovieTitle), but I can't make it work on the id.
Any idea how to solve this?
I tried the same with JpaRepository instead of QueryDslJpaRepository.
The SQL is generated correctly:
select movieowner0_.id as id1_1_, movieowner0_.owner_id as owner_id2_1_, movieowner0_.movie_id as movie_id3_1_
from movie_ownership movieowner0_
left outer join user user1_ on movieowner0_.owner_id=user1_.id
left outer join movie movie2_ on movieowner0_.movie_id=movie2_.id
where user1_.id=? and (movie2_.id in (?))
So it must be a QueryDslJpaRepository implementation bug.
I would suggest you use JpaRepository instead.

Resources