Try to use EmbeddedId in Superclass - jpa

I have the following problem:
I have a base class (used as superclass) with a composite primary key. And now I'am trying to setup subclasses correctly from the base class, but this doesn't work! Could somebody help me? Thanks
Here are my classes:
BasePK.java
#Embeddable
public class BasePK implements Serializeable {
protected String base1;
protected Timestamp base2;
..
}
Base.java
#MappedSuperclass
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Base implements Serializeable {
#EmbeddedId
protected BasePK id;
..
}
SubClassA.java
#Entity
public class SubClassA extends Base implements Serializeable {
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="attrA")
protected List<SubClassB> attrsB;
protected String attrA1;
protected String attrA2;
..
}
SubClassB.java
#Entity
public class SubClassB extends Base implements Serializeable {
#ManyToOne(fetch=FetchType.LAZY)
private SubClassA attrA;
protected String attrB1;
protected String attrB2;
..
}
SubClassC.java
#Entity
public class SubClassC extends SubClassA implements Serializeable {
protected String attrC1;
protected String attrC2;
..
}
When I try to run the project, the following excetions are made:
[EL Info]: 2012-11-01 23:31:18.739--ServerSession(2063330336)--EclipseLink, version: Eclipse Persistence Services - 2.4.1.v20121003-ad44345
[EL Warning]: metadata: 2012-11-01 23:31:19.199--ServerSession(2063330336)--Reverting the lazy setting on the OneToOne or ManyToOne attribute [controlledObject] for the entity class [class logyourlife.entities.ChangeRecord] since weaving was not enabled or did not occur.
[EL Severe]: 2012-11-01 23:31:19.229--ServerSession(2063330336)--Exception [EclipseLink-0] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.IntegrityException
Descriptor Exceptions:
---------------------------------------------------------
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [SUBCLASSB.BASE1]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.ManyToOneMapping[attrA]
Descriptor: RelationalDescriptor(test.entities.SubClassB --> [DatabaseTable(SUBCLASSB)])
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [SUBCLASSB.BASE2]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.ManyToOneMapping[attrA]
Descriptor: RelationalDescriptor(test.entities.SubClassB --> [DatabaseTable(SUBCLASSB)])
fyi: The equals and hashcode method are overwritten in all classes.

First of all, remove the #Inheritance annotation on Base. It doesn't serve any purpose.
Then, try to explicitely define join columns for the ManyToOne association. It seems EclipseLink uses the same column names by default both for the embedded ID fields and for the many-to-one association with SubClassA. See http://docs.oracle.com/javaee/6/api/javax/persistence/JoinColumn.html for an example of using this annotation.
Side note: composite IDs are both inefficient, and complex to handle. You should really consider using autogenerated, single-column IDs for all your entities. Everything would be much easier.

Related

JPA Hibernate 5: OneToOne in nested Embeddable causes metamodel issue

I have an entity:
#Entity
public class Test {
#Embedded
Content content;
// getters setters..
}
This contains an embedded class as you can see:
#Embeddable
public class Content {
#OneToOne
Person person;
#Embedded
Language language;
// getters setters..
}
This contains again an embeddable. 2 times nested embeddable
#Embeddable
public class Language {
String format;
#OneToOne
IdentifierCode identifierCode;
// getters setters..
}
When using the automatic schema generation feature of JPA all columns are generated in the correct way.
I use the #Data annotation on each #Entity and #Embeddable to generate getters, setters, constructors, etc..
When starting the application server (EAP 7), I notice this warning in the logs:
HHH015011: Unable to locate static metamodel field :
org.package.Language_#identifierCode; this may or may not indicate a
problem with the static metamodel
Indeed, when opening the metamodel class Language_; no identifierCode column reference is present:
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(Language.class)
public abstract class Language_ {
public static volatile SingularAttribute<Language, String> format;
}
I don't see what I'm doing wroing. Is it not possible to use #OneToOne in a nested #Embeddable? The metamodel Content_ correctly generates the singular attribute for person!
It seems when using multiple nested embeddables, something goes wrong. When using only one level of embeddables, it works.
I tried other stuff:
Adding Access.Field on the class. Nothing happens.
Instantiation the #Embedded class, like #Embedded Language language = new Language(). Nothing happens.
Replaced the #OneToOne with #ManyToOne. Nothing happens.
This sounds like a bug in your JPA provider, which you should report to them.
The JPA provider I use (DataNucleus) creates a
public static volatile SingularAttribute<Language, mydomain.model.IdentifierCode> identifierCode;
One option you have is to just use the datanucleus-jpa-query.jar in your CLASSPATH to generate the static metamodel and use those generated classes with your existing provider, alternatively use it for persistence too.

EclipseLink MultiTenant and Spring Data JPA - #IdClass annotation required - Why?

I'm developing a multi-tenant (multi-schema) application using Spring-Data-JPA and EclipseLink.
When not using multi-tenant capabilities everything is ok, JPA entity works as a charme and obviously works with only one schema.
When I try to activate the multi-tenant adding the folloqing annotation to the entity :
#Multitenant(value=MultitenantType.TABLE_PER_TENANT)
#TenantTableDiscriminator(type=TenantTableDiscriminatorType.SCHEMA, contextProperty="eclipselink-tenant.id")
and I restart the application, i get the following exception :
Caused by: java.lang.IllegalArgumentException: No #IdClass attributes exist on the IdentifiableType [EntityTypeImpl#15818739:CrsMomiJob [ javaType: class com.gpdati.momi.model.core.CrsMomiJob descriptor: RelationalDescriptor(com.gpdati.momi.model.core.CrsMomiJob --> [DatabaseTable(CRS_MOMI_JOB)]), mappings: 7]]. There still may be one or more #Id or an #EmbeddedId on type.
at org.eclipse.persistence.internal.jpa.metamodel.IdentifiableTypeImpl.getIdClassAttributes(IdentifiableTypeImpl.java:169)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation$IdMetadata.<init>(JpaMetamodelEntityInformation.java:170)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:71)
at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:65)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:146)
at com.gpdati.momi.jpa.MultiTenantJpaRepositoryFactory.getTargetRepository(MultiTenantJpaRepositoryFactory.java:30)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:67)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:136)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:153)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:43)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
... 79 more
It seems like the #Id annotation on the Id field is no more read from Spring-Data that look for a #IdClass annotation (I thought #IdClass annotation is required when using a composite primary key, that's not my case)
Any clue?
Thanks!
Here the full entity code :
#Entity
#Table(name="CRS_MOMI_JOB")
#Multitenant(value=MultitenantType.TABLE_PER_TENANT)
#TenantTableDiscriminator(type=TenantTableDiscriminatorType.SCHEMA, contextProperty="eclipselink-tenant.id")
public class CrsMomiJob implements Serializable {
private static final long serialVersionUID = -432489894772L;
private String abilitata;
#Column(name="HOT_CODICE")
private String hotCodice;
#Column(name="INT_CODICE")
private String intCodice;
private Long intervallo;
private String note;
private String parametri;
#Id
private BigDecimal id;
public CrsMomiJob() {
}
... all getters and setters ...
}
Seems to be a bug in the EclipseLink meta model code in hasSingleIdAttribute(), this is returning true (as the id is composite for multitenants) but this should be hidden, so should be returning false.
Please log a bug.

#AdditionalCriteria in combination with #OneToOne gives QueryException

I'm trying to create a RESTful service. For multi tenancy I'm applying the #AdditionalCriteria annotation. However, when I join an entity using the #OneToOne annotation the following Exception is raised:
Exception [EclipseLink-6174] (Eclipse Persistence Services - 2.3.0.v20110604-
r9504): org.eclipse.persistence.exceptions.QueryException
Exception Description: No value was provided for the session property
[SECURITYID]. This exception is possible when using additional criteria or
tenant discriminator columns without specifying the associated contextual
property. These properties must be set through Entity Manager, Entity Manager
Factory or persistence unit properties. If using native EclipseLink, these
properties should be set directly on the session.
Query: ReadObjectQuery(name="readObject" referenceClass=Addresses
sql="SELECT id, city, country, postalcode, province, security_id, street
FROM addresses WHERE ((id = ?) AND (security_id = ?))")
I set the property [SECURITYID] at EntityManager level and without the join everything works just fine. But when I join an entity using the #OneToOne I see that property is not there. I don't have enough experience to determine if this is caused by me doing something wrong or if this is a bug. To me it looks like a different EntityManager is used to fetch the joined entity. But I'm guessing because of me lacking in knowledge. I also tried to set the properties on EntityManagerFactory level but to no avail.
Here is my setup.
Entity:
#Entity
#AdditionalCriteria("this.securityId=:SECURITYID")
#Table(name = "tasks", catalog = "catalog", schema = "schema")
#XmlRootElement
#NamedQueries(
{
#NamedQuery(name = "Tasks.findAll", query = "SELECT t FROM Tasks t")
})
public class Tasks implements Serializable
{
...
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="service_address_id")
private Addresses serviceAddress;
...
}
RESTFacade class
#Stateless
#Path("tasks")
public class TasksFacadeREST extends AbstractFacade<Tasks>
{
#PersistenceContext(unitName = "UnitName")
private EntityManager em;
...
#java.lang.Override
protected EntityManager getEntityManager()
{
// Temp! REMOVE WHEN DONE
sessionId = "123456789";
Identifier.setIdentity(em,sessionId);
em.setProperty("SECURITYID",Identifier.securityId);
em.setProperty("USERID",Identifier.userId);
return em;
}
Thanks & Rgds,
M
My guess is this is related to caching.
Normally the tenant property would be set on the EntityManagerFactory, not the EntityManager. This would mean you have a different EntityManagerFactory for each tenant. You could also define the tenant property in your persistence.xml and have a different persistence unit per tenant.
If you need to set the tenant per EntityManager, then you need to disable the shared cache, or setting the cache mode to protected.
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Single-Table_Multi-Tenancy#Setting_Properties_and_Caching_Scope
For reference the property to be disabled is eclipselink.cache.shared.default
<property name="eclipselink.cache.shared.default" value="false"/>

javax.inject.Qualifier Spring JavaConfig

I have the following code
The 2 javax.Inject Qualifiers
#Qualifier
#Target(value={ElementType.FIELD,ElementType.TYPE,ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface Hibernate {
--nothing goes here
}
#Qualifier
#Target(value={ElementType.FIELD,ElementType.TYPE,ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface Toplink{
--nothing goes here
}
I Qualify the repositories
#Named
#Hibernate
public class HibernateRepository implements IRepository{
-- some code
}
#Named
#Toplink
public class ToplinkRepository implements IRepository{
-- some code
}
These repositories are injected using javax.Inject
public class InvoiceService {
#Inject
//#Hibernate I alternate between the two to test
#Toplink
private IRepository iRepository;
public void saveInvoice(Invoice invoice){
iRepository.save(invoice);
}
using the following configuration class
#Configuration
public class Myconfig {
#Bean
public IRepository getHibernateRepository(){
return new HibernateRepository();
}
#Bean
public InvoiceService getInvoiceService(){
return new InvoiceService();
}
#Bean
public IRepository getToplinkRepository(){
return new ToplinkRepository();
}
}
This code works perfectly fine when I use the XML configuration , any idea how to get it working with javaConfig ?? Or is there something fundamentally wrong in my code ?? When used its throws the following exception
Exception in thread "main"
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'getInvoiceService': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private com.domain.IRepository
com.service.InvoiceService.iRepository; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
matching bean of type [com.domain.IRepository] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for
this dependency. Dependency annotations: {#javax.inject.Inject(),
#com.domain.Toplink()}
Thanks in anticipation.
In the case of #Bean methods, it's the return type that counts. Even though you may be returning a TopLinkRepository from one method, and a HibernateRepository from another, from the container's point of view, all it knows is that there are two beans of type IRepository, and therefore does not understand that one is #Toplink annotated and one is #Hibernate annotated.
You have several of choices here. The simplest, given your current configuration, would be to change the return types to make them more specific.
The second is to leave the return types generic, but move the #Toplink and #Hibernate qualifier annotations to the #Bean method level.
The third is to component-scan for the repository types instead of declaring them as #Bean methods.
The third approach is generally recommended, given that you're already using #Inject on the repository components, and have them marked with #Named. This makes them natural candidates for component-scanning in the first place. Check out the Javadoc for #ComponentScan to see how to do this in the #Configuration class world.

Entity bean 3.0 composite key issue

1: I have a table as shown below :
Name Null? Type
ATX_ID NOT NULL NUMBER(16)
ATX_GLM_CD NOT NULL NUMBER(5)
ATX_CRDR_FLG NOT NULL VARCHAR2(1)
ATX_AMT NOT NULL NUMBER(15,2)
ATX_STTS NOT NULL VARCHAR2(1)
ATX_TCM_ID NOT NULL NUMBER(16)
ATX_TXN_DT NOT NULL DATE
ATX_CRTE_BY NOT NULL VARCHAR2(30)
ATX_CRTE_DT NOT NULL DATE
The columns ATX_ID,ATX_GLM_CD and ATX_CRDR_FLG form a composite primary key.
2: I have created an entity bean class for the above table as follows :
#Entity
public class AcctngTxns implements Serializable {
private BigDecimal atxAmt;
private String atxStts;
private BigDecimal atxTcmId;
private Date atxTxnDt;
private String atxCrteBy;
private Date atxCrteDt;
#EmbeddedId
private AcctngTxnsPK acctngTxnsPK;
public AcctngTxns() {
//super();
}
/*getters and setters*/
}
#Embeddable
public class AcctngTxnsPK implements Serializable {
private long atxId;
private long atxGlmCd;
private String atxCrdrFlg;
private static final long serialVersionUID = 1L;
public AcctngTxnsPK() {
//super();
}
/*necessary overrides*/
}
3: /orm.xml/
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
version="1.0">
4: /persistence.xml/
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
DataSource
com.nseit.ncfm2.data.ejb.entity.AcctngTxns
5: While accessing the entity bean via a session bean,I am getting the following exception :
<[weblogic.servlet.internal.WebAppServletContext#1a1bc8f - appName: '_auto_generated_ear_', name: 'AWebApp', context-path: '/AWebApp', spec-version: '2.5'] Servlet failed with Exception
javax.ejb.EJBException: EJB Exception: ; nested exception is:
org.apache.openjpa.persistence.ArgumentException: Fields "com.nseit.ncfm2.data.ejb.entity.AcctngTxns.acctngTxnsPK" are not a default persistent type, and do not have any annotations indicating their persistence strategy. If you do not want these fields to be persisted, annotate them with #Transient.
at weblogic.ejb.container.internal.RemoteBusinessIntfProxy.unwrapRemoteException(RemoteBusinessIntfProxy.java:105)
at weblogic.ejb.container.internal.RemoteBusinessIntfProxy.invoke(RemoteBusinessIntfProxy.java:87)
at $Proxy127.gottaAccessEntity3(Unknown Source)
at jsp_servlet.__result.jspService(_result.java:115)
at weblogic.servlet.jsp.JspBase.service(JspBase.java:34)
Truncated. see log file for complete stacktrace
org.apache.openjpa.persistence.ArgumentException: Fields "com.nseit.ncfm2.data.ejb.entity.AcctngTxns.acctngTxnsPK" are not a default persistent type, and do not have any annotations indicating their persistence strategy. If you do not want these fields to be persisted, annotate them with #Transient.
at org.apache.openjpa.persistence.PersistenceMetaDataFactory.validateStrategies(PersistenceMetaDataFactory.java:399)
at org.apache.openjpa.persistence.PersistenceMetaDataFactory.load(PersistenceMetaDataFactory.java:205)
at org.apache.openjpa.meta.MetaDataRepository.getMetaDataInternal(MetaDataRepository.java:474)
at org.apache.openjpa.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:294)
at org.apache.openjpa.kernel.BrokerImpl.newObjectId(BrokerImpl.java:1114)
Truncated. see log file for complete stacktrace
7: Certainly,I do not want the primary key fields to be updated.
8: I tried to figure out the implementation of the below points mentioned in JPA documentation :
A composite primary key must be represented and mapped to multiple fields or properties of the entity class, or must be represented and mapped as an embeddable class.
If the class is mapped to multiple fields or properties of the entity class, the names and types of the primary key fields or properties in the primary key class must match those of the entity class.
8: Please help me in resolving the issue.
Thanks !
I found a solution by trial-and-error method. It seems that with JPA 1.0,it is necessary to mention the embedded-id in orm.xml file as follows :
Thanks.