Where to chase pointers?

50 views
Skip to first unread message

Haakon

unread,
Feb 18, 2011, 6:46:30 PM2/18/11
to play-fr...@googlegroups.com
Hi, I`m building a simple forum system and I encountered this JPA/Play
problem where I`m deleting a thread which then deletes all it`s posts,
and the posts in turn try to delete a ManyToMany mapping to Users
(viewedBy field). I found the problem explained in my Hibernate book,
and it called it chasing pointers. This is fine with me but I can`t
seem to find what method to override to be able to do this? The delete
thread invocation is done through the CRUD module at the moment. This
is really hurting my flow so I hope someone can give me some help?

Regards,
Håkon

This is the stack:
org.hibernate.exception.ConstraintViolationException: Could not
execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:114)
at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:109)
at org.hibernate.jdbc.AbstractBatcher.prepareBatchStatement(AbstractBatcher.java:244)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2650)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2895)
at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:97)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:267)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:259)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:183)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:32
4)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1206)
at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:791)
at play.db.jpa.JPABase._delete(JPABase.java:73)
at controllers.CRUD.delete(CRUD.java:152)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:408)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:403)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:176)
at play.server.PlayHandler$NettyInvocation.execute(PlayHandler.java:169)
at play.Invoker$Invocation.run(Invoker.java:187)
at play.server.PlayHandler$NettyInvocation.run(PlayHandler.java:149)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.j
ava:98)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:206
)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
Caused by: java.sql.BatchUpdateException: Cannot delete or update a
parent row: a foreign key constraint fails (`charttr
aders`.`post_user`, CONSTRAINT `FKEEC7040A388562DE` FOREIGN KEY
(`Post_id`) REFERENCES `post` (`id`))
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2020)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1451)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 33 more

Manuel Bernhardt

unread,
Feb 19, 2011, 9:12:52 AM2/19/11
to play-fr...@googlegroups.com, Haakon
Hi,

you have to reduce the cascading in the ManyToMany mapping. I suppose
you want to remove a thread and the posts, but not the Users, so your
mapping should look like

@ManyToMany(fetch = FetchType.LAZY, cascade =
{CascadeType.PERSIST, ... }) // all cascadeType-s but REMOVE
public User user;

(I assume your CascadeType is "ALL")

Note that if you delete a User, you will have to first nullify the
references from the posts to that user, using e.g.

JPA.em().query("update Posts set viewedBy = null where viewedBy.id =
?", user.getId());

Hope this helps

> --
> You received this message because you are subscribed to the Google Groups "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>
>

Haakon

unread,
Feb 19, 2011, 4:52:59 PM2/19/11
to play-fr...@googlegroups.com
Thanks, I tried the new Cascading:
@ManyToMany(fetch=FetchType.LAZY, cascade=
{CascadeType.PERSIST, CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH})
public List<User> viewedBy = new ArrayList();

But it still complains. I probably need to do something similar to:


JPA.em().query("update Posts set viewedBy = null where viewedBy.id =
> ?", user.getId());

also here in this instance. My question is where is the best place to
do this? I see myself overriding a delete method in the Model but I
haven`t had success with that. Is the invoked controller method the
only point I can include this?

Thanks,
Håkon

Manuel Bernhardt

unread,
Feb 19, 2011, 5:13:51 PM2/19/11
to play-fr...@googlegroups.com, Haakon
hi,

> But it still complains. I probably need to do something similar to:
> JPA.em().query("update Posts set viewedBy = null where viewedBy.id =
>> ?", user.getId());

do you maybe have a reverse mapping from the User to the Posts? Or
from one Post to another?

> also here in this instance. My question is where is the best place to
> do this? I see myself overriding a delete method in the Model but I
> haven`t had success with that. Is the invoked controller method the
> only point I can include this?

how do you delete a Post at the moment? I would de-reference the Posts
before calling any delete method. Or do you use e.g. the CRUD module?
In that case I'm not entirely sure how things work, but I suppose
there must be a way to customize the controller actions, even for the
CRUD module

Haakon

unread,
Feb 20, 2011, 4:11:25 AM2/20/11
to play-fr...@googlegroups.com
Hello, I might as well put it all here:
@Entity
public class Post extends Model {

public String content;
public Date created;
public Date lastEdited;

@ManyToOne
public User user;

@ManyToOne
public Thread thread;

@ManyToMany(fetch=FetchType.LAZY, cascade=
{CascadeType.PERSIST, CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH})
public List<User> viewedBy = new ArrayList();
}

@Entity
public class User extends Model {

@Required
public String userName;

@Required
public String email;

@Required
public String password;

@Required
public Date signedUp;

public Blob avatar;

@ManyToOne
public UserGroup userGroup;

@OneToOne
public Location location;

@OneToMany
public List<Journal> journals;

public boolean isPasswordConfirmed(String passwordConfirm) {
if(password.equals(passwordConfirm)) return true;
else return false;
}

public void hashThePassword() {
password = Codec.hexSHA1(password);
}

@OneToMany(mappedBy="user", cascade=CascadeType.REMOVE)
public List<Post> posts;

public Long getNumberOfPosts() {
return Post.count("user = ?", this);
}

@Override
public String toString() {
return userName;
}
}

Yes, I`m using the CRUD module at the moment, and I can`t override it
because of the static methods. I can spend time making my own of
course. My concern though is that it should be possible to override a
method closer to the JPA delete logic.....? I don`t want to have logic
about deleting user references to posts in my future deleteThread
public method (I`ll do it if I have to of course :)).

Thanks,
Håkon

On Sat, Feb 19, 2011 at 11:13 PM, Manuel Bernhardt

Manuel Bernhardt

unread,
Feb 20, 2011, 7:37:16 AM2/20/11
to play-fr...@googlegroups.com, Haakon
Hi,

are you trying to delete a whole Thread or a single Post?

You need to also not propagate the deletion cascade to the User of your Post:

@ManyToOne
public User user;

It's basically the same issue as with the viewedBy list: everything
that you map with a CascadeType.ALL will cause cascade deletion.

Also it looks like you have a reverse mapping from the users to the posts:

@OneToMany(mappedBy="user", cascade=CascadeType.REMOVE)
public List<Post> posts;

In that case you'll most likely be more successful deleting the Post
by removing it from that List, but I am not sure if the CRUD module
operates that way. Do you really need the reverse mapping? You also
can get the posts of a User by writing a finder method like this:

public static List<Post> getPosts() { Post.find("where user = ?", this); }

Haakon

unread,
Feb 21, 2011, 1:22:47 PM2/21/11
to play-fr...@googlegroups.com
Hi Manuel,
I`m trying to the delete the thread with it`s posts and the viewedBy
associations of the posts. I realize now that this is beyond the scope
of the relation mapping or CRUD module to handle magically, so I`ll
write a customized admin or fork CRUD. I`ll also go over all my
associations when it gets more complete and make sure they have the
right cascade instead of all of them having the default ALL.

Thanks for your help Manuel!
Håkon

On Sun, Feb 20, 2011 at 1:37 PM, Manuel Bernhardt

Reply all
Reply to author
Forward
0 new messages