Finding mutual friends of the same entity - java

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.

Related

How to sort by pagerequest with inner join

I have two tables with many to one realtion. I want to sort data in table "user" by column "street" that is in "address" table in ASC or DESC direction defined by request param:
localhost:8080/getUsers?sort=address,desc
When I execute sql script:
SELECT * FROM user INNER JOIN address ON user.address_id=address.id ORDER BY street DESC
in workbench or phpMyAdmin it works good. All data are sorted by street name;
But when i try get it in postman by:
getUsers?sort=address,desc
I have this error in console output:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'INNER.address' in 'order clause'
Where is problem?
#Table(name = "user")
public class User {
private Long id;
private String name;
#ManyToOne()
#JoinColumn(name = "address_id")
private Address address;
}
#Table(name = "address")
public class Address {
private Long id;
private String street
#OneToMany(mappedBy = "address")
private List<User> user;
}
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "SELECT * FROM user INNER JOIN address ON user.address_id=address.id",
countQuery = "SELECT * FROM contact_messages INNER JOIN contact_topics ON contact_messages.contact_topic_id=contact_topics.id",
nativeQuery = true)
Page<User> findAll(Pageable pageable);
}
Also when i wrote query in repository like this it works as good as in workbench:
#Query(value = "SELECT * FROM user INNER JOIN address ON user.address_id=address.id ORDER BY address DESC",
countQuery = "SELECT * FROM contact_messages INNER JOIN contact_topics ON contact_messages.contact_topic_id=contact_topics.id ORDER BY address DESC",
nativeQuery = true)
But i want to have controll of request and sort data when i want (using sort param).
Try replacing the SQL to
SELECT u, a FROM user u INNER JOIN u.address a ORDER BY street DESC

Need help translating a MYSQL DB Sql Query into a Jpql query

I have 3 entities - usergroup, user, usergroupmemberships. usergroupmemberships has a 1 to 1 to both user and usergroup. im trying to get a users groups with his id
The following Query works in my DB:
SELECT *
FROM UserGroup g
JOIN UserGroupMembership ON
UserGroupMembership.usergroup_id = g.id
WHERE UserGroupMembership.user_id = 868
Wildfly:
the same as a Java REST POST that uses a query from my entitymanager doesnt with a nullpointerexception
SELECT g
FROM UserGroup g
JOIN UserGroupMembership
ON UserGroupMembership.usergroup = g.id
WHERE UserGroupMembership.user = :id
How do i write this correctly?
Tried different ways to write the query and tried writing user_id
im on wildfly and java ee
public List<UserGroup> findUserGroupbyUser(long id) {
return em.createQuery("SELECT g FROM UserGroup g JOIN
UserGroupMembership ON UserGroupMembership.usergroup = g WHERE
UserGroupMembership.user.id = :id", UserGroup.class).setParameter("id",
id).getResultList();
}
#Entity
public class UserGroupMembership {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(cascade = CascadeType.ALL)
private UserGroup usergroup;
#OneToOne(cascade = CascadeType.ALL)
private User user;
expected result is 1 entry

How to filter select from table by another table by exclusion principle

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();

How to use hibernate lazy loading with column that can be null

This is my entity:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.MERGE)
#JoinColumn(name = "id_city")
private City city;
//...
}
In my Repository I have:
public interface UserRepository extends JpaRepository<User, Long>{
#Query("SELECT u FROM User u JOIN FETCH u.city")
public List<User> findAllUserForApi();
}
If there are any cities in table, findAllUserForApi() shows me full information about user:
[{"id":1,"name":"John","surname":"Pillman","city":{"id":1,"name":"New York"}]
If there are no cities, I want to get at least [{"id":1,"name":"John","surname":"Pillman","city":null]
But I've got nothing: []
Help me please.
Given that you are already using a custom query, the simplest solution would be a LEFT JOIN FETCH:
#Query("SELECT u FROM User u LEFT JOIN FETCH u.city")
This way all users will be loaded regardless of whether they have a city or not; and for those who have a city, it'll be available by user.getCity().
Why you write custom query here. You dont need.
Firstly you have to follow general convention:
#ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.MERGE)
#JoinColumn(name = "CITY_ID")
private City city;
...
And here JPA shows all information related with User.
public interface UserRepository extends JpaRepository<User, Long>{
public List<User> findAll();
}
It looks like you are trying to use Lazy Loading with a predefined Query, I don't think this is going to work.
See, the JOIN FETCH in your query state the following:
Get all the Users which has u.City
So if you don't have a u.City for a user, the return would be empty.
More info on Join and Fetch
What you really want is the following:
public User findUserByID(Long userId)
{
Session session = sessionFactory.getCurrentSession();
User user = (Users) session.createCriteria(User.class).add(Restrictions.idEq(userId)).uniqueResult();
// this will force SQL to execute the query that will join with the user's city and populate
// the appropriate information into the user object.
Hibernate.initialize(user.geCity());
return user;
}
If the u.City is NULL, it will return a NULL. while the User object contains data.
Or in your case Find all users :
public List<User> findUserByID(Long userId)
{
Session session = sessionFactory.getCurrentSession();
List<User> users = (List<User>) session.createCriteria(User.class);
// this will force SQL to execute the query that will join with the user's city and populate
// the appropriate information into the user object.
for (User user : users)
Hibernate.initialize(user.geCity());
return user;
}
Note:
I did not test the code, this is pseudo so you may want to change some of it.
source

JPA Join on specific field

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();

Categories