-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Can't understand why a related entity is being flushed
PostPosted: Mon Dec 24, 2007 1:43 pm 
Beginner
Beginner

Joined: Wed Nov 30, 2005 10:25 pm
Posts: 25
I am getting an error from one of my test cases. The test case involves a user attempting to delete an Address of a User that they don't have permission to. What is odd is that hibernate seems to be attempting to save the user object which I have not altered.

I am not sure what I have done wrong and would appreciate some insight.

Test case (note: Seam code in use):
Code:
  @Test(dependsOnMethods = "ownerCreate")
  public void ownerDelete() throws Exception
  {
    new ComponentTest()
    {
      protected void testComponents() throws Exception
      {
        loginUser(InitialUser.USER);
       
        getEntityManager().getTransaction().begin();
       
        User user = (User)getValue("#{user}");
        for (Address addr : user.getAddresses())
        {
          if (!addr.isPrimary())
          {
            assert "54321".equals(addr.getPostalCode());
            getEntityManager().remove(addr);
            break;
          }
        }
       
        getEntityManager().getTransaction().commit();
      }
    }.run();
  }


Here is the error:
Code:
FAILED: ownerDelete
javax.persistence.RollbackException: Error while commiting the transaction
   at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
   at com.christws.entities.AddressTest$7.testComponents(AddressTest.java:211)
   at org.jboss.seam.mock.BaseSeamTest$ComponentTest.run(BaseSeamTest.java:167)
   at com.christws.entities.AddressTest.ownerDelete(AddressTest.java:213)
Caused by: org.hibernate.ObjectDeletedException: deleted entity passed to persist: [com.christws.entities.Address#<null>]
   at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:90)
   at org.hibernate.impl.SessionImpl.firePersistOnFlush(SessionImpl.java:644)
   at org.hibernate.impl.SessionImpl.persistOnFlush(SessionImpl.java:636)
   at org.hibernate.engine.CascadingAction$9.cascade(CascadingAction.java:323)
   at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
   at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
   at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
   at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
   at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:131)
   at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:122)
   at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
   at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
   at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
   at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54)
   ... 25 more
... Removed 22 stack frames


Here are the fields from Address:
Code:
@Entity
@Table(name="user_address")
@SequenceGenerator(name="seq", sequenceName="user_address_seq")
@Restrict
public class Address
  extends BaseEntity
{
  private final static long serialVersionUID = 1l;
 
  @ManyToOne
  @NotNull(message="{valid.address.user.notnull}")
  @JoinColumn(name="user_id")
  private User user;
 
  @Basic
  @NotNull(message="{valid.address.location.notnull}")
  @Length(max=50, message="{valid.address.location.length}")
  private String location;
 
  @Basic
  @NotNull(message="{valid.address.street.notnull}")
  @Length(max=2000, message="{valid.address.street.length}")
  private String street;
 
  @Basic
  @NotNull(message="{valid.address.city.notnull}")
  @Length(max=250,message="{valid.address.city.length}")
  private String city;
 
  @Basic
  @NotNull(message="{valid.address.region.notnull}")
  @Length(max=100,message="{valid.address.region.length}")
  private String region;
 
  @Basic
  @Column(name="postal_code")
  @NotNull(message="{valid.address.postalcode.notnull}")
  @Length(max=100,message="{valid.address.postalcode.length}")
  private String postalCode;
 
  @Basic
  @NotNull(message="{valid.address.country.notnull}")
  @Length(max=100,message="{valid.address.country.length}")
  private String country;
 
  @Basic
  @Column(name = "`primary`")
  private boolean primary;
 
  @OneToMany(mappedBy = "address")
  @OnDelete(action = OnDeleteAction.CASCADE)
  @Cascade( { CascadeType.ALL, CascadeType.DELETE_ORPHAN })
  private Set<AddressPermission> permissions;
...


Here are some of the user fields:
Code:
@Entity
@Table(name = "site_user")
@SequenceGenerator(name = "seq", sequenceName = "site_user_seq")
@Roles({ @org.jboss.seam.annotations.Role(name = "user", scope = ScopeType.SESSION ) })
public class User
  extends BaseEntity
{
  private final static long serialVersionUID = 1l;

  // Fields
  @Basic
  @NotNull(message = "{valid.user.username.notnull}")
  @Length(min = 4, max = 250, message = "{valid.user.username.length}")
  private String username;

  @Basic
  @Column(name = "password")
  @Length(max = 32)
  private String passwordHash;

  @Basic
  @Column(name = "first_name")
  @Length(max = 15, message = "{valid.user.firstname.length}")
  @NotNull(message = "{valid.user.firstname.notnull}")
  private String firstName;

  @Basic
  @Column(name = "last_name")
  @Length(max = 25, message = "{valid.user.lastname.length}")
  @NotNull(message = "{valid.user.lastname.notnull}")
  private String lastName;

  @Basic
  @Column(name = "time_zone")
  private TimeZone timeZone;

  @Basic
  private Locale locale;

  @ManyToMany
  @JoinTable(name = "user_role_j", joinColumns = { @JoinColumn(name = "user_id", nullable = false) }, inverseJoinColumns = { @JoinColumn(name = "role_id", nullable = false) })
  @Cascade( { CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.SAVE_UPDATE })
  private Set<Role> roles;

  @OneToMany(mappedBy = "user")
  @OnDelete(action = OnDeleteAction.CASCADE)
  @Cascade( { CascadeType.ALL, CascadeType.DELETE_ORPHAN })
  private List<Address> addresses;
...


Here is BaseEntity:
Code:
@AccessType("field")
@MappedSuperclass
public abstract class BaseEntity
  implements Serializable, Comparable<BaseEntity>, IdentifiedEntity
{
  @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
  private Long id;
 
  @Basic
  @Column(name="created_dt", updatable = false)
  private Timestamp created;
 
  @Basic
  @Version
  @Column(name="modified_dt")
  private Timestamp modified;
...


Why is hibernate trying to save the User object when I haven't made changes to it? What must I do to stop hibernate from updating every object in memory?

I think there must be something wrong with my mapping, but I am not sure what. Any help would be appeciated.

Hibernate versions:
hibernate-3.2.4.sp1.jar
hibernate-annotations-3.3.0.ga.jar
hibernate-commons-annotations-3.0.0.ga.jar
hibernate-entitymanager-3.3.1.ga.jar
hibernate-validator-3.0.0.GA.jar
persistence-api-1.0.jar

Name and version of the database you are using:
postgresql-8.2-504.jdbc4.jar

The generated SQL (show_sql=true):
Code:
Hibernate: select user0_.id as id20_, user0_.created_dt as created2_20_, user0_.modified_dt as modified3_20_, user0_.first_name as first4_20_, user0_.last_name as last5_20_, user0_.locale as locale20_, user0_.password as password20_, user0_.time_zone as time8_20_, user0_.username as username20_ from christwstest.christwstest.site_user user0_ where user0_.username=? and user0_.password=?
Hibernate: select roles0_.user_id as user1_1_, roles0_.role_id as role2_1_, role1_.id as id6_0_, role1_.created_dt as created2_6_0_, role1_.modified_dt as modified3_6_0_, role1_.comments as comments6_0_, role1_.hidden as hidden6_0_, role1_.name as name6_0_ from christwstest.christwstest.user_role_j roles0_ left outer join christwstest.christwstest.role role1_ on roles0_.role_id=role1_.id where roles0_.user_id=?
Hibernate: select addresses0_.user_id as user12_1_, addresses0_.id as id1_, addresses0_.id as id14_0_, addresses0_.created_dt as created2_14_0_, addresses0_.modified_dt as modified3_14_0_, addresses0_.comments as comments14_0_, addresses0_.city as city14_0_, addresses0_.country as country14_0_, addresses0_.location as location14_0_, addresses0_.postal_code as postal8_14_0_, addresses0_."primary" as primary9_14_0_, addresses0_.region as region14_0_, addresses0_.street as street14_0_, addresses0_.user_id as user12_14_0_ from christwstest.christwstest.user_address addresses0_ where addresses0_.user_id=?
Hibernate: select permission0_.address_id as address6_2_, permission0_.id as id2_, permission0_.id as id19_1_, permission0_.created_dt as created2_19_1_, permission0_.modified_dt as modified3_19_1_, permission0_.access_type as access4_19_1_, permission0_.role_id as role5_19_1_, permission0_.address_id as address6_19_1_, role1_.id as id6_0_, role1_.created_dt as created2_6_0_, role1_.modified_dt as modified3_6_0_, role1_.comments as comments6_0_, role1_.hidden as hidden6_0_, role1_.name as name6_0_ from christwstest.christwstest.user_address_role permission0_ inner join christwstest.christwstest.role role1_ on permission0_.role_id=role1_.id where permission0_.address_id=?
Hibernate: select permission0_.address_id as address6_2_, permission0_.id as id2_, permission0_.id as id19_1_, permission0_.created_dt as created2_19_1_, permission0_.modified_dt as modified3_19_1_, permission0_.access_type as access4_19_1_, permission0_.role_id as role5_19_1_, permission0_.address_id as address6_19_1_, role1_.id as id6_0_, role1_.created_dt as created2_6_0_, role1_.modified_dt as modified3_6_0_, role1_.comments as comments6_0_, role1_.hidden as hidden6_0_, role1_.name as name6_0_ from christwstest.christwstest.user_address_role permission0_ inner join christwstest.christwstest.role role1_ on permission0_.role_id=role1_.id where permission0_.address_id=?


Thanks,
Andrew


Top
 Profile  
 
 Post subject: Re: Can't understand why a related entity is being flushed
PostPosted: Mon Dec 24, 2007 1:51 pm 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
As far as I guess the user object is loaded within scope of another transaction so the entity manager has to make sure what it has is what will be in the database and therefore it needs to update the database state with the user object. However, instead of removing the address from entity manager remove the address from the user and DELETE_ORPHAN cascade option should delete your address object.


Farzad-


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 24, 2007 2:08 pm 
Beginner
Beginner

Joined: Wed Nov 30, 2005 10:25 pm
Posts: 25
Yes, I can remove it from the user, but I won't always have the user reference when I want to delete the address (it may be from a web page that I am just listing the addresses).

If I have to always find a parent object to be able to delete a child the code will be hard to maintain (no to mention ugly).

The @Version should make sure that the user is not updated, but for some reason that is not working.

Any other ideas? I don't want a work around, but find a fix as this is definitely a bug in my code the way it is working.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 24, 2007 2:49 pm 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
arobinson74 wrote:
Yes, I can remove it from the user, but I won't always have the user reference when I want to delete the address (it may be from a web page that I am just listing the addresses).

If I have to always find a parent object to be able to delete a child the code will be hard to maintain (no to mention ugly).

The @Version should make sure that the user is not updated, but for some reason that is not working.

Any other ideas? I don't want a work around, but find a fix as this is definitely a bug in my code the way it is working.


You will always be able to delete an address without having the user. Just make sure you set the lazy option for user property in address to avoid unnecessary selects. However, if you have loaded a user somewhere in the same entity manager then you will definitely need to take care of it. I assume this is a big deal since you have full freedom on your app's design. For example, if it is a web page that manipulates addresses then each request to the application can get a fresh entity manager and delete an address object. If you know somewhere in your request you are also loading the user object then you can consider fetching the user from entity manager and removing the address. My understanding is that a request for a user object that has already been loaded in an entity manager will not result in a select command therefore it has no significant cost.

Right now I am working on an example that might help you better with this situation. If come to any answer I will keep you posted here.


Farzad-


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 24, 2007 3:20 pm 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
ok I have another news for you. The reason this is happening is that you are cascading everything from User to Address. This way, even though you are only modifying Address object, user (with assumption it is already in the session context) will be traversed for its properties and in this case addresses will be examined for modification since CascadeType.ALL is included and it bumps because there is an address in the list that is going to be deleted. My test passed the error when I changed from ALL to SAVE_UPDATE.

I hope this helps.


Farzad-


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 24, 2007 5:32 pm 
Beginner
Beginner

Joined: Wed Nov 30, 2005 10:25 pm
Posts: 25
Yes, thanks,

I was seeing the issue go away when I removed the ALL cascade type. It didn't look as much as if the User was being updated as perhaps hibernate is just looking for updates and didn't like finding a deleted item in the collection. I think I need to understand the cascading mechanism better.

-Andrew


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.