I have this scenario:
User and his related UserRole entity classes, as below:
#Entity
#Table(name="USER")
public class User implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="ID", unique=true, nullable=false)
private int id;
#Column(name="USERNAME", unique=true, nullable=false, length=255)
private String username;
#OneToMany(mappedBy="user")
private List<UserRole> userRoles;
}
and
#Entity
#Table(name="user_roles")
public class UserRole implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="user_role_id", unique=true, nullable=false)
private int userRoleId;
#Column(nullable=false, length=45)
private String role;
#ManyToOne
#JoinColumn(name="username", nullable=false)
private User user;
}
Now, I need to query all users that have a specific role. I'm trying making join with JPA Specifications, like that:
Join<User, UserRole> join = root.join(User_.userRoles);
Expression<String> match = join.get(UserRole_.role);
Predicate predicate = builder.equal(match, "ROLE_USER");
The problem is that the generated join will be between User.id and UserRole.username and the query will obviously have no results.
select count(user0_.ID) as col_0_0_ from USER user0_ inner join
user_roles userroles1_ on user0_.ID=userroles1_.username where
userroles1_.role='ROLE_USER'
I need instead to have the on clause both on username fields:
... from USER user0_ inner join
user_roles userroles1_ on user0_.USERNAME=userroles1_.username ...
I noticed that there is the .on method of Join class who:
Modify the join to restrict the result according to the specified ON
condition. Replaces the previous ON condition, if any. Return the join
object
Is this the correct approach? If so, how could I implement that?
Join<User, UserRole> join = root.join(User_.userRoles).on(????);
Thank you in advance.
UPDATE:
UserRole_ metamodel class
#StaticMetamodel(UserRole.class)
public class UserRole_ {
public static volatile SingularAttribute<UserRole, Integer> userRoleId;
public static volatile SingularAttribute<UserRole, String> role;
public static volatile SingularAttribute<UserRole, User> user;
}
User_ metamodel class:
#StaticMetamodel(User.class)
public class User_ {
public static volatile SingularAttribute<User, Integer> id;
public static volatile SingularAttribute<User, String> username;
public static volatile ListAttribute<User, UserRole> userRoles;
}
You need to use referencedColumnName:
#ManyToOne
#JoinColumn(name="username", referencedColumnName="username", nullable=false)
private User user;
With only #JoinColumn(name="username") you tell Hibernate that the join column in user_roles is named username - but it still expects that it contains the values of the #Id property of User. If you create the DDL for your schema you will see that Hibernate generates a number column for user_roles.username.
Once again, you should check what the user_roles.username column contains, because by default, it contains the #Id column of the referenced entity, here the referenced entity is User, and its #Id column is id (in your schema: ID) and not username (in your schema: USERNAME).
Although, here's an example of how to write what you described here:
The problem is that the generated join will be between User.id and
UserRole.username and the query will obviously have no results. I need
instead to have the on clause both on username fields:
In JPQL (using JPA 2.1)
// get entity manager as 'em'
TypedQuery<User> q = em.createQuery("SELECT u FROM User u INNER JOIN UserRole ur ON ur.user = u.username WHERE ur.role = 'ROLE_USER'", User.class);
List<User> results = q.getResultList();
Related
There are several entities in the project, such as User and enum Role, which is used in the User entity as #ElementCollection.
User entity
public class User {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(unique = true, nullable = false)
private String username;
#Column(unique = true, nullable = false)
private String email;
#Column(nullable = false, length = 80)
private String password;
#Column(nullable = false)
private Boolean isActive = false;
#ElementCollection(fetch = LAZY)
private Set<Role> roles = new HashSet<>(Set.of(USER));
}
Role enum
public enum Role implements GrantedAuthority {
USER, ADMIN;
#Override
public String getAuthority() {
return name();
}
}
For the User entity, I created a special projection that includes the following fields, including the role collection:
public interface UserProjection {
String getUsername();
String getPassword();
Set<Role> getRoles();
}
And a JPA repository with a method that finds by username the user using this projection:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
#EntityGraph(attributePaths = {"roles"})
Optional<UserProjection> findUserByUsername(String username);
}
When I call this method, I run into the problem that the query contains all fields, including those not specified in the projection:
select
user0_.id as id1_1_,
user0_.email as email2_1_,
user0_.is_active as is_activ3_1_,
user0_.password as password4_1_,
user0_.username as username5_1_,
roles1_.users_id as users_id1_2_0__,
roles1_.roles as roles2_2_0__
from
users user0_
left outer join
users_roles roles1_
on user0_.id=roles1_.users_id
where
user0_.username=?
But if I remove the collection from the projection, the query starts working correctly:
select
user0_.username as col_0_0_,
user0_.password as col_1_0_
from
users user0_
where
user0_.username=?
Can you tell me how to fix this error? I couldn't find any clues, including in the Spring Data JPA documentation, which gave examples of the most basic use cases of projections. Thanks
My application under Spring Boot v1.5.7
I have 3 entities (schematically):
#Entity
public class Word {
#Id
#GeneratedValue
private Integer id
...
}
#Entity
public class UserWordList {
#Id
#GeneratedValue
private Integer id
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#ManyToOne
#JoinColumn(name = "word_id")
private Word word;
}
#Entity
public class UserAnotherWordList {
#Id
#GeneratedValue
private Integer id
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#ManyToOne
#JoinColumn(name = "word_id")
private Word word;
}
And now I need to select all Words for User, but exclude Words placed in user's lists
Native SQL for user_id=1 is
select *
from Word w
left join UserWordList uwl
on w.id = uwl.word_id and uwl.user_id = 1
left join UserAnotherWordList uawl
on w.id = uawl.word_id and uawl.user_id = 1
where uwl.word_id is NULL
and uawl.word_id is NULL
What is a best way to do it? Ideally I would like to use Spring Data features or HQL, but I don't understand how...
UPD
I solve my problem with native query:
#Entity
#NamedNativeQuery(
name = "User.getWordsToProcess",
resultClass = Word.class,
query = "<...native query to select Words...>"
)
public class User {...}
...
public interface UserRepository extends CrudRepository<User, Integer> {
List<Word> getWordsToProcess(Integer userId);
}
Fastest answer is Criteria api (but that is deprecated in hibernate 5.2 and above.)
So you can use Hql :
getSession().createQuery(" select * from UserWordList u left join fetch u.word
left join fetch u.user").list()
And you can use union or create another query to fetch UserAnotherWordList.
Also you can set any restrictions in Hql like below:
Query query = getSession().createQuery(" select * from UserWordList u left join fetch u.word left join fetch u.user us where us.user = :sample").list();
query.setParameter("sample",value);
query.list();
I would like to be able to get mutual friends of both users in hql.
Is there any way to do it with h2 db.
I'm using spring-boot 1.5.6 with h2 embedded db.
Below is my User entity.
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#Version
#Column(name="version")
private int version;
#NotNull
#Column(name="username", nullable=false, unique=true)
private String username;
#Column(name="email", nullable=false, unique=true)
private String email;
#Column(name="password", nullable=false)
private String password;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "friends")
private Set<User> friends;
and repository
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
User findByEmailAndPassword(String email, String password);
Here's my latest try which gives me the exception for some reason.
#Query("Select friends from User U join U.friends friends where size (friends.username) > 1 and exists (select friends from User U1 join U1.friends friends1 where friends1.username = ?1) and exists (select friends from User U2 join U2.friends friends2 where friends2.username = ?2)")
List<User> findMutualFriends(String username, String friend);
And when I try to get only duplicate values from it I get the exception. Can't access friends of username even though I'm using join on them. Here's stack trace:
Caused by: org.h2.jdbc.JdbcSQLException: Column "USER2_.USER_ID" not found; SQL statement:
select user2_.id as id1_4_, user2_.acc_status as acc_stat2_4_, user2_.email as email3_4_, user2_.gender as gender4_4_, user2_.password as password5_4_, user2_.username as username6_4_, user2_.version as version7_4_ from user user0_ inner join friends friends1_ on user0_.id=friends1_.user_id inner join user user2_ on friends1_.friends_id=user2_.id where (select count(user2_.user_id) from friends friends1_, user user2_ where user0_.id=friends1_.user_id and friends1_.friends_id=user2_.id)>1 and (exists (select user2_.id as id1_4_ from user user3_ inner join friends friends4_ on user3_.id=friends4_.user_id inner join user user5_ on friends4_.friends_id=user5_.id where user5_.username=?)) and (exists (select user2_.id as id1_4_ from user user6_ inner join friends friends7_ on user6_.id=friends7_.user_id inner join user user8_ on friends7_.friends_id=user8_.id where user8_.username=?)) [42122-196]
Any idea how to do it cause I couldn't find the answer. Maybe it can be done easier but I'm new to hql and can't figure it out.
Thanks.
I have two tables: Organization(Parent) and Department(Child).
There is One to Many relationship, and is mentioned in Organization table only.
#Entity
#Table(name="TBL_STD_ORGANIZATION")
public class Organization implements Serializable {
#Id
#GeneratedValue
#Column(name="FLD_ORG_ID")
private Long organizationId;
#Column(name="FLD_ORG_NAME")
private String orgName;
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private java.util.List<Department> listOfDepartMents = new java.util.ArrayList<Department>();
}
Below is Department Class:
#Entity
#Table(name="TBL_STD_DEPARTMENT")
public class Department implements Serializable {
#Id
#GeneratedValue
#Column(name = "FLD_DEPARTMENT_ID")
private Long departmentId;
#Column(name = "FLD_DEPARTMENT_NAME")
private String departmentName;
}
I wrote relationship in Parent table, because of it hibernate creates third table.
Now, I have to retrieve departments start with "sa" keyword and in specific organization.
So I want the HQL or SQL query query. I am not getting it how to write such complex query.
Any suggestions?
I'm fairly certain the HQL/JPQL would be:
SELECT d FROM Organization o JOIN o.listOfDepartMents d WHERE d.departmentName LIKE "sa%"
Hi i'm New to write HQL Query please help me.....
my Hibernate have three ValueObjects ie.
#Entity
#Table(name="user")
public class UserVO {
#Id
#Column(name="S_ID")
private String s_id;
#Column(name="FIRSTNAME")
private String firstName;
private String email;
}
CourseVO class
#Entity
#Table(name="course")
public class CourseVO
{
#Id
#Column(name="S_ID")
public String s_id;
#Column(name="NAME")
public String name;
}
Skillset VO
#Entity
#Table(name="skillset")
public class SkillsetVO
{
#Id
#Column(name="S_ID")
public String s_id;
#Column(name="COURSE_ID")//Foreign Key "USER"
public String course_id;
#Column(name="USER_ID")//Foreign key "COURSE"
public String user_id;
#Column(name="TEACH_EXP")
public String teach_Exp;
}
Now How to get Values of FirstName,NAME,TEACH_EXP values using EMAIL of USER table using HQL query
If you want to use join syntax in HQL you must work out your mapping accordingly as joins are enabled by mapping.
#Entity class A { #OneToMany private List<B> bs; }
#Entity class B { #Basic int n; }
Enables
select b from A a inner join a.b where a = :id
But with your mapping this is not possible. Also bear in mind that in terms of efficiency most of the RDBMs will perform an inner join on a where a.id = b.id.
select u.firstName, c.name, s.teach_Exp
from UserVO u, CourseVO c, SkillsetVO s
where
u.s_id = s.user_id
and c.s_id = s.course_id
and u.email = :email
But I think that you must review you association. Since looks to me that SkillsetVO.user_id should be SkillsetVO.User (an association to a UserVO entity and same for CourseVO).