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.
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.
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 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.
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.
Is there any way of getting discriminated associations to work in Entity Framework 4? That is, where we have the following tables
TableA
RelatedEntityTypeId
RelatedEntityTypeKey
TableB (1)
Id
TableC (2)
Id
TableD (3)
Id
and I want to have three associations on the entity for TableA:
TableB
TableC
TableD
which are defined by the RelatedEntityTypeId and RelatedEntityTypeKey fields...when RelatedEntityTypeId = 1, then the association is to EntityB, when RelatedEntityTypeId = 2, then the association is to EntityC, etc.
Thanks.
I don't know your purpose of doing this. I have used following approach to solve similer problem.
You can define a base type for all three tables (A,B,C). And when you want retrieve a object use a generic method for all tables (which returns a base object). And then you can check the type of the returned object to get the A,B,C object.
TableBase
Id
TableB (1):TableBase
TableC (2):TableBase
TableD (3):TableBase