Select record where id is one of collection id - mysql

In a spring mvc application i have 3 database tables (including one mapping table) and their 2 corresponding java entities.
The entities are:-
public class User {
private Long id;
private String userName;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_location",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "location_id", referencedColumnName = "id")}
)
private Set<Location> location;
}
public class Location {
private Long id;
private String locationCode;
}
Three tables are users, location and user_location.
I want to select a user whose location id is equal to a particular id.
Since user can have multiple locations i am not sure how to write a hibernate query for this. I tried few combinations below but i am either getting a exception,
illegal attempt to dereference collection [{synthetic-alias}{non-qualified-property-ref}] with element property reference [id]
or getting a list of User and Location objects. i just want a list of user objects.
from User where userName = :userName and :locationId in (location.id)
from User user inner join user.location loc where user.userName = :usersName and loc.id = :locationId
Update:
I tried query,
Query query = getCurrentSession().createQuery("from User user inner join user.location loc where user.userName = :usersName and loc.id = :locationId");
Hibernate generates following plain SQL from above query and returns a list of User and Location objects. For example if there is one location for an user that matches above query hibernate returns a list that contains one User and one Location object.
select
user0_.id as id1_18_0_,
location2_.id as id1_5_1_,
user0_.user_name as user_na11_18_0_,
location2_.location_code as location3_5_1_
from
users user0_
inner join
user_location location1_
on user0_.id=location1_.user_id
inner join
location location2_
on location1_.location_id=location2_.id
where
user0_.user_name=?
and location2_.id=?

You could try to use next criteria:
Criteria criteria = getCurrentSession().createCriteria(User.class, "u");
criteria.createAlias("location", "loc");
criteria.add(Restrictions.eq("u.userName", "userName");
criteria.add(Restrictions.eq("loc.id", locationId);
List<User> users = criteria.list();
Or you could try HQL typed query:
TypedQuery<User> query =
getCurrentSession().createQuery("SELECT User.* FROM User u JOIN user_location ul ON u.id = ul.user_id JOIN Location l ON ul.location_id = l.id WHERE u.userName = :userName AND l.id = :locationId", User.class)
.setParameter("userName", "userName")
.setParameter("locationOd", locationId);
List<User> users = query.getResultList();

Related

JpaRepository custom #Query generates unwanted CROSS JOIN

account entity code:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
#NotFound(action = NotFoundAction.IGNORE)
private User user;
user entity code:
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Account> account = new ArrayList<>();
the repository cusotom method query annotion
#Query(value = "FROM Account a WHERE (:userId IS NULL Or a.user.id = :userId))
It generates this query:
from account account0_ cross join user user1_ where account0_.user_id=user1_.id and (? is null or user1_.id=?)
It worked fine before I updating springboot from 2.1.4.release to 2.6.5
which is expected to be the following:
from account account0_ WHERE (:userId IS NULL Or account0_.user_id = :userId)
Don't know if it's due to the version change or not.
Can someone give some clues?
It's a stange behavior on JPA if you use
#NotFound(action = NotFoundAction.IGNORE)
this above annotion,
remove this will behave normally.

Many to many Jpa Query Spring boot

I have three mysql tables with a many to many relationship and I am trying to make a Jpa Query on spring boot. The tables are
product with fields id, name
extra with fields id, name
product_extra with fields id, product_id, extra_id
Product table has a many to many relationship with extra table, as a product can have many extras hence the need for product_extra table
Here is the query i would like to include in my project
SELECT extra.name
FROM extra
INNER JOIN product_extra ON extra_id = extra.id
WHERE product_id = ?;
Should i have like a #ManyToMany annotation and where should i have it
Yes you should. Use #ManyToMany to map between Product and Extra.Make sure to use Set instead of List for the mapping in order to have better performance. It looks like:
#Entity
#Table(name="product")
public class Product{
#ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinTable(name = "product_extra",
joinColumns = #JoinColumn(name = "product_id"),
inverseJoinColumns = #JoinColumn(name = "extra_id")
)
private Set<Extra> extras = new HashSet<>();
}
#Entity
#Table(name="extra")
public class Extra{
#ManyToMany(mappedBy = "extras")
private Set<Product> products = new HashSet<>();
}
After mapping them , you can then use JPQL to get a product by id together with its extra by :
select p from Product p left join fetch p.extras where p.id = :productId;

How to update 3 tables using one query in JPA?

I have 3 tables UserDetail, RequestorDetail and AddressDetail. UserDetail is having userId(PK), userName, AddressDetail is having city and suburb, and RequestorDetail is having reqEmail and reqPhone.
I want to update all these details using one query. As of now, I m using 3 different Queries as below.
#Modifying
#Query("update UserDetail u set "u.userName = :userName, u.password = :password where u.userEmailId = :userEmailId")
int saveUserDetailData(#Param("userName") String userName, #Param("password") String password, #Param("userEmailId") String userEmailId);
#Modifying
#Query("update RequestorDetail r set r.requestorEmailid = :requestorEmailid, r.requestorPhone = :requestorPhone where r.userId = :userId")
int saveRequestorDetailData(#Param("requestorEmailid") String requestorEmailid,
#Param("requestorPhone") String requestorPhone, #Param("userId") int userId);
#Modifying
#Query("update AddressDetail a set a.city = :city, a.suburb = :suburb where a.userId = :userId")
int saveAddressDetailData(#Param("city") String city, #Param("suburb") String suburb,
#Param("userId") int userId);
Can anyone please tel me how to use all these in single Query?
It is not possible to update multiple tables with single sql query.
See https://community.oracle.com/thread/2225393
You'll have to split this it into different queries.

JPA How to build a join criteria query

I have the following situation 3 tables, person, job, category.
Person has a job and Job has a category. How do I get the person records from the same category.
public List<Person> findPplByCategory(Category category) {
javax.persistence.criteria.CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<Person> e = cq.from(Person.class);
javax.persistence.criteria.Root<Job> a = cq.from(Job.class);
//...not sure how to create the query here..
}
This is easy to do using a JPQL query
String query = "select P from Person P join P.job J join J.category C where C = :cat"
List<Person> = entitiyManager.createQuery(query, Person.class).setParameter("cat", myCategory).getResultList();
Some assumptions:
One Person has one Job
One Job has one Category
Person's job field is named job
Job's category field is named category
haven't tested but may be something like
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> q = cb.createQuery(Person.class);
Root<Person> person = q.from(Person.class);
Join<Person,Job> job = person.join(Person_.job);
Predicate predicate = cb.conjunction();
Predicate p = cb.equal(job.get(Job_.category), cat);
predicate = cb.and(p);
q.where(predicate);
q.select(person);
TypedQuery<Person> tq = entityManager.createQuery(q);
List<Person> all = tq.getResultList();
return all;
javax.persistence.criteria.CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<Person> e = cq.from(perdon.class);
cq.where(cb.equal(e.get("idCompany").get("idCategory"), cat));
cq.orderBy(cb.desc(e.get("idPerson")));
return getEntityManager().createQuery(cq).getResultList();
my version is:
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> root = cq.from(Person.class);
Join<Person,Job> job = root.join(Person_.job);
Join<Job,Category> category =car.join(Car_.category).where(cb.equal(job.get(Job_.category).get(Category_.name),"your category"));
cq.select(root);
TypedQuery<Person> query = getEntityManager().createQuery(cq);
return query.getResultList();
Here you have that Person is one-to-one with Job and the latter is one-to-one with Category :) You will receive you all persons which is in specified category of job.

Not returning all the required rows using MySQL

so here is my issue:
i have 3 tables:
ROLE : RID ,NAME
CLIENT : CID, NAME
USER : UID, RID, CID, USERNAME, PASSWORD
Below is the SQL statement that I have written:
SELECT USER.UID,USERNAME,PASSWORD,ROLE.NAME, ROLE.RID
FROM USER
INNER JOIN ROLE ON USER.RID=ROLE.RID
WHERE CID=1;
The above statement is returning only 1 row when there should actually be 2 rows.
I don't understand what is not working.
When i do the following, i get my 2 rows:
SELECT *
FROM USER
WHERE CID =1;
Note that i am using spring framework and also implementing a RowMapper. Below is my actual code with the field names as per the dbase.
public List<User> viewUserClient(int client_id) {
String sql =
"SELECT USER.ID,USERNAME,PASSWORD,ACTIVE,ROLE.NAME, ROLE.ID FROM USER INNER JOIN ROLE ON USER.ROLE_ID=ROLE.ID WHERE CLIENT_ID=?";
List<User> users = this.getJdbcTemplate().query(sql, new Object[] { client_id }, new UserClientRowMapper());
return users;
}
private static final class UserClientRowMapper implements RowMapper<User> {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
Client client = new Client();
Role role = new Role();
user.setID(rs.getInt("ID"));
user.setUSERNAME(rs.getString("USERNAME"));
user.setPASSWORD(rs.getString("PASSWORD"));
user.setACTIVE(rs.getBoolean("ACTIVE"));
role.setNAME(rs.getString("NAME"));
role.setID(rs.getInt("ROLE.ID"));
client.setId(rs.getInt("id"));
client.setName(rs.getString("name"));
user.setRole(role);
user.setClient(client);
return user;
}
}
Thanks in advance for your help.
The INNER JOIN keyword returns rows when there is at least one match in both tables. If there are rows in "USER" that do not have matches in "ROLE", those rows will NOT be listed; of the two users returned by your plain select query, probably one has a null RID column value, or a value that is not in ROLE table.
Use a LEFT JOIN.