Hibernate, many to many на двух таблицах.
От: vsb Казахстан  
Дата: 07.11.07 17:39
Оценка:
Сразу предупрежу, дизайн БД кривой, с этим ничего не поделаешь. Надо с этим жить
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.
Re: Hibernate, many to many на двух таблицах.
От: vsb Казахстан  
Дата: 07.11.07 20:04
Оценка:
Сейчас, в качестве workaround-а кажется работает session.clear после запроса. Я так понимаю, если данные не сохранялись, то ничего страшного не будет.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.