User-agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.1) Gecko/20120208 Thunderbird/10.0.1
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.
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.
ORACLE Canada, 45 O'Connor Street Suite 400
Ottawa, Ontario Canada K1P 1A4 Oracle is committed to
developing practices and products that help
protect the environment
ORACLE Canada,
45 O'Connor Street
Suite 400
Ottawa, Ontario
Canada K1P 1A4
Oracle is committed to developing
practices and products that help protect the environment