How can I fetch multiple rows from db in Hibernate , Spring, JSP? - java

I'm designing a hospitality app. and having some problem with fetching multiple rows from database. I'm using Hibernate, Spring Web MVC, mySQL and JSP. I have layers as Controller, Service, Dao, Model. I've designed a search page to see the user profiles according to their city. For example when I write 'NewYork' to the city field on the search screen it will show a list of user profiles who live in NewYork and mark isHosting value as true.
Here is my User class:
public class User{
#Column(unique = true, nullable = false)
private String username;
...
private String city;
private String isHosting;
public boolean isHosting() {
return hosting;
}
public void setHosting(boolean hosting) {
this.hosting = hosting;
}
...
}
Search class:
public class Search {
private String city;
private String sdate;
private String fdate;
private String numOfvisitor;
...
}
This my Dao class:
#Repository
public class SearchDao extends GenericDao<User> {
public User findByUserCity(final String city){
final Criteria c = createCriteria(User.class).add(Restrictions.eq("city", city));
return (User) c.uniqueResult();
}
}
Service class:
#Service
#Transactional
public class SearchService extends GenericService<User>{
#Autowired
public SearchService(SearchDao dao) {
super(dao);
}
...
public User findByUserCity(final String city) {
return ((SearchSurferDao) this.dao).findByUserCity(city);
}
}
And Controller class:
#RequestMapping(value = "/search", method = RequestMethod.GET)
public ModelAndView search(#ModelAttribute Search search) {
User user = SearchService.findByUserCity(search.getCity());
ModelAndView result = new ModelAndView("hello");
...
result.addObject("username", user.getUsername());
return result;
}
I know that I need to write a database query which returns a list and the list is needed to be send to JSP file and with foreach tag I can see profiles on the screen. But how can write a database query to get such a list from db, actually put it in which class? What I need to do in Controller? Where can I check isHosting value?

Criteria c = createCriteria(User.class).add(Restrictions.eq("city", city));
return (User) c.uniqueResult();
The above code does create a query which finds the users with the given city. But it assumes that only one user exists in the given city, which is probably not the case. The method should be
public List<User> findByUserCity(final String city) {
Criteria c = createCriteria(User.class).add(Restrictions.eq("city", city));
return c.list();
}
Also, The Criteria API leads to hard to read code, and is suitable when you have to dynamically compose a query based on several search criteria. For such a static query, you should use HQL:
public List<User> findByUserCity(String city) {
return session.createQuery("select u from User u where u.city = :city")
.setString("city", city)
.list();
}

To see only isHosting=true user you have two ways:
Fetch only isHosting=true users
For this, your query will change to :
session.createQuery("select u from User u where u.city = :city and u.isHosting is true")
.setString("city", city)
.list();
Fetch all users with matching city, then filter them in your java code.
ArrayList<User> fetchedList=session.createQuery("select u from User u where u.city = :city")
.setString("city", city)
.list();
for(User u: fetchedList){
if(u.isHosting()){
display(u);
}
}
Here, I would recommend using first option, as db queries are generally faster than iterating through fetched data on the client side.
However, if you want to have info on all of your users and want to filter isHosting=true users, the 2nd option is better than asking DB again and again.

Related

Map Custom JdbcTemplate query result in an Object

I new in java and try to use spring framework. I have a question.
By example, I have table :
employee (id_employee, name)
employee_product (id_employee_product, id_employee, product_name)
if I select an employee data from my Employee table, I can map it in a POJO model User and define the tables structure in that model, like this:
public class Employee {
private final int id_employee;
private final String nama;
public Employee(int id_employee, String nama){
this.id_employee = id_employee;
this.nama = nama;
}
public int getId() {
return id_employee;
}
public String getNama() {
return nama;
}
}
And this is the map from jdbcTemplate:
final String sql = "SELECT id_employee, nama FROM employee";
return jdbcTemplate.query(sql, (resultSet, i) -> {
return new Employee(
resultSet.getInt("id_employee"),
resultSet.getString("nama")
);
});
That is clear example for select data from 1 table.
My question is, how to map data from query if my data is custom query? Such us using join and select custom field from that tables, Am I need to create POJO every query?
Sometimes I need to select only employee.id_employee, and employee.name field from my employee table.
And in another controller I need to select employee.id_employee from my employee table.
In another case, I need only select employee.name, and employee_product.product_name
Is there an alternative to map the data without creating POJO for every case?
Create a one POJO combining two tables like this
public class Employee {
private int id_employee;
private String name;
private int id_employee_product.
private String product_name
//getter and setters
//Don't create a constructor its Entiry
}
Now by using a BeanPropertyRowMapper Doc Link write your repository like
public List<Employee> fetchEmployeeProduct(){
JdbcTemplate jdbcTemplate = new JdbcTemplate("Your_DataSource");
StringBuilder query = new StringBuilder();
query.append("Your Query");
List<Employee> employeeProductList =
jdbcTemplate.query(query.toString(), new BeanPropertyRowMapper<Employee>(Employee.class));
}
Make sure SELECT clause in the query and Employee POJO's filed name is same.
Once if you execute your query it will automatically map to POJO. You no need to write a custom mapper BeanPropertyRowMapperwill take care of mapping.

How i can receive one object instead many using spring JdbcTemplate when i using inner join comand in sql?

I have a class User with fields:
private Long id;
private String u_name;
private String u_surname;
private int u_age;
private Set<Roles> roles = new HashSet<>(); //its enum
When i tried to display it in response using JdbcTemplate and rest controller I have a problem because i receive three object in it.
My repository classes:
public List<User> findAll() {
String sql = "select users.id, users.u_name, users.u_surname, users.u_age, user_roles.user_role from users inner join user_roles on users.id = user_roles.user_id";
return jdbc.query(sql, new RowMapper<User>(){
#Override
public User mapRow(ResultSet rs, int row) throws SQLException {
Set<Roles> roles = new HashSet<>();
roles.add(Roles.valueOf(rs.getString("user_role")));
return new User(rs.getLong("id"), rs.getString("u_name"), rs.getString("u_surname"), rs.getInt("u_age"), roles);
}
});
}
public User findById(Long id) {
String sql = "select users.id, users.u_name, users.u_surname, users.u_age, user_roles.user_role from users inner join user_roles on users.id = user_roles.user_id where users.id = ?";
return jdbc.queryForObject(sql, new RowMapper<User>(){
#Override
public User mapRow(ResultSet rs, int row) throws SQLException {
Set<Roles> roles = new HashSet<>();
roles.add(Roles.valueOf(rs.getString("user_role")));
return new User(rs.getLong("id"), rs.getString("u_name"), rs.getString("u_surname"), rs.getInt("u_age"), roles);
}
}, id);
}
What do I need to do to receive only one object instead many?
That happens because you probably have more then one role attached to your user, so your SQL will result in a matrix (more roles, more lines), to solve that you will have to remove the join, but only if you don't need to see roles. However, if you have to retrieve user that has roles, you can change your sql to use 'exists' like that:
select
users.id,
users.u_name,
users.u_surname,
users.u_age,
user_roles.user_role
from users
where exists (
select 1 from user_roles
where users.id = user_roles.user_id)
and users.id = ?
*haven't tested
If, after all, you really need to know the roles, i recommend you to create a separate method to retrieve them

Spring boot: Optional parameter query in Query method

I am new to Spring boot and hibernate. Here I am trying run a search based optional parameter query Where i can search by name, country etc. If I kept this field null then query should all list. But the problem is my method is returning all data ignoring my search parameter. my model class look like
#Entity(name="MLFM_ORDER_OWNER")
public class ModelOrderOwner {
#Id #GenericGenerator(name = "custom_sequence", strategy =
"com.biziitech.mlfm.IdGenerator")
#GeneratedValue(generator = "custom_sequence")
#Column(name="ORDER_OWNER_ID")
private Long orderOwnerId;
#Column(name="OWNER_NAME")
private String ownerName;
#OneToOne
#JoinColumn(name="BUSINESS_TYPE_ID")
private ModelBusinessType businessTypeId;
#Column(name="SHORT_CODE")
private String shortCode;
#ManyToOne
#JoinColumn(name="OWNER_COUNTRY")
private ModelCountry ownerCountry;
// getter setter..
My Repository interface looks like
public interface OrderOwnerRepository extends
JpaRepository<ModelOrderOwner,Long>{
#Query("select a from MLFM_ORDER_OWNER a where a.businessTypeId.typeId=coalsec(:typeId,a.businessTypeId.typeId) and a.ownerCountry.countryId=coalsec(:countryId,a.ownerCountry.countryId) and a.ownerName LIKE %:name and a.shortCode LIKE %:code")
public List <ModelOrderOwner> findOwnerDetails(#Param("typeId")Long typeId,#Param("countryId")Long countryId,#Param("name")String name,#Param("code")String code);
}
And here is my method in controller
#RequestMapping(path="/owners/search")
public String getAllOwner(Model model,#RequestParam("owner_name") String name,#RequestParam("shortCode") String code,
#RequestParam("phoneNumber") String phoneNumber,#RequestParam("countryName") Long countryId,
#RequestParam("businessType") Long typeId
) {
model.addAttribute("ownerList",ownerRepository.findOwnerDetails(typeId, countryId, name, code));
return "data_list";
}
Can Any one help me in this regard? please?
It is too late too answer, but for anyone who looks for a solution yet there is a more simple way as below:
In my case my controller was like:
#RestController
#RequestMapping("/order")
public class OrderController {
private final IOrderService service;
public OrderController(IOrderService service) {
this.service = service;
}
#RequestMapping(value = "/{username}/", method = RequestMethod.GET)
public ResponseEntity<ListResponse<UserOrdersResponse>> getUserOrders(
#RequestHeader Map<String, String> requestHeaders,
#RequestParam(required=false) Long id,
#RequestParam(required=false) Long flags,
#RequestParam(required=true) Long offset,
#RequestParam(required=true) Long length) {
// Return successful response
return new ResponseEntity<>(service.getUserOrders(requestDTO), HttpStatus.OK);
}
}
As you can see, I have Username as #PathVariable and length and offset which are my required parameters, but I accept id and flags for filtering search result, so they are my optional parameters and are not necessary for calling the REST service.
Now in my repository layer I have just created my #Query as below:
#Query("select new com.ada.bourse.wealth.services.models.response.UserOrdersResponse(FIELDS ARE DELETED TO BECOME MORE READABLE)" +
" from User u join Orders o on u.id = o.user.id where u.userName = :username" +
" and (:orderId is null or o.id = :orderId) and (:flag is null or o.flags = :flag)")
Page<UserOrdersResponse> findUsersOrders(String username, Long orderId, Long flag, Pageable page);
And that's it, you can see that I checked my optional arguments with (:orderId is null or o.id = :orderId) and (:flag is null or o.flags = :flag) and I think it needs to be emphasized that I checked my argument with is null condition not my columns data, so if client send Id and flags parameters for me I will filter the Result with them otherwise I just query with username which was my #PathVariable.
Don't know how but below code is working for me:
#Query("select a from MLFM_ORDER_OWNER a
where a.businessTypeId.typeId=COALESCE(:typeId,a.businessTypeId.typeId)
and a.ownerCountry.countryId=COALESCE(:countryId,a.ownerCountry.countryId)
and a.ownerName LIKE %:name and a.shortCode LIKE %:code")
public List <ModelOrderOwner> findOwnerDetails(
#Param("typeId")Long typeId,
#Param("countryId")Long countryId,
#Param("name")String name,
#Param("code")String code);
and in my controller class:
#RequestMapping(path="/owners/search")
public String getAllOwner(Model model,
#RequestParam("owner_name") String name,
#RequestParam("shortCode") String code,
#RequestParam("phoneNumber") String phoneNumber,
#RequestParam("countryName") Long countryId,
#RequestParam(value = "active", required = false) String active, #RequestParam("businessType") Long typeId) {
if(typeId==0)
typeId=null;
if(countryId==0)
countryId=null; model.addAttribute("ownerList",ownerRepository.findOwnerDetails(typeId, countryId, name, code, status));
return "data_list";
}
JPQL doesn't support optional parameters.
There is no easy way of doing this in JPQL. You will have to write multiple WHERE clauses with OR operator.
Refer these answers to similar questions: Answer 1 & Answer 2
PS: You might want to look into Query by Example for your use case. It supports handling of null parameters.
Use JpaSpecificationExecutor //import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
Step 1: Implement JpaSpecificationExecutor in your JPA Repository
Ex:
public interface TicketRepo extends JpaRepository<Ticket, Long>, JpaSpecificationExecutor<Ticket> {
Step 2 Now to fetch tickets based on optional parameters you can build Specification query using CriteriaBuilder
Ex:
public Specification<Ticket> getTicketQuery(Integer domainId, Calendar startDate, Calendar endDate, Integer gameId, Integer drawId) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.equal(root.get("domainId"), domainId));
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), startDate));
predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), endDate));
if (gameId != null) {
predicates.add(criteriaBuilder.equal(root.get("gameId"), gameId));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
Step 3: Pass the Specification instance to jpaRepo.findAll(specification), it will return you the list of your entity object (Tickets here in the running example)
ticketRepo.findAll(specification); // Pass output of function in step 2 to findAll

Spring-Boot Data MongoDB - How to getting a specific nested object for a super specific object

I have the following data model, and I want to get a specific object in the sub list objects, I know it's possible to get the entire list and go through each object and compare with what the search id, but I wonder if it is possible use MongoRepository to do this.
#Document
public class Host {
#Id
private String id;
#NotNull
private String name;
#DBRef
private List<Vouchers> listVoucher;
public Host() {
}
//Getters and Setters
}
And..
#Document
public class Vouchers {
#Id
private String id;
#NotNull
private int codeId;
public Vouchers() {
}
//Getters and Setters
}
The Repository Class:
public interface HostRepository extends MongoRepository<Host, String> {
List<Host> findAll();
Host findById(String id);
Host findByName(String name);
//How to build the correct query ??????????
List<Vouchers> findVouchersAll();
Vouchers findByVouchersById(String hostId, String voucherId);
}
The Controller Class:
#RestController
#RequestMapping(value = "api/v1/host")
public class VoucherController {
#Inject
HostRepository hostRepository;
#RequestMapping(value = "/{hostId}/voucher",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<Vouchers> list() {
return hostRepository.findVouchersAll();
}
#RequestMapping(value = "/{hostId}/voucher/{voucherId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public Vouchers getOneVoucher(#PathVariable String hostId, #PathVariable String voucherId) {
Vouchers voucher = hostRepository.findByVouchersById(hostId, voucherId);
if (voucher != null) {
return voucher;
} else {
throw new VoucherNotFoundException(String.format("There is no voucher with id=%s", voucherId));
}
}
}
Thanks in Advance!
I think there is a way to do this although I have not tried this myself but maybe I can shed some light in how I would do it.
Firstly, I would rather use the more flexible way of querying mongodb by using MongoTemplate. MongoTemplate is already included in the Spring Boot Mongodb data library and it looks like you are already using the library so it is not an additional library that you will have to use. In Spring there is a way to #Autowired your MongoTemplate up so it is quick and easy to get the object for completing this task.
With mongoTemplate, you would do something like this:
Query query = new Query();
query.addCriteria(Criteria.where("listVouchers.id").is("1234"));
List<Host> host = mongoTemplate.find(query, Host.class);
Please see docs here: https://docs.mongodb.org/manual/tutorial/query-documents/

parameterizing object properties

Can someone show me a code efficient way to have an object property in spring mvc change based on parameters sent to it from a hyperlink?
I am modifying the spring petclinic sample application so that an "owner" detail page can show separate lists of each type of "pet" that the specific "owner" owns. Currently, a list of "pets" is a property of each "owner" and is accessible in jstl as owner.pets. What I want is for my jstl code to be able to call owner.cats, owner.dogs, owner.lizards, etc from jstl, and to populate several separate lists in different parts of the web page, even though all the cats, dogs, and lizards are stored in the same underlying data table.
How do I accomplish this?
Here are the relevant methods of JpaOwnerRepositoryImpl.java:
#SuppressWarnings("unchecked")
public Collection<Owner> findByLastName(String lastName) {
// using 'join fetch' because a single query should load both owners and pets
// using 'left join fetch' because it might happen that an owner does not have pets yet
Query query = this.em.createQuery("SELECT DISTINCT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName");
query.setParameter("lastName", lastName + "%");
return query.getResultList();
}
#Override
public Owner findById(int id) {
// using 'join fetch' because a single query should load both owners and pets
// using 'left join fetch' because it might happen that an owner does not have pets yet
Query query = this.em.createQuery("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id");
query.setParameter("id", id);
return (Owner) query.getSingleResult();
}
Here are relevant aspects of Owner.java:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
private Set<Pet> pets;
protected Set<Pet> getPetsInternal() {
if (this.pets == null) {this.pets = new HashSet<Pet>();}
return this.pets;
}
public List<Pet> getPets() {
List<Pet> sortedPets = new ArrayList<Pet>(getPetsInternal());
PropertyComparator.sort(sortedPets, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedPets);
}
Here is the part of OwnerController.java that manages the url pattern "/owners" from which I want my jstl to be able to separately list cats, dogs, lizards, etc in separate parts of the page (not in one grouped list, but in several separate lists.):
#RequestMapping(value = "/owners", method = RequestMethod.GET)
public String processFindForm(#RequestParam("ownerID") String ownerId, Owner owner, BindingResult result, Map<String, Object> model) {
Collection<Owner> results = this.clinicService.findOwnerByLastName("");
model.put("selections", results);
int ownrId = Integer.parseInt(ownerId);
model.put("sel_owner",this.clinicService.findOwnerById(ownrId));
return "owners/ownersList";
}
Since you asked for a non-verbose solution, you could just do this kind of semi-dirty fix.
Owner.java
#Transient
private Set<Pet> cats = new HashSet<Pet>();
[...]
// Call this from OwnerController before returning data to page.
public void parsePets() {
for (Pet pet : getPetsInternal()) {
if ("cat".equals(pet.getType().getName())) {
cats.add(pet);
}
}
}
public getCats() {
return cats;
}
ownerDetail.jsp
[...]
<h3>Cats</h3>
<c:forEach var="cat" items="${owner.cats}">
<p>Name: <c:out value="${cat.name}" /></p>
</c:forEach>
<h3>All pets</h3>
[...]
OwnerController.java
/**
* Custom handler for displaying an owner.
*
* #param ownerId the ID of the owner to display
* #return a ModelMap with the model attributes for the view
*/
#RequestMapping("/owners/{ownerId}")
public ModelAndView showOwner(#PathVariable("ownerId") int ownerId) {
ModelAndView mav = new ModelAndView("owners/ownerDetails");
Owner owner = this.clinicService.findOwnerById(ownerId);
owner.parsePets();
mav.addObject(owner);
return mav;
}

Categories