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;
}
Related
How to check that the List<Object> exists and contains an object of class whose partnerRole field value is equal to "CREATOR" and companyId field value is equals to "123"?
Document class:
#Document
public class ApplicationDocument {
List<PartnerDocument> partners = new ArrayList<>();
}
#Document
public class PartnerDocument {
private PartnerRole partnerRole;
private String companyId;
...
public enum PartnerRole {
UNKNOWN_ROLE,
CREATOR,
PARTICIPANT
}
}
My method for generating a List of Criteria. But that won't work because I'm referring to partners as if it were a PartnerDocument object, but partners is actually a List<PartnerDocument>.
public List<Criteria> getCriteria(Partner.PartnerRole role, String companyId) {
List<Criteria> criteriaList = new ArrayList<>();
criteriaList.add(Criteria.where("partners").exists(true));
criteriaList.add(
Criteria.where("partners.partnerRole").in(role)
);
criteriaList.add(
Criteria.where("partners.partnerRole").in(companyId)
);
return criteriaList;
}
There is a mistake in your second criteria.
Criteria.where("partners.partnerRole").in(companyId)
You are checking companyId against partnerRole.
It should be
Criteria.where("partners.companyId").in(companyId)
How can I execute Multiple SQL statements in a single sql query using hibernate native sql.
String sql = "SELECT * FROM user; SELECT * FROM product;";
UserVO valueObject = new UserVO();
databaseObject.select(sql, valueObject);
Database Object
public List select(String sql, Object valueObject) throws Exception {
Session session = Entitlement.getSessionFactory().openSession();
session.beginTransaction();
List list = session.createSQLQuery(sql).setProperties(valueObject).list();
session.close();
return list;
}
Use union to form a query which has same returning data
(Select EMPLOYEEID as id, EMPLOYEE_NAME as name, "EMPOYEE" as type) UNION (SELECT PRODUCTID as id, Product_NAME as name, "PRODUCT" as type)
form an Entity to hold it
class EntityDetail {
String id;
String name;
String type;
}
I have added an additional column value type to simply identify from which table row is coming from. And yes you will need to form a proper Entity with all valid annotations the above Entity is just for example.
just a lateral approach.
public List<List<Object[]>> execute(String sqls, Object valueObject) throws Exception {
String[] queries = sqls.split(";");
List<List<Object[]>> result = new ArrayList<>();
for(int i=0; i<queries.length; i++) {
result.add(this.select(queries[i], valueObject));
}
return result;
}
I am new to spring + hibernate. When I add a customer and its destinations (one to many relationship), everything is fine. But when I update the customer's destination, all previous destinations remain in the database with a null customer foreign key.
Suppose I insert 4 destinations a, b, c, d. After updating the customer, I insert x, y. Then it stores total 6 destinations: a, b, c, d with null references and x, y with customer references.
Here is my code:
1). Customer Entity
Has one-to-many relationship with destination and relationship is unidirectional.
#Entity
#Table(name="customers")
#Proxy(lazy=false)
public class CustomerEntity {
#Id
#Column(name="id")
#GeneratedValue
private Integer id;
private String description;
private String panNo;
private String cstNo;
private String vatNo;
#OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinColumn(name = "customer_id", referencedColumnName = "id")
public List<DestinationsEntity> destination = new AutoPopulatingList<DestinationsEntity>(DestinationsEntity.class);
//getter and setters
}
2). Destination Entity
#Entity
#Table(name = "destinations")
#Proxy(lazy = false)
public class DestinationsEntity {
#Id
#Column(name = "id")
#GeneratedValue
private Integer id;
#Column(name="destination")
private String destination;
// getter and setter
}
1). AddCustomer.jsp
This code for adding more destinations in Autopopulate list
<div id="destination_container">
<div><textarea row="3" col="5" class="destination_address" name= "destination[${0}].destination" placeholder="Please enter address"></textarea></div>
</div>
<script type="text/javascript">
$(document).ready(function(){
var index = 1;
/*
* Add more destination
*/
$('#add_more_destination').click(function(){
$('#destination_container').append('<div><textarea row="3" col="5" class="destination_address" name= "destination[${"'+index+'"}].destination" placeholder="Please enter address"></textarea><span class="remove_dest">*</span></div>');
index++;
});
});
</script>
2). updateCustomer.jsp
All destinations added by customer is show here and he/she can be change destinations(like before inserted pune, mumbai , banglore) now updating destinations( delhi, punjab)
<c:set var="index" scope="page" value="${fn:length(destinationss)}"/>
<c:forEach items="${destinationss}" var="dest" varStatus="i">
<div>
<textarea class="destination_address" name= "destination[${i.index}].destination" placeholder="Please enter address">${dest.destination}</textarea><span class="remove_dest">*</span>
</div>
</c:forEach>
<button type ="button" id="add_more_destination">Add More Destinations</button>
<script type="text/javascript">
$(document).ready(function(){
/*
* Add a destination
*/
var index = ${index};
$('#add_more_destination').click(function(){
$('#destination_container').append('<div><textarea row="3" col="5" class="destination_address" name=destination["'+index+'"].destination placeholder="Please enter address"></textarea><span class="remove_dest">*</span></div>');
alert(index);
index++;
});
</script>
Controller
#RequestMapping(value = "/addCustomerForm", method = RequestMethod.GET)
public String addCustomerForm(ModelMap map) {
return "master/addCustomer";
}
#RequestMapping(value = "/addCustomer", method = RequestMethod.POST)
public String addCustomer(#ModelAttribute(value = "customer") CustomerEntity customer,BindingResult result, HttpServletRequest request) {
customerService.addCustomer(customer);
return "redirect:/customer";
}
Update Customer
This is new thing I tried last night. Problem is solved partially.
#ModelAttribute
public void updateOperation(HttpServletRequest request, ModelMap map) {
if(null !=request.getParameter("id"))
map.addAttribute("customer1", customerService.findOne(Integer.parseInt(request.getParameter("id"))));
}
#RequestMapping(value = "/updateCustomerForm/{customerId}", method = RequestMethod.GET)
public String updateCustomerForm(#PathVariable("customerId") Integer customerId, ModelMap map, HttpServletRequest request) {
CustomerEntity customerEntity = customerService.findOne(customerId);
map.addAttribute("customer", customerEntity);
map.addAttribute("destinationss",customerEntity.getDestination());
}
#RequestMapping(value = "/updateCustomer", method = RequestMethod.POST)
public String updateCustomer(#ModelAttribute(value = "customer1")CustomerEntity customer1,BindingResult result, HttpServletRequest request,HttpServletResponse response) {
customerService.updateCustomer(customer1);
return "redirect:/customer";
}
}
1). CustomerServiceImpl
public class CustomerServiceImpl implements CustomerService{
#Autowired
private CustomerDao customerDao;
#Override
#Transactional
public void addCustomer(CustomerEntity customer) {
customerDao.addCustomer(customer);
}
#Override
#Transactional
public CustomerEntity findOne(Integer id){
return customerDao.findOne(id);
}
#Override
#Transactional
public void updateCustomer(CustomerEntity customerEntity){
if (null != customerEntity) {
customerDao.updateCustomer(customerEntity);
}
}
}
2).CustomerDaoImpl
public class CustomerDaoImpl implements CustomerDao{
#Autowired
private SessionFactory sessionFactory;
#Override
#Transactional
public void addCustomer(CustomerEntity customer){
this.sessionFactory.getCurrentSession().save(customer);
}
#Override
public CustomerEntity findOne(Integer id){
return (CustomerEntity) sessionFactory.getCurrentSession().load(CustomerEntity.class, id);
}
#Override
#Transactional
public void updateCustomer(CustomerEntity customerEntity){
if (null != customerEntity) {
this.sessionFactory.getCurrentSession().update(customerEntity);
}
}
}
The issue is Spring will give you new Customer entity, so I guess the Destination entities in this Customer is empty initially. So in your update operation you are just adding some new Destination entities and then adding them to customer as per your code.
So in this case, the customer entity is having only the new Destination objects where as the already existing Destination entities which were mapped earlier are not present in your Customer entity.
To fix the issue, first get the Customer entity from database, then this entity will have the set of Destination objects. Now to this Customer you can add new Destination objects and also update the existing Destination objects if needed then ask Hibernate to do the update operation. In this case Hibernate can see your earlier destination objects and also the new destination objects and based on that it will run the insert & update queries.
The code looks something like this:
// First get the customer object from database:
Customer customer = (Customer) this.sessionFactory.getCurrentSession().get(Customer.class, customerId);
// Now add your destination objects, if you want you can update the existing destination entires here.
for (int i = 0; i < destinationAddrs.length; i++) {
DestinationsEntity destination = new DestinationsEntity();
destination.setDestination(destinationAddrs[i]);
customer.getDestinationEntity().add(destination);
}
// Then do the update operation
this.sessionFactory.getCurrentSession().update(customer);
I have a method to get a Category by its "id" and I need a similar method to get a Category by its "name". I did these methods by using Hibernate. How can I fix my second method to get a Category by name?
My source code is as follow:
// It works
#Override
public Category getById(int id) {
return (Category) sessionFactory.getCurrentSession().get(Category.class, id);
}
// It doesn't works
#Override
public Category getByName(String name) {
return (Category) sessionFactory.getCurrentSession().
createSQLQuery("SELECT FROM Category WHERE name="+name);
}
I have this error with the second method:
java.lang.ClassCastException: org.hibernate.impl.SQLQueryImpl cannot
be cast to com.sedae.model.Category
These are my controllers.
#RequestMapping(value = "/getCategoryById/{id}")
public String getCategoryById(Model model, #PathVariable ("id") int id){
model.addAttribute("category", categoryService.getById(id));
return "/getCategoryById";
}
#RequestMapping(value = "/getCategoryByName/{name}")
public String getCategoryByName(Model model, #PathVariable("name") String name){
model.addAttribute("category", categoryService.getByName(name));
return "/getCategoryByName";
}
Thanks in advance people.
If you are sure you have only one entry per category name in your table then you can use Query#uniqueResult:
Query query= sessionFactory.getCurrentSession().
createQuery("from Category where name=:name");
query.setParameter("name", name);
Category category = (Category) query.uniqueResult();
Make sure to handle the exceptions thrown by uniqueResult.
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.