Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > Наследование в Hibernate


Автор: ming 13.10.2009, 07:15
Приветствую форумчан!


Имеется такая иерархия классов (модификаторы доступа и ненужные для общей картины элементы опущены):
Код

/** абстрактный класс, представляющий содержимое файлообменника */
abstract class Content {
    // общие поля типа Name, Description, Size, etc
}
class Film extends Content {
    int year; // год выпуска фильма
}
class Software extends Content {
    String version; // номер версии софтины
}

/** Категория, хранящая экземпляры подклассов класса Contеnt
Один подкласс - одна категория */
class Category {
    List<Content> items;
}


Наследование замаплено примерно так

Content.hbm.xml
Код

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="ru.balza.filex.logic.content">
    <class name="Content" abstract="true">
        <id column="CONTENT_ID" name="id">
            <generator class="native" />
        </id>        
        <discriminator column="DSK" type="string" length="30" not-null="true"/>
        
        <property name="name" type="string"/>
        <property name="description" type="string"/>
        <property name="size" type="int"/>
        <property name="URL" type="string"/>     
 
    <many-to-one name="category" class="Category" column="CATEGORY_ID" not-null="true" lazy="false"/>
 
    <bag name="tags" cascade="save-update" table="tags_and_content" lazy="false">
         <key column="CONTENT_ID"/>
         <many-to-many class="Tag" column="TAG_ID"/>
     </bag>
             
    <bag name="comments" cascade="save-update" lazy="false">
        <key column="CONTENT_ID"/>
        <one-to-many class="Comment"/>
    </bag>
    </class>
</hibernate-mapping>


Film.hbm.xml
Код

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="ru.balza.filex.logic.content">
    <subclass name="Film" extends="Content" discriminator-value="Film">
     <property name="originalName" column="original_name" type="string"/>
        <property name="director" type="string"/>
        <property name="year" type="int"/>
        <property name="country" type="string"/>           
    </subclass>
</hibernate-mapping>


Экземпляр Content (точнее экземпляр любого из его субклассов) вне Категории существовать не может и не должен. 
Это связывает меня по рукам и ногам тем, что когда я хочу вытащить из базы экзмепляр Contenta (т.е. его субклассов), 
мне нужно явно указывать конкретный субкласс, к которому полученный объект нужно будет прикастить.

Код

// где то в ContentDAO implementation
public Content getContentById(int contentId) throws SQLException {
    ...
    session = HibernateUtil.getSessionFactory().openSession();
    session.beginTransaction();    
    // 1.
    Content c = session.get(Content.class, contentId); // ошибка, Content - абстрактный класс
    // 2.
    Content c = session.get(Film.class,    contentId); // корректно
    // 3.
    Content c = session.get(Software.class, contentId); // так тоже корректно
    ...
    
    return c;
}

 
Получается что все время надо "таскать" за собой ссылку на этот нужный класс 
(Film или Software или любой другой подкласс Content).
В данный момент у меня это реализовано в виде метода в классе Category, 
возвращающего ссылку на класс объектов, которые хранит данная категория :

Код

class Category {
    List<Content> items;
    ...

    static final int UNDEFINED_TYPE = 0;
    static final int FILMS_TYPE    = 1;
    static final int SOFTWARE_TYPE    = 2;
    
    static final Map<Integer, Class> typesMap;
    static {
        typesMap = new HashMap<Integer, Class>();
        typesMap.put(UNDEFINED_TYPE,    null);
        typesMap.put(FILMS_TYPE,    Film.class);
        typesMap.put(SOFTWARE_TYPE,    Software.class);
    }
    ...

    /** тип категории, сигнализирует о том, объекты какого класса хранит категория */
    private int type; 
    ...

    public Class getContentClass() {
        return typesMap.get(type);
    }
}


инициализация категории:

Код

Category cat = new Category();
cat.setType(Category.FILMS_TYPE);
...


Применение:

Код

public class ContentDAOHibernateImpl implements ContentDAO {

...
    public Content getContentById(Category category, int contentId) throws SQLException {
        Session session = null;
        Content content = null;
        try {
            session = HibernateUtil.getSessionFactory().openSession();
            session.beginTransaction();    

            Class contentClass = category.getContentClass(); // ссылка на класс
            if (contentClass != null) {
                content = (Content) session.get(contentClass, contentId);
            }
        } catch(Exception ex) {
            ex.printStackTrace();
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
        return content;
    }    
...
}



Хотелось бы узнать насколько такой способ хорош (или вообще допустим)?
Можно ли избавиться от повсеместного "таскания" Category?
Какова Best Practice, так сказать...

Автор: Quester82 13.10.2009, 08:57
Цитата(ming @ 13.10.2009,  07:15)
Хотелось бы узнать насколько такой способ хорош (или вообще допустим)?
Можно ли избавиться от повсеместного "таскания" Category?
Какова Best Practice, так сказать...

Почитай вот это...
http://black-zorro.com/mediawiki/Hibernate%3A_отображая_иерархии_классов
Очень наглядно показано как сделать именно то что ты просишь, а также как просто избавиться от дискриминатора и прочей лабуды, хотя и не без издержек...

Автор: ming 17.10.2009, 15:50
спасибо, полезная ссылочка!
переделал маппинг по методу "таблица для суперкласса, и по таблице для каждого подкласса"

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)