I have a unique situation working with a legacy application where I have a parent/child relationship that is based on two integer values. Unfortunately, these fields are not id and parentId or something similar. The columns are ItemId and SubItemId. When these two columns equal each other, the item is assumed to be a parent. When they differ, the ItemId references the Items parent and the SubItemId is simply an identifier of which child it is.
Here is an example
ItemId = 1, SubItemId = 1: Parent Item
ItemId = 1, SubItemId = 6: Sub Item, whose parent is the Item where the id fields are (1, 1)
ItemId = 2, SubItemId = 2: Parent Item
ItemId = 2, SubItemId = 9: Sub Item, whose parent is the Item where the id fields are (2, 2)
So with this information, I have a class hierarchy set up like this:
public class Item
public property ItemId as Integer
public property SubItemId as Integer
end class
class ParentItem : Item
end class
class SubItem : Item
public property ParentItem as ParentItem
end class
So I would like to map the ParentItem property of the SubItem class to its corresponding ParentItem using NHibernate and I can't for the life of me figure out how. I was able to get NHibernate to instantiate the correct class based on a formula discriminator, and I was hoping something similar was available for many-to-one relationships.
I'm not able to change the table structure, which certainly limits my options. Also, I'm using FluentNHibernate for my mappings, so feel free to offer suggestions using its syntax.
Thanks!
Update
I created a view that added two new columns that equated to a foreign key pointing to the parent and now I'm getting an NHibernate mapping error:
Foreign key (FK163E572EF90BD69A:ItemsNHibernateView [ParentItemID, ParentrSubitemID])) must have same number of columns as the referenced primary key (ItemsNHibernateView [ItemID, SubitemID])
This is throwing me off because to me, it looks like both keys do have the same number of columns...
Ok, I figured it out based on the comment from Ben Hoffstein. I created the view as I mentioned in the update to my original question. The error was caused by an embarassingly stupid error. When I created the array to list the column names for the foreign key, I put both names in the quotes like this:
new string() {"ParentItemID, ParentSubitemID"}
instead of:
new string() {"ParentItemID", "ParentSubitemID"}
The way that NHibernate was displaying the error threw me off the scent.
Related
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.
I am having two tables One is a Master table called TRANSACTION and second is record of the transaction this table is called TRANSACTION_RECORD.
TRANSACTION
CREATE TABLE `e3_transaction` (
`transactionid` bigint(20),
`transactiontype` varchar(10),
`transactionstatus` varchar(10),
PRIMARY KEY (`transactionid`)
);
TRANSACTION_RECORD
CREATE TABLE `e3_as2805msg4` (
`transectionid` bigint(20),
`messageType` int(4),
`cardAcceptorName` varchar(40),
`adNational` varchar(1000),
`adPrivate` varchar(1000),
KEY `transectionidFK` (`transectionid`),
CONSTRAINT `transectionidFK` FOREIGN KEY (`transectionid`) REFERENCES `e3_transaction` (`transactionid`)
);
It will have one to one mapping between Transaction and transaction record. It means one transaction can have only one record. I have kept this table separately for some reasons. So my class will look like this:
#Entity
#Table(name = "e3_transaction")
public class Transaction {
#Id
#GeneratedValue(generator = "assigned-by-code")
#GenericGenerator(name = "assigned-by-code", strategy = "assigned")
#Column(name = "transactionid", unique = true, nullable = false)
private Long transactionid;
#Column(name = "transactiontype", nullable = false, length = 10)
private String transactiontype;
#Column(name = "transactionstatus", nullable = false, length = 10)
private String transactionstatus;
#oneToOne
private TransactionRecord record;
}
I want to persist both objects at a same time. when I persist a TRANSACTION, TRANSACTION_RECORD should be persist in it's table. Is there any way to do this ?
You can change the table structure if you want. Only thing i need it TWO tables.
Works with
#OneToOne(cascade=CascadeType.ALL)
#MapsId
private TransactionRecord record;
TransactionRecord must have an #Id of the same type as Transaction with no value generation.
Tried with Hibernate as JPA 2.0-provider.
There are a few options to map this, but it looks like you are handling it with two separate entities. As both entities share the same primary key value, your reference mapping will need to change based on which entity you wish to have controlling the pk value generation - as it stands, the e3_transaction.transactionid field is being set by two separate mappings; the transactionid long and the TransactionRecord record reference.
If you wish to use the #MapsId as is suggested in the other answer, you will need to move your #GeneratedValue code to the TransactionRecord entity, as the JPA provider will use the value in the referenced TransactionRecord to set the transactionid attribute and the database field. This is a simple elegant solution, but you can also remove the Long transactionid attribute from Transaction and just mark the record reference with #Id (instead of #MapsId). The long transactionId value within TransactionRecord would still be used as Transaction's id for EntityManager getReference and find calls.
A different option that allows keeping the #GeneratedValue on the transactionid within Transaction is to define the #JoinColumn annotation on the record reference and specify that the field is insertable=false, updatable=false. You then need to modify the TransactionRecord so that it has a back relationship to the Transaction so that it can pull the transectionid value from the Transaction instance to use as its id. This can be accomplished by simply marking the relationship with #ID though.
I want to retrieve each column name and data type then check if the columns is foreign key then query they key table of that relation !!! could be done ?? I googled for 3 days I know that I have to use Mappping model OR Reflection or both ,,,, but i cant do it .
I will simplify what i need assuming :
TABLE1 hase foreign key( COL3) refer to the primary key (COL1) in TABLE0 :
iterate TABLE1 Columns check EACH columns if it is a foreign key ( also get its data type)
Get the relation to determine the associated table(TABLE0)
retrieve the primary key tables (TABLE0)
I got it
I make a function that return the type of each foreign key and the related table class type
Private Function GetForeignKeyTables(ByVal myTableType As Type) As List(Of myForeignKeys)
Dim myDx = New Tester.DataClasses1DataContext
Dim mymodel As New AttributeMappingSource
Dim myAsociations = mymodel.GetModel(GetType(DataClasses1DataContext)).GetTable(myTableType).RowType.Associations
Dim asc = From m In myAsociations Where m.IsForeignKey
Select New myForeignKeys With {.KeyDataType = m.ThisKey.First.DbType, .RelatedTableType = m.OtherType}
Return asc.ToList
End Function
Private Class myForeignKeys
Property KeyDataType As String
Property RelatedTableType As MetaType
End Class
But I still need to retrieve the data from those related table .
I mean how to create an instance of the class from its MetaType variable?
I am looking for a howto or someone expert in ADO.NET who can explain me how to properly solve the following scenario:
I have two datatables in a dataset:
ParentTable (ParentID, Name) for user data
ChildTable (ParentID, ActivityID, ...) for schedule data
Tables are linked together at the database level by ParentID which is an Identity column in ParentTable.
Both tables are data bound to a separate DataGridView on the GUI. There supposed to be a "1 parent/N children" relationship between the tables, meaning if I create a new entry in the ParentTable (a new user) I get a clean DataGrid in the child grid to type schedule data for the user. So I setup two DataAdapters for each table to fill their result into a DataSet. I also set up a DataRelation object and assign it to the DataSet to link the two tables by their ParentID columns. Also when I add a row into the ChildTable via DataGrid I use SetParentRow to set the parent row.
With DAUser
.SelectCommand = New SqlCommand("UsersSelect", conn)
.SelectCommand.CommandType = CommandType.StoredProcedure
.SelectCommand.Parameters.AddWithValue("#UserID", MyID)
.Fill(DSData)
End With
With DAActivity
.SelectCommand = New SqlCommand("ActivitiesSelect", conn)
.SelectCommand.CommandType = CommandType.StoredProcedure
.SelectCommand.Parameters.AddWithValue("#UserID", MyID)
.Fill(DSData)
End With
DSData.Relations.Add(New DataRelation("UserActivity", DSData.Tables(0).Columns("ParentID"), DSData.Tables(1).Columns("ParentID")))
Adding row into the ChildTable:
Private Sub DTChildData_TableNewRow(sender As Object, e As System.Data.DataTableNewRowEventArgs) Handles DTChildData.TableNewRow
e.Row.SetParentRow(ParentDataRow)
End Sub
Still, the ParentID column in ChildTable is not populated with the Identity value retrieved from the DB when ParentTable is updated.
Why? I am starting to loose serious amount of hair over this problem...
A couple of notes:
1) The relation between the two tables is most likely incorrect, since you are referencing ParentID on both sides of the relationship. I am guessing that you need to change it to:
DSData.Relations.Add(New DataRelation("UserActivity", DSData.Tables(0).Columns("UserID"), DSData.Tables(1).Columns("ParentID")))
2) You aren't showing your insert or update commands, but if they are stored procedures and the UserID is generated in that stored procedure, then you will need to pass it back out of the stored procedure as an OUTPUT parameter. You will have to change both the SP and the parameter definition (if you have one) in the insert/updated command.
I have the Tables
PatientEligibilit
and
PatientsEligibilitiesDoctorsSpecialties
and
DoctorsSpecialties
PatientEligibilit
has foreign key PatientsEligibilitiesDoctorsSpecialtyID from
PatientsEligibilitiesDoctorsSpecialties
table
and
PatientsEligibilitiesDoctorsSpecialty
has foreign key DoctorsSpecialtyID from
DoctorsSpecialties
table
THEN USING VB.NET LINQ: i'm tring to add child item ( PatientsEligibilitiesDoctorsSpecialty)
to it's parent (PatientEligibilit)
then I submit Changes
like :
PatientEligibilityObject.PatientsEligibilitiesDoctorsSpecialties.Add(New PatientsEligibilitiesDoctorsSpecialty With {.DoctorSpecialtyID = si.ID, .RegDate = Date.Now}) PatientEligibilityObject.PatientsEligibilitiesDoctorsSpecialties.Add(PEDS)
HMSData.SubmitChanges()
it's worked fine and save record in Database with correct date
BUT
DoctorSpecialtyID
always saved with value 1
I solve it. the problem was in the relation between the tables.
The foreign key between PatientEligibilit and PatientsEligibilitiesDoctorsSpecialties
was not correct..