J888
J888

Reputation: 1964

Need to copy a field of first object into second object

I am trying to remove a record from database, but before removing it I am copying one of its children into another record, which runs into following error.

SEVERE: org.hibernate.StaleStateException: Batch update returned unexpected row count 
from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:81)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:73)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:59)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3224)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3126)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3456)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:140)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:364)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:356)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:278)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:328)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1234)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:404)
.....

1) In first function (get the list from record 18 and add to a new record)

 ....
 User user = (User) session.get(User.class, 18L);
 tx.commit();

 User client = new User();
 client.getmyList().setItems(user.getmyList().getItems());
 return client;

2) In second function (remove record 18)

 User user = (User) session.get(User.class, 18L);
 session.delete(user);

3) In third function (save the new record)

 session.save(client);             

User class

@Entity
@DynamicUpdate
public class User{

    .....

    @OneToOne(cascade = CascadeType.ALL)
    public UnitWatchList getmyList() {
        return myList;
    } 

    ...
} 

Upvotes: 0

Views: 151

Answers (3)

erencan
erencan

Reputation: 3763

Hibernate has a default first level caching which associates with session object. So, once an access to a object will be cached and further accesses will be fetched from cache.

Second, assigning a reference object to other variable will only change references.

Lets trace your code

  1. You retrieve a User object, lets say it in the memory location (a) User user = (User) session.get(User.class, 18L)
  2. User object is assiged to another object. Note that it is only set as memory location. there is no actual copy. client.getmyList().setItems(user.getmyList().getItems());
  3. Remove the object again. However, the retrieved objects is still in memory location (a), because it is fetched from cache User user = (User) session.get(User.class, 18L);
  4. Delete object which is in memory location (a) session.delete(user);
  5. Try to save a object which also involves User Object which is located in memory location (a) session.save(client); However, there is no actual object. Which is deleted.

Prototype pattern is generally used for coping objects. clone method can also be overrided. However, once a copy of entity object created. New object will not be maintained by hibernate session which is another problem.

In my humble idea, removing an object if it will be saved again is not a good practice. Why do you remove if you want to save? Mappings should also be rearranged to solve these issues.

See also

Prototype Pattern

Hibernate Caching

Types, Values, and Variables

Upvotes: 1

Ashish Jagtap
Ashish Jagtap

Reputation: 2819

I would probably just write a DAO method deleteAndMoveChildrensTo(Long victimId, Long recipientId), like this:

DAO Class:

public boolean deleteAndMoveChildrensTo(Long victimId, Long recipientId) {
        User victim = (User) getSession().load(User.class, victimId);
        User recipient = (User) getSession().load(User.class, recipientId);
        for (UnitWatchList unitWatch : victim.getmyList().getItems()) {
            unitWatch.setUser(recipient);
        }
        recipient.getmyList().addAll(victim.getmyList().getItems());
        getSession().saveOrUpdate(recipient);
        getSession().flush();
        getSession().evict(victim);
        victim = (User) getSession().load(User.class, victimId);
        getSession().delete(victim);
        return true;
    }

and corresponding Service class as follows

Service Class.

@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public boolean deleteAndMoveChildrensTo(Long victimId, Long recipientId) {
    DAO.deleteAndMoveChildrensTo(victimId, recipientId);
}

try this out hope this will solve your problem...

Upvotes: 1

angel_navarro
angel_navarro

Reputation: 1777

Do you close the transaction in the second function, after session.delete(user);? Maybe your problem is in session.delete() and not in session.save().

How is mapped items in UnitWatchList?

Regards,

Upvotes: 1

Related Questions