JPA 2.0 Eclipselink - Adding/Deleting to child list gets optimistic lock - jpa

I am having an issue where eclipselink (2.5) is throwing an OptimisticLockException even though the only thing I'm modifiying is trying to either add or remove an item from a child list.
Entities :
#Entity
#Table(name="PLAN_ORG_RELATIONSHIP")
#Customizer(GridCacheCustomizer.class)
#AdditionalCriteria("CURRENT_TIMESTAMP BETWEEN this.startDate AND this.endDate")
#NamedQuery(name="PlanOrganizationRelationship.findAll", query="SELECT p FROM PlanOrganizationRelationship p")
#Portable
public class PlanOrganizationRelationship extends PrismObject implements Serializable {
#OneToMany(mappedBy="planOrganizationRelationship", cascade=CascadeType.PERSIST, orphanRemoval=true)
#PortableProperty(10)
private List<PlanOrganizationAction> planOrganizationActions;
public PlanOrganizationAction addPlanOrganizationAction(PlanOrganizationAction planOrganizationAction) {
getPlanOrganizationActions().add(planOrganizationAction);
planOrganizationAction.setPlanOrgRelationship(this);
return planOrganizationAction;
}
public PlanOrganizationAction removePlanOrganizationAction(PlanOrganizationAction planOrganizationAction) {
getPlanOrganizationActions().remove(planOrganizationAction);
planOrganizationAction.setPlanOrgRelationship(null);
return planOrganizationAction;
}
#Column(name="LST_UPDT_DT")
#Version
#PortableProperty(5)
private Timestamp lastUpdatedDate;
}
Other side of One To Many:
#Entity
#Table(name="PLAN_ORGANIZATION_ACTION")
#Customizer(GridCacheCustomizer.class)
#AdditionalCriteria("CURRENT_TIMESTAMP BETWEEN this.startDate AND this.endDate")
#NamedQuery(name="PlanOrganizationAction.findAll", query="SELECT p FROM PlanOrganizationAction p")
#Portable
public class PlanOrganizationAction extends PrismObject implements Serializable {
#ManyToOne
#JoinColumn(name="PLN_ORG_RLTNP_SEQ_ID")
#PortableProperty(7)
private PlanOrganizationRelationship planOrganizationRelationship;
}
I have 3 paths - Adding new Relationship with Actions (both entities new) or Add an Action or Remove Action
When I am adding both, I perist the parent and the children are persisted as well and that is the expected behavior.
When I try to add or remove I try something like
PrismOrganizationRelationship por = findById (..) //we are spring-data-jpa
por.addPlanOrganizationAction(action);
repo.save(por); // Throws optimistic lock - even though #Version is the same
Not sure what is causing this issue ?

Check cascade=CascadeType.PERSIST. It work only for persist(save newly entity) operation. your operation remove child entity mean updating main entity. Thats why, you need to useCascadeType.MARGE` for update operation.

Related

change the OptimisticLockPolicy to use local-time

I'm using Eclipselink JPA, I have an Entity with a Timestamp field annotated with #Version por optimistic locking.
By default, this sets the entitymanager to use database time, so, if I have to do a batch update it doesn't work properly as it query the database for time each time it wants to do an insert.
How can I change the TimestampLockingPolicy to use LOCAL_TIME?
The class org.eclipse.persistence.descriptors.TimestampLockingPolicy.class has a public method useLocalTime() but I dont know how to use or, from where should I call it.
Found the answer:
first lets create a DescriptorCustomizer
public class LocalDateTimeCustomizer implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) throws Exception {
OptimisticLockingPolicy policy = descriptor.getOptimisticLockingPolicy();
if (policy instanceof TimestampLockingPolicy) {
TimestampLockingPolicy p = (TimestampLockingPolicy) policy;
p.useLocalTime();
}
}
}
then annotate the entity that has the #Version with
#Customizer(LocalDateTimeCustomizer.class)

#ElementCollection to non-collection field

I have this #ElementCollection mapping so i could bring a legacy table with no unique id to work:
#Entity #Table(...)
#Inheritance(...) #DiscriminatorColumn(...)
class Notification {
#Id
#Column(name="NOTIFICATION_ID")
private BigInteger id;
}
#Entity
#DiscriminatorValue(...)
class SomeNotification extends Notification {
#ElementCollection
#CollectionTable(name="LEGACY_TABLE", joinColumns=#JoinColumn(name="NOTIFICATION_ID"))
private Set<NotificationInfo> someInformations;
}
#Embeddable
class NotificationInfo { // few columns }
I really can't touch the structure of LEGACY_TABLE, and now i am facing this:
#Entity
#DiscriminatorValue(...)
class SpecialNotification extends Notification {
// ? This is not a Collection, and it can't be a ManyToOne or OneToOne
// since there is no ID declared on NotificationInfo.
private NotificationInfo verySpecialInformation;
}
I know this is not supported by default, but i am fine to implement a Customizer to make it work with EclipseLink. The point is that for SpecialNotification instances, there will be only up to one NotificationInfo associated, instead of many, that is the case of SomeNotification.
Any thoughts about where i could start in the Customizer?
Thank you!
I'm not sure this will work, but it's worth a shot. Try a combination of #SecondaryTable and #AttributeOverride
#Entity
#SecondaryTable(name="LEGACY_TABLE",
pkJoinColumns=#PrimaryKeyJoinColumn(name="NOTIFICATION_ID"))
#DiscriminatorValue(...)
class SpecialNotification extends Notification {
...
#Embedded
#AttributeOverrides({
#AttributeOverride(name="someField", column=#Column(table = "LEGACY_TABLE", name="SOME_FIELD")),
#AttributeOverride(name="someOtherField", column=#Column(table = "LEGACY_TABLE", name="SOME_OTHER_FIELD"))
})
private NotificationInfo verySpecialInformation;
...
}
UPDATE
Since #SecondaryTable by default makes an inner join, which may not be desired, it can be worked around with vendor specific APIs.
If you use Hibernate (which you don't, judging by the question tags, but nevertheless), it can be done with #org.hibernate.annotations.Table, by setting optional = true.
With EclipseLink, you should make use of #DescriptorCustomizer and DescriptorQueryManager#setMultipleTableJoinExpression, you can find a (not spot-on, but close enough) code example here.

JPA not updating ManyToMany relationship in returning result

Here are my entities:
#Entity
public class Actor {
private List<Film> films;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="actor_id"),
inverseJoinColumns = #JoinColumn(name="film_id"))
public List<Film> getFilms(){
return films;
}
//... more in here
Moving on:
#Entity
public class Film {
private List actors;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="film_id"),
inverseJoinColumns = #JoinColumn(name="actor_id"))
public List<Actor> getActors(){
return actors;
}
//... more in here
And the join table:
#javax.persistence.IdClass(com.tugay.sakkillaa.model.FilmActorPK.class)
#javax.persistence.Table(name = "film_actor", schema = "", catalog = "sakila")
#Entity
public class FilmActor {
private short actorId;
private short filmId;
private Timestamp lastUpdate;
So my problem is:
When I remove a Film from an Actor and merge that Actor, and check the database, I see that everything is fine. Say the actor id is 5 and the film id is 3, I see that these id 's are removed from film_actor table..
The problem is, in my JSF project, altough my beans are request scoped and they are supposed to be fetching the new information, for the Film part, they do not. They still bring me Actor with id = 3 for Film with id = 5. Here is a sample code:
#RequestScoped
#Named
public class FilmTableBackingBean {
#Inject
FilmDao filmDao;
List<Film> allFilms;
public List<Film> getAllFilms(){
if(allFilms == null || allFilms.isEmpty()){
allFilms = filmDao.getAll();
}
return allFilms;
}
}
So as you can see this is a request scoped bean. And everytime I access this bean, allFilms is initially is null. So new data is fetched from the database. However, this fetched data does not match with the data in the database. It still brings the Actor.
So I am guessing this is something like a cache issue.
Any help?
Edit: Only after I restart the Server, the fetched information by JPA is correct.
Edit: This does not help either:
#Entity
public class Film {
private short filmId;
#ManyToMany(mappedBy = "films", fetch = FetchType.EAGER)
public List<Actor> getActors(){
return actors;
}
The mapping is wrong.
The join table is mapped twice: once as the join table of the many-to-many association, and once as an entity. It's one or the other, but not both.
And the many-to-many is wrong as well. One side MUST be the inverse side and use the mappedBy attribute (and thus not define a join table, which is already defined at the other, owning side of the association). See example 7.24, and its preceeding text, in the Hibernate documentation (which also applies to other JPA implementations)
Side note: why use a short for an ID? A Long would be a wiser choice.
JB Nizet is correct, but you also need to maintain both sides of relationships as there is caching in JPA. The EntityManager itself caches managed entities, so make sure your JSF project is closing and re obtaining EntityManagers, clearing them if they are long lived or refreshing entities that might be stale. Providers like EclipseLink also have a second level cache http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching

Flush() required for multiple Eclipselink merges in the same transaction?

I'm having an issue with multiple EntityManager.merge() calls in a single transaction. This is using an Oracle database. Neither object exists yet. Entities:
public class A {
#Id
#Column("ID")
public Long getID();
#OneToOne(targetEntity = B.class)
#JoinColumn("ID")
public B getB();
}
public class B {
#Id
#Column("ID")
public Long getID();
}
The merge code looks something like this:
#Transactional
public void create(Object A, Object B) {
Object A = entitymanager.merge(A);
B.setId(A.getId());
entitymanager.merge(B);
}
Object A's ID is generated through a sequence and it gets correctly set on B. Looking at the log, merge on A is called before merge on B is called. There is a #OneToOne mapping from A to B. However, at the end of the method when it goes to actually commit, it tries to do an INSERT on B before it goes to do an INSERT on A, which throws an IntegrityConstraintViolation because the "parent key not found".
If I add entitymanager.flush() before the 2nd merge, it works fine.
#Transactional
public void create(Object A, Object B) {
Object A = entitymanager.merge(A);
entitymanager.flush();
B.setId(A.getId());
entitymanager.merge(B);
}
However, flush() is an expensive operation that shouldn't be necessary. All of this should be happening in the same transaction (default propagation of #Transactional is Propagation.REQUIRED).
Any idea why this doesn't work without flush(), and why even though the merge on A happens before the merge on B, the actual INSERT on COMMIT is reversed?
If entity A and B do not have a relationship (i.e. #OneToOne, #OneToMany, ...), then the persistence provider cannot calculate the correct insertion order. IIRC EclipseLink does not use the object-creation order when it comes to sending SQL statements to the database.
If you like to refrain from using flush(), simply set your constraints to be deferred.
As Frank mentioned, the code you have shown does not set a A->B relationship, so there is no way for the provider to know that this B object needs to be inserted before the A. Other relationships may cause it to think that in general A needs to be inserted first.
Deferring constraints can be done on some databases, and refers to setting the database to defer constraint processing until the end of the transaction. If you defer or remove the constraints, you can then see if the SQL that is being generated is correct or if there is another problem with the code and mappings that is being missed.
It appears that the merges are alphabetical (at least, that is one possibility) unless there are bidirectional #OneToOne annotations.
Previously:
public class A {
#OneToOne(targetEntity = B.class)
#JoinColumn("ID")
public B getB();
}
public class B {
#Id
#Column("ID")
public Long getID();
}
Now:
public class A {
#OneToOne(targetEntity = B.class)
#JoinColumn("ID")
public B getB();
}
public class B {
#Id
#Column("ID")
public Long getID();
#OneToOne(targetEntity = A.class)
#JoinColumn("ID")
public A getA();
}
For what I'm doing it doesn't matter that B has a way to get A, but I still don't understand why the annotations in A aren't sufficient.

JPA: Creating an entity whenever another entity is created

I'm using JPA/Hibernate over PGSQL DB.
I have an entity in my application, and I want to persist another entity (of a different type) every time the first entity is persisted. For example, whenever an "ORDER" is created, I want to immediately persist an empty "ORDER_INVOICE" entity and connect it to the order. These reside in two different tables.
At first I thought about writing a #PostPersist function for the ORDER entity and persisting the ORDER_INVOICE in it, but my problem is that I don't have the Entity Manager in this context.
I'm looking to avoid remembering to persist the ORDER_INVOICE upon every ORDER persistence.
Is that the right way to go? If so, how do I get the EM into the PostPersist? And if not, what would be a better way?
Why don't you simply create it in the constructor of your master entity, and set cascade=persist on the relationship?
#Entity
public class Order {
#OneToMany(mappedBy = "order", cascade=CascadeType.PERSIST)
private List<Invoice> invoices = new ArrayList<Invoice>();
public Order() {
Invoice i = new Invoice();
i.setOrder(this);
this.invoices.add(i);
}
// ...
}
EDITED :
To avoid creating a new invoice each time the Order's constructor is invoked (by JPA, for example), you could use this kind of code :
#Entity
public class Order {
#OneToMany(mappedBy = "order", cascade=CascadeType.PERSIST)
private List<Invoice> invoices = new ArrayList<Invoice>();
/**
* Constructor called by JPA when an entity is loaded from DB
*/
protected Order() {
}
/**
* Factory method; which creates an order and its default invoice
*/
public static Order createOrder() {
Order o = new Order();
Invoice i = new Invoice();
i.setOrder(o);
o.invoices.add(i);
}
// ...
}
If the order is persisted after having been instanciated by the factory method, then the invoice will be persisted as well (thanks to the cascade). If the order is not persisted, then it will be garbage collected at some point, and its default invoide as well.