I have two entities with many-to-many relationship defined on them.
<set name="TreasuryCodes" table="ServiceProviderAccountTreasuryCode" lazy="true" cascade="all">
<key column="ServiceProviderAccountId" />
<many-to-many column="TreasuryCodeId" class="TreasuryCode" />
</set>
<set name="ServiceProviderAccounts" table="ServiceProviderAccountTreasuryCode" lazy="true" inverse="true" cascade="all">
<key column="TreasuryCodeId" />
<many-to-many column="ServiceProviderAccountId" class="ServiceProviderAccount" />
</set>
Now I want to delete all ServiceProviderAccounts by ServiceProviderId. I write this code:
public void DeleteAllAccount(int serviceProviderId)
{
const string query = "delete ServiceProviderAccount spa where spa.ServiceProvider.Id = :serviceProviderId";
repository.Session.CreateQuery(query)
.SetInt32("serviceProviderId", serviceProviderId)
.ExecuteUpdate();
repository.Session.Flush();
}
and I receive this exception:
Test method Test.ServiceRepositoryTest.DeleteAllAccountTest threw exception:
NHibernate.Exceptions.GenericADOException: could not execute update query[SQL: delete from ServiceProviderAccount where ServiceProviderId=?] ---> System.Data.SqlClient.SqlException: The DELETE statement conflicted with the REFERENCE constraint "FKBC88A84CB684BF79". The conflict occurred in database "Test", table "dbo.ServiceProviderAccountTreasuryCode", column 'ServiceProviderAccountId'.
The statement has been terminated.
I'm confused, as I have defined cascade on the entity, shouldn't nhibernate remove rows from ServiceProviderAccountTreasuryCode?
UPDATE
ok, looks like ExecuteUpdate is not looking for NHibernate cascade, probably because it's not loading entities before deleting it? Anyway is there any other way to delete from ServiceProviderAccountTreasuryCode table and then from ServiceProviderAccounts via HQL? I know I can use cascades on database, but I want to avoid that. What I want is to delete rows from many-to-many association table by HQl. Is it possible? Or I should use plain SQL?
looks like you have a referential integrity problem i.e a foregin key relation ship where the id that you are deleting is being referenced somewhere else and that table will end up referencing nothing. if that is what you want to do then you can run the Truncate command but I am not sure why you will do that..
I would suggest you do a normal delete i.e using the nhibernate session and Linq like below:
foreach(var sessionProvider in Session.Linq<ServiceProviderAccount >().Where(x=>x.ServiceProvider.Id==servinceProviderId))
Session.Delete(sessionProvider);
Now note this is not at all a bad way to do your deletion as they are not fired against the dB immediately and is part of the Session till your transaction is committed and this should handle your referential integrity problems if your mappings are defined crrectly.
Hope this works..
Looks like it doesn't obey the cascading. HQL batch operations for update/delete is relatively new, and translate more or less directly to SQL. I believe that you must keep track of the related tables as well.
If you only delete single entities then the batch-delete doesn't do you much good. In order for NHibernate to actually take cascading into account, it must load the actual entitity, which you don't with your example.
I asked a similar question, the answer I got might interest you
Remove entity in NHibernate only by primary key
Related
I want to start with a specific example where a table is created with a column having a constraint indicating this column is a foreign key:
<changeSet id="1" author="A1">
<createTable tableName="TABLE_A">
<!-- Other columns -->
<column name="FK_TABLE_B" type="BIGINT">
<!-- the 'TABLE_B' will be renamed soon ! -->
<constraints references="TABLE_B(ID_PK)"
foreignKeyName="JUST_UNIQUE_I_GUESS" nullable="false" />
</column>
</createTable>
</changeSet>
Now a first question: Assume TABLE_B would be renamed to TABLE_NEW... how would i want to change the above constraint so it points to the renamed Tables column ID_PK?
I see a few possibilities:
modify the changeSet itself (no Problem with a H2 inMemory DB... but cmon... thats not the idear right?)
Drop the whole column in my own changeSet and add it again with the new constraint which could make sence since the column name will probably change to FK_TABLE_NEW anyways... but thats not a real possibilitie for a productive environment
somehow alter that constraint -> BUT HOW!? the documentation is no
help at all...
The next question then is why to have this way of constraints if there is a
<addForeignKeyConstraint ...>
as well?
So currently i dont feel Liquibase at all just because this was my very first try to change an existing changeSet and my biggest question is: Is this a issue of not understanding the best practise or acctually a problem of the "not so verbouse" documentation?
Thanks for any help!
What i tried
assumed above constraints definition somehow leads to the same as a when done with a
<addForeignKeyConstraint ...>
tag but with less attributes. So i thought i could just use the
<dropForeignKeyConstraint ...>
tag first delete the ForeignKeyConstraint and then add a new one. But it still tells me it wont find table TABLE_B when trying to execute the first changeSet in a H2 in-memory DB.
My Changeset looked like this:
<changeSet id="1" author="A2">
<dropForeignKeyConstraint baseTableName="TABLE_A"
constraintName="JUST_UNIQUE_I_GUESS" />
<addForeignKeyConstraint
constraintName="JUST_UNIQUE_I_GUESS"
referencedTableName="TABLE_NEW" baseColumnNames="FK_TABLE_B"
baseTableName="TABLE_A" referencedColumnNames="ID_PK" />
</changeSet>
Background Information
Since were currently just building a POC using a in-memory DB only (H2) its no big deal to just change the first changeSet until we have quite of a final scheme-state... but how to deal with such things if you already have a existing DB, millions of entrys and stuff? Currently i highly doubt Liquibase is the right decision for a company with 1k+ developpers without hiring Liquibase Experts...
You should only modify your changesets as long as they (resp. the software/database that go along with the changesets) have not been released.
We keep our changeset files (along with the code) in our source code repository. During development phase everyone is allowed to change changesets defined for the version currently under development.
As soon as the version is released the changeset files are considered to be fixed and should not be changed anymore. (With the release the software is shipped and customers then have a database that reflects whatever the changesets define).
So after release you will have to create new changeSets that
drop the foreign key constraint
rename your table
add a new foreign key constraint
More or less, you need to do exactly what you would do with pure sql on the database as well.
At this point liquibase will more or less just translate your changeSets to sql and apply them to the database. So once you found a way to do your changes with slq it should also be possible to put those into liquibase changesets.
If one would want to insert into a view, what would be required to setup on NHibernate to allow this.
Neither
<generator class="identity" />
nor
<generator class="native" />
allows insert.
Error I get when I try this is either "Null id" or "Null identifier".
You can not insert, update or delete from a view even with regular SQL commands, nor with NHiberanate. Views are designed for read only purposes.
You should model your class from the original tables in a similar fashion as the view and then you could do any CRUD operation on it.
I have never done this but have you tried assigned:-
<generator class="assigned" />
Any modifications, including UPDATE, INSERT, and DELETE statements,
must reference columns from only one base table.
See here MSDN for more info
And there is one more thing if you use assigned, 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.
I need to change the primary key (#Id) of an entity from natural key to a new field that represents a surrogate key (it will use #GeneratedValue(strategy=GenerationType.AUTO)).
What is the easiest way to update the database schema other than dropping the table and letting Hibernate to create it again?
I was trying to let Hibernate update the schema automagically with hibernate.hbm2ddl.auto set to update, but it didn't work out. I suppose that Hibernate autoupdate doesn't support such drastic changes of database schemas.
If it were only one entity, I'd
make the changes in the database manually (SQL)
and update the Hibernate mapping accordingly
I recently added new property in one class, its list property and I have written xml mapping for that property,due to few reasons I am not suppose to delete database or use create option in hibernate config file to update changes. I have to do it manual by executing sql queries on database.
xml mapping file:
<list name="items" table="ITEM_ITEM_GROUP" lazy="false" cascade="save-update">
<key column="ITEM_GROUP_ID"></key>
<list-index column="IG_INDEX" />
<many-to-many column="ITEM_ID" class="Item" />
</list>
can anyone please help how do i do it?
You are not just adding a column, you are adding a table.
The easiest approach is to use SchemaExport to create an sql script of your database schema. Then you can copy-paste the new table with indexes and everything.
In this case, you most probably don't have to change an existing table, just add the new stuff.
In java, you can call SchemaExport directly from the command line. Take a look at a tutorial like this here.
In C# you need to write a utility which export the schema, code looks something like this:
var schemaExport = new SchemaExport(configuration);
schemaExport
.Execute(
false,
false,
false,
null,
fileStream);
It will basically be something like this (depending on your rdms you are using)
create table ITEM_ITEM_GROUP(
ITEM_GROUP_ID int not null,
IG_INDEX int not null,
ITEM_ID int,
primary key (ITEM_GROUP_ID, IG_INDEX)
)
alter table ITEM_ITEM_GROUP
add constraint FKXXXX
foreign key (ITEM_GROUP_ID)
references YOUR_ENTITY
alter table ITEM_ITEM_GROUP
add constraint FKXXXX
foreign key (ITEM_ID)
references ITEM
I probably missed something. So it's best you let Hibernate create the schema and look into the generated SQL.
So you just want to add that column to the table? Use the ALTER TABLE statement:
ALTER TABLE table_name
ADD column_name datatype
See http://w3schools.com/sql/sql_alter.asp
I am building my mapping and then using schema export to update my DB. However, if I delete an association in my mapping, since it's no longer in the mapping, when I run SchemaExport, it will not delete the foreign key for the deleted association. This means that it then fails to drop the table associated with that foreign key. Which further means that it can't recreate the table and I get a "There is already an object named Foo in the database" exception. Is there any way to brute delete the table via Schema Export?
The cleanest way is to do SchemaExport.Drop with the old nhibernate configuration, then create with the new one.
Alternatively you could drop and recreate the database itself, here's an example which does this at file level for SQL Server Express: http://nicholas.piasecki.name/blog/2010/01/integration-testing-with-sql-server-express-2008-nhibernate-and-mstest/