Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[eclipselink-users] Problem with primaryKey=true in single table multitenancy discriminator

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
 

Back to the top