I'm pretty new to Nhibernate, so apologies for a long - winded description
I suspect that changing the structure of the legacy DB is probably the best option, but I want to try and get NHibernate to deal with it.
Basically the structure is this an EndPoint has an address and a contact. Endpoint is stored in a table with a composite ID (Address ID, Contact ID).
I'm having a problem when cascade saving an address, which has a custom ID generator - address ID are of the form "ADR000234" to fit in with a legacy DB structure.
The custom ID generator includes a query, and when I save the address as part of an endpoint, I get a stack overflow. When debugging the cursor gets to line that evaluates the query( var maxAddressID..), then jumps back to start of the method, and keeps on doing this until it raises a stack overflow.
Here's my generator class
public class AddressIdGenerator : IIdentifierGenerator
{
public object Generate(ISessionImplementor session, object obj)
{
var castAsSession = (ISession)session;
var allAddresses = castAsSession.CreateQuery("select max(Code) from Address a");
var maxAddressID = (string)allAddresses.List()[0];
var previousNumber = int.Parse(maxAddressID.Substring(3, 6));
return GetNewId("ADR", previousNumber);
}
private string GetNewId(string prefix, int number)
{
return prefix + (number + 1).ToString().PadLeft(6, '0');
}
}
Here's my mapping foe the EndPoint CLass
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="DataClasses"
namespace="DataClasses">
<class name="EndPoint" table="[Addresses_Contacts]">
<composite-id>
<key-property name ="Address" column ="[Address ID]" type="string" />
<key-property name ="Contact" column ="[Contact ID]" type="string"/>
</composite-id>
<many-to-one name="Address" class="DataClasses.Address, DataClasses" cascade="save-update"/>
<many-to-one name="Contact" class="DataClasses.Contact, DataClasses" cascade="save-update"/>
</class>
</hibernate-mapping>
and the mapping for address:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DataClasses" namespace="DataClasses">
<class name="Address" table="[Lookup Addresses]" >
<id name="Code" column="ID" type="string">
<generator class="Nhibernate.AddressIdGenerator, Nhibernate" />
</id>
<property name="OrganisationName" column="[Name of Organisation]"/>
<property name="StreetAddress1" column="[Park/centre/estate]" />
<property name="StreetAddress2" column="[Street Name]" />
<property name="Town" column="[Town/City]" />
<property name="State" column="[Region/ State]" />
<property name="PostCode" column="[Postal/ Area Code]" />
<property name="District" column="[Local District]" />
<property name="Airport" column="[Airport code]" />
<many-to-one name="Country" class="DataClasses.Country, DataClasses" column ="[Country Code]"/>
</class>
</hibernate-mapping>
If I try to save and Address on its own, it works fine, the ID is generated with no problems.
Also if I remove the Address and Contact properties from the mapping (but not from the composite ID), and save the Address and Contact before saving the Endpoint, it's fine too.
It seems to me that when I'm doing the cascade save, for some reason it can't run other queries during the process, but rather than throwing an exception it's behaving strangely (restarting the method again and again). I haven't ever seen a C# method do this before. I'd love to know if anyone has an idea of how to fix this.
i'm thinking that the problem resides that you are doing an nh-query inside the Generator and you are querying the entity type you want to save.
The generator is called not when you call Save() but whenever there is a need to flush/commit the data. Now, Save() places the entity on a action-queue of things to do. When you call CreateQuery/CreateCriteria and request the result via List()/UniqueResult() nhibernate's engine detects that you made a Save() request on an entity and so will try to flush/commit the entity first (and thus call the generator) and then perform the query and thus start an infinite loop; The logic is such that since you called Save() and then you are querying that class type you will want the result set to include the Saved object.
So, replace your Nh-query with a native SqlCommand (there is a possibility CreateSqlQuery will work too) and i think your problem will be solved.
please update the owner of the relation in the EndPoint xml Mapping.
please mention inverse="true"in the many to one proper for address and contacts.
thanks
Related
I'm using Nhibernate Envers and I want Envers to save audit info on a separate database to keep things cleaner/more maintainable.
I'm using this fluent configuration:
var enversCfg = new NHibernate.Envers.Configuration.Fluent.FluentConfiguration()
enversCfg.Audit(GetDomainEntities())
nhCfg.SetEnversProperty(ConfigurationKey.DefaultCatalog, "nhibernate_testAU")
but when I try to create the schema, I get a HibernateException (The specified schema name "nhibernate_testAU" either does not exist or you do not have permission to use it.)
for what it's worth, my backend is SQL Server 2005
In addition to actually creating the database, I specified the schema to "dbo".
c.SetEnversProperty(ConfigurationKey.DefaultSchema, "dbo");
c.SetEnversProperty(ConfigurationKey.DefaultCatalog, "MyCatalog_Audit");
Also, I have my own RevistionEntity class, so, I needed to add the catalog and schema to my hbm.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyAssembly" namespace="MyNamespace">
<class name="MyRevisionEntity" table="REVINFO" catalog="MyCatalog_Audit" schema="dbo">
<id name="Id" column="MyRevisionEntityId">
<generator class="identity"/>
</id>
<property name="AuditDate"></property>
<property name="UserName"></property>
<!--modifiedEntityNames-->
<set name="ModifiedEntityNames" table="REVCHANGES" catalog="MyCatalog_Audit" schema="dbo">
<key column="REV"/>
<element column="ENTITYNAME" type="string"/>
</set>
</class>
</hibernate-mapping>
HTH
Chuck
You need to manually create the catalog/database. AFAIK - NH's SchemaExport don't create database/catalog (or schemas) for you.
I'm having some issues when running a named query on NHibernate. The class I'm setting as the return value is Organization - not abstract -, and I have a second class (which inherits from this one) called FullOrganization. I have a table per concrete class schema and everything else is working just fine, but I keep getting an exception when running the named query (which BTW doesn't provide any details).
The mapping is close to the following:
<hibernate-mapping
xmlns="urn:nhibernate-mapping-2.2"
namespace="XXX"
assembly="XXX"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:nhibernate-mapping-2.2 ..\nhibernate-mapping.xsd" auto-import="false">
<class name="Organization" table="Organizations" lazy="false">
<id name="Identity" column="Id">
<generator class="identity"/>
</id>
<property name="Name" column="Name" />
<many-to-one name="OrganizationType" column="OrganizationTypeId"/>
...
<joined-subclass name="FullOrganization" table="FullOrganizations" lazy="false">
<key column="OrganizationId"/>
...
</joined-subclass>
</class>
And the named query goes something like:
<sql-query name="OrganizationSearch" read-only="true" cacheable="false">
<return class="Organization" />
<![CDATA[
SELECT *
FROM Organizations o
INNER JOIN OrganizationTypes ot ON o.OrganizationTypeId = ot.Id
LEFT JOIN FullOrganizations fo ON o.Id = fo.OrganizationId
WHERE
// Several Filters Here
]]>
How should I map the query results? Please take into account that returned objects could be instances of Organization or FullOrganization.
If I am understanding the question correctly, do you have two methods for the search? i.e.
SearchOrganisations and SearchFullOrganisations
If answer is yes then I would remove the
<return class="Organization" />
and in code you can use the AliasToBeanResultTransformer
so one method
return Session
.GetNamedQuery("OrganizationSearch")
.SetString("Param1", param1)
.SetResultTransformer(new AliasToBeanResultTransformer(typeof(Organization )))
.List<Organization >();
and also
return Session
.GetNamedQuery("OrganizationSearch")
.SetString("Param1", param1)
.SetResultTransformer(new AliasToBeanResultTransformer(typeof(FullOrganization )))
.List<FullOrganization >();
If you have one method then you can set a switch to apply the desired SetResultTransformer
Need help with a simple NHibernate relationship...
Tables/Classes
Request
-------
RequestId
Title
…
Keywords
-------
RequestID (key)
Keyword (key)
Request mapping file
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="CR.Model" assembly="CR">
<class name="CR.Model.Request, CR table="[dbo].[Request]" lazy="true">
<id name="Id" column="[RequestID]">
<generator class="native" />
</id>
<property name="RequestorID" column="[RequestorID]" />
<property name="RequestorOther" column="[RequestorOther]" />
…
Keyword??
</class>
</hibernate-mapping>
How do I simply map multiple keywords to a request? I don't need another mapping file for the keyword class, do I?
It's be great if I could not only get the associated keywords, but add them too...
You'll need a set (or some other kind of collection mapping, but I think a set is the best-fit).
check this
Currently I have database with the following associations:
One Client to Many Intakes
One Intake to Many CaseManagements
One CaseManagement to Many Interventions
Client, Intake, CaseManagement are single classes per table
Intervention is a class-hierarchy-per-table.
Currently, if I do something like this:
var client = new Client();
clientRepo.Add(client);
var intake = new Intake();
client.Add(intake);
var caseMan = new CaseManagement();
intake.Add(caseMan);
clientRepo.Update(client);
Everything works fine, and NHibernate creates a Client, then an Intake and then a CaseManagement in the database (all appropriately linked).
However, if do the following:
var client = new Client();
clientRepo.Add(client);
var intake = new Intake();
client.Add(intake);
var caseMan = new CaseManagement();
intake.Add(caseMan);
var intervention = new SubIntervention();
caseMan.Add(intervention);
clientRepo.Update(client);
It screwed up and runs the following SQL:
INSERT INTO TblClient ... (*)
INSERT INTO TblIntake ...
INSERT INTO TblCaseManagement ...
INSERT INTO TblClient ... (*)
Where the starred lines are identical. I have no clue why using inheritance is causing this.
Here is my mapping for my Intake class (which is pretty much the same as Client and CaseManagement).
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="WebTracker4"
namespace="WebTracker4.Domain">
<!-- Class Mapping -->
<class name="Intake" table="TblIntake">
<!-- Id -->
<id name="Id" column="IntakeId">
<generator class="sequence">
<param name="sequence">IntakeSequence</param>
</generator>
</id>
<!-- TCN -->
<version name="Tcn" column="IntakeTcn" type="Int64" />
<!-- Client -->
<many-to-one name="Client" column="ClientId" class="Client" />
<!-- Case Management -->
<bag name="CaseManagements" inverse="true" cascade="all" table="TblCaseManage" order-by="CaseManageId desc">
<key column="IntakeId" />
<one-to-many class="CaseManagement" />
</bag>
<!-- Properties -->
...
</class>
</hibernate-mapping>
Here's my mapping for the Intervention class hierarchy:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="WebTracker4"
namespace="WebTracker4.Domain.Interventions">
<!-- Class Mapping -->
<class name="Intervention" table="TblIntervention" abstract="true">
<!-- Id -->
<id name="Id" column="InterventionId">
<generator class="sequence">
<param name="sequence">InterventionSequence</param>
</generator>
</id>
<!-- Discriminator -->
<!-- This is used so that we can have Intervention subclasses. -->
<discriminator column="InterventionType" type="String" />
<!-- TCN -->
<version name="Tcn" column="InterventionTcn" type="Int64" />
<!-- Case Management -->
<many-to-one name="CaseManagement" column="CaseManageId" class="WebTracker4.Domain.CaseManagement, WebTracker4" />
<!-- Properties -->
....
<!-- Assessment Subclass -->
<subclass name="SubIntervention" discriminator-value="Sub">
...
</subclass>
</class>
What am I missing that is making it try to re-add the Client entity? What also may be of note is that if I screw up the column names in the subclass, NHibernate doesn't say anything.
This is driving me crazy, please help :)
I figured out the problem. It was because I was using the InterventionType column as both the discriminator and a property. I found that if I changed
<discriminator column="InterventionType" type="String" />
into
<discriminator column="InterventionType" type="String" insert="false" />
the problem went away.
I think it's because you're basically telling NHibernate not to manage the discriminator column.
I couldn't figure out why NHibernate is doing this.
Here's part of my table in MS SQL
CREATE TABLE Person (
nameFamily text,
nameGiven text
)
Here's Person.hbm.xml
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Infosci.Dpd.Model.Person,Infosci.Dpd.Model" table="Person" lazy="true">
<property type="string" name="NameGiven" column="nameGiven" />
<property type="string" name="NameFamily" column="nameFamily" />
</class>
</hibernate>
And in my Person.cs I name my property like the following
string NameFamily
{
get ;
set ;
}
string NameGiven
{
get ;
set ;
}
Then I try to create a record using the following code
ISession session = NHibernateHelper.GetCurrentSession();
Person person = new Person();
person.NameFamily = "lee";
session.Save(person);
And what's weird is when I try do execute it, NHibernate actually change the column name by doing the following
NHibernate: INSERT INTO Person (name_family,name_given.......
So here NHibernate chagne my column name from NameFamily to name_family although I did specify the column name.
Is this a NHibernate bug? Or is there any configuration I need to do?
And here's my hibernate config file
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Data Source=xxxxxxx;Initial Catalog=xx;Integrated Security=True;Pooling=False;User ID=xxxx;Password=xxxx</property>
<property name="show_sql">true</property>
</session-factory>
</hibernate-configuration>
And this is how I create the sessionFactory
sessionFactory = new `Configuration().Configure("hibernate.cfg.xml").
SetNamingStrategy(ImprovedNamingStrategy.Instance).
AddFile("Person.hbm.xml").
BuildSessionFactory();`
That's a "feature" of SetNamingStrategy(ImprovedNamingStrategy.Instance)
It will add underscores before each uppercase letter in mixed-cased names.
Not sure how you disable it (me not being a nhibernate user)
What I end up doing is just use the default naming strategy or SetNamingStrategy(DefaultNamingStrategy.Instance)
the default naming strategy will leave mix-cased name untouched