Query in arraylist by one element in spring boot h2 - spring

I have a ClassRoom class, in my Spring Boot application. Here is what it looks like :
public class ClassRoom {
#Id #GeneratedValue Long classRoomID;
ArrayList<User>adminList=new ArrayList<>();
}
And in my ClassRoomRepository class, I have :
public interface ClassRoomRepository extends JpaRepository<ClassRoom,Long> {
#Query("select ClassRoom from ClassRoom c where c.adminList = ?1")
ArrayList<ClassRoom> findByAdminList(ArrayList<User> adminList);
/*
#Query("select ClassRoom from ClassRoom c where c. = ?1")
ArrayList<ClassRoom> findByAdmin(User admin);
*/
}
I can query to select ClassRoom where ArrayList of ClassRoom gets passed parameter.
But I want to query to select ClassRoom where I pass only one User as parameter and returns ArrayList of ClassRoom.(Commented section-nothing done so far)
If it is possible in this interface, how can I do so?

Asuming you habe many-to-many association using join table, this is what you need,
#Query(value = "SELECT DISTINCT cr FROM ClassRoom cr JOIN cr.admins a WHERE a = ?1")
List<ClassRoom> findAllByAdmin(final Admin admin);

When mapping collection , Hibernate requires it to declare it using interface type such as List , Set , Map etc. But you are using ArrayList to declare the admin list , I am guessing the whole list will be stored to a single column with some binary data type in DB . No one would store the data in this funny way when using RDBMS unless you have strong reason to do it.
From what you describe , ClassRoom and User seem to be many to many. For demonstration convenience , I would map it as #ManyToMany:
#Entity
#Table
public class ClassRoom {
#ManyToMany
#JoinTable(name ="classroom_user",
joinColumns = #JoinColumn(name = "classroom_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
private List<User> admins = new ArrayList();
}
#Entity
#Table
public class User{
#ManyToMany(mappedBy = "admins")
private List<ClassRoom> classRooms = Lists.newArrayList();
}
Then given an user admin ,to get all of his administrative class rooms :
#Query("select distinct cr from ClassRoom cr left join fetch cr.admins admin where admin = ?1 ")
public List<ClassRoom> findByAdmin(User admin);
Or :
#Query("select distinct cr from ClassRoom cr where ?1 member of cr.admins")
public List<ClassRoom> findByAdmin(User admin);

Related

Join 2 tables using Spring JPA

I have two entities(Invoice and InvoiceItems) without adding any relationship.
Invoice
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invoiceID;
#Column(name="code")
private String code;
//other columns
}
Invoice Items
public class InvoiceItems {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invItemID;
#Column(name="invoice_id")
private Integer invoiceId;
//other columns
}
Can I join these entities and get data without adding relationship using JPA?
If it isn't possible how to join 2 entities using JPQL or Native query?
If your data is valid then using native query you can do that
#Query(nativeQuery = true, "select * from Invoice i join InvoiceItems im on i.id = im.invoice_id")
public List<Invoice> findData();
But that is not a good way join without relation using JPA.
Yes, you can join these entities and get data without adding relationship using JPA, but it's a little bit losing the purpose of using JPA.
You need to create a java class first, which will be the returning data object from the DB. After that you can use entityManager's createNamedQuery method to get the result.
createNamedQuery(String sqlString, ResultClass.Class)
sqlString may be something like:
SELECT INV.INVOICE_ID
INV.CODE
INV_ITEMS.INV_ITEM_ID
FROM INVOICE INV
JOIN INVOICE_ITEMS INV_ITEMS
ON INV.INVOICE_ID = INV_ITEMS.INVOICE_ID;
And the corresponding ResultClass:
public class ResultClass {
private Long invoiceID;
private String code;
private Long invItemID;
// other columns
}
Or you can even use RowMapper to map the object all by yourself for more flexibility by using JdbcTemplate with query() method.

Hibernate performs update and delete on custom JPQL

I am trying to update the fields of an entity that has a ManyToMany relationship, however, as I just want to update the table fields and ignore the ManyToMany relationship. The relationship is between the Company and UserSystem entities, it was defined in the relationship that company_user_system is the union table of the entities. The problem is that when executing my update in Company, always before my update, Hibernate makes an update in company and the relationship delete in user_system_company and this erases the relationship between Company and UserSystem and I don't understand why these two queries occur if I don't execut.
These are the queries, the first and second are not executed by my code:
Hibernate: update company set active=?, email=?, identification_code=?, trading_name=?, update_on=? where id=?
Hibernate: delete from company_user_system where company_id=?
Hibernate: update company set email=?, phone=?, corporate_name=?, trading_name=?, identification_code=?, email=?, phone2=? where id=?
Hibernate: select company0_.id as id1_0_, company0_.active as active2_0_, company0_.corporate_name as corporat3_0_, company0_.created_on as created_4_0_, company0_.email as email5_0_, company0_.email2 as email6_0_, company0_.identification_code as identifi7_0_, company0_.phone as phone8_0_, company0_.phone2 as phone9_0_, company0_.trading_name as trading10_0_, company0_.update_on as update_11_0_ from company company0_ where company0_.id=?
Following is the update implementation code:
public class CompanyRepositoryImpl implements CompanyRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
public Company updateCompanyFields(Company company) {
// ... fieldSql implementation omitted
String sql = "UPDATE Company SET "+ fieldsSql +" WHERE id = :id ";
Query query = entityManager.createQuery(sql);
// set the values for the fields
for (Method method : getMethods) {
query.setParameter(lowercaseFirstCharInString(cutGetInMethods(method.getName())), method.invoke(company));
}
// set id
query.setParameter("id", company.getId());
// execute update and search the database to return the updated object
if (query.executeUpdate() == 1) {
query = entityManager.createQuery("SELECT c FROM Company c WHERE c.id = :id");
query.setParameter("id", company.getId());
Company getCompany = (Company) query.getResultList().get(0);
return getCompany;
}
return null;
}
// ... Other methods omitted
}
Repository Code:
#Repository
public interface CompanyRepository extends JpaRepository<Company, Long>, JpaSpecificationExecutor<Company> , CompanyRepositoryCustom {
#Modifying
Company updateCompanyFields(Company company);
}
Company entity code, I just added the attributes that I think may contain something useful to try to solve the problem:
#Entity
#DynamicUpdate
#Table(name = "company")
public class Company implements Serializable {
#CreationTimestamp
#Column(name = "created_on", nullable = false)
private Instant createdOn;
#UpdateTimestamp
#Column(name = "update_on")
private Instant updateOn;
#ManyToMany
#JoinTable(
name = "company_user_system",
joinColumns = #JoinColumn(
name = "company_id", referencedColumnName = "id"
),
inverseJoinColumns = #JoinColumn(
name = "user_system_id", referencedColumnName = "id"
)
)
private Set<UserSystem> userSystems = new HashSet<>();
}
The UserSystem class defines the relationship as follows:
#ManyToMany(mappedBy = "userSystems")
private Set<Company> companies = new HashSet<>();
What may be causing this update and delete before my update?
This happens because you changed somewhere the value(s) of your relationship. EntityManager tracks such changes and marks the entity as dirty. When you execute a custom SQL query Hibernate will perform all the pending queries (submit any dirty entities).
You may prevent it by calling EntityManager.clear().

Spring Boot many to many post method not updating data

My User class looks like this :
#Data
#Entity
public class User {
#Id
Long userID;
#ManyToMany(mappedBy = "admins")
private List<ClassRoom> classRooms = new ArrayList<>();
}
And my ClassRoom class like this :
#Data
#Entity
public class ClassRoom {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long classRoomID;
#ManyToMany
#JoinTable(name ="classroom_user",
joinColumns = #JoinColumn(name = "classroom_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
private List<User> admins = new ArrayList<>();
}
And in my UserController class, I have :
#PostMapping("user/{id}/c")
User addClassRoom(#PathVariable Long id,#RequestBody ClassRoom newClassRoom)
{
logger.debug(repository.findById(id));
return repository.findById(id)
.map(user -> {
user.getClassRooms().add(newClassRoom);
user.setClassRooms(user.getClassRooms());
return repository.save(user);
})
.orElseGet(() -> {
return null;
});
}
And I POST and empty JSON ({}) and I see no change in my users. The Classroom or an empty Classroom doesn't get added in the User.
What is the problem here? How can I resolve this ?
user.getClassRooms().add(newClassRoom); is suffice, user.setClassRooms(user.getClassRooms()); not required.
You will have to perform cascade save operation.List all cascade types explicitly and don't use mappedBy, instead use joincolumns annotation.
Can you paste the logs, please? Is Hibernate doing any insert into your table? Has the database schema been created in the DB correctly? One thing I recommend you to do is to add a custom table name on the top of your User class, using annotations like so: #Table(name = "users"). In most SQL dialects user is a reserved keyword, hence it is recommended to always annotate User class a bit differently, so that Hibernate won't have any problems to create a table for that entity.
IMO you must find classRoom by its id from repository, if it's new, you must create a new entity and save it first. Then assign it to user and save it.
The object you receive from the post method was not created by the entity manager.
After using user.getClassRooms().add(newClassRoom);
We must use userRepository.save(user);

Selecting from Multiple Tables in Spring JPA with Pageable and Sorting

I saw the Selecting from Multiple Tables in Spring Data already had the solution for multiple tables.
I would like to know if it is possible to write custom query that has tables with pageable and sorting feature at the same time in Spring JPA/DATA.
SELECT s.service_id, s.name, us.rating_id
FROM services s,
ratings r,
user_services us
where
us.service_id = s.service_id and
us.rating_id = r.rating_id and
us.user_id= ?
;
Thanks for you help in advance.
Sorting feature is under question, but pagination is possible to use.
Assume that we have:
#Entity
public class Service {
#Id
private Long id;
private String name;
//...
}
#Entity
public class UserService {
#Id
private Long id;
#ManyToOne
User user;
#ManyToOne
Service service;
#ManyToOne
Rating rating;
//...
}
Then we create a projection:
public interface ServiceRating {
Long getServiceId();
String getServiceName();
Long getRatingId();
}
And then create a query method supported pagination:
public interface UserServiceRepo extends CrudRepository<UserService, Long> {
#Query("select s.id as serviceId, s.name as serviceName, us.rating.id as ratingId from UserService us join us.service s where us.user.id = ?1")
Page<ServiceRating> getServiceRating(Long userId, Pageable pageable);
}
(Since this query does not contain grouping it's not necessary to use an additional countQuery (see the parameter of #Query)).
Test:
Page<ServiceRating> pages = userServiceRepo.getServiceRating(1L, new PageRequest(0, 10));
assertThat(pages.getContent()).hasSize(10));
UPDATE
Sorting also working perfectly.
Just create a Sort object, specify direction and filed name (from the projection):
Sort sort = new Sort(Sort.Direction.ASC, "serviceName");
userServiceRepo.getServiceRating(1L, new PageRequest(0, 10, sort));

Select Count using Criteria builder in a OneToMany Relationship

I'm building a Spring web app and i'm new to JPA and I need to get the number of users in a specific group in my database.
Here is the sample code :
public long countAllUsersByGroup(int groupId) {
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Long> cq = qb.createQuery(Long.class);
cq.select(qb.count(cq.from(User.class)));
//cq.where(qb.equal(cq.from(User.class).get("userGroup").get("id"),groupId));
return em.createQuery(cq).getSingleResult();
}
This code is working, it allows me to retrieve the number of users I have in the database which is pretty trivial.
This is my user Model :
public class User {
private String userFirstName;
private String userLastName;
/* some stuff */
#ManyToOne
private Group userGroup;
}
And my group model has an int attribute annotated with #Id and named id. How Can I get the number of user by group id in this case ?
P.S : I've tried and commented my try in the code above, unfortunately it was a failure ...
You can try the code below
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> q = cb.createQuery(Long.class);
ParameterExpression<Integer> p = cb.parameter(Integer.class);
q.select(q.count(q.from(User.class))).where(cb.gt(c.get("userGroup"), someId));

Resources