Results 1 to 15 of 15
  1. #1
    videanuadrian is offline Member
    Join Date
    Dec 2010
    Location
    Bucharest Romania
    Posts
    40
    Rep Power
    0

    Default jpa entity update

    Hi all, i`m trying here to better understand how jpa works, so a little help would be appreciated.
    The app is running with spring 3 jpa2 hibernate 3 and struts2
    So, i have 3 entities chained like this :
    Company oneToMany Location
    Location ManyToMany User


    let`s say that when i open the app and want to see what locations a company has. I click on the CompanyName button and all locations are displayed. If a click on a location, all users are displayed also. Everything fine until there, but, if a go to another menu and remove from Location A the user B and then submit this, the change is propagated into DB, but if a click again on CompanyName and location name, the users are the same as in the beginning. So , the jpa does not automatically update my data appwide after an update. I do not know if this is the default behavior, or if this is the way that should happen. I guess that if i run entityManager.refresh() before getting the locations everything is ok. But if that`s the normal behavior so i have to always run refresh ? or do i have a flag like : isDirty so that i know that i have to refresh that entity ?

    2. i`m using spring transactions in this app, and i noticed that if a get a row from database and then modify one property when the transaction commits this is automatically put in DB. So, my question is : When i get something from the DB via jpql or criteria api that entity is automatically managed so that any modification will be propagated in DB when transaction commits?

    3. In order to display/get information from client i use the same beans that are marked with Entity. But if i want to let`s say alter a little bit the content of a bean before displaying him the update is made in DB too which is not the desired behavior. Due this i found that is i detach the entity from the persistance context before alter everything is ok. The question is : is this a good practice ?

    Thank you!

  2. #2
    Tolls is offline Moderator
    Join Date
    Apr 2009
    Posts
    12,014
    Rep Power
    20

    Default

    First off, you are probably holding onto a set of beans that you are redisplaying, rather than getting them afresh from the db.

    Second, within a session your beans will be persisted I believe. At least that's how hibernate works.

    Third. Yes, that's a Bad Thing. Those beans are windows onto your database. If you change the data within them there is an underlying assumtpion that you would liek that tp be persisted. If there are changes required for display then these should be handled by a different object or the front end (ie Javascript).

  3. #3
    videanuadrian is offline Member
    Join Date
    Dec 2010
    Location
    Bucharest Romania
    Posts
    40
    Rep Power
    0

    Default

    1. when i get the companies for display i use this code :
    Java Code:
    Company c = null;    	
        	try{
    	    	CriteriaBuilder criteria = this.em.getCriteriaBuilder();
    	    	CriteriaQuery<Company> query = criteria.createQuery(Company.class);
    	    	Root<Company> companyRoot = query.from(Company.class);
    	    	Predicate cond = criteria.equal(companyRoot.get("id"),companyId);
    	    	query.where(cond);
    	    	c = this.em.createQuery(query).getSingleResult();
    }...........
    so, here c contains old data. I guess that this should read directly from db, right?
    3. So in order to put/get info from html forms via struts you advise me to use a completly different set of beans used only for this operation ?

    Thank you

  4. #4
    Tolls is offline Moderator
    Join Date
    Apr 2009
    Posts
    12,014
    Rep Power
    20

    Default

    1. That should get whatever's in the database. Unless you have multiple sessions and the one that saved the data hasn't committed.

    For displaying the data, if the data you get from the database needs some modification then you need to do that modification somewhere other than the bean, otherwise you risk saving this change.

  5. #5
    videanuadrian is offline Member
    Join Date
    Dec 2010
    Location
    Bucharest Romania
    Posts
    40
    Rep Power
    0

    Default

    1. well, the fact is that if i look directly into the database the information is there...... wierd... i`ll search more to find where the problem is
    thanks

  6. #6
    Tolls is offline Moderator
    Join Date
    Apr 2009
    Posts
    12,014
    Rep Power
    20

    Default

    How are you getting your sessions?

  7. #7
    videanuadrian is offline Member
    Join Date
    Dec 2010
    Location
    Bucharest Romania
    Posts
    40
    Rep Power
    0

    Default

    i have here an container managed entitymanager:
    Java Code:
    .....................
     @PersistenceContext(type = PersistenceContextType.EXTENDED,unitName = "vaPU")
        protected EntityManager em;
    .................
    
    and all operations in service layer are made by this.em;

  8. #8
    Tolls is offline Moderator
    Join Date
    Apr 2009
    Posts
    12,014
    Rep Power
    20

    Default

    So that doesn't look like the problem.
    I wouldn't expect that to hang onto a session.

  9. #9
    videanuadrian is offline Member
    Join Date
    Dec 2010
    Location
    Bucharest Romania
    Posts
    40
    Rep Power
    0

    Default

    my assumption is that the data remains somehow cached, because if i do not go to to click on company first in order to view locations in order to view users, and go directly and add/remove a location from a user, when i click on companies i see the correct information. After that is like location which belongs to this company does not refresh the user list that was just updated.

  10. #10
    Tolls is offline Moderator
    Join Date
    Apr 2009
    Posts
    12,014
    Rep Power
    20

    Default

    So are you holding onto the company then, between calls to the server?

  11. #11
    videanuadrian is offline Member
    Join Date
    Dec 2010
    Location
    Bucharest Romania
    Posts
    40
    Rep Power
    0

    Default

    well the flow is something like:
    login into the app -> click on the company name -> the locations are displayed
    behind the scenes when you click o a company i call this method :
    Java Code:
     @Override
        public String execute() {
    
            Map<String,String[]> pars = this.getParameters();
            String value = pars.get("companyId")[0]; // error happens here
            Integer companyId = Integer.parseInt(value);
    
            User user = (User)session.get("USER_INFO");
            Company c = companyService.getCompanyForUser(companyId,user);
            this.company = c;
            return SUCCESS;
        }
    so i get the company Id and try to fetch all the info. The problem is that a company might have multiple locations. Locations also have multiple users, so i wat to get only the locations that loggedin user is assigned to. That`s why the method is called getCompanyForUser.

    Java Code:
    @Transactional
    public class CompanyServiceImpl extends GenericVsService<Company, Integer> implements iCompanyService {
    
        @Override
    	public Company getCompanyForUser(int companyId,User user){
        	
        	Company c = null;    	
        	try{
    	    	CriteriaBuilder criteria = this.em.getCriteriaBuilder();
    	    	CriteriaQuery<Company> query = criteria.createQuery(Company.class);
    	    	Root<Company> companyRoot = query.from(Company.class);
    	    	Predicate cond = criteria.equal(companyRoot.get("id"),companyId);
    	    	query.where(cond);
    	    	c = this.em.createQuery(query).getSingleResult();
    	    	
    	    	List<Location> newLocationList = new ArrayList<Location>();	    		    	
    	    	List<Location> alreadyLocList = c.getLocations();
    	    	Iterator<Location> ali = alreadyLocList.iterator();
    	    	while(ali.hasNext()){
    	    		Location l = (Location) ali.next();
    	    		//this.em.refresh(l);
    	    		if (l.getUsers().contains(user)){
    	    			newLocationList.add(l);
    	    		}
    	    	}	    	
    	    	//detach the entity  in order to alter the locationList (to keep only the locations assigned to current user)
    	       this.em.detach(c);
    	    	//remove locations that this user is not assigned to
    	    	c.setLocations(newLocationList);	    	
        	}catch (NoResultException nre) {
        		logger.debug(nre.getMessage());
    		}
            return c;
        }
    ............
    this is how i display the locations for one user.

    and now if i go and remove/add a location from and them click on the company name i get the data from first call.
    here is how i add/remove a location from a user:
    Java Code:
    //this is used to create two dropdown list with all available users and users already asigned to a location
    
    public String prepareAssignUserToLocation(){
    
            Map<String,String[]> pars = this.getParameters();
    
            try{
                Integer lid = Integer.parseInt(pars.get("locationId")[0]);
                this.users = userService.getUsers();
                this.locationId = lid;
                location = locationService.getLocation(lid);
                this.locationUsers = new ArrayList<User>();
                this.locationUsers = location.getUsers();
            }catch(NumberFormatException nfe){
                logger.error("NFE:"+nfe.getMessage());
            }
    
            return INPUT;
        }
    
    //this is used to assign a user to a location
        public String assignUserToLocation(){
            //get the assigned
            if (selectedUsers != null){
                
                List<User> usersForm = new ArrayList<User>();
                if (selectedUsers.indexOf("|")!=-1) {                
                    String sts[] = selectedUsers.split("\\|");
                    //transform users string to users array
                     for(int i=0;i<sts.length;i++) {
                         if (sts[i].compareTo("0")!=0){
                            try{                            
                                Integer userId = Integer.parseInt(sts[i]);
                                User u = userService.getUser(userId);
                                usersForm.add(u);
                             }catch(NumberFormatException nfe){
                                 logger.error("NFE:"+nfe.getMessage());
                             }
                         }
                     }
                }else{
                    if (selectedUsers.compareTo("0")!=0){
                        try{
                               Integer userId = Integer.parseInt(selectedUsers);
                               User u = userService.getUser(userId);
                               usersForm.add(u);
                         }catch(NumberFormatException nfe){
                             logger.error("NFE:"+nfe.getMessage());
                        }
                    }
                }
    
                ArrayList<User> toBeRemoved = new ArrayList<User>();
                ArrayList<User> toBeAdded = new ArrayList<User>();
    
                List<User> usersDB = new ArrayList<User>();
                Location loc = locationService.getLocation(locationId);
                usersDB = loc.getUsers();
    
                
                //eliminate removed users from location
                Iterator<User> it = usersDB.iterator();
                while(it.hasNext()){
                    User us = (User)it.next();
                    if(!usersForm.contains(us)){
                        // mark user for removal
                        toBeRemoved.add(us);
                    }
                    
                }
    
                Iterator<User> ituf = usersForm.iterator();
                while(ituf.hasNext()){
                    User user = (User)ituf.next();
                    if(!usersDB.contains(user)){
                        // mark user for add 
                        toBeAdded.add(user);
                    }
                }
    
                boolean succesfullSaved = true;
                User lu = (User) session.get("USER_INFO");
                Integer currentUserId = lu.getId();
    
                if(toBeRemoved.size()>0){
                    Iterator<User> itbr = toBeRemoved.iterator();
                    while(itbr.hasNext()) {
                        User u = (User) itbr.next();
                        u.removeLocation(loc);
                        if(!this.userService.addOrUpdateUser(u, currentUserId))
                            succesfullSaved = false;
                    }
                }
    
                //add users to
                if(toBeAdded.size()>0){
                    Iterator<User> itba = toBeAdded.iterator();
                    while(itba.hasNext()) {
                        User u = (User) itba.next();
                        u.addLocation(loc);
                        if(!this.userService.addOrUpdateUser(u, currentUserId))
                            succesfullSaved = false;
                    }
                }          
    
                if (succesfullSaved == true){
                    addActionMessage("Location update succesfully!");
                    return SUCCESS;
                }
    
                addActionError("ERROR - Location update failed.");
                return ERROR;
           }
                 
       return SUCCESS;
    }
    ............................................................
    
    //and this is the method that updates the entities:
    public Boolean addOrUpdateUser(User u,Integer modifiedBy) {
    
            try{
            	if (u.getId() != null){        		
    	            //update
    	            try{
    	               Date d = new Date();
    	               Long tsU = d.getTime();
    	               u.setChangeTs(tsU);
    	               u.setChangedBy(modifiedBy);
    	               em.merge(u);
    	              // em.flush();
    	               return true;
    	           }catch(PersistenceException pe){
    	                logger.error("Persitance Exception -"+pe.getMessage());
    	                return false;
    	           }catch(TransactionException te){
    	               logger.error("Transaction Exception -"+te.getMessage());
    	               return false;
    	           }
               }else{
                   //insert
                   try{
                       Date d = new Date();
                       Long tsU = d.getTime();
                       u.setChangeTs(0L);
                       u.setChangedBy(0);
                       u.setCreateTs(tsU);
                       u.setCreatedBy(modifiedBy);
                       em.persist(u);
                      // em.flush();
                       return true;
                   }catch(PersistenceException pe){
                        logger.error("Persitance Exception -"+pe.getMessage());
                        return false;
                   }catch(TransactionException te){
                       logger.error("Transaction Exception -"+te.getMessage());
                       return false;
                    }             
               }
            }catch(Exception e){
                logger.error("Exceptie: "+e.getMessage());
                return false;
            }
      }
    after i run this the result of this method ( prepareAssignUserToLocation()) is correct but the result of getCompanyForUser is still old.

    thank you.

  12. #12
    videanuadrian is offline Member
    Join Date
    Dec 2010
    Location
    Bucharest Romania
    Posts
    40
    Rep Power
    0

    Default

    that is what hibernate shows in console after an addOrUpdateUser() method.... maybe this helps. so basically delete all the old associations and insert the new ones.

    INFO: Hibernate: select location0_.id as id102_1_, location0_.company_id as company6_102_1_, location0_.name as name102_1_, location0_.outbound_prefix as outbound3_102_1_, location0_.queue_id as queue4_102_1_, location0_.welcomeMessage as welcomeM5_102_1_, company1_.id as id97_0_, company1_.name as name97_0_ from locations location0_ left outer join company company1_ on location0_.company_id=company1_.id where location0_.id=?
    INFO: Hibernate: update users set agent_id=?, change_ts=?, changed_by=?, create_ts=?, created_by=?, emailSignature=?, firstName=?, is_ldap_user=?, lastName=?, password=?, userName=? where id=?
    INFO: Hibernate: delete from locations_users where user_id=?
    INFO: Hibernate: insert into locations_users (user_id, location_id) values (?, ?)
    INFO: Hibernate: insert into locations_users (user_id, location_id) values (?, ?)
    INFO: Hibernate: insert into locations_users (user_id, location_id) values (?, ?)
    INFO: Hibernate: insert into locations_users (user_id, location_id) values (?, ?)
    INFO: Hibernate: insert into locations_users (user_id, location_id) values (?, ?)

  13. #13
    Tolls is offline Moderator
    Join Date
    Apr 2009
    Posts
    12,014
    Rep Power
    20

    Default

    Something doesn't feel right about your modelling then. If you have to go through hoops to get the data format you need for the front end then you might have your model upside down...or possibly missing a view onto the data. What if you had the User map to its Locations? That's what you want to display.

    I suspect your problem lies with all that manipulation.

  14. #14
    videanuadrian is offline Member
    Join Date
    Dec 2010
    Location
    Bucharest Romania
    Posts
    40
    Rep Power
    0

    Default

    well, the user is mapped directly to location via ManyToMany. But i need to view all locations (that a user is assigned to ) starting from company.
    Java Code:
    /**
     * @author adrian.videanu
     */
    import java.io.Serializable;
    import java.util.List;
    import javax.persistence.Access;
    import javax.persistence.AccessType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    
    @Entity
    @Access(AccessType.PROPERTY)
    @Table(name = "company")
    public class Company implements Serializable {
    
        private static final long serialVersionUID = 7526471155622776147L;
        private Integer id;
        private String name;
        
        private List<Location> locations;
    
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)   
        @Column(name = "id")
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
        
        @Column(name = "name")
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @OneToMany(mappedBy="company")
        public List<Location> getLocations(){
            return this.locations;
        }
    
        public void setLocations(List<Location> l){
            this.locations = l;
        }
    
        @Override
        public String toString() {
            return "vs.common.hibernate.entities.virtualSecretary.Company[id=" + id + "]";
        }
    }
    
    
    import java.io.Serializable;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Table;
    import java.util.List;
    import javax.persistence.Access;
    import javax.persistence.AccessType;
    import javax.persistence.GenerationType;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToMany;
    import javax.persistence.ManyToOne;
    
    /**
     *
     * @author adrian.videanu
     */
    @Entity
    @Table(name = "locations")
    @Access(AccessType.PROPERTY)
    public class Location implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private Integer id;    
        private String name;
        private String welcomeMessage;
        private Integer queueId;
        private String outboundPrefix;
        
        private List<User> users;
        private List<Employee> employees;
        private Company company;
        
    
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name = "id")
        public Integer getId() {
            return id;
        }
        
        public void setId(Integer id) {
            this.id = id;
        }
    
        @Column(name = "queue_id")
        public Integer getQueueId() {
            return queueId;
        }
    
        public void setQueueId(Integer qid) {
            this.queueId = qid;
        }
    
    
        @Column(name = "name")
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Column(name = "welcomeMessage")
        public String getWelcomeMessage() {
            return this.welcomeMessage;
        }
        public void setWelcomeMessage(String wm) {
            this.welcomeMessage = wm;
        }
    
       
        @Column(name = "outbound_prefix")
        public String getOutboundPrefix() {
            return this.outboundPrefix;
        }
        public void setOutboundPrefix(String op) {
            this.outboundPrefix  = op;
        }
    
       public void setUsers(List<User> u){
                this.users = u;
        }
    
       @ManyToMany(mappedBy="locations")
       public List<User> getUsers(){
            return this.users;
       }
    
       public void removeUser(User u){
           if (this.users.contains(u))
               this.users.remove(u);
        }
    
       public void addUser(User u){
           if(!this.users.contains(u))
               this.users.add(u);
       }
    
     
        @ManyToOne
        @JoinColumn(name="company_id")
        public Company getCompany(){
            return this.company;
        }
    
        public void setCompany(Company c){
            this.company = c;
        }
    
        @Override
        public String toString() {
            return "Location[id=" + id + "]";
        }
    
          @Override
        public int hashCode() {
            int hash = 0;
            hash += (id != null ? id.hashCode() : 0);
            return hash;
        }
    
        @Override
        public boolean equals(Object object) {
            // TODO: Warning - this method won't work in the case the id fields are not set
            if (!(object instanceof Location)) {
                return false;
            }
            Location other = (Location) object;
            if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
                return false;
            }
            return true;
        }
    
    }
    
    
    /**
     *
     * @author adrian.videanu
     */
    
    import java.io.Serializable;
    import javax.persistence.Entity;
    import javax.persistence.Column;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    import java.util.ArrayList;
    import java.util.List;
    import javax.persistence.Access;
    import javax.persistence.AccessType;
    import javax.persistence.GenerationType;
    import javax.persistence.JoinColumn;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.OneToMany;
    
    @Entity
    @Access(AccessType.PROPERTY)
    @Table(name="users")
    public class User implements Serializable {
    
        public static final long serialVersionUID = 7526471155622776147L;
    
        private Integer id;
        private String userName;
        private String password;
        private String firstName;
        private String lastName;    
        private Integer agentId;
        private Long createTs;
        private Long changeTs;
        private Integer createdBy;
        private Integer changedBy;
        private Byte isLdapUser;
        private String emailSignature;
    
        private List<Group> groups;
        private List<Location> locations;
     
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="id")
        public Integer getId() {
            return this.id;
        }
        
        @Column(name="userName")
        public String getUserName() {
            return this.userName;
        }
    
        @Column(name="password")
        public String getPassword() {
            return this.password;
        }
    
        @Column(name="firstName")
        public String getFirstName(){
            return this.firstName;
        }
    
        @Column(name="lastName")
        public String getLastName(){
            return this.lastName;
        }
    
        @Column(name="agent_id")
        public Integer getAgentId(){
            return this.agentId;
        }
    
        @Column(name="create_ts")
        public Long getCreateTs() {
            return this.createTs;
        }
    
        @Column(name="change_ts")
        public Long getChangeTs(){
            return this.changeTs;
        }
    
        @Column(name="created_by")
        public Integer getCreatedBy(){
            return this.createdBy;
        }
    
        @Column(name="changed_by")
        public Integer getChangedBy(){
            return this.changedBy;
        }
    
      
    
         public void setId(Integer id){
            this.id = id;
        }
        public void setUserName(String un){
            this.userName = un;
        }
        public void setPassword(String pass){
            this.password = pass;
        }
        public void setFirstName(String fn){
            this.firstName = fn;
        }
        public void setLastName(String ln){
            this.lastName = ln;
        }
        public void setAgentId(Integer aid){
            this.agentId = aid;
        }
        public void setGroups(List<Group> gl){
            this.groups = gl;
        }
    
        public void setIsLdapUser(Byte ildu){
            this.isLdapUser = ildu;
        }
    
         public void setCreateTs(Long cts){
            this.createTs = cts;
        }
        public void setChangeTs(Long chts){
            this.changeTs = chts;
        }
        public void setCreatedBy(Integer cby){
            this.createdBy = cby;
        }
        public void setChangedBy(Integer chBy){
            this.changedBy = chBy;
        }
    
        @ManyToMany
        @JoinTable(
          name="groups_users",
          joinColumns={@JoinColumn(name="user_id")},
          inverseJoinColumns={@JoinColumn(name="group_id")})
        public List<Group> getGroups(){
            return this.groups;
        }
    
        public void setLocations(List<Location> l){
            this.locations = l;
        }
    
        @ManyToMany
        @JoinTable(name="locations_users",
                    joinColumns = { @JoinColumn(name="user_id")},
                    inverseJoinColumns = {@JoinColumn(name="location_id")}
                    )
        public List<Location> getLocations(){
            return this.locations;
        }
    
        public void addLocation(Location l){
            if (!this.locations.contains(l))
                  if (!l.getUsers().contains(this))
                    l.addUser(this);
                
                this.locations.add(l);
            }
    
        public boolean removeLocation(Location l){        
        	if (this.locations.contains(l)){
                if(l.getUsers().contains(this))
                    l.removeUser(this);
                this.locations.remove(l);
            }
            
            if (this.locations.contains(l))
                return false;
            
            return true;
        }
    
      
        @Override
        public int hashCode() {
            int hash = 0;
            hash += (id != null ? id.hashCode() : 0);
            return hash;
        }
    
        @Override
        public boolean equals(Object object) {
            // TODO: Warning - this method won't work in the case the id fields are not set
            if (!(object instanceof User)) {
                return false;
            }
            User other = (User) object;
            if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
                return false;
            }
            return true;
        }
    
    
    
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("User[id]=").append(id).append("\n");
            sb.append("User[userName]=").append(userName).append("\n");
            return sb.toString();
        }
    }
    some of setters and getters are missing for simplicity.
    do you think that this is ok ?

  15. #15
    Tolls is offline Moderator
    Join Date
    Apr 2009
    Posts
    12,014
    Rep Power
    20

    Default

    So why are you going through hoops in getCompanyForUser if the User already has a list of Locations?
    Why not just pull the Locations from the User?

Similar Threads

  1. Jpa2 entity update problem
    By videanuadrian in forum Advanced Java
    Replies: 6
    Last Post: 06-08-2011, 11:05 AM
  2. Entity - Free For All
    By entity in forum Introductions
    Replies: 0
    Last Post: 11-15-2010, 09:09 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •