Announcement Announcement Module
Collapse
No announcement yet.
how to eliminate auto generated select queries when executing EntityManager.merge Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • how to eliminate auto generated select queries when executing EntityManager.merge

    I'm fairly new to Spring so please bear with me. I have two Spring MVC Controllers, a Register controller and EditProfile controller. The Register controller allows a user to register (inserts a new User using entityManager.persist) and the EditProfile controller updates a logged in user (using entityManager.merge). When a user's profile is updated using merge, two select queries are automatically issued before the SQL update. I believe this is to reattach the user object to the PersistenceContext since merge (I believe) works only on attached objects. My question is: How can I eliminate the two select queries from being issued? This seems like an unecessary performance hit.

    Here is my EditProfile controller.

    Code:
    package com.idream.servlet;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.SessionAttributes;
    
    import com.idream.manager.StateManager;
    import com.idream.manager.UserManager;
    import com.idream.model.Role;
    import com.idream.model.User;
    
    @Controller
    @RequestMapping(value="/editprofile")
    public class EditProfile {
    
    	@Autowired
    	private UserManager userManager;
    	
    	@Autowired
    	private StateManager stateManager;
    	
    	@RequestMapping(method = RequestMethod.GET)
    	public String setupForm(Model model, HttpServletRequest request) {
    		
    		model.addAttribute("stateList", stateManager.getStatesForDropdown());
    		model.addAttribute("user", request.getSession().getAttribute("user"));
    		
    		return "editprofile/edit";
    	}	
    	
    	
    	private void updateSessionUser(User user, HttpServletRequest request) {
    		
    		User sessionUser = (User)request.getSession().getAttribute("user");
    		
    		sessionUser.setFirstName(user.getFirstName());
    		sessionUser.setLastName(user.getLastName());
    		sessionUser.setAddress1(user.getAddress1());
    		sessionUser.setAddress2(user.getAddress2());
    		sessionUser.setCity(user.getCity());
    		sessionUser.setState(user.getState());
    		sessionUser.setZipCode(user.getZipCode());
    		sessionUser.setAutoSave(user.getAutoSave());
    		
    		
    	}
    	
    	private void 
    			fixUpModelUser(User springModelUser, 
    					User sessionUser) {
    
    		/**
    		 * update the spring model user so that it can be persisted.
    		 * it needs the fields that aren't updated by the form (email password)
    		 * and the user id (so that jpa can detect that's it's already
    		 * there and update not insert)
    		 */
    		springModelUser.setUserId(sessionUser.getUserId());
    		springModelUser.setEmail(sessionUser.getEmail());
    		springModelUser.setPassword(sessionUser.getPassword());
    		
    		Role r = new Role();
    		r.setRoleId(1);
    		springModelUser.setRole(r); //set role to customer
    		
    		
    	}
    	
    	@RequestMapping(method = RequestMethod.POST)
    	/**
    	public String onSubmit(@ModelAttribute("user") User user, BindingResult result, Model model,
    			HttpServletRequest request) {
    	 */
    	
    	public String onSubmit(@ModelAttribute("user") User user, BindingResult result, Model model,
    				HttpServletRequest request) {
    		
    		
    		
    		fixUpModelUser(user, (User)request.getSession().getAttribute("user"));
    		//updateSessionUser(user, request);
    		
    		//User u = (User)request.getSession().getAttribute("user");
    		
    		//user.setUserId(u.getUserId()); //update the spring user so it has a user id, this way
    		
    		//it can be updated by jpa
    		
    		//System.out.println(u.getUserId());
    		
    		model.addAttribute("stateList", stateManager.getStatesForDropdown());
    		
    		if (user.getState() != null && user.getState().trim().equals("NONE")) {
    			user.setState(null);
    		}
    		
    		userManager.mergeUser(user);
    		//userManager.flush();
    		
    		//update session user to have new values that were entered into the form
    		
    		updateSessionUser(user, request);		
    		
    		return "editprofile/edit";
    	}
    	
    }
    Here is the UserManager

    Code:
    package com.idream.manager;
    
    import java.util.List;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import javax.persistence.PersistenceContextType;
    import javax.persistence.Query;
    
    import org.springframework.stereotype.Repository;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.idream.model.User;
    
    
    @Transactional
    public class UserManager {
    
    	//@PersistenceContext(type=PersistenceContextType.TRANSACTION)
    	@PersistenceContext
    	private EntityManager em;
    	
    
    	@PersistenceContext public void setEntityManager(EntityManager em) { this.em = em; }
    	
    	//@Transactional(readOnly = true)
    	@SuppressWarnings("unchecked")
    	public User getUserByEmail(String email) {
    		Query query = this.em.createQuery("SELECT user FROM User user where user.email = :email1 ");
    		
    		query.setParameter("email1", email);
    		
    		List list = query.getResultList();
    		
    		if (list == null || list.size() == 0) return null;
    		
    		return (User)list.get(0);
    	}
    
    
    	
    	public void persistUser(User user) {
    		
    		this.em.persist(user);
    		
    		
    	}
    	
    	//@Transactional(propagation = Propagation.REQUIRED)
    
    	public User mergeUser(User user) {
    		
    	
    		//EntityTransaction t = em.getTransaction();
    		
    		
    		
    		User merged = this.em.merge(user);
    		this.em.flush();
    		
    		
    		return merged;
    	}
    	
    	public void flush() {
    		this.em.flush();		
    	}
    	
    	
    	
    }
    Here is the sql logging of the queries that are issued when the merge and flush occurs:


    Code:
    Hibernate: select user0_.user_id as user1_1_0_, user0_.address1 as address2_1_0_, user0_.address2 as address3_1_0_, user0_.auto_save as auto4_1_0_, user0_.city as city1_0_, user0_.date_saved as date6_1_0_, user0_.email as email1_0_, user0_.first_name as first8_1_0_, user0_.last_name as last9_1_0_, user0_.password as password1_0_, user0_.role_id as role14_1_0_, user0_.state as state1_0_, user0_.verified as verified1_0_, user0_.zip_code as zip13_1_0_ from User user0_ where user0_.user_id=?
    Hibernate: select role0_.role_id as role1_4_0_, role0_.date_saved as date2_4_0_, role0_.role_name as role3_4_0_ from Role role0_ where role0_.role_id=?
    Hibernate: update User set address1=?, address2=?, auto_save=?, city=?, date_saved=?, email=?, first_name=?, last_name=?, password=?, role_id=?, state=?, verified=?, zip_code=? where user_id=?
    The code that generates the log messages is in the UserManager.


    Code:
    public User mergeUser(User user) {
    		
    		User merged = this.em.merge(user);
    		this.em.flush();
    		
    		
    		return merged;
    	}
    Last edited by vonrosen; Feb 10th, 2012, 01:34 PM.

  • #2
    the find by id select is very optimized so this is not really that big of a performace hit imo. It might actually find it already in the persistence context without having to go to the database which is actually very likely in your case. However if you are trying to keep the entity attached after being passed to the view take a look at OpenEntityManagerInViewFilter.

    Also there are type safe queries as if JPA 2 so you would not have to have the supress warnings. Also if you add your @Transactional back in there is no need to call flush as it will automatically be flushed when the transaction finishes.

    Comment

    Working...
    X