Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 367241 - Incorrect warning about IdClass when TenantDiscriminatorColumn primaryKey is used
Summary: Incorrect warning about IdClass when TenantDiscriminatorColumn primaryKey is ...
Status: NEW
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: Eclipselink (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P2 normal with 3 votes (vote)
Target Milestone: ---   Edit
Assignee: Project Inbox CLA
QA Contact:
URL:
Whiteboard: multitenancy
Keywords:
Depends on:
Blocks:
 
Reported: 2011-12-20 14:16 EST by Karen Butzke CLA
Modified: 2022-06-09 10:05 EDT (History)
4 users (show)

See Also:


Attachments
Maven project containing working example that generates exception. Uses derby embedded. (93.24 KB, application/zip)
2013-02-11 17:32 EST, John Goyer CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Karen Butzke CLA 2011-12-20 14:16:22 EST
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.
Comment 1 John Goyer CLA 2013-02-11 17:32:03 EST
Created attachment 226886 [details]
Maven project containing working example that generates exception.  Uses derby embedded.
Comment 2 John Goyer CLA 2013-02-11 17:39:17 EST
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.
Comment 3 Eclipse Webmaster CLA 2022-06-09 10:05:41 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink