JPA optional relationship - jpa

I'm using EclipseLink.
I have an entity DIDRequest that has a one to Many relationship with DIDAllocation.
The DIDRequest does not always have an associated DIDAllocation (i.e. the DIDAlloction should be optional).
I have the following entity
#Entity
#Table(name = "tblDIDRequest")
public class DIDRequest
{
int id;
#ManyToOne(optional = true)
private DIDAllocation didAllocation;
}
The optional argument doesn't seem to be working as EclipseLink is generating a foriegn key constraint as follows:
CREATE TABLE `tblDIDRequest` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`DIDALLOCATION_ID` bigint(20) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `FK_tblDIDRequest_DIDALLOCATION_ID` (`DIDALLOCATION_ID`),
CONSTRAINT `FK_tblDIDRequest_DIDALLOCATION_ID` FOREIGN KEY (`DIDALLOCATION_ID`) REFERENCES `tblDIDAllocation` (`ID`),
);
How do I modify the annotation so it doesn't generate the foreign key constraint?
Is there a way to have the constraint so if didAllocation exist then there must be a related entity but if it is null we don't care?

This works just like it should!
A required association can be mapped by having a NOT NULL constraint on a column (to make sure it has a value) and also a FOREIGN KEY constraint, to make sure that value corresponds to the ID in another table.
If you have an optional association, you still want a FOREIGN KEY constraint, so you can't just put gibberish into the column. But you don't have the NOT NULL constraint, so NULL indicates "the association is not present".
`DIDALLOCATION_ID` bigint(20) DEFAULT NULL,
This is nullable and has a default value of NULL.
You may also want to set fetch = FetchType.LAZY on the association and explicitly join/eager-fetch it only when you need it - for efficiency. And in general I highly recommend all articles by Vlad Mihalcea to learn more about efficient JPA use: https://vladmihalcea.com/manytoone-jpa-hibernate/

Related

OData $metadata error

I have a WCF Data Service (5.5) sitting over an EF (5.0) model,
I'm getting the following error when I query $metadata:
"An IEdmModel instance was found that failed validation. The following errors were reported:
InvalidMultiplicityOfDependentEnd : The multiplicity of the dependent end 'QuestionsetMember' is not valid. Because the dependent properties don't represent the dependent end key, the the multiplicity of the dependent end must be '*'."
QuestionsetMember has a composite primary key of 2 columns, each of which is hooked to a primary key of another table, i.e. a foreign key exist from each column of the key to the two tables' primary keys.
I've searched but cannot find any info on "InvalidMultiplicityOfDependentEnd".
Also tried fiddling with the relationships in the EDMX, but changing the End Multiplicity causes errors which won't allow the model to compile.
Any ideas how to get round this (hopefully without changing my schema) ?
This seems to be a very rare error. I did not find anywhere else an explanation of that error. So i did find for me a solution after inspecting every single constraint and every column in both tables. To my great surprise the order of the primary key columns seems to be relevant.
For explanation: I do the database-design within the sql server management studion, and update my model with the entity framework designer.
First Table:
CREATE TABLE Table1
(
Column1 int NOT NULL,
Column2 int NOT NULL,
PRIMARY KEY (Column1,Column2)
)
Second Table:
CREATE TABLE Table2
(
Column1 int NOT NULL,
Column2 int NOT NULL,
PRIMARY KEY (Column1,Column2)
FOREIGN KEY (Column1,Column2) REFERENCES Table1(Column1,Column2)
)
This would work. But it do not work, if you would define the columns of the primary key in the second table in another order:
CREATE TABLE Table2
(
-- Changed order in definition:
Column1 int NOT NULL,
Column2 int NOT NULL,
-- Changed order in PK group:
PRIMARY KEY (Column2,Column1)
-- Leave the FK definition untouched:
FOREIGN KEY (Column1,Column2) REFERENCES Table1(Column1,Column2)
)
I think the order of the column definition has impact on the generated model. And this order could maybe have an impact in the model validation within the IEdmModel class. Who knows...

In a JPA entity hierarchy using InheritanceType.JOINED, all relationships with subclasses results in foreign key constraints on the superclass table

I have the following JPA 2.0 Entities
#Entity
#Inheritance(strategy= InheritanceType.JOINED)
public abstract class BookKeepingParent implements Serializable {
#Id
protected Long Id;
...
}
#Entity
public class Employee extends BookKeepingParent {
private String name;
#ManyToOne
private Role role;
...
}
#Entity
public class Role extends BookKeepingParent {
private String name;
...
}
I want to let JPA generate tables for me, since it makes it easier to install at multiple locations. I would usually expected it to generate this:
CREATE TABLE bookkeepingparent (
id bigint NOT NULL,
dtype character varying(31),
CONSTRAINT bookkeepingparent_pkey PRIMARY KEY (id )
)
CREATE TABLE role (
id bigint NOT NULL,
name character varying(255),
CONSTRAINT role_pkey PRIMARY KEY (id ),
CONSTRAINT fk_role_id FOREIGN KEY (id) REFERENCES bookkeepingparent (id)
)
CREATE TABLE employee (
id bigint NOT NULL,
name character varying(255),
role_id bigint,
CONSTRAINT employee_pkey PRIMARY KEY (id ),
CONSTRAINT fk_employee_id FOREIGN KEY (id) REFERENCES bookkeepingparent (id),
CONSTRAINT fk_employee_role_id FOREIGN KEY (role_id) REFERENCES role (id)
)
First two tables where the same, but it generated the employee table this way:
CREATE TABLE employee (
id bigint NOT NULL,
name character varying(255),
role_id bigint,
CONSTRAINT employee_pkey PRIMARY KEY (id ),
CONSTRAINT fk_employee_id FOREIGN KEY (id) REFERENCES bookkeepingparent (id),
CONSTRAINT fk_employee_role_id FOREIGN KEY (role_id) REFERENCES bookkeepingparent (id)
)
You can notice that the fk_employee_role_id references the bookkeepingparent table, instead of the role table. I have a large heirarchy of JPA entities, and I want the bookkeepingparent to be the superclass of most of them. This is primarily because of some very specific Id generation strategies and other bookkeeping activities. This design, helps keep all this book keeping code separate from the functional code, and let programmers working on the functional code not worry about it.
All this worked alright till the the number of tables grew. Now we see that, for all ManyToOne and OneToOne relationships, JPA is generating foreign keys referring to the parent table. With 200 odd tables, the inserts are already slow because all foreign key constraints refer to the bookekeepingparent, and every entity, when persisted for the first time, inserts into the bookkeeping parent table. Which I guess is checking some 150 odd constraints.
So, following are my questions: why is JPA doing it? Is it a standard JPA behaviour? (I'm using EclipseLink) If I manually change the DB schema, are their any pitfalls to expect?
This is my first question on StackOverflow, I tried my best to search for any existing answers. Apologies if I missed any. Thanks.
You are using joined inheritance, which means that for every class, the bookkeepingparenttable is the main table and any subclass table is secondary. The primary key for the subclasses are inherited from the parent, and foreign keys must reference the id, so the will all reference the id in bookkeepingparenttable by design. Different providers allow referencing non-pk fields, but it can cause problems as resolving references can require database hits instead of using the cache.
The database constraints are not JPA related, so you can change them as required and not affect the app as long as inserts updates and deletes will still conform.

Create one to one relationship by using entity framework database first

In EF Code First, we can create one-to-one relationship by coding like this:
public class User
{
public int UserID {get;set;}
public string Name {get;set;}
public int UserDetailID {get;set;}
public UserDetail Detail {get;set;}
}
public class UserDetail
{
public int UserDetailID {get;set;}
public string Address {get;set:}
public int UserID {get;set;}
public User User {get;set;}
}
However, when I tried to create the same relationship by using EF Database first in visual studio 2012, I got in trouble. Here is my code:
CREATE TABLE [dbo].[Users] (
[UserID] UNIQUEIDENTIFIER CONSTRAINT [DF_Users_UserID] DEFAULT (newid()) NOT NULL,
[UserDetailID] UNIQUEIDENTIFIER NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ([UserID] ASC),
CONSTRAINT [FK_Users_UserDetails] FOREIGN KEY ([UserDetailID]) REFERENCES [UserDetails]([UserDetailID])
);
CREATE TABLE [dbo].UserDetails] (
[UserDetailID] UNIQUEIDENTIFIER CONSTRAINT [DF_UserDetails_UserDetailID] DEFAULT (newid()) NOT NULL,
[UserID] UNIQUEIDENTIFIER NOT NULL,
[Address] NVARCHAR(100) NOT NULL,
CONSTRAINT [PK_UserDetails] PRIMARY KEY CLUSTERED ([UserDetailID] ASC),
CONSTRAINT [FK_UserDetails_Users] FOREIGN KEY ([UserID]) REFERENCES [dbo].[Users] ([UserID])
The error message is something like
"Error 2 SQL01767: Foreign key 'FK_Users_UserDetails' references invalid table 'UserDetails'.
I think the reason for this error probably be when it tries to reference the foreign key "UserDetailID", it finds that it hasn't been created yet. But I don't know how to fix this, and I don't even know this is the way to do it, I know doing one-to-one relationship with EF is tricky, or some people even says it's impossible. Can anyone give me any suggestion? Thank you.
Update: Just to clarify my case, I am trying to design the database in visual studio 2012 database project, then publish it to the SQL server, afterward, create/update my .edmx file from the database in SQL server. I am not sure about how to create a one-to-one relationship that the EF can recognize correctly and create the right classes in .edmx file.
Creating a 1:1 relationship is not that tricky and certainly not impossible, although it is not a particularly common requirement and in this case I can't see why you would want it? If people are saying this then you are talking to the wrong people.
Anyhow using SQL queries as you seem to be is not to do with EF, you are just working directly with the database, In the first CREATE you are trying to add the constraint but you haven't created the other table yet... As you mentioned in your question.
I think you need to create both tables first and then add the constraint with ALTER TABLE.
Additionally searching SO for questions about 1:1 turns up quite a lot so I suggest you do that.
EDIT: So using a database project (I only have VS Express so I don't have those) you want to create a "1:1" relationship using SQL and then add an Entity Data Model to a (probably different) project which references the database and automatically create 1:1 relationship?
That is a whole different story unfortunately. When I was talking about possibility to create 1:1 that was in reference to EF only and not to databases as such. It is actually very difficult/impossible as you said to create 1:1 in SQL. I think that it makes sense that in order to insert into a 1:1 realationship you would need to somehow insert into both tables at exactly the same time or fiddle about with disabling constraints briefly when adding rows.
In general there are a few different option.
Don't split the tables unnecessarily. In true 1:1 all data is required so the only reason to split is for performance reasons (e.g partioning) which I would avoid in this case.
Map multiple table to a single entity as show here.
Create a 1:0..1 relationship and enforce you own requirements in the application.
In either option 2 or 3 you can use the following SQL to create a relationship which uses the same PK on the second table as the FK in the relationship.
CREATE TABLE [dbo].[Users] (
[UserID] UNIQUEIDENTIFIER CONSTRAINT [DF_Users_UserID] DEFAULT (newid()) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ([UserID] ASC),
);
CREATE TABLE [dbo].[UserDetails] (
[UserID] UNIQUEIDENTIFIER NOT NULL,
[Address] NVARCHAR(100) NOT NULL,
CONSTRAINT [PK_UserDetails] PRIMARY KEY CLUSTERED ([UserID] ASC),
CONSTRAINT [FK_UserDetails_Users] FOREIGN KEY ([UserID]) REFERENCES [dbo].[Users] ([UserID]) ON DELETE CASCADE
);
I suggest you also use store generated identity as well where you can.
Just remove UserDetailID from the UserDetail table and make UserID both primary key and a foreign key to the UserID column of the User table.
This the correct way to make 1:1 relationships in a database and EF recognizes it and maps the entities appropriately with database-first approach.
The question is a couple years old.. and the ef version wasn't stated.. but one answer is to remove UserDetailID from both tables. UserID should be the only primary key on both tables.
the 'unqieidentifier' (GUID) data type shouldn't pose an issue (opposed to using INT), but you certainly don't want to populate it with newId..

ON DELETE SET NULL ON UPDATE SET NULL using JPA

How do i add ON DELETE SET NULL ON UPDATE SET NULL Constraint while creating the table using JPA . Below the entity definition
table
CREATE TABLE `node` (
`id` bigint(20) NOT NULL,
`parentNode_id` bigint(20) default NULL,
PRIMARY KEY (`id`),
KEY `FK1EC1DD0F28AB6BA5` (`parentNode_id`),
CONSTRAINT `FK1EC1DD0F28AB6BA5` FOREIGN KEY (`parentNode_id`) REFERENCES `node` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
#Entity
public class Node {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "NodeSequence")
private long id;
#ManyToOne(cascade=CascadeType.REMOVE)
#JoinColumn(name = "parentNode_id", nullable = true)
protected Node parentNode = null;
}
It is a bad idea to use the JPA schema generator for this purpose. It is almost always preferable to have DDL scripts that are hand-coded and placed under version control, to manage your database.
Also, setting foreign key values to null via database constraints, will only result in your persistence context having dirty values, unless the JPA provider is aware of the foreign key constraints in the database, and consciously updates the corresponding values in the persistence context to null.
Also, it is unlikely that JPA schema generators support this feature. For instance, Hibernate does not support this yet, and there are multiple feature requests that have been raised, and none of them have been resolved. EclipseLink also does not appear to support this feature.

How do I eliminate Error 3002?

Say I have the following table definitions in SQL Server 2008:
CREATE TABLE Person
(PersonId INT IDENTITY NOT NULL PRIMARY KEY,
Name VARCHAR(50) NOT NULL,
ManyMoreIrrelevantColumns VARCHAR(MAX) NOT NULL)
CREATE TABLE Model
(ModelId INT IDENTITY NOT NULL PRIMARY KEY,
ModelName VARCHAR(50) NOT NULL,
Description VARCHAR(200) NULL)
CREATE TABLE ModelScore
(ModelId INT NOT NULL REFERENCES Model (ModelId),
Score INT NOT NULL,
Definition VARCHAR(100) NULL,
PRIMARY KEY (ModelId, Score))
CREATE TABLE PersonModelScore
(PersonId INT NOT NULL REFERENCES Person (PersonId),
ModelId INT NOT NULL,
Score INT NOT NULL,
PRIMARY KEY (PersonId, ModelId),
FOREIGN KEY (ModelId, Score) REFERENCES ModelScore (ModelId, Score))
The idea here is that each Person may have only one ModelScore per Model, but each Person may have a score for any number of defined Models. As far as I can tell, this SQL should enforce these constraints naturally. The ModelScore has a particular "meaning," which is contained in the Definition. Nothing earth-shattering there.
Now, I try translating this into Entity Framework using the designer. After updating the model from the database and doing some editing, I have a Person object, a Model object, and a ModelScore object. PersonModelScore, being a join table, is not an object but rather is included as an association with some other name (let's say ModelScorePersonAssociation). The mapping details for the association are as follows:
- Association
- Maps to PersonModelScore
- ModelScore
ModelId : Int32 <=> ModelId : int
Score : Int32 <=> Score : int
- Person
PersonId : Int32 <=> PersonId : int
On the right-hand side, the ModelId and PersonId values have primary key symbols, but the Score value does not.
Upon compilation, I get:
Error 3002: Problem in Mapping Fragment starting at line 5190: Potential runtime violation of table PersonModelScore's keys (PersonModelScore.ModelId, PersonModelScore.PersonId): Columns (PersonModelScore.PersonId, PersonModelScore.ModelId) are mapped to EntitySet ModelScorePersonAssociation's properties (ModelScorePersonAssociation.Person.PersonId, ModelScorePersonAssociation.ModelScore.ModelId) on the conceptual side but they do not form the EntitySet's key properties (ModelScorePersonAssociation.ModelScore.ModelId, ModelScorePersonAssociation.ModelScore.Score, ModelScorePersonAssociation.Person.PersonId).
What have I done wrong in the designer or otherwise, and how can I fix the error?
Many thanks!
Very late to your question, I had the same issue and discovered that the entity framework designer had identified my "ScoreId" column (relative to your PersonModelScore table) as a primary key column. I changed my setting to false for my ScoreId, and all worked well afterward.
You can set single primary key in the Entity in order to avoid this error.Right Click on the Scalar Properties of the field in the Entity and disable Entity Key if there are many primary keys.
You should create a single Identity key for each table.
ModelScore should have a ModelScoreId, PersonModelScore should have a PersonModelScoreId.
References between table should be a single field.
Your PersonModelScore table should define an Id column that is identity and primary key, then
you should create a unique key on PersonId, ModelId
as for Error 3002, i had the same problem ALL my field had been marked Entity key by EF
"Go to your .edmx file, right click on the background and select 'Mapping Details'. Click on the Table you need to edit the mappings of in your .edmx window and the details should appear in your new 'Mapping Details' window. Open Properties tab (Hit F4 to bring up the 'Properties' if not open) Click on the 'Value/Property' in your 'Mapping Details' to change the Properties displayed, and from your Properties window you can now set the 'Entity Key' value to 'False'. – Chris Paton Oct 4 '14 at 18:54"
This worked for me - Thanks
This is now part of my workflow when using EF Database First. And we have a task out to update the data model.