I have the following model: An entity of type A has an association to many pairs of entities of types (B, C). Each B entity appears in at most one pair in A, but the same C entity can show in multiple pairs. For example:
A1 --> (B1, C1)
--> (B2, C2)
--> (B3, C1)
A2 --> (B1, C3)
--> (B2, C4)
--> (B3, C4)
This follows the semantics of a java.util.Map<B, C> stored in A. Further, each C only appears for one entity A, so the A to C relationship is OneToMany. I would like to persist such a Map, and a similar example appears in the Java EE6 doc. As far as I can tell, my code (below), is essentially identical to the example:
#Entity
public class A implements Serializable {
#OneToMany(fetch= FetchType.EAGER)
#JoinTable(name="A_BC_MAP",
joinColumns=#JoinColumn(name="A_ID"),
inverseJoinColumns=#JoinColumn(name="C_ID"))
#MapKeyJoinColumn(name="B_ID")
private Map<B, C> pinnedCourses = new HashMap<Course, ScheduleTerm>();
...
}
The database schema for A_BC_MAP emerges as expected, with a three-column join table (A_ID, B_ID, C_ID). However, the primary key constraint consists of the (A_ID, C_ID) pair. Thus, I cannot store more than one (map key) B in the database with the same (map value) C, as I can in the map.
Is this the expected behavior? For map semantics, I would expect the primary key to consist of (A_ID, B_ID). Am I doing something wrong?
I'm currently using EclipseLink 2.3.0.
I assume you are using EclipseLink to define your DDL? You could always use your own DDL script.
Having duplicates in a OneToMany is unusual, but is probably possible with the map key, you could log a bug to have the DDL generate define the map key as the primary key instead of the target foreign key.
Related
I have built this database
and I wonder why are my foreign and composite keys filling the tables. I need some help
To generalize, you have following setting: Tables A (id) and B (id, a_id) with B referencing A.
Now if you add a record to A, there will not be an automatically generated record in B referencing the new record in A. So not every record in A has to be referenced by a record in B.
In fact, foreign keys work the other way around. If you insert a new record into B, there already has to be a record in A, which has the corresponding id. And if there is no such record in A, you will get an exception. This means, that every record in B needs to reference a record in A.
Anyways, also in the second case, there is no auto-generation of records.
I have an EF 4 data model with TPT mapping.
I have a strange behaviour about the generated SQL of a query .
Lets say entity A is a base entity , A has two derived entities B and C , also A has many associations with other entities (say E,D).
When I make a simple select on A , Context.A.First() , I profiled the generated SQL from this entity and it has all the joins with the other entities.
Do you have a ny suggestion why this happen ? fixes ? any tip.
Thanks in advance ...
Context.A is the set of all A entities - including all B and C entities because every B and C is an A. It is not the set of all A entities that are not a B or C.
Therefore, if you request the first A in the database by Context.A.First() it could be a B or C or just an A. To find the concrete type of that first A the only way with TPT inheritance is to check if there are related records in the B or C table that have the same primary key like the first record in the A table. If there are related records this A is of type B (or C) and all column values from the record in table B (or C) have to be loaded together with the column values from the base record in table A in order to materialize an entity object of the correct type B (or C). If there are no related records in table B or C the concrete type is just an A.
In any case a join to the related B or C tables is required to figure out if there is a record or not and to determine the concrete type of the first A.
So, the joins you are seeing are expected behaviour when you use TPT inheritance and you can't avoid them. It has a negative impact on performance, yes, which is the biggest downside of TPT modeling.
Let'say i have the following tables:
Table A: id b-id
Table B: id property
Can I filter elements of table A like the following in JPQL language?
SELECT a FROM A a JOIN a.b-id targetId WHERE targetId.property = : someValue
I'd like to get table A's elements in which the referenced B element has property = someValue
And if I introduce a third table
Table A: id b-id
Table B: id c-id
Table C: id property
How can I get A's elements where c.property=someValue ?
I'm starting to get a sense of the power of ORM but some concepts are still vague to me.
Thank you for your answer
JPQL queries operate to entities, not to database tables. I assume names of entities and persistent attributes match to the names of tables and database columns given in question.
Because all relations in question are single valued, one-to-one or many-to-one (each A is connected only to one B (or possibly not to any), each B is connected to one C), specifying joins in query is not needed at all.
SELECT a FROM A a WHERE a.b.c.property = someValue
There is no need to worry about null values in path, because as said in JPA 2.0 specification:
Path expression navigability is composed using “inner join” semantics.
That is, if the value of a non-terminal field in the path expression is null,
the path is considered to have no value, and does not participate in the
determination of the result.
Same does not work for collection valued attributes (one-to-many, many-to-many), because it is not possible to navigate to their attributes via path expressions.
I have an inheritance chain rooted A with sub types B and C. Next, I've an entity E like this:
public class E
{
public Guid Id {get; set;}
public B B {get; set;}
public C C {get; set;}
}
The navigation properties should be mapped to two one-2-one associations where E is the principal and B, C the dependents. This won't work because EF will use the value of E.Id as foreign keys for B and C, which will result in a duplicate key in the table A. It doesn't matter if I use TPT, TPC, or TPH.
If I would map it two two one-2-many associations and use distinct foreign keys in in E, i.e. with the mapping:
Entity<E>().HasRequired(x=>x.B).WithMany().HasForeignKey(x=>x.BId);
Entity<E>().HasRequired(x=>x.C).WithMany().HasForeignKey(x=>x.CId);
Than cascading delete wont work the in the right direction. That is E.B and E.C should be deleted when deleting e.
Any suggestions how to deal with this?
EF requires one mapped column per relationship (inheritance or association). You use one column up for the inheritance, therefore this column is not available in subclasses for association relationships (because you have to delete it from the subclasses).
You have 2 choices:
You can map E's 1-1 relationship with B and C by linking it to A (the superclass). But this is not an accurate representation of your model because the {B,C} relationships are to E, not to A.
Add another column to B and C that will be used for the associations to E. This new column should be unique and not null - because effectively you are creating another primary key. Both the existing primary key and the new column uniquely identify the record (for B or C). In the database, create the constraints between {B,C} and E using this new column. Now EF will be able to both represent the inheritance {A<-B, A<-C} and the association {E--B, E--C}
Suppose the following database schema:
Table A: AId (PK)
Table B: BId (PK)
Table C: CId (PK)
Table AB: AId, BId (composite PK, FKs to A and B), Data
Table BC: BId, CId (composite PK, FKs to B and C), Data
Table ABC: AId, BId, CId, Data
In the database, ABC has two FKs: one to AB on AId and BId, and one to BC on BId and CId.
Use the EF Designer and attempt to create a Model from this database.
If you have Include foreign key columns in the model checked, it works; but having FK Columns in the model isn't very nice.
If you have Include foreign key columns in the model unchecked, only one of the FKs from ABC will be successfully mapped. To see what went wrong, you have to view the .edmx xml (thanks Craig!) and you see this error:
warning 6037: Foreign key constraint 'FK_ABC_BC' has been omitted from the storage model. Column 'BId' of table 'Model.Store.ABC' is a foreign key participating in multiple relationships. A one-to-one Entity Model will not validate since data inconsistency is possible.
I've read the only other mention of this problem I can find on SO, and I don't think this is the same problem. I can't see anything wrong at a database design level. I'm going to work round this for the time being by imposing surrogate keys on AB and BC, but what I'd really like to know is:
What possible data inconsistency is EF worried about happening here, if it created a model to match the database?
And is there anything I can do to persuade it that everything's going to be OK?
My opinion is that EF is too clever in this scenario and it prevents you from using entity where you can assign only one relation and make the entity non-savable because relation to second entity will not exists.
There is also possibility that EF has some internal problem with tracking state of independent associations if more than one association is based on the same foreign key column but that is just another guess. Generally database features used to map EF features cannot be shared among multiple constructions. The only exceptions I can think about now are primary keys and in their own way discriminator columns.
I would like to mention that I don't like this type of relations in database at all.