|
Posted on 2006-07-04 10:29 东人EP 阅读(1453) 评论(0) 编辑 收藏 引用 所属分类: .NET
NHibernate - Relational Persistence for Idiomatic .NET
NHibernate Reference Documentation
Working with object-oriented software and a relational database can be cumbersome and time consuming in today's enterprise environments. NHibernate is an object/relational mapping tool for .NET environments. The term object/relational mapping (ORM) refers to the technique of mapping a data representation from an object model to a relational data model with a SQL-based schema.
NHibernate not only takes care of the mapping from .NET classes to database tables (and from .NET data types to SQL data types), but also provides data query and retrieval facilities and can significantly reduce development time otherwise spent with manual data handling in SQL and ADO.NET.
NHibernate's goal is to relieve the developer from 95 percent of common data persistence related programming tasks. NHibernate may not be the best solution for data-centric applications that only use stored-procedures to implement the business logic in the database, it is most useful with object-oriented domain models and business logic in the .NET-based middle-tier. However, NHibernate can certainly help you to remove or encapsulate vendor-specific SQL code and will help with the common task of result set translation from a tabular representation to a graph of objects.
If you are new to NHibernate and Object/Relational Mapping or even .NET, please follow these steps:
-
Read Chapter 1, Architecture to understand the environments where NHibernate can be used.
-
FAQs are answered on the NHibernate website.
If you have questions, use the user forum linked on the NHibernate website. We also provide a JIRA issue trackings system for bug reports and feature requests. If you are interested in the development of NHibernate, join the developer mailing list. If you are interested in translating this documentation into your language, contact us on the developer mailing list.
A (very) high-level view of the NHibernate architecture:
This diagram shows NHibernate using the database and configuration data to provide persistence services (and persistent objects) to the application.
We would like to show a more detailed view of the runtime architecture. Unfortunately, NHibernate is flexible and supports several approaches. We will show the two extremes. The "lite" architecture has the application provide its own ADO.NET connections and manage its own transactions. This approach uses a minimal subset of NHibernate's APIs:
The "full cream" architecture abstracts the application away from the underlying ADO.NET API and lets NHibernate take care of the details.
Heres some definitions of the objects in the diagrams:
-
SessionFactory (NHibernate.ISessionFactory)
-
A threadsafe (immutable) cache of compiled mappings for a single database. A factory for Session and a client of ConnectionProvider. Might hold an optional (second-level) cache of data that is reusable between transactions, at a process- or cluster-level.
-
Session (NHibernate.ISession)
-
A single-threaded, short-lived object representing a conversation between the application and the persistent store. Wraps an ADO.NET connection. Factory for Transaction. Holds a mandatory (first-level) cache of persistent objects, used when navigating the object graph or looking up objects by identifier.
-
Persistent Objects and Collections
-
Short-lived, single threaded objects containing persistent state and business function. These might be ordinary objects, the only special thing about them is that they are currently associated with (exactly one) Session. As soon as the Session is closed, they will be detached and free to use in any application layer (e.g. directly as data transfer objects to and from presentation).
-
Transient Objects and Collections
-
Instances of persistent classes that are not currently associated with a Session. They may have been instantiated by the application and not (yet) persisted or they may have been instantiated by a closed Session.
-
Transaction (NHibernate.ITransaction)
-
(Optional) A single-threaded, short-lived object used by the application to specify atomic units of work. Abstracts application from underlying ADO.NET transaction. A Session might span several Transactions in some cases.
-
ConnectionProvider (NHibernate.Connection.ConnectionProvider)
-
(Optional) A factory for ADO.NET connections. Abstracts application from underlying IDbConnection. Not exposed to application, but can be extended/implemented by the developer.
-
TransactionFactory (net.sf.hibernate.TransactionFactory)
-
(Optional) A factory for Transaction instances. Not exposed to the application, but can be extended/implemented by the developer.
Given a "lite" architecture, the application bypasses the Transaction/TransactionFactory and/or ConnectionProvider APIs to talk to ADO.NET directly.
Chapter 2. ISessionFactory Configuration
Because NHibernate is designed to operate in many different environments, there are a large number of configuration parameters. Fortunately, most have sensible default values. The NHibernate.Test.dll contains an example of the hibernate properties in an app.config file that shows the various options.
2.1. Programmatic Configuration
An instance of NHibernate.Cfg.Configuration represents an entire set of mappings of an application's .NET types to a SQL database. The Configuration is used to build a (immutable) ISessionFactory. The mappings are compiled from various XML mapping files.
You may obtain a Configuration instance by instantiating it directly. Heres an example of setting up a datastore from mappings defined in two XML configuration files (in the same folder as the exe):
Configuration cfg = new Configuration()
.AddXmlFile("Item.hbm.xml")
.AddXmlFile("Bid.hbm.xml");
An alternative (usually better) way is to let NHibernate load a mapping file using GetManifestResourceStream():
Configuration cfg = new Configuration()
.AddClass( typeof(NHibernate.Auction.Item) )
.AddClass( typeof(NHibernate.Auction.Bid) );
Then NHibernate will look for mapping files named NHibernate.Auction.Item.hbm.xml and NHibernate.Auction.Bid.hbm.xml as an Embedded Resource in the Assembly those types are contained in. This approach eliminates any hardcoded filenames.
Another alternative (probably best) way is to let NHibernate load all of the mapping files contained in an Assembly:
Configuration cfg = new Configuration()
.AddAssembly( "NHibernate.Auction" );
Then NHibernate will look through the Assembly for any resources that end with hbm.xml. This approach eliminates any hardcoded filenames and ensures the mapping files in the Assembly get added.
If VisualStudio.NET or NAnt is used to build the Assembly then make sure that the hbm.xml files are added as Embedded Resources.
A Configuration also specifies various optional properties:
Hashtable props = new Hashtable();
...
Configuration cfg = new Configuration()
.AddClass( typeof(NHibernate.Auction.Item) )
.AddClass( typeof(NHibernate.Auction.Bid) );
cfg.Properties = props;
A Configuration is intended as a configuration-time object, to be discarded once a SessionFactory is built.
2.2. Obtaining an ISessionFactory
When all mappings have been parsed by the Configuration, the application must obtain a factory for ISession instances. This factory is intended to be shared by all application threads:
ISessionFactory sessions = cfg.BuildSessionFactory();
However, NHibernate does allow your application to instantiate more than one ISessionFactory. This is useful if you are using more than one database.
2.3. User provided ADO.NET connection
An ISessionFactory may open an ISession on a user-provided ADO.NET connection. This design choice frees the application to obtain ADO.NET connections wherever it pleases:
IDbConnection conn = myapp.GetOpenConnection();
ISession session = sessions.OpenSession(conn);
// do some data access work
The application must be careful not to open two concurrent ISessions on the same ADO.NET connection!
2.4. NHibernate provided ADO connection
Alternatively, you can have the ISessionFactory open connections for you. The ISessionFactory must be provided with ADO connection properties in one of the following ways:
-
Pass an instance of IDictionary to Configuration.Properties.
-
Add the properties to a configuration section that is a System.Configuration.NameValueSectionHandler named nhibernate.
-
Include <property> elements in hibernate.cfg.xml (discussed later).
If you take this approach, opening an ISession is as simple as:
ISession session = sessions.OpenSession(); // open a new Session
// do some data access work, an ADO connection will be used on demand
All NHibernate property names and semantics are defined on the class NHibernate.Cfg.Environment. We will now describe the most important settings for ADO connection configuration.
NHibernate will obtain connections using the ADO.NET Data Provider if you set the following properties:
Table 2.1. NHibernate ADO.NET Properties
Property name |
Purpose |
hibernate.connection.provider_class
|
The type of a custom IConnectionProvider.
eg.full.classname.of.ConnectionProvider if the Provider is built into NHibernate, or full.classname.of.ConnectionProvider, assembly if using an implementation of IConnectionProvider not included in NHibernate. |
hibernate.connection.driver_class
|
The type of a custom IDriver.
full.classname.of.Driver if the Driver is built into NHibernate, or full.classname.of.Driver, assembly if using an implementation of IDriver not included in NHibernate. |
hibernate.connection.connection_string
|
Connection string to use to obtain the connection. |
hibernate.connection.isolation
|
Set the ADO.NET transaction isolation level. Check System.Data.IsolationLevel for meaningful values and the database's documentation to ensure that level is supported.
eg.Chaos, ReadCommitted, ReadUncommitted, RepeatableRead, Serializable, Unspecified |
This is an example of how to specify the database connection properties inside a web.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<nhibernate>
<add
key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider"
/>
<add
key="hibernate.connection.driver_class"
value="NHibernate.Driver.SqlClientDriver"
/>
<add
key="hibernate.connection.connection_string"
value="Server=127.0.0.1;Initial Catalog=thedatabase;Integrated Security=SSPI"
/>
<add
key="hibernate.connection.isolation"
value="ReadCommitted"
/>
<add
key="hibernate.dialect"
value="NHibernate.Dialect.MsSql2000Dialect"
/>
</nhibernate>
<!-- log4net (required by NHibernate) and other app specific config follows -->
</configuration>
2.5. Optional configuration properties
There are a number of other properties that control the behavior of NHibernate at runtime. All are optional and have reasonable default values.
Table 2.2. NHibernate Configuration Properties
Property name |
Purpose |
hibernate.dialect
|
The classname of a NHibernate Dialect - enables certain platform dependent features.
eg.full.classname.of.Dialect if a Dialect built into NHibernate, or full.classname.of.Dialect, assembly if using an implementation of Dialect not included in NHibernate. |
hibernate.default_schema
|
Qualify unqualified tablenames with the given schema/tablespace in generated SQL.
eg.SCHEMA_NAME |
hibernate.prepare_sql
|
Enables preparing of sql statements.
eg.true | false |
hibernate.session_factory_name
|
The SessionFactory will be automatically bound to this name after it has been created.
eg.some.name |
hibernate.use_outer_join
|
Enables outer join fetching.
eg.true | false |
hibernate.cache.provider_class
|
The classname of a custom CacheProvider.
eg.full.classname.of.CacheProvider if the ICacheProvider is built into NHibernate, or full.classname.of.CacheProvider, assembly if using an implementation of ICacheProvider not included in NHibernate. |
hibernate.query.substitutions
|
Mapping from tokens in NHibernate queries to SQL tokens (tokens might be function or literal names, for example).
eg.hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC |
You should always set the hibernate.dialect property to the correct NHibernate.Dialect.Dialect subclass for your database. This is not strictly essential unless you wish to use native or sequence primary key generation or pessimistic locking (with, eg. ISession.Lock() or IQuery.SetLockMode()). However, if you specify a dialect, NHibernate will use sensible defaults for some of the other properties listed above, saving you the effort of specifying them manually.
Table 2.3. NHibernate SQL Dialects (hibernate.dialect)
RDBMS |
Dialect |
DB2 |
NHibernate.Dialect.DB2Dialect
|
PostgreSQL |
NHibernate.Dialect.PostgreSQLDialect
|
MySQL |
NHibernate.Dialect.MySQLDialect
|
Oracle (any version) |
NHibernate.Dialect.OracleDialect
|
Oracle 9/10g |
NHibernate.Dialect.Oracle9Dialect
|
Sybase |
NHibernate.Dialect.SybaseDialect
|
Microsoft SQL Server 2000 |
NHibernate.Dialect.MsSql2000Dialect
|
Microsoft SQL Server 7 |
NHibernate.Dialect.MsSql7Dialect
|
Firebird |
NHibernate.Dialect.FirebirdDialect
|
2.5.2. Outer Join Fetching
If your database supports ANSI or Oracle style outer joins, outer join fetching might increase performance by limiting the number of round trips to and from the database (at the cost of possibly more work performed by the database itself). Outer join fetching allows a graph of objects connected by many-to-one, one-to-many or one-to-one associations to be retrieved in a single SQL SELECT.
By default, the fetched graph when loading an objects ends at leaf objects, collections, objects with proxies, or where circularities occur.
For a particular association, fetching may be enabled or disabled (and the default behaviour overridden) by setting the outer-join attribute in the XML mapping.
Outer join fetching may be disabled globally by setting the property hibernate.use_outer_join to false. A setting of true enables outer join fetching for all one-to-one and many-to-one associations, which are, also by default, set to auto outer join. However, one-to-many associations and collections are never fetched with an outer-join, unless explicitly declared for each particular association. This behavior can also be overriden at runtime with Hibernate queries.
2.5.3. Custom CacheProvider
You may integrate a second-level cache system by implementing the interface NHibernate.Cache.ICacheProvider. You may select the custom implementation by setting hibernate.cache.provider_class.
2.5.4. Query Language Substitution
You may define new Hibernate query tokens using hibernate.query.substitutions. For example:
hibernate.query.substitutions true=1, false=0
would cause the tokens true and false to be translated to integer literals in the generated SQL.
hibernate.query.substitutions toLowercase=LOWER
would allow you to rename the SQL LOWER function.
NHibernate logs various events using Apache log4net.
You may download log4net from http://logging.apache.org/log4net/. To use log4net you will need a log4net configuration section in the app.config or web.config. An example of the configuration section is distributed with NHibernate in the src/NHibernate.Test project.
We strongly recommend that you familiarize yourself with NHibernate's log messages. A lot of work has been put into making the NHibernate log as detailed as possible, without making it unreadable. It is an essential troubleshooting device.
Chapter 3. Basic O/R Mapping
Object/relational mappings are defined in an XML document. The mapping document is designed to be readable and hand-editable. The mapping language is .NET-centric, meaning that mappings are constructed around persistent class declarations, not table declarations.
Note that, even though many Hibernate users choose to define XML mappings be hand, a number of tools exist to generate the mapping document, including XDoclet, Middlegen and AndroMDA.
Lets kick off with an example mapping:
<?xml version="1.0" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="Eg.Cat, Eg" table="CATS" discriminator-value="C">
<id name="Id" column="uid" type="Int64">
<generator class="hilo"/>
</id>
<discriminator column="subclass" type="Char"/>
<property name="Birthdate" type="Date"/>
<property name="Color" not-null="true"/>
<property name="Sex" not-null="true" update="false"/>
<property name="Weight"/>
<many-to-one name="Mate" column="mate_id"/>
<set name="Kittens">
<key column="mother_id"/>
<one-to-many class="Eg.Cat, Eg"/>
</set>
<subclass name="Eg.DomesticCat, Eg" discriminator-value="D">
<property name="Name" type="String"/>
</subclass>
</class>
<class name="Eg.Dog, Eg">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>
We will now discuss the content of the mapping document. We will only describe the document elements and attributes that are used by Hibernate at runtime. The mapping document also contains some extra optional attributes and elements that affect the database schemas exported by the schema export tool. (For example the not-null attribute.)
All XML mappings have to use the nhibernate-mapping-2.0 schema. The actual schema may be found in the NHibernate source directory, or as an Embedded Resource in NHibernate.dll. NHibernate will always use the Embedded Resource as the source for the schema.
To get intellisense while working with the hibernate-mapping xml inside of VisualStudio.NET you should copy the schema to the folder C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml
This element has four optional attributes. The schema attribute specifies that tables referred to by this mapping belong to the named schema. If specified, tablenames will be qualified by the given schema name. If missing, tablenames will be unqualified. The default-cascade attribute specifies what cascade style should be assumed for properties and collections which do not specify a cascade attribute. The auto-import attribute lets us use unqualified class names in the query language, by default. The default-access attribute tells us how to access property values.
<hibernate-mapping
schema="schemaName" (1)
default-cascade="none|save-update" (2)
auto-import="true|false" (3)
default-access="property|field|nosetter|ClassName" (4)
>
(1)
|
schema (optional): The name of a database schema.
|
(2)
|
default-cascade (optional - defaults to none): A default cascade style.
|
(3)
|
auto-import (optional - defaults to true): Specifies whether we can use unqualified class names (of classes in this mapping) in the query language.
|
(4)
|
default-access (optional - defaults to property): The strategy NHibernate should use for accessing the property value.
|
If you have two persistent classes with the same (unqualified) name, you should set auto-import="false". NHibernate will throw an exception if you attempt to assign two classes to the same "imported" name.
You may declare a persistent class using the class element:
<class
name="ClassName" (1)
table="tableName"(2)
discriminator-value="discriminator_value"(3)
mutable="true|false"(4)
schema="owner"(5)
proxy="ProxyInterface"(6)
dynamic-update="true|false"(7)
dynamic-insert="true|false"(8)
polymorphism="implicit|explicit"(9)
where="arbitrary sql where condition"(10)
persister="PersisterClass"(11)
/>
(1)
|
name: The fully qualified .NET Type name of the persistent class (or interface).
|
(2)
|
table: The name of its database table.
|
(3)
|
discriminator-value (optional - defaults to the class name): A value that distiguishes individual subclasses, used for polymorphic behaviour.
|
(4)
|
mutable (optional, defaults to true): Specifies that instances of the class are (not) mutable.
|
(5)
|
schema (optional): Override the schema name specified by the root <hibernate-mapping> element.
|
(6)
|
proxy (optional): Specifies an interface to use for lazy initializing proxies. You may specify the name of the class itself as long as all Properties are virtual.
|
(7)
|
dynamic-update (optional, defaults to false): Specifies that UPDATE SQL should be generated at runtime and contain only those columns whose values have changed.
|
(8)
|
dynamic-insert (optional, defaults to false): Specifies that INSERT SQL should be generated at runtime and contain only the columns whose values are not null.
|
(9)
|
polymorphism (optional, defaults to implicit): Determines whether implicit or explicit query polymorphism is used.
|
(10)
|
where (optional) specify an arbitrary SQL WHERE condition to be used when retrieving objects of this class
|
(11)
|
persister (optional): Specifies a custom IClassPersister.
|
It is perfectly acceptable for the named persistent class to be an interface. You would then declare implementing classes of that interface using the <subclass> element. You may persist any inner class. You should specify the class name using the standard form ie. Eg.Foo+Bar.
Immutable classes, mutable="false", may not be updated or deleted by the application. This allows NHibernate to make some minor performance optimizations.
The optional proxy attribute enables lazy initialization of persistent instances of the class. NHibernate will initially return proxies generated by Castle.DynamicProxy which implement the named interface or extend the class. The actual persistent object will be loaded when a method of the proxy is invoked. See "Proxies for Lazy Initialization" below.
Implicit
polymorphism means that instances of the class will be returned by a query that names any superclass or implemented interface or the class and that instances of any subclass of the class will be returned by a query that names the class itself. Explicit polymorphism means that class instances will be returned only be queries that explicitly name that class and that queries that name the class will return only instances of subclasses mapped inside this <class> declaration as a <subclass> or <joined-subclass>. For most purposes the default, polymorphism="implicit", is appropriate. Explicit polymorphism is useful when two different classes are mapped to the same table (this allows a "lightweight" class that contains a subset of the table columns).
The persister attribute lets you customize the persistence strategy used for the class. You may, for example, specify your own subclass of NHibernate.Persister.EntityPersister or you might even provide a completely new implementation of the interface NHibernate.Persister.IClassPersister that implements persistence via, for example, stored procedure calls, serialization to flat files or LDAP. See NHibernate.DomainModel.CustomPersister for a simple example (of "persistence" to a Hashtable).
Note that the dynamic-update and dynamic-insert settings are not inherited by subclasses and so may also be specified on the <subclass> or <joined-subclass> elements. These settings may increase performance in some cases, but might actually decrease performance in others. Use judiciously.
Mapped classes must declare the primary key column of the database table. Most classes will also have a Property holding the unique identifier of an instance. The <id> element defines the mapping from that property to the primary key column.
<id
name="propertyName" (1)
type="typename" (2)
column="column_name" (3)
unsaved-value="any|none|null|id_value" (4)
access="field|property|nosetter|ClassName"> (5)
<generator class="generatorClass"/>
</id>
(1)
|
name (optional): The name of the identifier property.
|
(2)
|
type (optional): A name that indicates the NHibernate type.
|
(3)
|
column (optional - defaults to the property name): The name of the primary key column.
|
(4)
|
unsaved-value (optional - defaults to null): An identifier property value that indicates that an instance is newly instantiated (unsaved), distinguishing it from transient instances that were saved or loaded in a previous session.
|
(5)
|
access (optional - defaults to property): The strategy NHibernate should use for accessing the property value.
|
If the name attribute is missing, it is assumed that the class has no identifier property.
The unsaved-value attribute is important! If the identfier property of your class does not default to null, then you should specify the actual default. This is especially important when using a System.ValueType such as System.Int32 or System.Guid as your <id> property. Make sure to explicity set this attribute because System.ValueType objects can not be null.
There is an alternative <composite-id> declaration to allow access to legacy data with composite keys. We strongly discourage its use for anything else.
The required <generator> child element names a .NET type used to generate unique identifiers for instances of the persistent class. If any parameters are required to configure or initialize the generator instance, they are passed using the <param> element.
<id name="Id" type="Int64" column="uid" unsaved-value="0">
<generator class="NHibernate.Id.TableHiLoGenerator">
<param name="table">uid_table</param>
<param name="column">next_hi_value_column</param>
</generator>
</id>
All generators implement the interface NHibernate.Id.IdentifierGenerator. This is a very simple interface; some applications may choose to provide their own specialized implementations. However, NHibernate provides a range of built-in implementations. There are shortcut names for the built-in generators:
-
identity
-
supports identity columns in DB2, MySQL, MS SQL Server, Sybase and HypersonicSQL. The returned identifier is of type Int64, Int32 or Int16.
-
sequence
-
uses a sequence in DB2, PostgreSQL, Oracle. The returned identifier is of type Int64, Int32 or Int16
-
hilo
-
uses a hi/lo algorithm to efficiently generate identifiers of type Int64, Int32 or Int16, given a table and column (by default hibernate_unique_key and next respectively) as a source of hi values. The hi/lo algorithm generates identifiers that are unique only for a particular database.
-
seqhilo
-
uses a hi/lo algorithm to efficiently generate identifiers of type Int64, Int32 or Int16, given a named database sequence.
-
uuid.hex
-
uses System.Guid and its ToString(string format) method to generate identifiers of type string. The length of the string returned depends on the configured format.
-
uuid.string
-
uses a new System.Guid to create a byte[] that is converted to a string.
-
guid
-
uses a new System.Guid as the identifier.
-
guid.comb
-
uses the algorithm to generate a new System.Guid described by Jimmy Nilsson in the article http://www.informit.com/articles/article.asp?p=25862.
-
native
-
picks identity, sequence or hilo depending upon the capabilities of the underlying database.
-
assigned
-
lets the application to assign an identifier to the object before save() is called.
-
foreign
-
uses the identifier of another associated object. Usually used in conjunction with a <one-to-one> primary key association.
The hilo and seqhilo generators provide two alternate implementations of the hi/lo algorithm, a favorite approach to identifier generation. The first implementation requires a "special" database table to hold the next available "hi" value. The second uses an Oracle-style sequence (where supported).
<id name="Id" type="Int64" column="cat_id">
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
</generator>
</id>
<id name="Id" type="Int64" column="cat_id">
<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">100</param>
</generator>
</id>
Unfortunately, you can't use hilo when supplying your own Connection to NHibernate. NHibernate must be able to fetch the "hi" value in a new transaction.
3.1.4.3. UUID Hex Algorithm
<id name="Id" type="String" column="cat_id">
<generator class="uuid.hex">
<param name="format">format_value</param>
<param name="seperator">seperator_value</param>
</generator>
</id>
The UUID is generated by calling Guid.NewGuid().ToString(format). The valid values for format are described in the MSDN documentation. The default seperator is - and should rarely be modified. The format determines if the configured seperator can replace the default seperator used by the format.
3.1.4.4. UUID String Algorithm
The UUID is generated by calling Guid.NewGuid().ToByteArray() and then converting the byte[] into a char[]. The char[] is returned as a String consisting of 16 characters.
The guid identifier is generated by calling Guid.NewGuid(). To address some of the performance concerns with using Guids as primary keys, foreign keys, and as part of indexes with MS SQL the guid.comb can be used. The benefit of using the guid.comb with other databases that support GUIDs has not been measured.
3.1.4.6. Identity columns and Sequences
For databases which support identity columns (DB2, MySQL, Sybase, MS SQL), you may use identity key generation. For databases that support sequences (DB2, Oracle, PostgreSQL) you may use sequence style key generation. Both these strategies usually require two SQL queries to insert a new object. When working with MS SQL and the identity key generator then select SCOPE_IDENTITY() will be appended to the insert sql thus avoiding the executions of a two distinct IDbCommands.
<id name="Id" type="Int64" column="uid">
<generator class="sequence">
<param name="sequence">uid_sequence</param>
</generator>
</id>
<id name="Id" type="Int64" column="uid" unsaved-value="0">
<generator class="identity"/>
</id>
For cross-platform development, the native strategy will choose from the identity, sequence and hilo strategies, dependant upon the capabilities of the underlying database.
3.1.4.7. Assigned Identifiers
If you want the application to assign identifiers (as opposed to having NHibernate generate them), you may use the assigned generator. This special generator will use the identifier value already assigned to the object's identifier property. Be very careful when using this feature to not assign keys with business meaning (almost always a terrible design decision).
Due to its inherent nature, entities that use this generator cannot be saved via the ISession's SaveOrUpdate() method. Instead you have to explicitly specify to NHibernate if the object should be saved or updated by calling either the Save() or Update() method of the ISession.
<composite-id
name="propertyName"(1)
class="ClassName"(2)
unsaved-value="any|none"(3)
access="field|property|nosetter|ClassName">
<key-property name="propertyName" type="typename" column="column_name"/>
<key-many-to-one name="propertyName class="ClassName" column="column_name"/>
......
</composite-id>
For a table with a composite key, you may map multiple properties of the class as identifier properties. The <composite-id> element accepts <key-property> property mappings and <key-many-to-one> mappings as child elements.
<composite-id>
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id>
Your persistent class must override Equals() and GetHashCode() to implement composite identifier equality. It must also be Serializable.
Unfortunately, this approach to composite identifiers means that a persistent object is its own identifier. There is no convenient "handle" other than the object itself. You must instantiate an instance of the persistent class itself and populate its identifier properties before you can Load() the persistent state associated with a composite key. We will describe a much more convenient approach where the composite identifier is implemented as a seperate class in TODO:LINKTOCOMPENENTS. The attributes described below apply only to this alternative approach:
(1)
|
name (optional): A property of component type that holds the composite identifier (see next section).
|
(2)
|
class (optional - defaults to the property type determined by reflection): The component class used as a composite identifier (see next section).
|
(3)
|
unsaved-value (optional - defaults to none): Indicates that transient instances should be considered newly instantiated, if set to any.
|
The <discriminator> element is required for polymorphic persistence using the table-per-class-hierarchy mapping strategy and declares a discriminator column of the table. The discriminator column contains marker values that tell the persistence layer what subclass to instantiate for a particular row. A restricted set of types may be used: String, Char, Int32, Byte, Int16, Boolean, YesNo, TrueFalse.
<discriminator
column="discriminator_column"(1)
type="discriminator_type"(2)
force="true|false"(3)
/>
(1)
|
column (optional - defaults to class) the name of the discriminator column.
|
(2)
|
type (optional - defaults to String) a name that indicates the Hibernate type
|
(3)
|
force (optional - defaults to false) "force" NHibernate to specify allowed discriminator values even when retrieving all instances of the root class.
|
Actual values of the discriminator column are specified by the discriminator-value attribute of the <class> and <subclass> elements.
The force attribute is (only) useful if the table contains rows with "extra" discriminator values that are not mapped to a persistent class. This will not usually be the case.
3.1.7. version (optional)
The <version> element is optional and indicates that the table contains versioned data. This is particularly useful if you plan to use long transactions (see below).
<version
column="version_column"(1)
name="propertyName"(2)
type="typename"(3)
access="field|property|nosetter|ClassName"(4)
/>
(1)
|
column (optional - defaults to the property name): The name of the column holding the version number.
|
(2)
|
name: The name of a property of the persistent class.
|
(3)
|
type (optional - defaults to Int32): The type of the version number.
|
(4)
|
access (optional - defaults to property): The strategy NHibernate should use for accessing the property value.
|
Version numbers may be of type Int64, Int32, Int16, Ticks, Timestamp, or TimeSpan.
3.1.8. timestamp (optional)
The optional <timestamp> element indicates that the table contains timestamped data. This is intended as an alternative to versioning. Timestamps are by nature a less safe implementation of optimistic locking. However, sometimes the application might use the timestamps in other ways.
<timestamp
column="timestamp_column"(1)
name="propertyName"(2)
access="field|property|nosetter|ClassName"(3)
/>
(1)
|
column (optional - defaults to the property name): The name of a column holding the timestamp.
|
(2)
|
name: The name of a property of .NET type DateTime.
|
(3)
|
access (optional - defaults to property): The strategy NHibernate should use for accessing the property value.
|
Note that <timestamp> is equivalent to <version type="timestamp">.
The <property> element declares a persistent, property of the class.
<property
name="propertyName"(1)
column="column_name"(2)
type="typename"(3)
update="true|false"(4)
insert="true|false"(5)
formula="arbitrary SQL expression"(6)
access="field|property|nosetter|ClassName"(7)
/>
(1)
|
name: the name of the property in the same case as the Propery in your API
|
(2)
|
column (optional - defaults to the property name): the name of the mapped database table column.
|
(3)
|
type (optional): a name that indicates the NHibernate type.
|
(4)
|
update (optional - defaults to true) : specifies that the mapped columns should be included in SQL UPDATE
|
(5)
|
insert (optional - defaults to true) : specifies that the mapped columns should be included in SQL INSERT statements. Setting both insert and update to false allows a pure "derived" property whose value is initialized from some other property that maps to the same colum(s) or by a trigger or other application.
|
(6)
|
formula (optional): an SQL expression that defines the value for a computed property. Computed properties do not have a column mapping of their own.
|
(7)
|
access (optional - defaults to property): The strategy NHibernate should use for accessing the property value.
|
typename
could be:
-
The name of a NHibernate basic type (eg. Int32, String, Char, DateTime, Timstamp, Single, Byte[], Object, ...).
-
The name of a .NET type with a default basic type (eg. System.Int16, System.Single, System.Char, System.String, System.DateTime, System.Byte[], ...).
-
The name of a your of System.Enum (eg. Eg.Color).
-
The name of a serializable .NET type.
-
The type of a custom type (eg. Illflow.Type.MyCustomType, Illflow).
If you do not specify a type, NHibernate will use reflection upon the named property to take a guess at the correct NHibernate type. NHibernate will try to interpret the name of the return class of the property getter using rules 2, 3, 4 in that order. However, this is not always enough. In certain cases you will still need the type attribute. (For example, to distinguish between NHibernate.DateTime and NHibernate.Timestamp, or to specify a custom type.)
The access attribute lets you control how NHibernate will access the value of the property at runtime. The value of the access attribute should be text formatted as access-strategy.naming-strategy. The .naming-stragey is not always required.
Table 3.1. Access Strategy
Access Strategy Name |
Description |
property
|
The default implementation. NHibernate uses the get/set of your Property. No Naming Strategy should be used with this Access Strategy because the name attribute is the name of the Property.
|
field
|
NHibernate will access the Field directly. NHibernate uses the <name> as the name of the field. This can be used when a Property's get and set have extra actions in them that you don't want to occur when NHibernate is populating or reading the object. If you want the name of the Property and not the Field to be what the consumers of your API use with HQL, then a Naming Strategy is needed.
|
nosetter
|
NHibernate will access the Field directly when setting the value and will use the Property when getting the value. This can be used when a Property only exposes a get because the consumers of your API can't change the value directly. A Naming Strategy is required because NHibernate uses the name attribute as the Property so it needs to be told what the name of the Field is.
|
ClassName
|
If NHibernate's built in Access Strategies are not what is needed for your situation then you can build your own by implementing the interface NHibernate.Property.IPropertyAccessor. The value of the access attribute should be an Assembly Qualified Name that can be loaded with Activator.CreateInstance(string AssemblyQualifiedName).
|
Table 3.2. Naming Strategy
Naming Strategy Name |
Description |
camelcase
|
The name attribute is converted to CamelCase to find the Field. <property name="Foo" ... > uses the Field foo.
|
camelcase-underscore
|
The name attribute is converted to CamelCase and prefixed with an underscore to find the Field. <property name="Foo" ... > uses the Field _foo.
|
lowercase-underscore
|
The name attribute is converted to all LowerCase and prefixed with an underscore to find the Field. <property name="FooBar" ... > uses the Field _foobar.
|
pascalcase-m-underscore
|
The name attribute is prefixed with the character m and an underscore to find the Field. <property name="Foo" ... > uses the Field m_Foo.
|
3.2.1. Entities and values
To understand the behaviour of various .NET language-level objects with respect to the persistence service, we need to classify them into two groups:
An entity exists independently of any other objects holding references to the entity. Contrast this with the usual .NET model where an unreferenced object is garbage collected. Entities must be explicitly saved and deleted (except that saves and deletions may be cascaded from a parent entity to its children). Entities support circular and shared references. They may also be versioned.
An entity's persistent state consists of references to other entities and instances of value types. Values are structs, collections, components and certain immutable objects. Unlike entities, values (in particular collections and components) are persisted and deleted by reachability. Since value objects (and structs) are persisted and deleted along with their containing entity they may not be independently versioned. Values have no independent identity, so they cannot be shared by two entities or collections.
All NHibernate types except collections support null semantics if the .NET type also supports it.
Up until now, we've been using the term "persistent class" to refer to entities. We will continue to do that. Strictly speaking, however, not all user-defined classes with persistent state are entities. A component is a user defined class with value semantics.
The basic types may be roughly categorized into three groups - System.ValueType types, System.Object types, and System.Object types for large objects. Just like the .NET Types, columns for System.ValueType types can not store null values and System.Object types can store null values.
Table 3.3. System.ValueType Mapping Types
NHibernate Type |
.NET Type |
Database Type |
Remarks |
Boolean
|
System.Boolean
|
DbType.Boolean
|
Default when no type attribute specified. |
Byte
|
System.Byte
|
DbType.Byte
|
Default when no type attribute specified. |
Char
|
System.Char
|
DbType.StringFixedLength - 1 char
|
Default when no type attribute specified. |
DateTime
|
System.DateTime
|
DbType.DateTime - ignores the milliseconds |
Default when no type attribute specified. |
Decimal
|
System.Decimal
|
DbType.Decimal
|
Default when no type attribute specified. |
Double
|
System.Double
|
DbType.Double
|
Default when no type attribute specified. |
Guid
|
System.Guid
|
DbType.Guid
|
Default when no type attribute specified. |
Int16
|
System.Int16
|
DbType.Int16
|
Default when no type attribute specified. |
Int32
|
System.Int32
|
DbType.Int32
|
Default when no type attribute specified. |
Int64
|
System.Int64
|
DbType.Int64
|
Default when no type attribute specified. |
PersistentEnum
|
A System.Enum |
The DbType for the underlying value. |
Do not specify type="PersistentEnum" in the mapping. Instead specify the Assembly Qualified Name of the Enum or let NHibernate use Reflection to "guess" the Type. The UnderlyingType of the Enum is used to determine the correct DbType. |
Single
|
System.Single
|
DbType.Single
|
Default when no type attribute specified. |
Ticks
|
System.DateTime
|
DbType.Int64
|
type="Ticks" must be specified. |
TimeSpan
|
System.TimeSpan
|
DbType.Int64
|
Default when no type attribute specified. |
Timestamp
|
System.DateTime
|
DbType.DateTime - as specific as database supports. |
type="Timestamp" must be specified. |
TrueFalse
|
System.Boolean
|
DbType.AnsiStringFixedLength - 1 char either 'T' or 'F' |
type="TrueFalse" must be specified. |
YesNo
|
System.Boolean
|
DbType.AnsiStringFixedLength - 1 char either 'Y' or 'N' |
type="YesNo" must be specified. |
Table 3.4. System.Object Mapping Types
NHibernate Type |
.NET Type |
Database Type |
Remarks |
AnsiString
|
System.String
|
DbType.AnsiString
|
type="AnsiString" must be specified. |
CultureInfo
|
System.Globalization.CultureInfo
|
DbType.String - 5 chars for culture |
Default when no type attribute specified. |
Binary
|
System.Byte[]
|
DbType.Binary
|
Default when no type attribute specified. |
Type
|
System.Type
|
DbType.String holding Assembly Qualified Name. |
Default when no type attribute specified. |
String
|
System.String
|
DbType.String
|
Default when no type attribute specified. |
Table 3.5. Large Object Mapping Types
NHibernate Type |
.NET Type |
Database Type |
Remarks |
StringClob
|
System.String
|
DbType.String
|
type="StringClob" must be specified. Entire field is read into memory. |
BinaryBlob
|
System.Byte[]
|
DbType.Binary
|
type="BinaryBlob" must be specified. Entire field is read into memory. |
Serializable
|
Any System.Object that is marked with SerializableAttribute. |
DbType.Binary
|
type="Serializable" should be specified. This is the fallback type if no NHibernate Type can be found for the Property. |
For those of you coming over from Hibernate or using some of the tools to generate hbm.xml files that are intended for Hibernate, there is a Hibernate compatiblity layer for type names. A type="integer" will map to an Int32 NHibernateType, type="short" to an Int16 NHibernateType. To see all of the conversions you can view the source of static constructor of the class NHibernate.Type.TypeFactory.
3.3. SQL quoted identifiers
You may force NHibernate to quote an identifier in the generated SQL by enclosing the table or column name in backticks in the mapping document. NHibernate will use the correct quotation style for the SQL Dialect (usually double quotes, but brackets for SQL Server and backticks for MySQL).
<class name="LineItem" table="`Line Item`">
<id name="id" column="`Item Id`">
<generator class="assigned"/>
</id>
<property name="itemNumber" column="`Item #`"/>
...
</class>
|