Сразу предупрежу, дизайн БД кривой, с этим ничего не поделаешь. Надо с этим жить
CREATE TABLE VSB.ADDRESS
(ADDRESSID INTEGER NOT NULL PRIMARY KEY,
ADDRESS VARCHAR(64) NOT NULL,
ADDRESSCODE INTEGER NOT NULL
);
CREATE TABLE VSB.PERSON
(PERSONID INTEGER NOT NULL PRIMARY KEY,
PERSONNAME VARCHAR(32) NOT NULL,
PERSONADDRESSCODE INTEGER NOT NULL
);
Строка в таблице PERSON связана с адресами через поле PERSONADDRESSCODE; ADDRESS.ADDRESSCODE может содержать дубликаты, поэтому получается связь многие-ко-многим.
Вот то, что родилось у меня:
public class Person {
private Integer id;
private String name;
private Integer addressCode;
private Set addresses;
// getters and setters
}
public class Address {
private Integer id;
private String address;
// getters and setters
}
Mappings:
<hibernate-mapping package="htest" schema="vsb">
<class name="Person" table="PERSON">
<id name="id" column="PERSONID"/>
<property name="name" column="PERSONNAME"/>
<property name="addressCode" column="PERSONADDRESSCODE"/>
<set name="addresses" fetch="join" >
<key column="ADDRESSCODE" property-ref="addressCode"/>
<one-to-many class="Address"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping package="htest" schema="vsb">
<class name="Address" table="Address">
<id name="id" column="ADDRESSID"/>
<property name="address" column="ADDRESS"/>
<!-- <property name="code" column="ADDRESSCODE"/>-->
</class>
</hibernate-mapping>
Но это не работает!
public static void main(final String[] args) {
Session session = new Configuration().configure().buildSessionFactory().getCurrentSession();
session.beginTransaction();
List persons = session.createCriteria(Person.class).list();
for (Iterator iterPerson = persons.iterator(); iterPerson.hasNext();) {
Person person = (Person) iterPerson.next();
System.out.println("Name: " + person.getName());
for (Iterator iterAddress = person.getAddresses().iterator(); iterAddress.hasNext();) {
Address address = (Address) iterAddress.next();
System.out.println(" " + address.getAddress());
}
}
session.flush(); // специально
session.getTransaction().commit();
}
Падает на строке с flush со следующим стектрейсом:
Exception in thread "main" org.hibernate.HibernateException: Found shared references to a collection: htest.Person.addresses
at org.hibernate.engine.Collections.processReachableCollection(Collections.java:163)
at org.hibernate.event.def.FlushVisitor.processCollection(FlushVisitor.java:37)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:61)
at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:138)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:615)
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:301)
at $Proxy0.flush(Unknown Source)
at htest.Main.main(Main.java:28)
Если мы меняем fetch="select", то получаем ещё более интересное исключение:
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: htest.Person.addresses, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:343)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:163)
at htest.Main.main(Main.java:23)
Признаться, я в полном недоумении. Гуглил по этим исключениям, ничего внятного найти не удалось. Полагаю, что я неверно записал маппинги, но лучше не получается — все примеры, которые я видел, предполагают "хорошие" схемы — либо на ключевые поля one-to-many, либо через промежуточную таблицу many-to-many.
Сейчас, в качестве workaround-а кажется работает session.clear после запроса. Я так понимаю, если данные не сохранялись, то ничего страшного не будет.