-
1. Re: ejb 3 + lazy loading + detached entities
svetok Oct 5, 2009 5:55 AM (in response to svetok)May be the problem is that we use javax.persistence.EntityManager and not org.hibernate.Session ?
-
2. Re: ejb 3 + lazy loading + detached entities
adamw Oct 6, 2009 2:21 AM (in response to svetok)Hello,
not, that shouldn't be a problem. Please report a bug in JIRA, with a test case, if possible. (Even better, with a fix patch ;) )
Adam -
3. Re: ejb 3 + lazy loading + detached entities
svetok Oct 6, 2009 9:11 AM (in response to svetok)Adam,
I spent almost a day to investigate where the problem is.
For some mysterious reason when the object of type ListProxy is being recieved via RMI to the client-side, it 'losts' it's delegate field (our wrapped collection). Just before the return from server it was not null, and on client it's get null. So, method ListProxy#checkInit() try to load this collection from database and of course he can't.
From one side it seems to be not the problem of Envers... But from the other side I can't undesrtand how during serializing/deserializing object could 'lost' part of field values and no exception is given by RMI.
Can you advice me anything?
Some parts of my code:
Person.class@Entity @Table(name="People",schema="X") @Audited @AuditTable(value = "People", schema="history") public class Person extends CISEntity{ private Integer id; private String name = ""; private List<Phone> phones = new ArrayList<Phone>(); @Id @GeneratedValue public Integer getId() { return id; } @SuppressWarnings("unused") private void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(cascade={CascadeType.ALL}) @Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN) @JoinColumn(name="pnt_object") @AuditJoinTable(name="People_Phones", schema="history") public List<Phone> getPhones() { return phones; } private void setPhones( List<Phone> phones ) { this.phones = phones; } }
Phone.class@Entity @Table (name="Phones",schema="X") @Audited @AuditTable(value = "Phones", schema="history") public class Phone implements Serializable{ private int id; private int pnt_object; private String phone = ""; @Id @GeneratedValue public int getId() { return id; } @SuppressWarnings("unused") private void setId(int id) { this.id = id; } public int getPnt_object() { return pnt_object; } public void setPnt_object(int pnt_object) { this.pnt_object = pnt_object; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }
Session bean@Stateless @RemoteBinding(jndiBinding="RemoteServices.HISTORY_MANAGER_JNDI_NAME") public class HistoryManagerTestBean implements HistoryTestManager{ @PersistenceContext(unitName = "cis2") private EntityManager em; public Person getFullPerson(Integer id, int revision_number){ AuditReader reader = AuditReaderFactory.get(em); Person p=reader.find(Person.class, id, revision_number); int i =p.getPhones().size(); return p; } }
Thanks
p.s. I'm not sure about bug report in JIRA, because possibly it's not the problem of Envers. -
4. small fix
svetok Oct 6, 2009 9:17 AM (in response to svetok)Sorry, method checkInit is in class CollectionProxy.
-
5. Re: ejb 3 + lazy loading + detached entities
svetok Oct 6, 2009 10:41 AM (in response to svetok)Can it be the effect of generics? I mean... this field delegate is of type T. It becomes Collection after compilation. And Collection does not implement Serializable interface...
I just don't know to think about all this problem... :( -
6. Re: ejb 3 + lazy loading + detached entities
svetok Oct 7, 2009 5:09 AM (in response to svetok)Hi, Adam!
I'm almost sure that the problem lies in the direction of generics - inheritance - serialization. But I haven't succeeded to find out the exact point.
BUT I've found a workaround. I know it's a "bad style", but for now it works and at least give me possibility to postpone the decision of this problem.
So I had to get rid of inheritance CollectionProxy <- ListProxy. I've moved everything from CollectionProxy to ListProxy. And also I've made the field "initializor" transient.
With this variant of ListProxy all mapped collections (that are Lists) work just fine.
I can post here my ListProxy class, may be it will help to find the real cause of problem.public class ListProxy<U> implements List<U>, Serializable { private transient org.hibernate.envers.entities.mapper.relation.lazy.initializor.Initializor<List<U>> initializor; protected List<U> delegate; public ListProxy() { } public ListProxy(org.hibernate.envers.entities.mapper.relation.lazy.initializor.Initializor<List<U>> initializor) { this.initializor = initializor; } protected void checkInit() { if (delegate == null) { delegate = initializor.initialize(); } } public boolean addAll(int index, Collection<? extends U> c) { checkInit(); return delegate.addAll(index, c); } public U get(int index) { checkInit(); return delegate.get(index); } public U set(int index, U element) { checkInit(); return delegate.set(index, element); } public void add(int index, U element) { checkInit(); delegate.add(index, element); } public U remove(int index) { checkInit(); return delegate.remove(index); } public int indexOf(Object o) { checkInit(); return delegate.indexOf(o); } public int lastIndexOf(Object o) { checkInit(); return delegate.lastIndexOf(o); } public ListIterator<U> listIterator() { checkInit(); return delegate.listIterator(); } public ListIterator<U> listIterator(int index) { checkInit(); return delegate.listIterator(index); } public List<U> subList(int fromIndex, int toIndex) { checkInit(); return delegate.subList(fromIndex, toIndex); } public int size() { checkInit(); return delegate.size(); } public boolean isEmpty() { checkInit(); return delegate.isEmpty(); } public boolean contains(Object o) { checkInit(); return delegate.contains(o); } public Iterator<U> iterator() { checkInit(); return delegate.iterator(); } public Object[] toArray() { checkInit(); return delegate.toArray(); } public <V> V[] toArray(V[] a) { checkInit(); return delegate.toArray(a); } public boolean add(U o) { checkInit(); return delegate.add(o); } public boolean remove(Object o) { checkInit(); return delegate.remove(o); } public boolean containsAll(Collection<?> c) { checkInit(); return delegate.containsAll(c); } public boolean addAll(Collection<? extends U> c) { checkInit(); return delegate.addAll(c); } public boolean removeAll(Collection<?> c) { checkInit(); return delegate.removeAll(c); } public boolean retainAll(Collection<?> c) { checkInit(); return delegate.retainAll(c); } public void clear() { checkInit(); delegate.clear(); } @Override public String toString() { checkInit(); return delegate.toString(); } @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"}) @Override public boolean equals(Object obj) { checkInit(); return delegate.equals(obj); } @Override public int hashCode() { checkInit(); return delegate.hashCode(); } }
What's your opinion on all this?
Thanks -
7. Re: ejb 3 + lazy loading + detached entities
adamw Oct 8, 2009 8:43 AM (in response to svetok)Hello,
well it looks very weird, but you maybe be right that if CollectionProxy doesn't implement Serializable, and ListProxy does, then the field may be null after sending it. Especially if it works after moving everything to ListProxy.
Anyway, as CP and LP are Envers classes, it's probably a bug in Envers. Then a JIRA would be appropriate :)
Adam -
8. Re: ejb 3 + lazy loading + detached entities
svetok Oct 9, 2009 4:48 AM (in response to svetok)hi Adam!
You were right. I've fixed the problem in more appropriate way by moving "implements Serializable" to CollectionProxy.
I've made a patch and posted it on JIRA - http://opensource.atlassian.com/projects/hibernate/browse/HHH-4488 , though I've never done patches before :) and it can be not correct in some ways.
Thanks a lot for your project, it's really helpful.
Svetlana -
9. Re: ejb 3 + lazy loading + detached entities
adamw Oct 27, 2009 3:58 PM (in response to svetok)Hello,
very good patch, although without a test case ;)
Adam