问题
I am using Fluent-NHibernate (with automapping) to generate my tables but would like to choose a different clustered index than the ID field which is used by default. How can you create clustered indexes with Fluent NHibernate on a field other than the default Primary Key field?
The primary reasoning behind this is simple. I am using Guids for my primary key fields. By default, NHibernate creates clustered indexes on the primary key fields. Since Guids are usually not sequential, clustering on the primary key field causes a performance issue.
As we all know, appending records at the end of a table is a much cheaper operation than inserting records within the table. Also, the records in the table are physically stored in the order of the items in the clustered index. Since Guids are somewhat "random" and are not sequential, new Guids may be generated that are less than the value of other Id Guids already in the table--resulting in table inserts rather than appends.
To minimize this, I have a column called CreatedOn which is of type DateTime. I need for the table to be clustered on this CreatedOn column so that all new records are appended rather than inserted.
Any ideas for how to accomplish this are welcome!!!
Note: I realize that I could use Sequential Guids but prefer not to go down that path for security reasons.
Note: I still do not have an answer for this post but I have a few ideas I am pondering at the moment.
Using NHibernate without Fluent, I think it may be possible to created clustered indexes directly in NHibernate. I don't yet know enough about NHibernate to know how to do this. I am just pretty (as in almost absolutely) sure it can be done.
Fluent-NHibernate used to include a way to set attributes (e.g. like a clustered index) on a SQL object before the recent rewrite. Now that option appears to have gone away. I will probably post a question somewhere to see if that option is still available. If so, I could probably use that to set the clustered index.
Fluent-NHibernate provides the ability to expose a configuration for manual editing once it has been fluently built. I have not tried this functionality but expect it may offer the level of granularity that is needed to set clustered indexes.
Worst case scenario, I can write a SQL script to change the clustered indexes on all my tables once they are generated. However, I have a couple of questions regarding this approach. A. Since I am using automatic schema generation, will NHibernate "undo" my clustered index changes the next time it evaluates the configuration? 2. Will NHibernate error if it detects the clustered index has been changed? I need to test this but have not done so just yet. I really hate this solution though. I am testing my DB against SQLServer2008 and MySQL. Part of the beauty of NHibernate is that it is database agnostic. Once we introduce scripts, all bets are off.
There is an interface that is used in fluent conventions called IPropertyInstance Classes which inherit from this interface have an Index property which allows an Index to be created on the field. The problem is that there is no flag or other option to allow the index to be created as clustered. The simplest solution would be to add a property to this method to allow for clustered indexes to be created. I think I may suggest this to the Fluent-NHibernate developers.
回答1:
This is an old post, but I hope could help someone else. This come from my experience on MS SQL Server. I believe different platforms require different solutions, but this should be a good starting point.
NHibernate doesn't set the CLUSTERED index on the primary key. It's the SQL Server default behavior. As there can be only one CLUSTERED per table, we need first to avoid the CLUSTERED creation on the primary key.
The only way I found to accomplish this is to create a custom Dialect, overriding the propery PrimaryKeyString. NHibernate's default comes from Dialect.cs:
public virtual string PrimaryKeyString
{
get { return "primary key"; }
}
For SQL Server
public override string PrimaryKeyString
{
get { return "primary key nonclustered"; }
}
This will force SQL Server to create a NONCLUSTERED primary key. Now you can add your own CLUSTERED index on your favorite column through the tag in the XML mapping file.
<database-object>
<create>
create clustered index IX_CustomClusteredIndexName on TableName (ColumnName ASC)
</create>
<drop>
drop index IX_CustomClusteredIndexName ON TableName
</drop>
</database-object>
回答2:
I can't answer that specifically, but I'll give you some database info since I'm here.
You'll need to tell NHibernate to create the primary key at a non-clustered index. There can only be only clustered index per table, so you need to create the table as a heap, and then put a clustered index on it.
回答3:
As you said yourself, another option is to switch to the guid.comb ID generation strategy where PK uids are based on a part which is a Guid and a part which ensures that the generated IDs are sequential.
Check out more info in a post by Jeffrey Palermo here.
But you mention that do not want to do that for security reasons - why is that?
回答4:
Just like @abx78 told, this is an old post, but I would like to share my knowledgde on a solution for this problem as well. I built the solution for idea 3 "Fluent NHibernate exposes mappings":
After the configuration has been build (thus the mappings are parsed), Fluent NHibernate gives us the oppertunity to look into the actual mappings with configuration.ClassMappings
and configuration.CollectionMappings
. The latter is used in the example below to set a composite primary key resulting in a clustered index in Sql Server (as @abx78 points out):
foreach (var collectionMapping in configuration.CollectionMappings) {
// Fetch the columns (in this example: build the columns in a hacky way)
const string columnFormat = "{0}_id";
var leftColumn = new Column(string.Format(
columnFormat,
collectionMapping.Owner.MappedClass.Name));
var rightColumn = new Column(string.Format(
columnFormat,
collectionMapping.GenericArguments[0].Name));
// Fetch the actual table of the many-to-many collection
var manyToManyTable = collectionMapping.CollectionTable;
// Shorten the name just like NHibernate does
var shortTableName = (manyToManyTable.Name.Length <= 8)
? manyToManyTable.Name
: manyToManyTable.Name.Substring(0, 8);
// Create the primary key and add the columns
// >> This part could be changed to a UniqueKey or to an Index
var primaryKey = new PrimaryKey {
Name = string.Format("PK_{0}", shortTableName),
};
primaryKey.AddColumn(leftColumn);
primaryKey.AddColumn(rightColumn);
// Set the primary key to the junction table
manyToManyTable.PrimaryKey = primaryKey;
// <<
}
Source: Fluent NHibernate: How to create a clustered index on a Many-to-Many Join Table?
来源:https://stackoverflow.com/questions/1356467/how-can-you-create-clustered-indexes-with-fluent-nhibernate