NHibernate SaveOrUpdate without primary key - nhibernate

The Situation
I've got a database table that is mapped via NHibernate (3.3.3-SP1). The application is running on .NET4.0 and the mapping is done via FluentNHibernate (1.4.0).
CREATE TABLE Movies
(id INT PRIMARY KEY,
yearPublished DATETIME NOT NULL,
name NVARCHAR(500) NOT NULL,
description NTEXT NOT NULL)
The data would be something like this:
id | yearPublished | name | description
---+---------------+------------------------+--------------------------------------------
1 | 1968 | 2001: A Space Oddyssey | An epic drama of adventure and exploration
The Problem
I'm creating new entities of this table and want to avoid adding more than one entity for the same real world thing. I know that there is Session.SaveOrUpdate and that there is also a way to make it work with composite and natural ids but that's not really what I want since my entities actually have a primary key and I really only need the composite key for making sure that no duplicates are in the DB.
var movie = new Movies
{
yearPublished = 1968,
name = "2001: A Space Oddyssey",
description = "An awesome journey to Jupiter"
};
// Behavior right now:
// Adds a new movie besides the fact that
// the movie is already in the database
// but now has two entries
session.SaveOrUpdate(movie);
Assert.IsTrue(movie.id == 2 && movie.description == "An awesome journey to Jupiter");
// What I really want is to be able to define what
// makes an object unique other than the primary key;
// in this scenario it should look for a combination
// of "yearPublished" and "name"
session.MyAwesomeSaveOrUpdate(movie);
Assert.IsTrue(movie.id == 1 && movie.description == "An epic drama of adventure and exploration");
Is this functionality in place in NHibernate (e.g. through a custom mapping) or do I have fetch the candidates from the DB and do it by hand?
Thanks!

I solve this by adding a unique constraint on the natural key fields in the database and using an exception converter to convert the SQL Server exception into one my application can handle.
public class SqlServerExceptionConverter : ISQLExceptionConverter
{
public Exception Convert(AdoExceptionContextInfo adoExceptionContextInfo)
{
var sqlException = adoExceptionContextInfo.SqlException as SqlException;
if (sqlException != null)
{
// 2601 is unique key, 2627 is unique index; same thing:
// http://blog.sqlauthority.com/2007/04/26/sql-server-difference-between-unique-index-vs-unique-constraint/
if (sqlException.Number == 2601 || sqlException.Number == 2627)
{
// my custom exception
return new UniqueKeyException(sqlException.Message, sqlException);
}
}
return adoExceptionContextInfo.SqlException;
}
}
Another approach I can think of is to query the database for a matching record before the insert but that's not foolproof because a record could be inserted between the select and your insert.

Related

The MERGE statement conflicted with the CHECK constraint - Entity framework

I have the following code updating the place table with entity framework. Place table has Check constraint on column SequenceNumber. ([SequenceNumber]>=(1))
I am trying to update the table with entity framework but when i try to update it gives me error:
SqlException: The MERGE statement conflicted with the CHECK constraint "CK_Places_SequenceNumber". The conflict occurred in database "XXX", table "dbo.Places", column 'SequenceNumber'.
Below is the code. Just to try I even commented SequenceNumber assignment to avoid the update but i think it depends on the underlying state of entity as well.
var places = placeDtos.Select(x => new Place
{
PlaceId = x.PlaceId,
Name = x.Name,
SequenceNumber = x.SequenceNumber,
PlaceAreaId = areaId,
PlaceInterestPlaces = x.PlaceInterests?.Select(d => new PlaceInterestPlace
{
PlaceId = x.PlaceId,
PlaceInterestId = d.PlaceInterestId
})?.ToList()
})?.ToList();
_dbContext.Places.UpdateRange(desks);
await _dbContext.SaveChangesAsync();
Can someone help in making me understand whats the problem here? Thanks.

Room insert into one-to-many relationship

When trying to use intermediary classes to model entity relationships in Room, I have run into an issue. Whilst documentation describes how to get from a one-to-many relationship, it does not describe how to insert.
I'm assuming this cannot be done automatically, therefore we need a query to insert the parent, retrieve the ID for the parent and assign it to the child's foreign key, and then insert the child.
The problem is that I am unsure where to put such a query. If I include it in my DAO, then I will have to include superflous methods for inserting the child. If I include it in my Repository, this makes testing very difficult (if not impossible).
Does anyone know how to resolve this?
I'm assuming this cannot be done automatically, therefore we need a query to insert the parent, retrieve the ID for the parent and assign it to the child's foreign key, and then insert the child.
The first assumption is correct, that is that you have to supply the id of the parent (otherwise how is to know the parent).
However the second assumption that you have to query the parent is not always the case and not so in the scenario you describe. If when inserting a parent then the id is returned if using the convenience #Insert as a Long or an array of Longs (if inserting multiple Parents).
For example, say you have :-
#Entity
data class Parent(
#PrimaryKey
var id: Long? = null,
var other: String
)
and
#Entity
data class Child(
#PrimaryKey
var id: Long? = null,
var parentId: Long,
var otherdata: String
)
and an #Dao annotated class with :-
#Insert
fun insert(parent: Parent): Long
#Insert
fun insert(child: Child): Long
Then you can use the following, without having to query the Parent:-
var lastParent = dao.insert(Parent(other = "Parent1 other data"))
dao.insert(Child(parentId = lastParent, otherdata = "Child1 other data"))
dao.insert(Child(parentId = lastParent, otherdata = "Child2 other data"))
// Insert a Child with it's Parent together
dao.insert(Child(
parentId = dao.insert(Parent(other = "Parent2 other data")),
otherdata = "Child3 other data"
))
note even if you define the id's as Int, a Long is returned when inserting.
It is incorrect to use Int for an id as SQLite stores the id as a 64bit signed integer which is to large for an Int.
However, issues would not occur until the id reached a value that is too large for an Int (32bit signed) i.e. greater than 2,147,483,647.

DataIntegrityViolationException from concurrent access

I have a table that has a unique index constraint on two columns. My bit of code that does the check and insert into the database is as below:
def createNewEntry(myTableType: MyTableType) = database withSession { implicit session: Session =>
val autoInc = myTableElems returning myTableElems.map(_.elemId)
getElemForId(myTableType.elemId) match {
case Some(oldTableType) =>
if (oldTableType.isModified(myTableType))
myTableElems.insert(myTableType.copy(version = oldTableType.version + 1))
case None =>
autoInc.insert(myTableType)
}
getElemForId(myTableType.elemId)
}
Is there any possibility that the above code could result in a DataIntegrityViolation exception because of concurrent access tp the above method? I have a unique index defined on a column called name and another column called version. Every time I call this createNewEntry method, I check if one exists if yes, I copy everything, increment the version and insert the new version in the database.
Since I'm using Slick for database access, the above method works within a database transaction context and in auto commit mode.
My table with entries in the database would look like:
id name version
1 abc 1
2 xyz 1
3 abc 2
name and version has unique index constraint enforced!
Now the question is could there be a scenario where I could have a DataIntegrityViolation exception? Should I guard against it?

how to automatically manage the serial number in a database?

I have created an application in vb.net which accepts values from the user and inserts them into the database (created in SQL server 2008). It accepts the name, address, phone numbers, etc from the application itself. But the problem is, the user also needs to enter the serial number into the application. Is there a way to manage the serial number automatically? Because no one remembers the serial number of the last record. So, one needs to check the serial number of the last record in the database every time before assigning the serial number for the new record.
I also want that if any record is deleted from the middle of the database, the serial number should be adjusted automatically. So, what I want is, the serial numbers should be assigned automatically like 1 for the first record, 2 for the next, 3,4,5,6... and so on. But if the record corresponding to the serial number 5 is deleted, then the s.no. 6 should become s.no. 5.
Thanks in advance
Well you have to call one function every time you register a record on database. Before assigning record no check last number on database and add a count and save the same number as serial number. In the database create a column with serial_no as a Primary key,
CREATE TABLE table_name
(
Serial_no int NOT NULL,
LastName varchar(255),
FirstName varchar(255),
Address varchar(255),
City varchar(255),
PRIMARY KEY (Serial_no)
);
and sorry Im not much familiar with VB, here Im providing solution in C# and I hope you get an idea with this piece of code.
public void create_serial_no()
{
using (var connection = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["my_database_name"].ConnectionString))
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT [Serial_no] FROM table_name";
connection.Open();
string strs = command.CommandText;
string i = null;
int j = 0;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
i = reader["Serial_no"].ToString(); ;
}
j = Convert.ToInt32(i);
int k = 0;
k = j + 1;
// this is a serial no generated automaticall by the database
textBox1.Text = k.ToString();
}
}
Enjoy!

An error occurred while entries were being updated.

First of all, Sorry for the bad english.
I'm working on a university project with EF 4.0. Everytime i want to delete an entity item from the collection i get the error : "An error occurred while entries were being updated."
I have foreign keys.
I can't post images because i justa have 1 point of rep, so i can't show you de diagramas.
private void Elimina_Pais_btn_Click(object sender, RoutedEventArgs e)
{
int IdPaisABorrar = ((Pais)Tabla_Paises_DataGrid.SelectedItem).Id;
MundialEntities db = new MundialEntities();
Pais PaisABorrar = db.Paises.Single(p => p.Id == IdPaisABorrar);
if(PaisABorrar != null)
db.Paises.Remove(PaisABorrar);
db.SaveChanges();
UpdatePaises();
}
Inner Exception {"The DELETE statement conflicted with the REFERENCE constraint \"FK_Pais_Visita\". The conflict occurred in database \"Mundial\", table \"dbo.Partido\", column 'IdVisita'.\r\nThe statement has been terminated."}
Thank You very Much
You are trying to delete (remove) a record that is still being used in another table. So unless cascade delete can be setup on this record type, you will need to remove the other record/s at the sametime or before.
Either you can you cascade delete in your migration table or you can load the entities to delete them
Something like (believing that you have some table called as Visita (replace it with your table name)
Pais PaisABorrar = db.Paises
.Include(p => p.Visita)
.Single(p => p.Id == IdPaisABorrar);
The statement Include(p => p.Visita) will load all the related entries and then they will be deleted by call db.Paises.Remove(PaisABorrar);
NOTE: If this had helped you then dont forgot to vote it up and mark it as answer