I have table columns with table number suffix
create table T1234_DATA (CHANGEUSER_1234 nvarchar(50), ...)
For tables I wrote fluent mapping. When used directly it works fine, but there are (for some historical reason) many views or stored procedures which are aliasing columns to generic suffic _0001:
create view v1234_DATA as select CHANGEUSER_1234 as CHANGEUSER_0001
Whenever I want to use such view I have to write full class mapping again. Is there some sort of alternative column mapping in Fluent or HBM to share it between all named queries? Wildcards in sql column names, selecting alternative class mapping on the query, multiple sql columns mapped to single property, etc.?
If I understood your question, a Join could solve the problem.
As you can see in:
NHibernate Mapping - <join/>
How to join table in fluent nhibernate
Or maybe you could use a formula (or some formulas) like:
public class UserMap : ClassMap<User> {
public User() {
Table("TB_USER");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.ChangeUser1234, "CHANGEUSER_1234");
Map(x => x.ChangeUser0001)
.Formula("(select CHANGEUSER_0001 from TABLE where ...)");
}
}
Related
I have 3 tables that represents a many to many mapping.
Two tables with different ids and a third table with a composite key referencing the other two.
How can i map this using the classmap in nhibernate?
The following doesn´t work:
HasManyToMany(m =>
m.ListBlockStatus)
.Table("BlockTypeAction")
.ParentKeyColumn("IdBlockActionDefinition")
.ChildKeyColumn("IdBlockTypeCategory")
.Table("BlockTypeCategory")
.ParentKeyColumn("Id");
Found what i need, unfortunately the query in the end is not an inner join.
HasManyToMany(m => m.ListBlockStatus)
.Table("BlockTypeAction")
.ChildKeyColumns.Add("IdBlockActionDefinition")
.ParentKeyColumn("IdBlockTypeCategory")
.Cascade.All();
I am using fluent nHibernate for my mappings as follow:
public class ContentTagMap: ClassMap<Employee>
{
public EmployeeMap()
{
Id(t => t.Id);
Map(t => t.Name);
HasManyToMany(t => t.Company);
}
}
public class CompanyMap: ClassMap<Company>
{
public HelpMap()
{
Id(h => h.Id);
Map(h => h.CompanyName).Length(6000);
Map(h => h.address).Length(6000);
HasManyToMany(h => h.Employee);
}
}
These mappings produce Employee Table ,Company Table and EmployeeToCompany Table
Employee Table
Id Name
1 John
2 MAX
Company Table
Id CompanyName address
1 HTC ABC
2 HTC2 India
EmployeeToCompany Table
Employee_Id Company_Id
1 1
2 1
How can I delete the employee with Id 1?
Unless I'm misunderstanding your question you should be asking:
How can i delete the content of the table using NHibernate?
Fluent NHibernate is only a tool to aid in the mapping of your entities. NHibernate is what you use to create, read, update and delete data. In any event:
9.5. Deleting persistent objects
ISession.Delete() will remove an object's state from the database. Of
course, your application might still hold a reference to it. So it's
best to think of Delete() as making a persistent instance transient.
From the NHibernate Documentation
You probably want to also define a Cascading style on your many to many relationship (HasManyToMany) in your mapping.
If you use Cascade.SaveUpdate in your many to many whenever you delete an entity on one side of the relationship it will delete that entity and any relationships. If you remove the association (ex. if you removed an Employee from a Company) it will only delete the relationship (EmployeeToCompany). This is what I've typically found to work in the case of many to many relationships.
Look at this article for more details on mapping and using a many to many relationship in Fluent NHibernate.
how to keep many to many relationships in sync with nhibernate?
In application I have a bunch of entity class NHibernate mappings with two common properties: {GUID Id, string Tag} (it is implemented through abstract base class).
In DB there are tables for each entity with common columns:
dbo.[EntityName] { uniqueidentifier Id, ... etc. }
Also there is a table that stores information about some global tag for each entity:
dbo.EntityTag { uniqueidentifier Id, nvarchar Tag }
This table is strictly needed and it's mandatory to have it in Db.
How could I implement custom SQL logic to update, select and insert values in Tag property?
For example in FluentNhibernate It would be helpful to have something like
Map(x => x.Tag)
.Insert("INSERT dbo.EntityTag VALUES({Id},{Tag})")
.Update("UPDATE dbo.EntityTag SET Tag={Tag} WHERE Id = {ID}")
.SELECT("...")
.Delete("... etc")
Formally speaking, you can do it using custom implementation of entity persister.
However, in this case, it seems easier to use a <join> mapping.
In order to use my Fluent NHibernate mappings on SQL Azure, I need to have a clustered index on every table. The default heap tables that Fluent NHibernate creates for many-to-many joins obviously don't do this as they don't have primary keys.
I want to be able to tell one side of the relationship to create a clustered index for its join table, but I'm not sure how. Here's what my mappings look like:
public class UserMap : ClassMap<User>{
public UserMap()
{
Table("Users");
Id(x => x.UserId).GeneratedBy.Identity().Column("UserId");
Map(x => x.UserName).Unique().Not.Nullable().Length(DataConstants.UserNameLength).Column("UserName");
Map(x => x.Email).Unique().Not.Nullable().Length(DataConstants.EmailAddressLength).Column("Email");
Map(x => x.Password).Not.Nullable().Length(DataConstants.PasswordHashLength).Column("Password");
HasMany(x => x.Clicks).Cascade.AllDeleteOrphan();
HasManyToMany(x => x.Roles).Cascade.SaveUpdate().Table("UsersInRole").ParentKeyColumn("UserId").
ChildKeyColumn("RoleId");
}
}
Please let me know if you need any more information!
I don't know if Fluent supports it directly (if not, just include the xml), but you can do it with Auxiliary Database Objects
<nhibernate-mapping>
<database-object>
<create>create clustered index ix on UsersInRole(UserId, RoleId)</create>
<drop>drop index UsersInRole.ix</drop>
</database-object>
</nhibernate-mapping>
I struggled with the same problem as the topic starter (as I'm combining Fluent NHibernate and Sql Azure as well) but the given answer didn't satify. This is because it is not dynamic by convention. Of course the HBM file could be dynamically created and added to the configuration afterwards with configuration.AddXmlFile("AddClusteredIndexesToManyToManyTables.hbm.xml"); but I just don't like the HBM files and I can't imagine there is a better way.
After several hours I found another solution which is dynamic (and readable!) and does not deal with hbm xml files. The solution is as follows:
Assumption #1: I will create a composite primary key for each junction table that results in a clustered index in SQL Server.
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 because we are interested in the many-to-many mappings.
foreach (var collectionMapping in configuration.CollectionMappings
// Filter on many-to-many
.Where(x => !x.IsOneToMany)) {
// 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
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;
}
And yes, the logic to get the left and right hand columnsAfter that the columns are a bit hacky but it works and you are free to adjust and edit my solution (^_-). The problem is that the collection mapping is fairly empty/unfilled.
Good luck with coding and creating conventions!
Any set of column(s) can be a clustered index... there is no requirement that I know of which forces you to use a PK constraint in order to build a clustered index.
More over I do not understand how a client could REQUIRE a clustered index. It might make them as a default but that's different than require. This is often reported as a "best practice" for SQL Server, but to the client, there's no real distinction between a secondary b-tree index on a column and the clustered index which orders the table's record. How would the client be able to distinguish the underlying storage of the data? One stores the data ordered by the cluster key, the other doesn't.
Maybe fluent-nhibernate performs better, or claims to - but it will "work" without any indexes.
But I'm not an expert in either so YMMV.
Great solution M.Mimpen.
When need map interfaces, put the ChildKeyColumn with interface name.
Ex:
HasManyToMany(x => x.Acessos).("IRole_id");
The class Acesso implements IRole interface. If you don´t inform child key name, the column created will be "Acesso_id", but when create the key will try "IRole_id".
I have a class that contains a collection that is mapped to a many-to-many database relationship using Fluent Nhibernate. The mapping is as below -
Table("Book");
Id(x => x.Id);
Map(x => x.Title);
Map(x => x.NumberOfPages);
HasManyToMany(x => x.Authors)
.Cascade.AllDeleteOrphan()
.Table("BookAuthor")
.ParentKeyColumn("BookId")
.ChildKeyColumn("AuthorId");
I then get an instance of this class and add an item to the authors collection using the code below -
var book = session.CreateCriteria(typeof(Book)).UniqueResult<Book>();
Author author = new Author {Name="Phil Moran",Age=51};
book.Authors.Add(author);
session.SaveOrUpdate(book);
The database is updated succesfully but Nhibernate updates the BookAuthor table by firstly deleting all the records within it linked to the updated book, then repopulating all the data plus the extra record required for the new author. Why is Nhibernate doing this? I would expect it to simply add a single record containing the book and author details to the many-to-many table (BookAuthor) and not perform any of the delete actions. Can I change this behaviour via my mappings?
That happens when the collection is mapped as a bag (I guess that's the default in FluentNH).
Use a set, list or idbag instead.