unidirectional #oneToMany without joinTable in openJpa 2 - openjpa

I am trying to create a unidirectional OneToMany mapping in openJpa 2.3.0, but i would also want to define the column name that will hold the foreighn key on the source table. As far as i know in JPA 2.0 this can be done as follows :
#Entity
public class Source {
private List<Target> targets = new ArrayList<>();
#OneToMany
#JoinColumn(name="SOURCE_FK")
public List<Target> getTargets() {
return targets;
}
}
But i get the following exception :
<openjpa-2.3.0-r422266:1540826 fatal user error> org.apache.openjpa.persistence.ArgumentException: You have supplied columns for "Source.targets", but this mapping cannot have columns in this context.
at org.apache.openjpa.jdbc.meta.MappingInfo.assertNoSchemaComponents(MappingInfo.java:382)
at org.apache.openjpa.jdbc.meta.strats.RelationToManyTableFieldStrategy.map(RelationToManyTableFieldStrategy.java:97)
at org.apache.openjpa.jdbc.meta.strats.RelationCollectionTableFieldStrategy.map(RelationCollectionTableFieldStrategy.java:94)
at org.apache.openjpa.jdbc.meta.FieldMapping.setStrategy(FieldMapping.java:146)
at org.apache.openjpa.jdbc.meta.RuntimeStrategyInstaller.installStrategy(RuntimeStrategyInstaller.java:82)
at org.apache.openjpa.jdbc.meta.FieldMapping.resolveMapping(FieldMapping.java:496)
at org.apache.openjpa.jdbc.meta.FieldMapping.resolve(FieldMapping.java:461)
at org.apache.openjpa.jdbc.meta.ClassMapping.resolveMapping(ClassMapping.java:854)
at org.apache.openjpa.meta.ClassMetaData.resolve(ClassMetaData.java:1811)
at org.apache.openjpa.meta.MetaDataRepository.processBuffer(MetaDataRepository.java:829)
at org.apache.openjpa.meta.MetaDataRepository.resolveMapping(MetaDataRepository.java:784)
at org.apache.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:664)
at org.apache.openjpa.meta.MetaDataRepository.getMetaDataInternal(MetaDataRepository.java:418)
at org.apache.openjpa.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:389)
at org.apache.openjpa.kernel.BrokerImpl.persistInternal(BrokerImpl.java:2666)
at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2604)
at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2587)
at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2491)
at org.apache.openjpa.kernel.DelegatingBroker.persist(DelegatingBroker.java:1077)
at org.apache.openjpa.persistence.EntityManagerImpl.persist(EntityManagerImpl.java:716)
at
It seems that openJpa 2.3.0 does not support the Joincolumn in unidirectional oneToMany. Is this true or am I missing something? If this is the case , is there a workaround without using join tables ?

Answering my own question :
OpenJpa 2 DOES support #JoinColumn in OneToMany relationship.
As I have found here there are various reasons that can lead to this exception.
In my case the problem was the false schema url in the persistence.xml
The problematic line was the followng one :
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
Which was telling the OpenJpa to comply with JPA 1.0 specifications (JPA 1.0 does not support this annotation in OneToMany relationship but JPA 2 does)
I changed version to 2.0 and it worked fine.
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">

Related

JPA EclipseLink HistoryPolicy example

I am following the example provided here by eclipselink.
When I start my tests, it fails with:
javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.1.v20171221-bd47e8f):
org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: relation "event_history" does not exist.
The framework isn't creating the table as I would expect. I have the following configuration:
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
From this link, I don't feel it's necessary to add the DescriptorCustomizer class to the persistence.xml file. But I may be wrong.
My question is, do I have to create the table manually? Or I am doing something wrong? The examples I found relative to the feature are quiet poor.
Some solutions are discussed in eclipse link forum.
Clovis Wichoski CLA Friend 2016-01-02 15:29:19 EST
The problem still occurs with 2.6.2
Follow a StringTemplate to be used to easy the creation by hand (for Postgres database)
CREATE TABLE <tableName>_audit (
LIKE <tableName> EXCLUDING ALL,
audit_date_start timestamp,
audit_date_end timestamp
)
Here is another possible solution:
Peter Hansson CLA Friend 2016-03-25 05:30:42 EDT
Yes, I've had the same issue.
I've explored a couple of avenues in order to get EclipseLink to generate the history tables for me (so that they always reflect their base table). I haven't been able to come up with a method that would work, less one that would be db agnostic.
I do believe the only way to solve this is in the core of EclipseLink, for example by adding a new annotation, #HistoryTable.
I'm thinking something along the lines of the following:
Suppose you have base class, Person:
#Entity
public class Person {
#Id
private Long personId;
private String firstName;
private String lastName;
..
}
Then we could define a history entity for that entity as follows:
#Entity
#HistoryTable(base=Person.class, primaryKeyFields="personId,rowStartTime")
public class PersonHist {
// Add here the extra fields/columns that should exist for the
// history table.
private Date rowStartTime;
private Date rowEndTime;
..
}
The #HistoryTable annotation would replicate all fields from the base entity, including most field annotations, except for annotations related to relations, which wouldn't be relevant on the history table.
By definition the history table's primary key will always be a composite of columns in the base table, typically it will be like in the example. In the example the PersonHist entity will think it has an #Id notation on fields personId and rowStartTime. (yeah, this area needs more brain work :-))

JPA EclipseLink uses a non-entity as target entity in the relationship attribute

I get the following error when i try to deploy my application on glassfish 4.1:
[class com.sample.model.Profile] uses a non-entity [class com.sample.model.ProfileEventMapping] as target entity in the relationship attribute [field events].
The tables for both entities are getting created in the database.
Profile:
#Entity
public class Profile
...
#OneToMany(mappedBy = "profile", orphanRemoval = true)
private Set<ProfileEventMapping> events = new HashSet<>();
ProfileEventMapping:
#Entity
public class ProfileEventMapping
...
#NotNull
#ManyToOne
private Profile profile;
and in my persistence.xml i choose to include all entities:
<exclude-unlisted-classes>false</exclude-unlisted-classes>
anybody an idea?
I renamed the hole project with just find and replace. The problem was that in the ear project was a dependency to an old .war file which had an persistence.xml in it. Just deleted the old dependency and BOOM. Now i just need to fix the other error messages ("com.Profile[id = null] is not a known entity type")

Eclipselink -Glassfish project Object is not a known entity type error

In a JSF 2.1 - Spring 3.1 integrated project.I m trying to handle transaction by container i m using eclipselink 2.3.2 ,Glassfish 3.1.2 and Maven on my project and working on Netbeans IDE 7.2 .
At service layer i injected entity manager by the code below and on debug it seems ok .
#Inject
public void setEntityManager() {
EntityManagerFactory emfactory =Persistence.createEntityManagerFactory
("E_DefterManagementPU");
this.em = emfactory.createEntityManager();
But after i filled the entity named EfaFunctions and try to persist with
em.persist(EfaFunctions);
it gives this error
java.lang.IllegalArgumentException: Object: org.eclipse.persistence.internal.jpa.EntityManagerImpl#599ebbf6
is not a known entity type.
But in **persistence.xml ** i have following nodes
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>EFA</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
My entity project location is com.edefter.entity.EfaFunctions ;not the location specified in error .Actually there is an answer written by Pascal in this link
but my generation strategy is none and my entites were generated from Database by netbeans .I need some experts advice . Thanks in Advance
I deleted the
#Table(name="EFA_FUNCTIONS")
annotation from top of Entity ,but #Entity still stays.
The error is gone but then the query starts as
INSERT INTO EFAFUNCTIONS
without underscore but i need
INSERT INTO EFA_FUNCTIONS
,becouse of it the data did not inserted . Why eclipselink gives error for #Table annotation and despite there is no table like EFAFUNCTIONS why it doesnt give any error
I solved the problem , As i mentioned in question i had generated Entities from Database via Netbeans,so that i didn't suspect the entity format.I had worked before hibernate as ORM and hibernate plus DB2 or MYSQL.These combinations didnt give an error with same usage.But with the combination Eclipselink - Oracle DB #Entity annotation has to be parameter as written below
#Entity(name="entityName")
#Table(name="TableName")
Thanks for comment.
Why would you try to persist an EntityManager ? Much better to pass in (to em.persist) an Entity (one of those classes tagged as #Entity)
I had a similar problem, when trying to persist an object of an entity class by calling persist() on the entitymanager:
java.lang.IllegalArgumentException: Object: entities.Documents[ id=null ] is not a known entity type.
it turned out that the name of the persistence unit declared in the persistence.xml in the line
<persistence-unit name="my-pu" transaction-type="RESOURCE_LOCAL">
was diffrent from the name I used when declaring the Entity Manager Factory in the java code:
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("my-persistenceunit");
After correcting both names to the same name, calling persist() worked as expected.

Controlling DDL generation in Toplink Essentials

I am having some trouble with the DDL generation of Toplink Essentials. I am developing a Glassfish 2.1 based application and use JPA for persistence.
I have an object graph where a parent entity of class A owns a set of entities of class B. Entites B come in several flavors which is modelled using inheritance. One such flavor is a composite entity class BC that bundles a set several other B entites. All entites B in a BC must also be owned by the same entity A as B. Note that not all entites B of an entity A have to be part of a composite BC, they can also be standalone.
So basically that maps to the following classes:
#Entity
class A {
#ManyToOne(mappedBy="owner", cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
Set<B> bs;
}
#Entity
#Inheritance
abstract class B {
#Id
long id;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
A owner;
#ManyToOne(optional = true)
BC composite;
}
#Entity
class BC extends B {
#OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, mappedBy = "composite")
Set<B> parts;
}
When toplink generates the DDL for this object hierarchy it creates all foreign key constraints as expected. However it does not set cascading rules for the constraints.
When I now try to delete an entire object graph via a reference to the A instance there can be situations where toplink fails to correctly remove the graph from the database. When toplink deletes a BC entity before deleting the contained B entities the foreign key constraint for the "composite" relationship is violated.
This situation can be corrected by manually adjusting the generated DDL to CASCADE (or SET NULL) on the relevant foreign key constraint which is fine for a production environment. This however fails in a test environment with in-memory (Derby) databases where DDL generation is managed entirely by toplink essentials and thus leads to the constraint violation described above.
Is there any way to influence the DDL generation process such that the required cascading rules are correctly set by toplink essentials?
Thanks for your help!
This is not an issue with DDL generation, but with deletion.
TopLink Essentials had some issues with resolving deletes from complex object graphs, or cyclic relationships. The are a few workarounds, such as deleting the dependent objects first and calling flush, then deleting the other objects, or setting the foreign key to null so they get updated. Using a customizer to mark the mapping privateOwned, or play with the constraint dependency may also work. You can also drop or defer the constraints.
All of the deletion issues have been fixed in EclipseLink, so upgrading the to latest EclipseLink release should resolve the issue.
EclipseLink also supports an #CascadeOnDelete annotation to add the cascade to the constraint in DDL generation.

Is there a way to change the JPA fetch type on a method?

Is there a way to change the JPA fetch type on a single method without editing the entity object?
I have a shared ORM layer consisting of JPA entity classes. This ORM layer is accessed by two DAO layers. One DAO needs lazy fetching, as it is for my web application, the other needs eager fetching, as I need it to be threadsafe.
Here is an example method from my threadsafe DAO,
#PersistenceContext(unitName = "PersistenceUnit", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
public ErrorCode findErrorCodeById(short id) {
return (ErrorCode) em.createNamedQuery("ErrorCode.findById").
setParameter("id", id).getSingleResult();
}
How would I make this method (or entire class) use eager fetching?
I assume that your entity associations (#OneToOne, #OneToMany, #ManyToOne) are fechted lazy (FetchType.Lazy)
Then I can think of two ways:
A. write two jpa query one which fetch the association of the lazy (thats the default way for hibernate) and a second query which explicit force eager loading of association (see "fetch" keyword in query).
Query q = HibernateUtil.getSessionFactory().getCurrentSession()
.createQuery("select c from Category as c" +
" left join fetch c.categorizedItems as ci" +
" join fetch ci.item as i");
B. use Hibernate.initialize(entity) to force eager loading of lazy relations of an entity after you have retrieved it (e.g. through finder ...)
ErrorCode lazyCode = findErrorCodeById(1);
// eager load associations
Hibernate.initialize(lazyCode);
In JPA the Fetch mode is specified on each persistence attribute, either through an annotation or in an xml mapping file.
So a JPA vendor agnostic way to accomplish your goal is to have separate mapping file for each DAO layer. Unfortunately this will require a separate PersistenceUnit for each mapping file, but you can at least share the same entity classes and the same JPQL query.
Code skeletons follow.
persistence.xml :
<persistence>
<persistence-unit name="dao-eager">
<mapping-file>orm-eager.xml</mapping-file>
</persistence-unit>
<persistence-unit name="dao-lazy">
<mapping-file>orm-lazy.xml</mapping-file>
</persistence-unit>
</persistence>
orm-eager.xml :
<entity-mappings>
<entity class="ErrorCode">
<attributes>
<basic name="name" fetch="EAGER"/>
</attributes>
</entity>
</entity-mappings>
orm-lazy.xml :
<entity-mappings>
<entity class="ErrorCode">
<attributes>
<basic name="name" fetch="LAZY"/>
</attributes>
</entity>
</entity-mappings>
Then it's just a matter of creating an EntityManagerFactory for the appropriate persistence-unit in your DAO layers.
Actually you don't need two mapping files, you could specify either LAZY or EAGER as an annotation in the Entity and then specify the opposite in an xml mapping file (you'll still want two persistence-units though).
Might be a little more code than the Hibernate solution above, but your application should be portable to other JPA vendors.
As an aside, OpenJPA provides similar functionality to the Hibernate solution above using FetchGroups (a concept borrowed from JDO).
One last caveat, FetchType.LAZY is a hint in JPA, the provider may load the rows eagerly if needed.
Updated per request.
Consider an entity like this :
#Entity
public class ErrorCode {
// . . .
#OneToMany(fetch=FetchType.EAGER) // default fetch is LAZY for Collections
private Collection myCollection;
// . . .
}
In that case you'd still need two persistence units, but you'll only need orm-lazy.xml. I changed the field name to reflect a more realistic scenario (only collections and blobs use FetchType.LAZY by default). So the resulting orm-lazy.xml might look like this :
<entity-mappings>
<entity class="ErrorCode">
<attributes>
<one-to-many name="myCollection" fetch="LAZY"/>
</attributes>
</entity>
</entity-mappings>
And persistence.xml will look like this :
<persistence>
<persistence-unit name="dao-eager">
<!--
. . .
-->
</persistence-unit>
<persistence-unit name="dao-lazy">
<!--
. . .
-->
<mapping-file>orm-lazy.xml</mapping-file>
</persistence-unit>
</persistence>
In JPA2 I use EntityGraphs, which allows you to define what related entities you want to retrieve:
https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs002.htm
https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs003.htm
You create a NamedQuery as you did, and you attach a Hint with key javax.persistence.loadgraph or javax.persistence.fetchgraph. It will retrieve the related entities that you defined in the graph.
You can find the details of difference between "loadgraph" and "fetchgraph" here: What is the diffenece between FETCH and LOAD for Entity graph of JPA?
Since no one mentioned OpenJPA, I will put an answer here.
In OpenJPA, the previously lazy configured collections and fields can be eagerly loaded as below
OpenJPAEntityManager kem = OpenJPAPersistence.cast(em);
kem.getFetchPlan().addField(Order.class, "products");
TypedQuery<Order> query = kem.createQuery(yourQuery, Order.class);
Reference:http://openjpa.apache.org/builds/1.0.3/apache-openjpa-1.0.3/docs/manual/ref_guide_fetch.html