Related entities not loaded - EAGER ignored? - jpa

Got GlassFish v3. I have an one-to-many entity. The problem is, that EclipseLink seems to ignore the fetch EAGER mode.
Here is my entities.
#Entity
public class Person implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Hobby> hobbies;
// getter and setter
}
A 1:n relationship
#Entity
public class Hobby
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
#JoinColumn
private Person person;
// getter and setter
}
And the bean
#javax.ejb.Remote
public interface Testing
{
public void addTestData();
public List<Person> getTestData();
}
#javax.ejb.Stateless
public class TestingBean implements Testing
{
#javax.persistence.PersistenceContext
private EntityManager entityManager;
public void addTestData()
{
Person p = new Person();
p.setName("JOE");
entityManager.persist(p);
Hobby h1 = new Hobby();
h1.setName("h1");
h1.setPerson(p);
entityManager.persist(h1);
}
public List<Person> getTestData()
{
TypedQuery<Person> gridQuery = entityManager.createQuery("SELECT e FROM Person e", Person.class);
return gridQuery.getResultList();
}
}
EDIT Client:
InitialContext context = new InitialContext();
Testing test = (Testing)context.lookup("java:global/dst2_1/TestingBean");
test.addTestData();
for(Person p: test.getTestData()) {
System.out.println(p.getName());
for(Hobby b : p.getHobbys()) {
System.out.println(b.getName());
}
}
context.close();
Using MySQL - Storing the data works. But if I fetch the data only the person is returned - not hobbies. Coudld you tell me what is wrong in my code?
EDIT sorry have tried so many things ... The code shown as above produces:
Exception Description: An attempt was made to traverse a
relationship using indirection that had a null Session. This often
occurs when a n entity with an uninstantiated LAZY relationship is
serialized and that lazy relationship is traversed after
serialization. To avoid this issue, ins tantiate the LAZY
relationship prior to serialization.
But the Person is returned correctly. Why does it specify LAZY while I am using EAGER?

You code looks correct. I can't see any way that the EAGER could be ignored.
Are you sure you get the error with this attribute, not another one?
Also ensure you recompile and deployed your code correctly. You most like have an old version deployed.

Make the eager object Serializable

Related

Spring Data JPA auditing fails when persisting detached entity

I've setup JPA auditing with Spring Data JPA AuditingEntityListener and AuditorAware bean. What I want is to be able to persist auditor details even on entities with predefined identifiers.
The problem is that when JPA entity with predefined id is being persisted and flushed it's auditor details cannot be persisted:
object references an unsaved transient instance - save the transient instance before flushing: me.auditing.dao.AuditorDetails
The interesting part is that when an entity with a generated id is saved - everything's fine. In both cases the entities are new. I could not pinpoint the problem digging through hibernate code so I've created a sample project to demonstrate this (test class me.auditing.dao.AuditedEntityIntegrationTest) It has both entities with predefined and generated identifiers and should be audited.
The entities are:
#Entity
public class AuditedEntityWithPredefinedId extends AuditableEntity {
#Id
private String id;
public String getId() {
return id;
}
public AuditedEntityWithPredefinedId setId(String id) {
this.id = id;
return this;
}
}
and:
#Entity
public class AuditedEntityWithGeneratedId extends AuditableEntity {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
public String getId() {
return id;
}
public AuditedEntityWithGeneratedId setId(String id) {
this.id = id;
return this;
}
}
where parent class is:
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class AuditableEntity implements Serializable {
private static final long serialVersionUID = -7541732975935355789L;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#CreatedBy
private AuditorDetails createdBy;
#CreatedDate
private LocalDateTime createdDate;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#LastModifiedBy
private AuditorDetails modifiedBy;
#LastModifiedDate
private LocalDateTime modifiedDate;
And the auditor getter implementation is:
#Override
public AuditorDetails getCurrentAuditor() {
return new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
}
Edit 2016-08-08: It seems that when a new entity with predefined id is saved, it gets two different instances of createdBy and modifiedBy AuditorDetails, which is quite logical if the entity wouldn't be actually new. So, a completely new entity with generated gets both AuditorDetails of same instance, and the one with manually set id doesn't. I tested it by saving auditor details in AuditorAware bean before returning it to AuditingHandler.
Ok, so for now the only solution I could find is to actually persist AuditorDetails before writing it to audited entities like so:
#Override
#Transactional
public AuditorDetails getCurrentAuditor() {
AuditorDetails details = new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
return auditorDetailsRepository.save(details);
}
It is not the most elegant solution, but it works for now.

JPA Criteria API predicates for objects in OneToMany relationship

Given the following code
#Entity
public class Invoice {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
public Long id;
#Embedded
private InvoiceData data = new InvoiceData();
}
#Embeddable
public class InvoiceData {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public Collection<InvoiceLineItem> lineItems;
}
#Entity
public abstract class InvoiceLineItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private String description;
}
#Entity
public class GoodsLineItem extends InvoiceLineItem {
}
#Entity
public class CostLineItem extends InvoiceLineItem {
}
How would I write a criteria api query that returns all Invoices with a CostLinesItem that's description is 'TAX'?
I am using the metadata API. I have tried various approaches most of which are variations of the 2 listed below. Any pointers/help or 'go read this's will be greatly appreciated.
Attempt 1 (of many):
#Test
public void criteria_api_and_collections() throws Exception {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Invoice> query = builder.createQuery(Invoice.class);
Root<Invoice> root = query.from(Invoice.class);
Join<InvoiceData, InvoiceLineItem> lineItems = root.join(Invoice_.data).join(InvoiceData_.lineItems);
query.where(builder.equal(lineItems.get(InvoiceLineItem_.description), ""));
List<Invoice> resultList = em.createQuery(query).getResultList();
System.out.println(resultList);
}
Attempt 2 (of a many):
#Test
public void criteria_api_and_collections() throws Exception {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Invoice> query = builder.createQuery(Invoice.class);
Root<Invoice> root = query.from(Invoice.class);
Join<InvoiceData, InvoiceLineItem> lineItems = root.join(Invoice_.data).join(InvoiceData_.lineItems, JoinType.LEFT);
Subquery<CostLineItem> subquery = query.subquery(CostLineItem.class);
Root<CostLineItem> fromLineItem = subquery.from(CostLineItem.class);
subquery.select(fromLineItem);
subquery.where(builder.equal(lineItems.get(InvoiceLineItem_.description), "TAX"));
query.where(builder.in(lineItems).value(subquery));
List<Invoice> resultList = em.createQuery(query).getResultList();
}
Both attempts causes a SQL grammer Exception. An alias is referred to in the resulting SQL that is never created. It looks like the alias should have been assigned to a join in the SQL that does not exist. In other words the InvoiceLineItems are not fetched in the query.
I am not able to make a test right now, but sticking to the Java EE 6 Tutorial, we see that
Embeddable classes may also contain relationships to other entities or
collections of entities. If the embeddable class has such a
relationship, the relationship is from the target entity or collection
of entities to the entity that owns the embeddable class.
This makes me think that the Join Predicate should be defined with the starting Entity Invoice instead of InvoiceData. And this is supported also by the fact that normally the starting Entity should be the query root itself. I would try with something like this:
Join<Invoice, InvoiceLineItem> lineItems = root.join(Invoice_.data).join(InvoiceData_.lineItems);
I swapped out Hibernate 4.1.0.Final for EclipseLink 2.0.0 and it worked.

JPA #OneToMany Mapping Problem

I am trying to do do JPA/Hibernate mappings to map two tables, but am getting this error. any help would be greatly appreciated!!
Restaurants.java
#Entity
#Table(name="RESTAURANTS")
public class Restaurants{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy="restaurant")
private LinkedList<Menus> menus = new LinkedList<Menus>();
/* constructors **/
public Restaurants(){
this.dateJoined = new Date();
};
/* getters and setters **/
#Id
#GeneratedValue(generator="increment")
#GenericGenerator(name="increment", strategy = "increment")
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public LinkedList<Menus> getMenus() {return menus;}
public void setMenus(LinkedList<Menus> menus) {this.menus = menus;}
}
Menus.java
#Entity
#Table(name = "MENUS")
public class Menus {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private Long restaurantID;
#OneToMany
#JoinColumn(name="restaurant")
private Restaurants restaurant;
/* constructors */
public Menus(){}
/* getters and setters */
#Id
#GeneratedValue(generator="increment")
#GenericGenerator(name="increment", strategy = "increment")
#Column(nullable = false)
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public Long getRestaurantID() {return restaurantID;}
public void setRestaurantID(Long restaurantID) {this.restaurantID = restaurantID;}
public void setRestaurant(Restaurants restaurant) {this.restaurant = restaurant;}
public Restaurants getRestaurant() {return restaurant;}
}
With this error
Exception in thread "main" org.hibernate.MappingException: Could not
determine type for: bb.entities.Restaurants, at table: MENUS, for
columns: [org.hibernate.mapping.Column(restaurant)] at
org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:306) at
org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:290) at
org.hibernate.mapping.Property.isValid(Property.java:217) at
org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:464)
at org.hibernate.mapping.RootClass.validate(RootClass.java:235) at
org.hibernate.cfg.Configuration.validate(Configuration.java:1362) at
org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1865)
at bb.TestMain.setUp(TestMain.java:26) at
bb.TestMain.main(TestMain.java:59)
Thanks.
It appears to be a misconception in the use of the #OneToMany annotation. The #OneToMany annotation is used to represent the 1-side in a 1:M relationship, and the inverse #ManyToOne relationship is used to represent the M-side. Therefore, a #OneToMany annotation should be defined on a collection-type in an entity and not on a normal reference type.
You should therefore:
use a #OneToOne association if that is the nature of the relationship between the entities.
or, decide which entity represents the 1-side in the 1:M relationship. Going by the use of the LinkedList class in Restaurants, I would consider the Restaurants class to be the 1-side, and use the #OneToMany annotation in the Restaurants class, while using the inverse #ManyToOne relationship in the Menus class. The refined code would be:
Restaurants.java
...
#OneToMany(mappedBy="restaurant")
private List<Menus> menus = new LinkedList<Menus>();
Menus.java
#ManyToOne
#JoinColumn(name="restaurant")
private Restaurants restaurant;
Note the change in the declaration of the menus member variable from LinkedList<Menus> to List<Menus>. Apparently, in this case, it is wiser to declare any collection with the interface-type of the collection, instead of the concrete collection class. The rationale is that the underlying JPA provider will use it's own concrete collection types at runtime, for the purpose of proxying the collection values. Hibernate for instance, will use a PeristentList at runtime, to represent the List in a managed entity, and not a LinkedList as created by the entity. If you use the concrete type, Hibernate might fail in mapping the column, or might fail in retrieving the associated records from the database; I'm not sure about the specifics of the runtime behavior, except that I know of the eventual failure.

Why am I getting "Duplicate entry" errors for related objects upon merge in eclipselink?

I have an entity class that contains a map of key-value pairs which live in a different table and there may be no such pairs for a given entity. The relevant code for the entity classes is below.
Now, when I insert such an entity with persist(), then add key-value pairs, and then save it with merge(), I get duplicate entry errors for the related table that stores the key-value pairs. I tried to hold back insertion until the keys were added, to have one call to persist() only. This led to duplicate entry errors containing an empty (zero) id in the foreign key column (ixSource).
I followed the process in the debugger, and found that eclipselink seems to be confused about the cascading. While it is updating the entity, it executes calls that update the related table. Nonetheless, it also adds those operations to a queue that is processed afterwards, which is when the duplicate entry errors occur. I have tried CascadeType.ALL and MERGE, with no difference.
I'm using static weaving, if it matters.
Here's the entities`code, shortened for brevity:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "sType")
#Table(name = "BaseEntity")
public abstract class BaseEntity extends AbstractModel
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ix")
private long _ix;
}
#Entity
#Table(name = "Source")
public class Source extends BaseEntity
{
#OneToMany(cascade = CascadeType.MERGE)
#JoinTable(name = "SourceProperty", joinColumns = { #JoinColumn(name = "ixSource") })
#MapKey(name = "sKey")
private Map<String, SourceProperty> _mpKeys;
// ... there's more columns that probably don't matter ...
}
#Entity
#Table(name = "SourceProperty")
#IdClass(SourcePropertyKey.class)
public class SourceProperty
{
#Id
#Column(name = "sKey", nullable = false)
public String sKey;
#Id
#Column(name = "ixSource", nullable = false)
public long ixSource;
#Column(name = "sValue", nullable = true)
public String sValue;
}
public class SourcePropertyKey implements Serializable
{
private final static long serialVersionUID = 1L;
public String sKey;
public long ixSource;
#Override
public boolean equals(Object obj)
{
if (obj instanceof SourcePropertyKey) {
return this.sKey.equals(((SourcePropertyKey) obj).sKey)
&& this.ixSource == ((SourcePropertyKey) obj).ixSource;
} else {
return false;
}
}
}
I can't see how those errors would occur. Could you include the SQL and ful exception.
What version of EclipseLink are you using, did you try the latest release?
Why are you calling merge? Are you detaching the objects through serialization, if it is the same object, you do not need to call merge.
It could be an issue with the #MapKey, does it work if you remove this?

JPA problem one-to-one association cascade= PERSIST

I have this ER model
Message 0..1 <--> 0..1 MessageDetail
PK:ID_MESSAGE PK: ID_DETAIL
NAME DETAIL
FK: ID_MESSAGE
And the relative Object mapping is:
class OnlineEventMessage {
#Id
#Column(name = "ID_EVENT_MESSAGE")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ")
private Long idEventMessage;
#OneToOne(mappedBy="onlineEventMessage", cascade=CascadeType.PERSIST)
private EventMessageAnagrafica eventMessageAnagrafica;
}
public class EventMessageAnagrafica {
#Id
#Column(name = "ID_EVENT_MESSAGE_ANAG")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ")
private Long idEventMessageAnagrafica;
#OneToOne(cascade=CascadeType.PERSIST)
#JoinColumn(name = "FK_ID_EVENT_MESSAGE")
private OnlineEventMessage<?> onlineEventMessage;
}
This test shows how I would like to handle the objects:
#Test
public void testSaveItem() {
EntityManager entityManager = factoryCont0.createEntityManager();
entityManager.getTransaction().begin();
OnlineEventMessage<String> eventMessage = new OnlineEventMessage<String>(EventType.ONLINE_REIMPIANTO_CONTRATTO);
EventMessageAnagrafica eventMessageAnagrafica = new EventMessageAnagrafica(multichannelId);
eventMessage.setEventMessageAnagrafica(eventMessageAnagrafica);
entityManager.persist(eventMessage);
entityManager.getTransaction().commit();
entityManager.close();
}
When I persist the eventMessage on the eventMessageAnagrafica it does not save the FK.
The two ways to save the underlaying association are:
1) add this line of code : eventMessageAnagrafica.setOnlineEventMessage(eventMessage);
and save the child object: entityManager.persist(eventMessageAnagrafica);
2) change the parent setter as below:
public void setEventMessageAnagrafica(EventMessageAnagrafica eventMessageAnagrafica) {
this.eventMessageAnagrafica = eventMessageAnagrafica;
if (eventMessageAnagrafica != null) {
eventMessageAnagrafica.setOnlineEventMessage(this);
}
}
Is there any other clean way to accomplish this?
P.S. Initially the FK was on the parent table Message, but the DBA told me that this wasn't a good ER design.
Kind regards
Massimo
Maintaining consistency between sides of bidirectional relationship between objects in memory is your responsibility. When saving relationship, JPA provider looks at the owning side of the relationship, that is at the side without mappedBy.
I think the second approach is the cleanest, since it maintains consistency automatically, so that you can't forget to do it. Alternatively you can create a special function for associating these entities, other than setter, and restrict access to setters.
entityManager.getTransaction().begin();
OnlineEventMessage<String> eventMessage = new OnlineEventMessage<String>(EventType.ONLINE_REIMPIANTO_CONTRATTO);
EventMessageAnagrafica eventMessageAnagrafica = new EventMessageAnagrafica(multichannelId);
eventMessage.setEventMessageAnagrafica(eventMessageAnagrafica);
//add this line
eventMessageAnagrafica.setEventMessage(eventMessage);
entityManager.persist(eventMessage);