简体   繁体   中英

Hibernate: Removing item from a List does not persist

I am having trouble when removing an item from a list. The list is defined in a superclass, but the Hibernate annotations are applied to property accessors in a subclass. There are two methods in the superclass that manipulate the list. The "add" method works fine, but the "remove" does not persist changes. I have checked my Cascade settings, and I seem to have things correct. Am I doing something that is impossible. If not, am I doing something incorrectly?

Here are my classes:

@Entity 
abstract class Temporal<T> { 
    @Id 
    @GeneratedValue 
    private Long id; 

    @Version 
    private Integer version = null; 

    @Transient 
    protected List<T> content = new ArrayList<T>(); 

    public void remove(T value) { 
        // business logic ... 
        content.remove(value); 
    } 

    public void add(T value) { 
        // business logic ... 
        content.add(value); 
    } 
} 

@Entity 
@AccessType("property") 
class TemporalAsset extends Temporal<Asset> { 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "temporal") 
    public List<Asset> getContent() { 
        return super.content; 
    } 

    protected void setContent(List<Asset> list) { 
        super.content = list; 
    } 
} 

I use an instance of the TemporalAsset class as follows (note that I am only use the "refresh" method to demonstrate the behavior. The list does not persist correctly even if I flush or close the session and open a new session):

temporalAsset.add(value1); 
temporalAsset.getContent().size() == 1; // true 
session.update(temporalAsset); 

session.refresh(temporalAsset); 

temporalAsset.getContent().size() == 1; // true 

temporalAsset.remove(value1); 
temporalAsset.getContent().size() == 0; // true 
session.update(temporalAsset); 

session.refresh(temporalAsset); 

temporalAsset.getContent().size() == 0; // false, its 1 

Thanks.

You have to explicitly specify cascade as CascadeType.DELETE_ORPHAN.

Try to change code to

@OneToMany    
@Cascade(cascade = {CascadeType.ALL, CascadeType.DELETE_ORPHAN}, mappedBy = "temporal")

Part from hibernate docs :

If the child object's lifespan is bounded by the lifespan of the parent object, make the parent a full lifecycle object by specifying CascadeType.ALL and org.hibernate.annotations.CascadeType.DELETE_ORPHAN (please refer to the Hibernate reference guide for the semantics of orphan delete)

Try removing the calls to Session.refresh(). From the docs:

Re-read the state of the given instance from the underlying database. It is inadvisable to use this to implement long-running sessions that span many business tasks. This method is, however, useful in certain special circumstances. For example

  • where a database trigger alters the object state upon insert or update
  • after executing direct SQL (eg. a mass update) in the same session
  • after inserting a Blob or Clob

http://www.hibernate.org/hib_docs/v3/api/org/hibernate/Session.html#refresh(java.lang.Object)

If you call flush() before refresh(), that might fix the problem too, since flush() guarantees that any pending SQL will be executed against the DB. In practice I've almost never seen anyone use refresh() and it doesn't look like from your code that you need it.

This chapter from the documentation is worth a read:

http://www.hibernate.org/hib_docs/v3/reference/en/html/objectstate.html

这是目前推荐的方式。

@OneToMany(mappedBy = "temporal", orphanRemoval = true, cascade = CascadeType.ALL)

You have marked the 'content' field as transient in the super class. I would at least suspect that this is causing problems. With the mapping in the subclass, you basically have now two contradicting mappings for the same attribute.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM