I have an interface IUserLocation and a concrete type UserLocation.
When I use ICriteria, specifying the interface IUserLocation, I want NHibernate to instantiate a collection of the concrete UserLocation type.
I have created an HBM mapping file using the table per concrete type strategy (shown below). However, when I query NHibernate using ICriteria I get:
NHibernate cannot instantiate abstract class or interface MyNamespace.IUserLocation
Can anyone see why this is? (source code for the relevant bit of NHibernate here (I think))
My ICriteria:
var filter = DetachedCriteria.For<IUserLocation>()
.Add(Restrictions.Eq("UserId", userId));
return filter.GetExecutableCriteria(UoW.Session)
.List<IUserLocation>();
My mapping file:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="MyNamespace.IUserLocation,MyAssembly" abstract="true" table="IUserLocations">
<composite-id>
<key-property name="UserId" column="UserId" type="System.Guid"></key-property>
<key-many-to-one name="Location" column="LocationId" class="MyNamespace.ILocation,MyAssembly"></key-many-to-one>
</composite-id>
<union-subclass table="UserLocations" name="MyNamespace2.UserLocation,MyAssembly2">
<property name="IsAdmin" />
</union-subclass>
</class>
</hibernate-mapping>
From the documentation it looks like your mapping file should do it to me. I've never tried table per concrete class though. And I notice the examples in the NHibernate documentation for it don't use interfaces for the base class. Perhaps it's not supported?
I have used Table per concrete class, using implicit polymorphism before with a separate mapping for each subclass.
<class name="MyNamespace.UserLocation,MyAssembly" table="UserLocations">
...
</class>
Related
Let's say that I have and database table called People, and entity People. Let's say that I need a quick way to remove a subset of people from displaying everywhere in application. So I add IsDeleted column to People table, and set values in that column to 1.
In Entity Framework there's a mechanism that specifies that for instans of entities with value in column IsDeleted set to 1 shouldn't be fetched and mapping framework filters data automatically. Is is possible to achieve with NHibernate?
You can define where attribute in your class mapping.
where (optional) specify an arbitrary SQL WHERE condition to be used
when retrieving objects of this class
<class ... where="IsDeleted = 0">
If you are using Fluent NHibernate then just define this in mapping class:
Where("IsDeleted = 0");
NH's mapping by code should be similar to Fluent NHibernate's mapping.
You can create abstract class, e.g. PeopleBase, from which your People class will be derived and map your entity to it.
After that you can use discriminator like this (didn't check for correctness, but it should work):
<class name="YourNamespace.PeopleBase,YourNamespace" table="People">
// ...
<discriminator column="IsDeleted" type="boolean" />
// Properties...
// ...
<subclass name="YourNamespace.People, YourNamespace" discriminator-value="false">
</subclass>
</class>
In order to achieve what I wanted, I've created base class + two subclasses. This is the configuration:
subclasses with discriminator-value:
<subclass name="People" discriminator-value="null">
</subclass>
<subclass name="PeopleHistory" discriminator-value="not null">
<property name="MasterRowId" />
</subclass>
discriminator in base class:
<discriminator column="MasterRowId" />
I am working on a legacy code base with an existing DB schema. The existing code uses SQL and PL/SQL to execute queries on the DB. We have been tasked with making a small part of the project database-engine agnostic (at first, change everything eventually). We have chosen to use Hibernate 3.3.2.GA and "*.hbm.xml" mapping files (as opposed to annotations). Unfortunately, it is not feasible to change the existing schema because we cannot regress any legacy features.
The problem I am encountering is when I am trying to map a uni-directional, one-to-many relationship where the FK is also part of a composite PK. Here are the classes and mapping file...
CompanyEntity.java
public class CompanyEntity {
private Integer id;
private Set<CompanyNameEntity> names;
...
}
CompanyNameEntity.java
public class CompanyNameEntity implements Serializable {
private Integer id;
private String languageId;
private String name;
...
}
CompanyNameEntity.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.jboss.org/dtd/hibernate/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example">
<class name="com.example.CompanyEntity" table="COMPANY">
<id name="id" column="COMPANY_ID"/>
<set name="names" table="COMPANY_NAME" cascade="all-delete-orphan" fetch="join" batch-size="1" lazy="false">
<key column="COMPANY_ID"/>
<one-to-many entity-name="vendorName"/>
</set>
</class>
<class entity-name="companyName" name="com.example.CompanyNameEntity" table="COMPANY_NAME">
<composite-id>
<key-property name="id" column="COMPANY_ID"/>
<key-property name="languageId" column="LANGUAGE_ID"/>
</composite-id>
<property name="name" column="NAME" length="255"/>
</class>
</hibernate-mapping>
This code works just fine for SELECT and INSERT of a Company with names. I encountered a problem when I tried to update and existing record. I received a BatchUpdateException and after looking through the SQL logs I saw Hibernate was trying to do something stupid...
update COMPANY_NAME set COMPANY_ID=null where COMPANY_ID=?
Hibernate was trying to dis-associate child records before updating them. The problem is that this field is part of the PK and not-nullable. I found the quick solution to make Hibernate not do this is to add "not-null='true'" to the "key" element in the parent mapping. SO now may mapping looks like this...
CompanyNameEntity.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.jboss.org/dtd/hibernate/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example">
<class name="com.example.CompanyEntity" table="COMPANY">
<id name="id" column="COMPANY_ID"/>
<set name="names" table="COMPANY_NAME" cascade="all-delete-orphan" fetch="join" batch-size="1" lazy="false">
<key column="COMPANY_ID" not-null="true"/>
<one-to-many entity-name="vendorName"/>
</set>
</class>
<class entity-name="companyName" name="com.example.CompanyNameEntity" table="COMPANY_NAME">
<composite-id>
<key-property name="id" column="COMPANY_ID"/>
<key-property name="languageId" column="LANGUAGE_ID"/>
</composite-id>
<property name="name" column="NAME" length="255"/>
</class>
</hibernate-mapping>
This mapping gives the exception...
org.hibernate.MappingException: Repeated column in mapping for entity: companyName column: COMPANY_ID (should be mapped with insert="false" update="false")
My problem now is that I have tryed to add these attributes to the key-property element but that is not supported by the DTD. I have also tryed changing it to a key-many-to-one element but that didn't work either. So...
How can I map "insert='false' update='false'" on a composite-id key-property which is also used in a one-to-many FK?
I think the annotation you are looking for is:
public class CompanyName implements Serializable {
//...
#JoinColumn(name = "COMPANY_ID", referencedColumnName = "COMPANY_ID", insertable = false, updatable = false)
private Company company;
And you should be able to use similar mappings in a hbm.xml as shown here (in 23.4.2):
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/example-mappings.html
"Dino TW" has provided the link to the comment Hibernate Mapping Exception : Repeated column in mapping for entity which has the vital information.
The link hints to provide "inverse=true" in the set mapping, I tried it and it actually works. It is such a rare situation wherein a Set and Composite key come together. Make inverse=true, we leave the insert & update of the table with Composite key to be taken care by itself.
Below can be the required mapping,
<class name="com.example.CompanyEntity" table="COMPANY">
<id name="id" column="COMPANY_ID"/>
<set name="names" inverse="true" table="COMPANY_NAME" cascade="all-delete-orphan" fetch="join" batch-size="1" lazy="false">
<key column="COMPANY_ID" not-null="true"/>
<one-to-many entity-name="vendorName"/>
</set>
</class>
I have an NHibernate object that is a superclass (let's call it "Super"), and a subclass that inherits from it (let's say it's called "Sub").
<class name="Super" table="SuperThings">
<id name="Id" type="System.Int32" column="SuperId">
<generator class="identity" />
</id>
<joined-subclass name="Sub" table="SubThings" extends="Super" lazy="true">
<key column="SubId" />
</joined-subclass>
</class>
I have a separate class (called "Widget") with a property of type Super.
<class name="Widget" table="Widgets" lazy="true">
<id name="Id" type="System.Int32" column="NoteId">
<generator class="identity" />
</id>
<many-to-one name="SuperProperty" column="SuperId" class="SuperClass" />
</class>
When I access SuperProperty on an instance of a Widget, NHibernate attempts to lazily load it, but I get this error:
More than one row with the given identifier was found: 1, for class: Super
There is only one record in SuperThings with an id of 1, and a separate record in SubThings associated to it. After using the NHibernate Profiler and debugging my code, it looks like NHibernate is trying to instantiate an object whose type is the subclass.
Why is it doing that? Is there something wrong with how I'm thinking this should be mapped?
Obviously, this is a simplified version of what I'm actually working with. The objects I'm working with have many more properties of different types, so maybe I've left out what's actually causing the problem, but I wanted to make sure that I'm understanding things on a basic level at least.
If there is a record in SuperThings with Id=1, and one record in SubThings with SubId=1, according to your mapping you have a Sub instance persisted, so NHibernate is right when it tries to instantiate it.
If this is not what you intended, you should reread Chapter 8. Inheritance Mapping to see the alternatives.
I have what appears to be a simple mapping problem in NHibernate, however I have been struggling to find a solution to the problem for a number of days now, and would appreciate some assistance. I am using VB.NET under VS2005. My VS2005 solution structure is as follows:
Solution: PsalertsIP
Project (Assembly): Core
Folder Data (Namespace PsalertsIp.Core.Data)
Contains Interfaces for communication with repository classes
example: PsalertsEventRepo Implements IPsalertsEventRepo
Folder Domain (Namespace PsalertsIP.Core.Domain)
Contains all POCO domain objects and related interfaces
example: PsalertsEvent Implements IPsalertsEvent
Also underneath the assembly 'Core' are the NHibernate config file and the mapping file for the PsalertsEvent class, which is as follows:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Core"
namespace="Core.Domain">
<class name="PsalertsEvent" table="Source_table" lazy="true">
<id name="Id" column="Id" type="long" unsaved-value="0"
access="field.camelcase-underscore">
<generator class="native" >
<param name="sequence">My_Oracle_Sequence</param>
</generator>
</id>
<property name="Substation" column="Field1" />
<property name="BusbarId" column="Field2" />
<property name="PlantId" column="Field3" />
<property name="AlarmName" column="Field4" />
<property name="AlarmStatus" column="Field5" />
<property name="EventTime" column="Field6" />
</class>
</hibernate-mapping>
When I attempt to carry out a simple test of the NHibernate environment through NUnit (appreciate that this isn't unit testing, however needed a simple vehicle to test the NHibernate setup), the test fails, and I observe the following output in NUnit:
PsalertsIp.Tests.Data.PSALERTSEventRepoTests (TestFixtureSetUp):
System.TypeInitializationException : The type initializer for 'Nested' threw an exception.
----> NHibernate.MappingException : Could not compile the mapping document: PsalertsEvent.hbm.xml
----> NHibernate.MappingException : persistent class Core.Domain.PsalertsEvent, Core not found
----> System.TypeLoadException : Could not load type 'Core.Domain.PsalertsEvent' from assembly 'Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
I suspect that the problem may be to do with the structure of the solution in VS2005, however I have tested multiple different assembly/namespace permutations to no avail.
I think you need to change the namespace attribute on the hibernate-mapping element to "PsalertsIP.Core.Domain" (as you've specified above).
Also ensure the assembly attribute on the hibernate-mapping element specifies the full assembly name of your project (right-click project -> Properties -> Application tab).
hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Core"
namespace="PsalertsIP.Core.Domain">
I'm using the NHibernate criteria API to load some entities from my database. My domain consists of an abstract class, Animal, which the concrete Cat class inherits from. Another concrete class, Tiger, inherits from Cat.
I need to load all Cats from the database, so I'm doing the following-
ICriteria criteria = session.CreateCriteria(typeof(Cat));
return criteria.List<Cat>();
What I'm finding is that both Cats and Tigers are returned by the query instead of just Cats. This makes sense, as a Tiger is a Cat. But in this particular case I only want Cats, and not the additional Tigers.
Does anyone know how I can achieve this?
This is actually a Feature. But i think you can do what you want by mixing "table per class hierarchy" with "table per subclass". Therefor you need a Discriminator Column on which you can perform the query on. This would look like the following:
<class name="Cat" table="Cat">
<id name="Id" type="Int64" column="ID">
<generator class="native"/>
</id>
<discriminator column="TYPE" type="string"/>
<subclass name="Tiger" discriminator-value="TIGER">
<join table="Tiger">
<property name="..." column="..."/>
</join>
</subclass>
</class>
After this you should be able to query on the discriminator-column like this:
session.CreateCriteria<Cat>()
.Add(Restrictions.IsNull("TYPE"))
.List<Cat>();