Пусть 2 разных файла сопоставления работают с одним доменом - PullRequest
2 голосов
/ 04 апреля 2011

Приложение, над которым я работаю, должно поддерживать 3 базы данных.Sybase, SQL и Oracle.

Теперь у нас проблема с Oracle.Что касается оракула, мы используем последовательности в качестве генераторов идентификаторов, для Sybase и Oracle мы используем нашу собственную реализацию для генерации идентификатора.

У нас проблема с одной таблицей, которая состоит из двойного первичного ключа.Мой файл сопоставления состоит из составного идентификатора.Но в составном идентификаторе вы не можете работать с сгенерированными значениями.Для Oracle и Sybase это не проблема, так как я вручную создаю новый идентификатор.Однако для Oracle у меня есть проблема, поскольку необходимо использовать последовательности.

Итак, я собираюсь создать второй файл отображения, специфичный для Oracle, который использует только 1 поле в качестве PK.Мой запрос не будет правильным, но я не вижу другой возможности сделать это ..

Итак, у меня есть 2 файла сопоставления:

Sybase и SQL:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-cascade="none" xmlns="urn:nhibernate-mapping-2.2"
    namespace="VS3.Domain.Address" assembly="VS3.Domain">

<class name="Top_Address" table="TOP_ADDRESS">
<composite-id name="ID" class="IDComposite_TopAddress">
  <key-property name="TnrAddress" column="TNR_ADDRESS" type="Int32"/>
  <key-many-to-one name="TopIdentity" class="Top_Identity" column="TNR_ID"/>
</composite-id>

<property name="nmAddress" column="NM_ADDRESS" type="String" not-null="true"/>
<property name="dPosx" column="D_POSX" type="Decimal" not-null="false"/>
<property name="dPosy" column="D_POSY" type="Decimal" not-null="false"/>

<property name="nmStreetAdd" column="NM_STREET_ADD" type="String" not-null="false"/>
<property name="nmStreetAdd2" column="NM_STREET_ADD2" type="String" not-null="false"/>
<property name="nmStreetAdd3" column="NM_STREET_ADD3" type="String" not-null="false"/>

<!-- Added By Transics -->
<property name="HouseNumber" column="CNR_HOUSE_ADD" type="String" not-null="false" />
<property name="BusNumber" column="CNR_BUS_ADD" type="String" not-null="false" />
<property name="PostalCode" column="CNR_ZIP" type="String" not-null="false" />
<property name="City" column="NM_ZIP" type="String" not-null="false" />
<property name="Country" column="COD_COUNTRY" type="String" not-null="false" />
<property name="Email" column="CNR_EMAIL_ADD" type="String" not-null="false" />
<property name="Phone" column="CNR_PHONE_ADD" type="String" not-null="false" />
<property name="CellPhone" column="CNR_GSM_ADD" type="String" not-null="false" />
<property name="NmAddress2" column="NM_ADDRESS2" type="String" not-null="false" />

<property name="Active" column="BOL_ACTIVE_ADD" type="Int32" not-null="false" />
<property name="Language" column="COD_LANG" type="String" not-null="false" />
<property name="AmFrom" column="T_TRB_AM_FROM" type="DateTime" not-null="false" />
<property name="AmUntil" column="T_TRB_AM_UNTIL" type="DateTime" not-null="false" />
<property name="PmFrom" column="T_TRB_PM_FROM" type="DateTime" not-null="false" />
<property name="PmUntil" column="T_TRB_PM_UNTIL" type="DateTime" not-null="false" />
<property name="Remark" column="DES_TRB_REMARK" type="String" not-null="false" />
<property name="ExtraInformation" column="TXT_ADDRESS" type="String" not-null="false" />
<property name="AddressType" column ="COD_ADDRESSTYPE" type="Char" not-null="false" />
<property name="AddressOrPlace" column ="COD_TRB_ADDRESSTYPE" type="String" not-null="false" />

</class>
</hibernate-mapping>

Oracle:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-cascade="none" xmlns="urn:nhibernate-mapping-2.2"
    namespace="VS3.Domain.Address" assembly="VS3.Domain">

<class name="Top_Address_Oracle" table="TOP_ADDRESS">
<id name="TnrAddress" column="TNR_ADDRESS" type="Int32">
  <generator class="sequence" />
</id>

<property name="nmAddress" column="NM_ADDRESS" type="String" not-null="true"/>
<property name="dPosx" column="D_POSX" type="Decimal" not-null="false"/>
<property name="dPosy" column="D_POSY" type="Decimal" not-null="false"/>

<property name="nmStreetAdd" column="NM_STREET_ADD" type="String" not-null="false"/>
<property name="nmStreetAdd2" column="NM_STREET_ADD2" type="String" not-null="false"/>
<property name="nmStreetAdd3" column="NM_STREET_ADD3" type="String" not-null="false"/>
<property name="HouseNumber" column="CNR_HOUSE_ADD" type="String" not-null="false" />
<property name="BusNumber" column="CNR_BUS_ADD" type="String" not-null="false" />
<property name="PostalCode" column="CNR_ZIP" type="String" not-null="false" />
<property name="City" column="NM_ZIP" type="String" not-null="false" />
<property name="Country" column="COD_COUNTRY" type="String" not-null="false" />
<property name="Email" column="CNR_EMAIL_ADD" type="String" not-null="false" />
<property name="Phone" column="CNR_PHONE_ADD" type="String" not-null="false" />
<property name="CellPhone" column="CNR_GSM_ADD" type="String" not-null="false" />
<property name="NmAddress2" column="NM_ADDRESS2" type="String" not-null="false" />

<property name="Active" column="BOL_ACTIVE_ADD" type="Int32" not-null="false" />
<property name="Language" column="COD_LANG" type="String" not-null="false" />
<property name="AmFrom" column="T_TRB_AM_FROM" type="DateTime" not-null="false" />
<property name="AmUntil" column="T_TRB_AM_UNTIL" type="DateTime" not-null="false" />
<property name="PmFrom" column="T_TRB_PM_FROM" type="DateTime" not-null="false" />
<property name="PmUntil" column="T_TRB_PM_UNTIL" type="DateTime" not-null="false" />
<property name="Remark" column="DES_TRB_REMARK" type="String" not-null="false" />
<property name="ExtraInformation" column="TXT_ADDRESS" type="String" not-null="false" />
<property name="AddressType" column ="COD_ADDRESSTYPE" type="Char" not-null="false" />
<property name="AddressOrPlace" column ="COD_TRB_ADDRESSTYPE" type="String" not-null="false" />

<many-to-one name="Identity" class="Identity" column="TNR_ID" not-null="true"/>
</class>
</hibernate-mapping>

Можно ли каким-либо образом убедиться, что эти файлы сопоставления используют один и тот же класс Domain?Или мне тоже нужно создать новый домен (как я сейчас сделал).

Но это не совсем вариант.Было бы много работы.

У кого-нибудь есть решение для этого?

Ответы [ 2 ]

1 голос
/ 04 апреля 2011

Вы можете иметь два файла сопоставления для одного и того же класса домена - если вам нужен только один во время выполнения вашего кода (что, как я полагаю, будет неудобно работать с двумя разными базами данных одновременно с одним и тем же таблицы и доменные классы). Перед созданием SessionFactory вы можете отфильтровать файлы hbm и выбрать только те, которые вам нужны, в зависимости от параметра.

Вам потребуется файл сопоставления для Oracle и еще один файл для остальных, например MyEntity.oracle.hbm.xml и MyEntity.default.hbm.xml. Для тех классов, которые в обоих случаях одинаковы, вы просто оставляете их такими, как они есть, например MyCommonEntity.hbm.xml.

Вот немного измененный пример кода, который я использую в нашем проекте. (Я разместил другую версию этого в другом вопросе здесь на SO: Динамически изменить генератор идентификаторов на «назначенный» в отображении класса NHibernate )

private ISessionFactory BuildSessionFactory(bool useOracleMapping)
{
    Configuration config = new Configuration();

    config.SetProperty(NHibernate.Cfg.Environment.ConnectionProvider, "...");
    config.SetProperty(NHibernate.Cfg.Environment.Dialect, "...");
    config.SetProperty(NHibernate.Cfg.Environment.ConnectionDriver, "...");
    config.SetProperty(NHibernate.Cfg.Environment.ConnectionString, "...");
    config.SetProperty(NHibernate.Cfg.Environment.Isolation, "Serializable");
    config.SetProperty(NHibernate.Cfg.Environment.ProxyFactoryFactoryClass, "...");
    config.SetProperty(NHibernate.Cfg.Environment.ShowSql, "true");
    config.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlKeyWords, "none");

    // filter hbm Files

    // Set reference to entity assembly
    System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(typeof(MyEntity));

    // get Resource-files
    string[] resources = assembly.GetManifestResourceNames();

    // scan through all the hbm files and filter them according to the parameter
    foreach (string hbmFile in resources)
    {
        // This filtering here could probably be done simpler, but this is easy to understand
        bool addFile = false;
        // ignore any file that does not end with .hbm.xml
        if (hbmFile.EndsWith(".hbm.xml"))
        {
            if (hbmFile.ToLower().EndsWith(".default.hbm.xml"))
            {
                if (!useOracleMapping)
                {
                    // we want that file for this SessionFactory
                    addFile = true;
                }
            }
            else if (hbmFile.ToLower().EndsWith(".oracle.hbm.xml"))
            {
                if (useOracleMapping)
                {
                    // we want that file for this SessionFactory
                    addFile = true;
                }
            }
            else
            {
                // neither default nor oracle -> we want that file no matter what
                addFile = true;
            }
            if (addFile)
            {
                using (System.IO.StreamReader sr = new System.IO.StreamReader(assembly.GetManifestResourceStream(hbmFile)))
                {
                    string resourceContent = sr.ReadToEnd();
                    config.AddXmlString(resourceContent);
                }
            }
        }
    }

    // create Sessionfactory with the files we filtered
    ISessionFactory sessionFactory = config.BuildSessionFactory();
    return sessionFactory;
}

Изменить:

Предполагая, что вы всегда можете получить доступ к знаниям, работаете ли вы в Oracle или в другом режиме, я бы обернул метод GetTop_AddressById() и сделал бы что-то вроде следующего:

public Top_Address GetTop_AddressById(IDComposite_TopAddress id)
{
    if (!oracle)
    {
        return session.CreateCriteria(DB, typeof(Top_Address))
           .Add(Restrictions.Eq("ID.TnrAddress", addressID))
           .Add(Restrictions.Eq("ID.TopIdentity.tnrId", tnrID))
           .Add(Restrictions.Eq("AddressType", 'R')) .UniqueResult<Top_Address>();
    }
    else 
    {
        return session.CreateCriteria(DB, typeof(Top_Address))
           .Add(Restrictions.Eq("TnrAddress", addressID))
           .Add(Restrictions.Eq("AddressType", 'R')) .UniqueResult<Top_Address>();
    }
}

Это может быть не красиво, но, думаю, хуже иметь два класса для одной цели. Вам понадобится подобная обертка для всех CRUD-операций, которые используют id. Конечно, вам также понадобится свойство TnrAddress в классе вашего домена.

0 голосов
/ 04 апреля 2011

Я предлагаю вам не разветвлять отображение, а вместо этого: через десериализованные метаданные отображения перед созданием SessionFacorty добавьте нужную модификацию.Я не могу дать вам код, но здесь вы можете ухватить идею: http://fabiomaulo.blogspot.com/2010/01/map-nhibernate-using-your-api.html

Или

Создайте собственный генератор идентификаторов, и здесь внутри обработайте разницу между системой двух ключей: http://nhforge.org/wikis/howtonh/creating-a-custom-id-generator-for-nhibernate.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...