1

I have read and reread everything here on SO and many other sites but can't seem to figure out why my updated objects are not updating.

The basic overview of what I'm doing:

  1. Service layer asks DAO for some People

  2. Return an ArrayList of People from the DB (DAO / @Repository)

  3. Service Layer manipulates object and adds them to a new arraylist

  4. Service Layers passes new list back to DAO to update

  5. NOTHING GETS Updated

If I throw a log message in my object has the new values, the children are correctly hydrated. I get no errors in the code is just doesn't commit my changes.

Here is some code:

PersonDAO annotated as @Repository

public void updatePeople(List<Person> people) { log.info("Updating " + people.size() + " people"); try { Transaction tx = getCurrentSession().beginTransaction(); for (Person person : people){ getCurrentSession().saveOrUpdate(person); } getCurrentSession().flush(); tx.commit(); getCurrentSession().close(); } catch (Exception e){ log.error("Exception Updating all people " + e.toString()); e.printStackTrace(); } } public List<Person> getAssociatesByStoreId(String storeId) { try { List<Person> usersInStore = (List<Person>) getCurrentSession() .createCriteria(Person.class).createCriteria("store") .add(Restrictions.eq("storeId", storeId)).list(); return usersInStore; } catch (Exception e) { log.error("exception in getAssociatesByStoreId ", e); } return null; } 

PersonService annotated as @Service - relevant method

/* I put the people into a map to do some other logic on them */ for (Person person : personDAO.getAllPeople()){ personMap.put(person.getEmployeeId() + "-" + person.getStore().getStoreId(), person); } /* I iterate a list creating new Person objects (based on some random stuff), including saving any children entities (managementcodes and stores) that need to be created After I have created a new Person I attempt to find it in the map from above. If I find it pull it out of the map and put it into an array list that is eventually passed back into the DAO */ 

Person annotated as @Entity

private int personid; private String firstName; private String lastName; private Store store; private ManagementCode managementCode; @Id @GeneratedValue(strategy=GenerationType.AUTO) @Column(name = "personid", unique = true, nullable = false) public int getPersonid() { return this.personid; } /*a bunch of getters and setters*/ @ManyToOne(fetch = FetchType.LAZY, optional = true, cascade = {CascadeType.MERGE, CascadeType.PERSIST}) @org.hibernate.annotations.Cascade( {org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.PERSIST}) @LazyCollection(LazyCollectionOption.EXTRA) @JoinColumn(name = "managementlevel", nullable = true) public ManagementCode getManagementCode() { return this.managementCode; } @ManyToOne(fetch = FetchType.LAZY, optional = true, cascade = {CascadeType.MERGE, CascadeType.PERSIST}) // @org.hibernate.annotations.Cascade( {org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.PERSIST}) @JoinColumn(name = "storeid", nullable = false) public Store getStore() { return this.store; } 

Store annotated as entity (Managementcode is the same)

/*fields + getters and setter */ @OneToMany(fetch = FetchType.LAZY, mappedBy = "store", cascade = {CascadeType.MERGE, CascadeType.PERSIST}) @LazyCollection(LazyCollectionOption.EXTRA) @JsonIgnore public Set<Person> getPersons() { return this.persons; } 

EDIT I added both sets of Cascade type annotations above and still no luck

EDIT 2 Updated the to show the primary key definition Someone please help me before I kick a puppy. Thanks

3
  • Are the revision numbers being incremented on update? Are these Person objects actually being altered before you try to save them again? Are you having any primary key collisions, those would probably be announced as an error Commented Aug 2, 2014 at 20:10
  • It would also help to see more code around the problem. How is the primary key defined for Person? Commented Aug 2, 2014 at 20:11
  • I'm seeing no collisions or exceptions being thrown in my update. As for what I'm manipulating....Right now I've simplified it to update the first name property. When I output the Person obejct before and after I can see the difference Commented Aug 2, 2014 at 21:07

3 Answers 3

2

There are three main types of entity objects with JPA:

  1. Managed. These are the objects that are cached by JPA-provider, so the JPA-provider keeps track of their states, and commits any changes made to them when the transaction commits. These are the objects you get when you query. They have a database id.
  2. New. These are entity objects that are not yet persisted in the database. The JPA-provider knows nothing about them. They do not have a database id.
  3. Detached. These are objects that used to be managed, but no longer are. Typically, objects that are returned to a method that is outside the transaction. These object have a database id, but they are not part of the cache, so the JPA-provider does not know them.

In order to persist a relationship between an entity and another, the referenced entity has to managed. There are multiple ways of achieving this.

  1. Cascade. Cascade from entity A to entity B means that when a cascading operation is performed on entity A by you in your code, the JPA-provider will perform the same operation on entity B. If you add CascadeType.MERGE to the relationship, and have entity A reference a detached entity B, then doing a merge() on entity A will first to a merge() on entity B, making it managed. When entity A is then stored, it has a reference to a managed B, and the relationship is persisted. (If you also want to persist references to objects that are not already in the database, add CascadeType.PERSIST as well.)
  2. Making sure you only reference managed objects. Inside your transaction, load the referenced objects (the managed ones) and replace the references with managed objects, before storing.
  3. Widen your transaction. That way, the referenced objects will never become detached.
Sign up to request clarification or add additional context in comments.

6 Comments

I've added cascadetype.merge to all of the child dependancies. I can see using the session.contains(person) that this entity is not managed by the session. I believe that this makes my objects detached based on your description. How do I reattach them? Merge -> use the returned object -> saveOrUpdate that? (trying now)
When I use the person object returned from the merge in my save or update I then get: Illegal attempt to associate a collection with two open sessions Not sure if this is better or worse
Why has this been marked up? It is a very generic answer rather than being specific to the problem at hand.
@JamesB I disagree, it's rather an elaborate explanation of what might cause the problem, as well as 3 potential solutions.
@Puttzy From what is provided at the time, I would think Hibernate's SAVE_UPDATE cascade should do the trick, without the need to change the Dao-code. But remove the @LazyCollection on getManagementCode in Person, that annotation is meant for collections (@*ToMany), and this is not.
|
1

It could be the case that you need to have "cascade = {CascadeType.MERGE, CascadeType.PERSIST}" on the ManyToOne annotation. The default behavior does not cascade anything to the associated objects.

 /** * (Optional) The operations that must be cascaded to * the target of the association. * * <p> By default no operations are cascaded. */ CascadeType[] cascade() default {}; 

Eg.

@ManyToOne(optional = true, cascade = {CascadeType.MERGE, CascadeType.PERSIST}, fetch = EAGER) 

UPDATE: In case you are using spring managed transaction manager and have the liberty to use annotation driven transaction demarcation, you can try the spring @Transactional annotation instead of manually starting transaction and committing/flushing.

I have following setup:

<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory"> <property name="persistenceUnitName" value="persistenceUnit"/> <property name="dataSource" ref="local-dataSource"/> <property name="packagesToScan" value="X"/> <property name="persistenceXmlLocation" value="classpath:persistence.xml"/> </bean> 

DAO class:

@Repository public class EventRepository { @PersistenceContext EntityManager entityManager; @Transactional public void persist(Event event) { entityManager.persist(event); } } 

4 Comments

great suggestions but still nothing. Strangely when I do different sql operations I see the SQL being output for those. (Select, and inserts) but nothing at all gets generated for this .update(Person) method.
By the way are you trying to test persistence through JUnit and SpringJUnit4ClassRunner. If you are, can you please post the test class with relevant code. I vaguely remember that i once had issue of nothing getting persisted when I was trying it through a test case, I think it was because of some @Transactional/@TransactionRollback annotation that I used in the my test class. Maybe post your transaction manager configuration.
I'm, sadly, not doing it through test classes, just another method I out in my DAO. @rbaljinder public MyEntity insertAndUpdate(MyEntity me) { getCurrentSession().save(me); me.setProperty("Value"); getCurrentSession().saveOrUpdate(me); return me; }
Transaction demarcation should not be on DAO methods but service methods instead.
0

First of all, make sure that you use the fitting CascadeType on those ManyToOne annotations.

Secondly, a merge Operation in the entityManager/session does not remain object equality by reference. That means, in an update case where a merge is performed, check if a different object is returned. You are supposed to always use the return values of save operations.

1 Comment

Based on your and the other comments I have added the following to my manytoone relations. optional = true, cascade = {CascadeType.MERGE, CascadeType.PERSIST}

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.