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

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

Related

ORM to create single entity from more than one database tables

Well tested running system have already defined entity called 'User'.
Now I need to add a new property to User entity (ex: Age)
To do this in the safe way, I do not like to do any changes with the existing data base table, because that is very risky in my case. I need a way to rebuild the User entity with the minimum code changes.
So my proposal is:
Create a new table (user_age), with two columns (user_id, age)
Modify the user entity to add property 'age' and its getter-setters
So my entity (User) properties, will be saved to two different tables (user and user_age)
Loading the user is also similarly.
Is this possible to do with hibernate....??
If not, Any other safer way to do this with Hibernate...?
what are the available ORMs that provide this kind of feature (nhibernate, entityframwork,etc... or any other ORM)...?
Yes, there are various approaches:
[1] See JPA Secondary Tables. This allows you to map an Entity to two or more tables.
Section 2.2.7: http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#d0e2235
[2] Create another Entity, say UserInfo, mapped to this new table. Create a one-to-one mapping from User to UserInfo.
Yes. You can do that.
I've used for a similar problem a joined-subclass.
Base:
<class name="User" table="Users">
<id name="Code" type="System.Guid">
<column name="Code" />
<generator class="guid.comb" />
</id>
...
</class>
Subclass:
<joined-subclass name="UserExt" extends=User" table="UsersExt">
<key column="Code" />
<property name="Age">
<column name="Age" not-null="true" />
</property>
</joined-subclass>
A good reference here.
NHibernate's join mapping is for exactly this case.
See Ayende's blog and the documentation for more information. From the documentation:
Using the <join> element, it is possible to map properties of one class to several tables, when there's a 1-to-1 relationship between the tables.
From my searches, it looks like it is also possible to do this with Entity Framework: Simon J Ince - Mapping two Tables to one Entity in the Entity Framework . I think this article is about Entity Framework v1, and things could have changed by now, but it appears that there is an important limitation in Entity Framework's version of this mapping:
... it requires a record in each table to exist as the generated SQL uses an INNER JOIN. It makes sense if you're using a new model, but I guess this is more tricky if you're mapping to an existing schema and data.
With NHibernate, you can set the optional attribute on the join mapping to tell it to use outer joins instead of inner joins.
optional (optional - defaults to false): If enabled, NHibernate will insert a row only if the properties defined by this join are non-null and will always use an outer join to retrieve the properties.

NHibernate duplicate insert in many-to-many and composite-element

I have in an NHibernate 2 application an Product entity which has an many-to-many relationship to an Location. The Location entity does not have any navigation properties or mappings back to the product. It is mapped like this:
<bag name="Locations" table="ProductLocation" cascade="none">
<key column="ProductId" />
<many-to-many column="LocationId" class="Location"/>
</bag>
The product also has an composite-element, a Component with a concentration mapped via the ProductComponent class. The class has no navigation property or mapping back to the product.
<bag name="ProductComponents" table="ProductComponent" access="nosetter.camelcase">
<key column="ProductId" />
<composite-element class="ProductComponent">
<property name="Concentration"/>
<many-to-one name="Component" column="ComponentId" access="nosetter.camelcase"/>
</composite-element>
</bag>
This all works fine when just inserting one product at a time. It however fails when batch inserting multiple products.
While the products itself get inserted fine, each product does get an own unique Id, the elements in the many-to-many (Locations) and composite-element (ProductComponent) doesn't get inserted well. This is because NHibernate multiple times executes the insert to the ProductLocation table with the same ProductId.
This causes an duplicate record in the link table. How can this be prevented?
You'll have to define one site of the relationship to be the owner so that only one side does the insert. This can be achieved with Inverse set to true on the other side.
Find a more detailed explanation here

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.

Can't understand EclipseLink warning

I'm using EclipseLink 2.3.1 to model self referencing table with JPA 2.
I get weird warning from EclipseLink when I create the EntityManager.
[EL Warning]: 2011-11-27 14:28:00.91--ServerSession(8573456)--Reverting the lazy setting on the OneToOne or ManyToOne attribute [redirectID] for the entity class [class lp.db.model.Site] since weaving was not enabled or did not occur.
I couldn't find any documentation about this warning, and I'm not sure what it means.
I also want to know how to solve the problem that causes this warning to appear...
I'm new to JPA so it might be a silly thing.
My program is really simple. Here is the entity definition:
#Entity
#Table(name="site")
public class Site implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="site_id")
public String siteID;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="redirect_id", referencedColumnName="site_id")
public Site redirectID;
#Column(name="name")
public String name;
}
Here is the persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="lpdb2" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>lp.db.model.Site</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/lpdb2"/>
<property name="javax.persistence.jdbc.user" value="blabla"/>
</properties>
</persistence-unit>
</persistence>
The code that causes this warning:
Persistence.createEntityManagerFactory("lpdb2").createEntityManager();
Note that the resulting EM is fine and can be used (for example) to find elements.
Also, I can traverse the graph of entities - I can find one entity in the database and then I get another entity using the redirectID field.
See http://wiki.eclipse.org/Introduction_to_EclipseLink_Application_Development_%28ELUG%29#Using_Weaving.
For lazy fetching to be possible on XxxToOne associations, the byte-code of the JPA entities must be modified (that's what weaving means). If it's not modified, an XxxToOne association can only be eager-fetched.
Eager fetching means that each time you load a Site from the database, its redirectID is also loaded. With lazy fetching, you load a site, and its redirect is only loaded (lazily) when you call a method on the redirectID field.

How to get store type of an entity set

I'm trying to filter entities based on their store types (either table or view).
I have 2 entities in my test project, one's source is a table and the other's source is a view.
<EntitySet Name="Test" EntityType="TestModel.Store.Test" store:Type="Tables" Schema="dbo" />
<EntitySet Name="TestView" EntityType="TestModel.Store.TestView" store:Type="Views" store:Schema="dbo" store:Name="TestView">
The code sample above is taken from model's edmx file's SSDL section.
I think the store:Type information in SSDL is what i need but i couldn't find a way to retrieve that value using entity-framework api.
Any help will be appreciated.
Well you can query the metadata in the SSDL by looking in the SSpace or the StoreItemCollection.
i.e.
var sspaceEntitySets = context.MetadataWorkspace
.GetItems<EntityContainer>(DataSpace.SSpace)
.First().BaseEntitySets.OfType<EntitySet>();
var entitySet = sspaceEntitySets.First();
var tableType = entitySet
.MetadataProperties["http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator:Type"]
.Value.ToString();
Unfortunately this isn't going to help you tie your classes to whether they come from a table or a view. Because the Entities (i.e. the ones you code against in CSpace) rather than the ones that describe the shapes of the table (i.e. SSpace ones) are in CSpace and in order to know whether an Entity comes from a view or table, you would need to be able to get from the CSpace EntitySet to SSpace EntitySet via the Mapping.
Unfortunately the EF doesn't expose public CSSPace (i.e. there is no way to use the API to read the MSL fragment of the EDMX).
So in order to do this you would have to manually reason over the MSL element, probably using LINQ to XML or something.
Hope this helps
Alex