| Summary: | Incorrect warning about IdClass when TenantDiscriminatorColumn primaryKey is used | ||||||
|---|---|---|---|---|---|---|---|
| Product: | z_Archived | Reporter: | Karen Butzke <karenfbutzke> | ||||
| Component: | Eclipselink | Assignee: | Project Inbox <eclipselink.orm-inbox> | ||||
| Status: | NEW --- | QA Contact: | |||||
| Severity: | normal | ||||||
| Priority: | P2 | CC: | guy.pelletier, jhgoyer, martin.grebac, tom.ware | ||||
| Version: | unspecified | ||||||
| Target Milestone: | --- | ||||||
| Hardware: | PC | ||||||
| OS: | Windows XP | ||||||
| Whiteboard: | multitenancy | ||||||
| Attachments: |
|
||||||
Created attachment 226886 [details]
Maven project containing working example that generates exception. Uses derby embedded.
Recent email thread regarding this bug: Hi John, When the discriminator column is not tagged as primaryKey=true, it is not treated as such and therefore does not form part of the id where clause. That being said, on a find, EclipseLink will augment the where clause to include the discriminator column. From those results, you can modify the objects and commit them back to the database. EclipseLink does not need to check the discriminator column once again since it already used it to filter your results. As for updating using a named query you would see the sql augmented with the tenant discriminator column so that you can not update other tenants. Try a update named with query with logging set to FINEST and you will see the discriminator column. From what I can tell the only issue is allowing a primaryKey=true specification with an EmbeddedId. Cheers, Guy On 29/01/2013 4:31 PM, John H Goyer wrote: > Thanks for the quick response. I will enter a bug, possibly two :) > > As for your questions, I am updating objects that were read from the db. > The find does not use the discriminator explicitly. In other words, I was > able to use entityManager.find( someclass.class, id ) where id did NOT include the > discriminator field. I did see the discriminator correctly generated as part of the > find criteria in this case. The update statement lacked the discriminator. > > This system is a service layer backing a web services application. Generally the > persistence context is recreated between the find of an object and its update as > the consumer is remote. > > On Tue, Jan 29, 2013 at 3:14 PM, Guy Pelletier <guy.pelletier@oracle.com> wrote: > > Hi John, > > Looks like a bug on our part with regards to the EmbeddedId setting, can you enter one and someone will have a look. > > When the primary-key is set to false, unless I have missed something, you're updating something that you were able to read. Meaning you would not have been able to update it if you couldn't read it? Therefore avoiding the need of the discriminator column on the update. Or were you updating through a named query? > > Cheers, > Guy > > On 29/01/2013 3:53 PM, John H Goyer wrote: >> Hi All, >> >> I have run into an issue using Multitenancy and wanted to see if there was a fix available >> now or in the future. I have a workaround for now, but it ain't pretty. I am using >> 2.4.0 but found the same behavior in 2.4.1. >> >> The issue involves the primaryKey=true flag in the tenant discriminator declaration. >> It affects two situations: Keys in classes that use JPA Inheritance and those that >> use @EmbeddedId classes for ids. This post concentrates on the @EmbeddedId >> situation but I think the cause is the same in both cases. >> >> I have a base class, BaseEntityFieldAccess, that is subclassed by a large number of entities. >> >> The annotations on this class look like so: >> >> @MappedSuperclass >> @Multitenant( MultitenantType.SINGLE_TABLE ) >> @TenantDiscriminatorColumn( name="xxxx", contextProperty="tenant.xxxx", primaryKey=true ) >> public abstract class BaseEntityFieldAccess extends AbstractBaseEntity { >> >> And its parent like this: >> >> /** >> * Parent of all entities. Allows convenient use of >> * <T extends AbstractBaseEntity> in generic classes. >> */ >> @MappedSuperclass >> public abstract class AbstractBaseEntity { >> public static final String MULTITENANT_CONTEXT_PROPERTY = "tenant.xxxx"; >> public abstract <K> K getId(); >> >> This arrangement allows me to have an entity class that does not declare the >> tenant discriminator as a field. Example: >> >> @Entity >> @Table(name="address") >> public class Address extends BaseEntityFieldAccess implements Serializable, Comparable<Address> >> @Id >> @Column(name="addressId") >> private String id; >> >> The code can then do CRUD operations using this class and eclipselink magically generates >> update statements that include both the id column declared in the entity (code) and the >> discriminator column (xxxx) which *is* part of the primary key in the database. >> >> PROBLEM: >> >> When I try to use a composite primary key class in place of a single-valued @Id (with @EmbeddedId >> annotation) I get an error with the primaryKey=true flag is set. The EmbeddedId class also >> does not know about the tenant discriminator. >> >> Here's an example of the exception I am seeing: >> ------------------------------------------------------------------------------- >> Test set: xxx.yyy.zzz.JPATestSuite >> ------------------------------------------------------------------------------- >> Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.278 sec <<< FAILURE! >> gov.usc.ao.TestClientDao2 Time elapsed: 0 sec <<< ERROR! >> org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.dao.annotation.PersistenceExcept >> ionTranslationPostProcessor#0' defined in class path resource [applicationContext.xml]: Initialization of bean failed; nested exception is o >> rg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource >> [applicationContext.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Exception [EclipseLi >> nk-28018] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.EntityManagerSetupException >> Exception Description: Predeployment of PersistenceUnit [pg1-jpa-model-test] failed. >> Internal Exception: Exception [EclipseLink-7163] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions >> .ValidationException >> Exception Description: Entity class [class xxx.yyy.zzz.ChronosContactCode] has both an @EmbdeddedId (on attribute [id]) and >> an @Id (on attribute []. Both ID types cannot be specified on the same entity. >> at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java >> :527) >> at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:4 >> 56) >> at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293) >> at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) >> at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290) >> at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196) >> at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:710) >> at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:410) >> at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139) >> at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83) >> at xxx.yyy.zzz.BaseTest.initContext(BaseTest.java:88) >> >> I can get around this by using @IdClass but that annotation is clumsy and confusing compared to @EmbeddedId. >> >> Finally, note that if I take the primaryKey=true flag out of the tenant discriminator definition, the code >> *appears* to work and in fact the correct SQL is generated for insert and read operations. However, the discriminator >> part of the where clause is missing for update and delete SQL. >> >> I count myself extremely lucky to have noticed this before a production rollout! Not good to see updates affect >> entries across multiple tenants. I can provide a ton more detail of course but I wanted to find out if this issue >> was on anyone's radar first. >> >> Thanks >> >> John Goyer >> >> >> >> _______________________________________________ >> eclipselink-users mailing list >> eclipselink-users@eclipse.org >> https://dev.eclipse.org/mailman/listinfo/eclipselink-users > > -- > > Oracle > Guy Pelletier > > ORACLE Canada, 45 O'Connor Street Suite 400 Ottawa, Ontario Canada K1P 1A4 > Green Oracle Oracle is committed to developing practices and products that help protect the environment > > _______________________________________________ > eclipselink-users mailing list > eclipselink-users@eclipse.org > https://dev.eclipse.org/mailman/listinfo/eclipselink-users John H Goyer <jhgoyer@gmail.com> Jan 30 (12 days ago) to guy.pelletier Hi Guy, Basic question: if the discriminator column is part of the primary key, is it required that it be marked as such in the tenant discriminator annotation? If so, then the behavior without the primaryKey=true flag is arbitrary, and there is no bug by definition. (That is, apart from the @EmbeddedId issue.) I am unclear on the behavior if an object is detached and then merged again into a different persistence context -- which is what we are doing since the objects are read/written by web service consumers in different transactions/HTTP request processing sequences. I will try to follow up also by looking at some test cases regarding the update issue without the flag set and see if I can reproduce the behavior I saw. Thanks again for your help. Guy Pelletier Jan 30 (12 days ago) to me Hi John, Yes for the discriminator column to be part of the primary key it must be marked primaryKey=true otherwise it is a column that is augmented to the sql on find and named queries to filter the tenants. The behavior by merging the detached objects from one tenant to another is that you expose objects from that tenant to another. You should never do this. Each context should read and update accordingly and not share its results with another. You can find more information on EclipseLink's multitenancy support here: http://www.eclipse.org/eclipselink/documentation/2.4/solutions/multitenancy.htm#CHDBJCJA Cheers, Guy John H Goyer <jhgoyer@gmail.com> Jan 30 (12 days ago) to guy.pelletier Guy, We have a security system in place that prevents a user who belongs to one tenant group from accessing data from another group. Each HTTP request coming into the service layer must be accompanied by the tenant discriminator value, and we have measures in place to make sure the user has permission to access data for that discriminator value. This is a natural consequence of having to detach and merge across requests for remote processing of the entities. The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink |
Using Eclipselink Version 2.4.0.v20111118-r10419 Given this example EclipseLink gives the following incorrect warning: @Entity @Multitenant(value=MultitenantType.SINGLE_TABLE) @TenantDiscriminatorColumn(name="TENANT_ID", primaryKey=true) public class Foo { @Id private int id; } [EL Warning]: ejb_or_metadata: You have specified multiple ids for the entity class [model.Foo] without specifying an @IdClass. By doing this you may lose the ability to find by identity, distributed cache support etc. Note: You may however use entity manager find operations by passing a list of primary key fields. Else, you will have to use JPQL queries to read your entities. For other id options see @PrimaryKey.