Here are 2 examples for a UserCollectionType and an Bag-Implementation, that work in Hibernate 3. The classes are called HistoryListType and HistoryListBag.
Note that HistoryListType does not need to implement java.util.List. Because my Entities should not use a Hibernate-dependent class, I have written an Interface "HistoryListIF". Entities only reference this Interface and the real Implementation "HistoryList".
If your Bag implements the java.util.List-Interface you do not need an own Bag-Implementation. Just set the collection-type-Attribute in the <bag>-Tag in in the Mapping-File.
However, your own Bag-Implementation will always have Dependencies to Hibernate. So you will have to put hibernate3.jar on your Client.
If your Client has a Gui, I think its best Practice to give no Entities and no DTOs to the Client. Instead you should give a FormBean to the Client that contains the Strings, that are shown in the Gui. Then you can create a Framework on the Client-Side that fill the GuiComponents with the FormBeans automatically.
[code]
package ba.history.hibernate;
import java.util.*;
import org.hibernate.HibernateException;
import org.hibernate.collection.*;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.usertype.UserCollectionType;
import ba.history.*;
import ba.history.interfaces.*;
public class HistoryListType implements UserCollectionType {
public HistoryListType() {
}
/**
* Optional operation. Does the collection contain the entity instance?
*/
public boolean contains(Object collection, Object obj) {
HistoryListIF historyList = (HistoryListIF)collection;
if( ! (obj instanceof History)) {
return false;
}
return historyList.contains((History)obj);
}
/**
* Return an iterator over the elements of this collection - the passed collection
* instance may or may not be a wrapper
*/
public Iterator getElementsIterator(Object collection) {
return ((HistoryListIF)collection).iterator();
}
/**
* Optional operation. Return the index of the entity in the collection.
*/
public Object indexOf(Object collection, Object obj) {
if( !(obj instanceof History)) {
return new Integer(-1);
}
int index = ((HistoryListIF)collection).indexOf((History)obj);//new Integer(((HistoryList)collection).indexOf(obj));
if(index == -1) {
return null;
}
return new Integer(index);
}
/**
* Instantiate an empty instance of the "underlying" collection (not a wrapper)
*/
public Object instantiate() {
return new HistoryList();
}
/**
* Instantiate an uninitialized instance of the collection wrapper
*/
public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister) throws HibernateException {
// Use hibernate's built in persistent set implementation
//wrapper
return new HistoryListBag(session);
}
/**
* Replace the elements of a collection with the elements of another collection
*/
public void replaceElements(
Object collectionA,
Object collectionB,
CollectionPersister persister,
Object owner,
Map copyCache,
SessionImplementor implementor) throws HibernateException {
HistoryListIF historyListA = (HistoryListIF)collectionA;
HistoryListIF historyListB = (HistoryListIF)collectionB;
historyListB.clear();
int length = historyListA.size();
for (int i = 0; i < length; i++) {
historyListB.add(historyListB.get(i));
}
}
/**
* Wrap an instance of a collection
*/
public PersistentCollection wrap(SessionImplementor session, Object collection) {
// Use hibernate's built in persistent set implementation
//wrapper.
return new HistoryListBag(session, (HistoryList)collection);
}
}
package ba.history.hibernate;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.type.Type;
import org.hibernate.collection.*;
import ba.history.*;
import ba.history.interfaces.HistoryListIF;
public class HistoryListBag extends AbstractPersistentCollection implements HistoryListIF {
private HistoryList bag;
public HistoryListBag() {} //needed for SOAP libraries, etc
public HistoryListBag(SessionImplementor session) {
super(session);
}
public HistoryListBag(SessionImplementor session, HistoryList historyList) {
super(session);
bag = historyList;
setInitialized();
setDirectlyAccessible(true);
}
public boolean isWrapper(Object collection) {
return bag == collection;
}
public boolean empty() {
return bag.isEmpty();
}
public Iterator entries(CollectionPersister persister) {
return bag.iterator();
}
public Object readFrom(ResultSet rs, CollectionPersister persister,
CollectionAliases descriptor, Object owner) throws
HibernateException, SQLException {
// note that if we load this collection from a cartesian product
// the multiplicity would be broken ... so use an idbag instead
Object element = persister.readElement(rs, owner,
descriptor.getSuffixedElementAliases(),
getSession());
if (element != null) bag.add( (History) element);
return element;
}
public void beforeInitialize(CollectionPersister persister) {
this.bag = new HistoryList();
}
public boolean equalsSnapshot(CollectionPersister persister) throws
HibernateException {
Type elementType = persister.getElementType();
EntityMode entityMode = getSession().getEntityMode();
java.util.List sn = (java.util.List) getSnapshot();
if (sn.size() != bag.size())return false;
Iterator iter = bag.iterator();
while (iter.hasNext()) {
Object elt = iter.next();
final boolean unequal = countOccurrences(elt, bag.iterator(), elementType,
entityMode) !=
countOccurrences(elt, sn.iterator(), elementType, entityMode);
if (unequal)return false;
}
return true;
}
private int countOccurrences(Object element, Iterator iter, Type elementType,
EntityMode entityMode) throws HibernateException {
int result = 0;
while (iter.hasNext()) {
if (elementType.isSame(element, iter.next(), entityMode)) result++;
}
return result;
}
protected Serializable snapshot(CollectionPersister persister) throws
HibernateException {
EntityMode entityMode = getSession().getEntityMode();
ArrayList clonedList = new ArrayList(bag.size());
Iterator iter = bag.iterator();
while (iter.hasNext()) {
clonedList.add(persister.getElementType().deepCopy(iter.next(),
entityMode, persister.getFactory()));
}
return clonedList;
}
public Collection getOrphans(Serializable snapshot, String entityName) throws
HibernateException {
java.util.List sn = (java.util.List) snapshot;
return getOrphans(sn, bagToCollection(), entityName, getSession());
}
public Object getIndex(Object entry, int i, CollectionPersister persister) {
throw new UnsupportedOperationException("Bags don't have indexes");
}
private Collection bagToCollection() {
int length = bag.size();
ArrayList erg = new ArrayList();
for (int i = 0; i < length; i++) {
erg.add(bag.get(i));
}
return erg;
}
public Serializable disassemble(CollectionPersister persister) throws
HibernateException {
int length = bag.size();
Serializable[] result = new Serializable[length];
for (int i = 0; i < length; i++) {
result[i] = persister.getElementType().disassemble(bag.get(i), getSession(), null);
}
return result;
}
public void initializeFromCache(CollectionPersister persister,
Serializable disassembled, Object owner) throws
HibernateException {
beforeInitialize(persister);
Serializable[] array = (Serializable[]) disassembled;
for (int i = 0; i < array.length; i++) {
Object element = persister.getElementType().assemble(array[i], getSession(),
owner);
if (element != null) bag.add( (History) element);
}
}
public boolean needsRecreate(CollectionPersister persister) {
return!persister.isOneToMany();
}
// For a one-to-many, a <bag> is not really a bag;
// it is *really* a set, since it can't contain the
// same element twice. It could be considered a bug
// in the mapping dtd that <bag> allows <one-to-many>.
// Anyway, here we implement <set> semantics for a
// <one-to-many> <bag>!
public Iterator getDeletes(CollectionPersister persister,
boolean indexIsFormula) throws HibernateException {
//if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
Type elementType = persister.getElementType();
EntityMode entityMode = getSession().getEntityMode();
ArrayList deletes = new ArrayList();
java.util.List sn = (java.util.List) getSnapshot();
Iterator olditer = sn.iterator();
int i = 0;
while (olditer.hasNext()) {
Object old = olditer.next();
Iterator newiter = bag.iterator();
boolean found = false;
if (bag.size() > i && elementType.isSame(old, bag.get(i++), entityMode)) {
//a shortcut if its location didn't change!
found = true;
}
else {
//search for it
//note that this code is incorrect for other than one-to-many
while (newiter.hasNext()) {
if (elementType.isSame(old, newiter.next(), entityMode)) {
found = true;
break;
}
}
}
if (!found) deletes.add(old);
}
return deletes.iterator();
}
public boolean needsInserting(Object entry, int i, Type elemType) throws
HibernateException {
//if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
java.util.List sn = (java.util.List) getSnapshot();
final EntityMode entityMode = getSession().getEntityMode();
if (sn.size() > i && elemType.isSame(sn.get(i), entry, entityMode)) {
//a shortcut if its location didn't change!
return false;
}
else {
//search for it
//note that this code is incorrect for other than one-to-many
Iterator olditer = sn.iterator();
while (olditer.hasNext()) {
Object old = olditer.next();
if (elemType.isSame(old, entry, entityMode))return false;
}
return true;
}
}
public boolean isRowUpdatePossible() {
return false;
}
public boolean needsUpdating(Object entry, int i, Type elemType) {
//if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
return false;
}
/**
* @see java.util.Collection#size()
*/
public int size() {
read();
return bag.size();
}
/**
* @see java.util.Collection#isEmpty()
*/
public boolean isEmpty() {
read();
return bag.isEmpty();
}
public boolean contains(History history) {
read();
return bag.contains(history);
}
/**
* @see java.util.Collection#iterator()
*/
public Iterator iterator() {
read();
return new IteratorProxy(bag.iterator());
}
/**
* @see java.util.Collection#toArray()
*/
public History[] toArray() {
read();
return bag.toArray();
}
/**
* @see java.util.Collection#add(Object)
*/
public boolean add(History history) {
if (!queueAdd(history)) {
write();
return bag.add(history);
}
else {
return true;
}
}
/**
* Liefert alle Historys, die eine Period haben, die date enthält.
* @param date das Datum
* @return die Historys, die eine Period haben, die date enthält
*/
public HistoryList getValid(Date date) {
read();
return bag.getValid(date);
}
public HistoryList getValid(Period period) {
read();
return bag.getValid(period);
}
public ChangeInfo setAllLimit(Date date) {
write();
return bag.setAllLimit(date);
}
public ChangeInfo minusAll(Period period) {
write();
return bag.minusAll(period);
}
/**
* @see java.util.Collection#remove(Object)
*/
public boolean remove(History history) {
write();
return bag.remove(history);
}
/**
* @see java.util.Collection#containsAll(Collection)
*/
public boolean containsAll(Collection c) {
read();
return bag.containsAll(c);
}
/**
* @see java.util.Collection#addAll(Collection)
*/
public boolean add(Collection c) {
if ( c.size()==0 ) return false;
if ( !queueAddAll(c) ) {
write();
return bag.add(c);
}else {
return c.size()>0;
}
}
/**
* Fügt dieser HistoryList ein Array von Historys hinzu.
* @param histories die Historys
*/
public boolean add(History[] histories) {
return add(Arrays.asList(histories));
}
public void delayedAddAll(Collection c) {
bag.add(c);
}
/**
* @see java.util.Collection#removeAll(Collection)
*/
public boolean remove(Collection c) {
if ( c.size()>0 ) {
write();
return bag.remove(c);
}else {
return false;
}
}
/**
* @see java.util.Collection#clear()
*/
public void clear() {
write();
bag.clear();
}
public Object getElement(Object entry) {
return entry;
}
public Object getSnapshotElement(Object entry, int i) {
HistoryList sn = (HistoryList) getSnapshot();
return sn.get(i);
}
public int occurrences(Object o) {
read();
Iterator iter = bag.iterator();
int result=0;
while ( iter.hasNext() ) {
if ( o.equals( iter.next() ) ) result++;
}
return result;
}
// History OPERATIONS:
/**
* @see java.util.List#get(int)
*/
public History get(int i) {
read();
return bag.get(i);
}
/**
* @see java.util.List#indexOf(Object)
*/
public int indexOf(History history) {
read();
return bag.indexOf(history);
}
public String toString() {
read();
return "HistoryListBag: " + bag.toString();
}
/* public boolean equals(Object other) {
read();
return bag.equals(other);
}
public int hashCode(Object other) {
read();
return bag.hashCode();
}*/
public boolean entryExists(Object entry, int i) {
return entry!=null;
}
/**
* Bag does not respect the collection API and do an
* JVM instance comparison to do the equals.
* The semantic is broken not to have to initialize a
* collection for a simple equals() operation.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
return super.equals(obj);
}
/**
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return super.hashCode();
}
///////////////////////////////////////////////////////////////////////////////////
// PRIVATE KLASSEN
///////////////////////////////////////////////////////////////////////////////////
final class IteratorProxy implements Iterator {
private final Iterator iter;
IteratorProxy(Iterator iter) {
this.iter=iter;
}
public boolean hasNext() {
return iter.hasNext();
}
public Object next() {
return iter.next();
}
public void remove() {
write();
iter.remove();
}
}
}
_________________ Lars Fiedler
|