Results 1 to 15 of 15
Thread: jpa entity update
- 06-14-2011, 08:15 AM #1
Member
- Join Date
- Dec 2010
- Location
- Bucharest Romania
- Posts
- 40
- Rep Power
- 0
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!
- 06-14-2011, 09:37 AM #2
Moderator
- Join Date
- Apr 2009
- Posts
- 10,460
- Rep Power
- 16
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).
- 06-14-2011, 11:16 AM #3
Member
- Join Date
- Dec 2010
- Location
- Bucharest Romania
- Posts
- 40
- Rep Power
- 0
1. when i get the companies for display i use this code :
so, here c contains old data. I guess that this should read directly from db, right?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(); }...........
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
- 06-14-2011, 11:32 AM #4
Moderator
- Join Date
- Apr 2009
- Posts
- 10,460
- Rep Power
- 16
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.
- 06-14-2011, 11:37 AM #5
Member
- Join Date
- Dec 2010
- Location
- Bucharest Romania
- Posts
- 40
- Rep Power
- 0
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
- 06-14-2011, 11:52 AM #6
Moderator
- Join Date
- Apr 2009
- Posts
- 10,460
- Rep Power
- 16
How are you getting your sessions?
- 06-14-2011, 11:56 AM #7
Member
- Join Date
- Dec 2010
- Location
- Bucharest Romania
- Posts
- 40
- Rep Power
- 0
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;
- 06-14-2011, 12:20 PM #8
Moderator
- Join Date
- Apr 2009
- Posts
- 10,460
- Rep Power
- 16
So that doesn't look like the problem.
I wouldn't expect that to hang onto a session.
- 06-14-2011, 01:14 PM #9
Member
- Join Date
- Dec 2010
- Location
- Bucharest Romania
- Posts
- 40
- Rep Power
- 0
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.
- 06-14-2011, 01:40 PM #10
Moderator
- Join Date
- Apr 2009
- Posts
- 10,460
- Rep Power
- 16
So are you holding onto the company then, between calls to the server?
- 06-14-2011, 03:40 PM #11
Member
- Join Date
- Dec 2010
- Location
- Bucharest Romania
- Posts
- 40
- Rep Power
- 0
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 :
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:@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; }
this is how i display the locations for one user.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; } ............
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:
after i run this the result of this method ( prepareAssignUserToLocation()) is correct but the result of getCompanyForUser is still old.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; } }
thank you.
- 06-14-2011, 03:48 PM #12
Member
- Join Date
- Dec 2010
- Location
- Bucharest Romania
- Posts
- 40
- Rep Power
- 0
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 (?, ?)
- 06-14-2011, 03:48 PM #13
Moderator
- Join Date
- Apr 2009
- Posts
- 10,460
- Rep Power
- 16
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.
- 06-14-2011, 04:12 PM #14
Member
- Join Date
- Dec 2010
- Location
- Bucharest Romania
- Posts
- 40
- Rep Power
- 0
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.
some of setters and getters are missing for simplicity.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(); } }
do you think that this is ok ?
- 06-14-2011, 04:18 PM #15
Moderator
- Join Date
- Apr 2009
- Posts
- 10,460
- Rep Power
- 16
Similar Threads
-
Jpa2 entity update problem
By videanuadrian in forum Advanced JavaReplies: 6Last Post: 06-08-2011, 11:05 AM -
Entity - Free For All
By entity in forum IntroductionsReplies: 0Last Post: 11-15-2010, 09:09 AM


LinkBack URL
About LinkBacks
Reply With Quote
Bookmarks