Layz loaded list gets reset upon detach from EntityManager - java

I have lazy 1:n relation from Bp to BpHistorisiert.
To load a Bp including the related BpHistorisiert id do
Bp bp= entityManager.find(Bp.class, anId);
bp.getBpHistorisiertList();
This works fine, the call to getBpHistorisiertList() loads the n side lazy as expected. However when bp gets detached, the bpHistorisiert List in bp is explicitely reset to null. I can see in the debugger that this is explicitely done by the detach funcionality of the OpenJPA EntityManager.
So my question is: How to load lazy relations and keep the values when I work with detached entities?
Bp
#Entity
#Table(name = "BP", schema = "INFOP_STAMMDATEN")
public class Bp extends BaseEntity implements EntityId, Serializable {
/** technische ID */
#Id
#Column(name = ID)
private Long id;
#Valid
#OneToMany(mappedBy = "bp", orphanRemoval = false, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<BpHistorisiert> bpHistorisiertList = new ArrayList<>();
}
BpHistorisiert
#Entity
#Table(name = "BP_HISTORISIERT", schema = "INFOP_STAMMDATEN")
public class BpHistorisiert implements EntityId, GueltigkeitOwner, AbkuerzungOwner, Serializable {
#Id
#Column(name = ID)
private Long id;
#NotNull
#ManyToOne
#JoinColumn(name = BP_ID)
#ForeignKey
private Bp bp;
}

The answer is: by reporting an issue on OpenJPA issue tracker and waiting for resolution.
According to the docs, you should not observe the behavior you described.

Thanks to #crizzis answer I found the reason for this behaviour. We actually have set
<property name="openjpa.DetachState" value="fetch-groups(DetachedStateField=true)"/>
in our persistence.xml. The consequence is, that all lazy fileds of all entities are set to the java default value up on detach. For a lazy List this is null.
So yes, this is an OpenJPA feature, not a bug.
And after all this is even explizitely documented: https://issues.apache.org/jira/browse/OPENJPA-1913?attachmentSortBy=fileName

Related

JPA: Mapping crossed OneToOne and ManyToOne relations

I have two entities, which we'll call A and B. B always has A as a parent with a ManyToOne relation.
However, I need A to have a OneToOne relation with the latest record inserted in table B.
This is because I need to save multiple versions of B but 99% of the time will only need to use the most recent one.
This looks something like this:
#Data
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(AccessLevel.NONE)
private Long id;
/* Properties
...
*/
#OneToOne(optional = false)
private B latest;
}
#Data
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(AccessLevel.NONE)
private Long id;
/* Properties
...
*/
#Column(nullable = false)
private Date lastModified;
#ManyToOne(optional = false)
private A parent;
}
Now, the issue at hand is that I cannot seem to persist these entities as one always appears to be transient:
A cannot be persisted because latest references B, yet B is not persisted.
B cannot be persisted because parent references A, yet A is not persisted.
Attempting to do so results in:
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : B.parent -> A
I tried wrapping the code responsible for persisiting them in a #Transactional method but the same happens:
#Transactional
public void saveAB(A parent, B child) {
parent.setLatest(child);
child.setParent(parent);
Arepository.save(parent);
Brepository.save(child);
}
I also thought of disregarding the OneToOne relation from A to B, instead having latest as a transient #Formula field which would query B to take the most recent record. However, #Formula seems to be limited to primitives, not full entities.
What would be the proper way to do this with JPA? Am I approaching this the wrong way?
Since A and B depend on each other they should probably be considered a single aggregate with A being the aggregate root.
This means you'd have only an ARepository and also CascadeType.ALL on the relationships.
The solution was to apply #JoinFormula as explained here.
#Data
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(AccessLevel.NONE)
private Long id;
/* Properties
...
*/
#ManyToOne
#JoinFormula(value = "(SELECT b.id FROM b " +
"WHERE b.id = id ORDER BY b.lastModified DESC LIMIT 1)")
private B latest;
}
Then on B:
#ManyToOne(optional = false)
private A parent;

JPA: 'CascadeType.REMOVE' or 'orphanRemoval = true', which use in a n:n relation that generate new table/class with EmbeddeId class?

I am developing an REST API to a pizzeria store. And here i'm trying to delete a Flavor and all data related to it. Further explained below:
Classes:
Flavor have at least one Filling, each one taking a position on it.
i.e: Souce (at pos. 1), mozzarela (at pos. 2) tomato (at pos. 3)
Flavors must have a price to each Size
With that in mind, we can conclude that exist two many-to-many relationships:
Flavor to many Filling
Flavor to many Size
Class diagram of actual implementation
The requirement is to: delete a Flavor, and automatically delete all the FillingPositionFlavor and FlavorPriceSize.
But,I'm confused on use of CascadeType.REMOVE and orphanRemoval = true:
When I use Cascade and OrphanRemoval on Flavor.sizePrices, get a HibernateException when trying to edit a Flavor, exclusion works fine:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.pkg.Flavor.sizePrices
When I use Cascade on Flavor.sizePrices, get a PSQLException when excluding a Flavor, editing works fine:
ERROR: update or delete on table "tb_flavor" violates foreign key constraint "fk9orw0yhtc0e06ka84dbcd2c82" on table "tb_flavor_size_price"
I'm doing unit testing of services in Spring Boot to test all the CRUD operations.
Below is the actual code, I hid properties like id and others to facilitate the read.
#Entity
#Table(name = "tb_flavor")
class Flavor {
#OneToMany(cascade = {CascadeType.PERSIST,CascadeType.REMOVE},orphanRemoval = true)
private Set<FlavorPositionFilling> flavors = new HashSet<FlavorPositionFilling>();
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE},orphanRemoval = true)
private Set<FlavorPriceSize> priceSizes;
// other properties and methods
}
#Entity
#Table(name = "tb_flavor_price_size")
class FlavorPriceSize {
#EmbeddedId
private FlavorPriceSizeEmbeddeId id;
private float price;
// other properties and methods
}
#Embeddable
class FlavorPriceSizeEmbeddeId implements Serializable {
#ManyToOne(cascade = { CascadeType.ALL })
#JoinColumn(name = "ID_FLAVOR_FK", referencedColumnName = "id_flavor")
private Flavor flavor;
#ManyToOne(cascade = { CascadeType.ALL })
#JoinColumn(name = "ID_SIZE_FK", referencedColumnName = "id_size")
private Size size;
}
#Entity
#Table(name = "tb_flabor_position_filling")
class FlaborPositionFilling {
#EmbeddedId
private FlaborPositionFillingEmbeddedId id;
private Integer position;
}
#Embeddable
class FlaborPositionFillingEmbeddedId implements Serializable {
#ManyToOne(cascade = CascadeType.REMOVE)
#JoinColumn(name="ID_FLAVOR_FK", referencedColumnName="id_flavor")
private Flavor sabor;
#ManyToOne()
#JoinColumn(name="ID_FILLING_FK", referencedColumnName="id_filling")
private Filling filling;
}
I've read a lot about both, but still not understand the right use of each and their effect on operations. Can anyone explain it to me? Show videos, images, code...
Let's assume that you have a parent -> child relationship.
If you set CacadeType.REMOVE on the relationship every EntityManager.remove call on the parent will also remove the children.
orphanRemoval = true is used to delete orphan children.
So if remove a child from the parent reference or collection and save the parent the child will be deleted because its no longer attached to the parent.

OpenJPA - Nested OneToMany relationships merge issue

Posting this here as I wasn't seeing much interest here: http://www.java-forums.org/jpa/96175-openjpa-one-many-within-one-many-merge-problems.html
Trying to figure out if this is a problem with OpenJPA or something I may be doing wrong...
I'm facing a problem when trying to use OpenJPA to update an Entity that contains a One to Many relationship to another Entity, that has a One to Many relationship to another. Here's a quick example of what I'm talking about:
#Entity
#Table(name = "school")
public class School {
#Column(name = "id")
protected Long id;
#Column(name = "name")
protected String name;
#OneToMany(mappedBy = "school", orphanRemoval = true, cascade = CascadeType.ALL)
protected Collection<ClassRoom> classRooms;
}
#Entity
#Table(name = "classroom")
public class ClassRoom {
#Column(name = "id")
protected Long id;
#Column(name = "room_number")
protected String roomNumber;
#ManyToOne
#JoinColumn(name = "school_id")
protected School school;
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
protected Collection<Desk> desks;
}
#Entity
#Table(name = "desk")
public class Desk {
#Column(name = "id")
protected Long id;
#ManyToOne
#JoinColumn(name = "classroom_id")
protected ClassRoom classRoom;
}
In the SchoolService class, I have the following update method:
#Transactional
public void update(School school) {
em.merge(school);
}
I'm trying to remove a Class Room from the School. I remove it from the classRooms collection and call update. I'm noticing if the Class Room has no desks, there are no issues. But if the Class Room has desks, it throws a constraint error as it seems to try to delete the Class Room first, then the Desks. (There is a foreign key constraint for the classroom_id column)
Am I going about this the wrong way? Is there some setting I'm missing to get it to delete the interior "Desk" instances first before deleting the Class Room instance that was removed?
Any help would be appreciated. If you need any more info, please just let me know.
Thanks,
There are various bug reports around FK violations in OpenJPA when cascading remove operations to child entities:
The OpenJPA FAQ notes that the following:
http://openjpa.apache.org/faq.html#reorder
Can OpenJPA reorder SQL statements to satisfy database foreign key
constraints?
Yes. OpenJPA can reorder and/or batch the SQL statements using
different configurable strategies. The default strategy is capable of
reordering the SQL statements to satisfy foreign key constraints.
However ,you must tell OpenJPA to read the existing foreign key
information from the database schema:
It would seem you can force the correct ordering of the statements by either setting the following property in your OpenJPA config
<property name="openjpa.jdbc.SchemaFactory"> value="native(ForeignKeys=true)"/>
or by adding the org.apache.openjpa.persistence.jdbc.ForeignKey annotation to the mapping:
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#org.apache.openjpa.persistence.jdbc.ForeignKey
protected Collection<Desk> desks;
See also:
https://issues.apache.org/jira/browse/OPENJPA-1936

hibernate, stackoverflow with particular entity mapping

I have the following mapping:
#Entity
public class Satellite implements Serializable, Comparable<Satellite> {
#NotNull #Id
private long id;
.....
#OrderColumn
#OneToMany(mappedBy = "satellite", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<DataModel> dataModel;
}
and a child entity:
#Entity #IdClass(value=DataModelPK.class)
public class DataModel implements Serializable, Comparable<DataModel> {
private static final long serialVersionUID = -3416403014857250990L;
#Id
private int orbit; // related to reference orbit file
private int dataPerOrbit; // in Gbit
#ManyToOne #Id
private Satellite satellite;
}
originally, DataModel was an embeddable entity, but for a better control over the primary key and the underlying structure of the db, I switched to a more traditional model.
The point is, during the loading of the entity now it generate a stack overflow!! I think there is some cyclic loading between those two entities and it got stuck!
I'm thinking to revert everything back to what it was, but I wish to understand why it gives me this error.
You have #IdClass for DataModel specified to be DataModelPK.class but your #Id annotation is on an int field.
This is a problem, it may be causing you stackoverflow but I am not certain.
Update I now see the second #Id annotation so I stand corrected, I will investigate furtuer.

OneToOne with shared key (MapsId) and lazy loading problem

My entities are:
#Entity
public class ReportSnapshot extends LightEntity {
#Id
#GeneratedValue
private long id;
#OneToOne(fetch = FetchType.LAZY, optional = false, mappedBy = "snapshot")
private ReportContents contents;
#Embedded
private WeekNumber week;
...
}
and
#Entity
public class ReportContents extends LightEntity {
#Id
private long id;
#MapsId
#OneToOne(optional = false)
private ReportSnapshot snapshot;
#Lob
#Column(nullable = false)
private String reportText = "";
...
}
here light entity class comes from gilead project and is intended for gwt serialization support - it shudn't affect the process. What i observe is: when I load ReportSnapshot by id from an entity manager, I automatically get the linked ReportContents despite the lazy association. According to what I've read that shudn't be hppening as I've set my relationship to be non-optional =(
Which provider are you using? Hibernate does not support lazy loading of OneToOne associations (at least not out of the box). That page documents some approaches you could try.

Categories

Resources