Using Named Entity Graphs in Eclipselink not work - jpa

I have 2 classes
Collections
#Entity
#NamedEntityGraph(
name = "cars-graph",
attributeNodes = #NamedAttributeNode("cars"))
public class Collections {
#Id
#ReturnInsert(returnOnly=true)
private Long id;
#ManyToMany(cascade = ALL)
#JoinTable(name="CarsCollections",
joinColumns= #JoinColumn(name="COLLECTIONS_ID", referencedColumnName="ID"),
inverseJoinColumns=
{#JoinColumn(name = "MODELSRANGE_ID", referencedColumnName = "MODELSRANGE_ID"),
#JoinColumn(name = "MODELS_ID", referencedColumnName = "MODELS_ID"),
#JoinColumn(name = "TYPES_ID", referencedColumnName = "TYPES_ID")
}
)
public List<Cars> cars;
and Cars
#Entity(name = "Cars")
public class Cars {
#EmbeddedId
private CarsId id;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("modelsRangeid")
#JoinColumn(name = "MODELSRANGE_ID")
private ModelsRange modelsrange;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("modelsid")
#JoinColumn(name = "MODELS_ID")
private Models models;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("typesid")
#JoinColumn(name = "TYPES_ID")
private Types types;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
#JoinColumn(name = "MAKE")
private Makes makes;
When i try to get Collections with Cars
EntityGraph entityGraph = em.getEntityGraph("cars-graph");
Collections collections = em.createQuery("select r from Collections r where r.id = :id", Collections.class)
.setParameter("id", 1L)
.setHint("javax.persistence.fetchgraph", entityGraph)
.getSingleResult();
System.out.println(collections.getCars());
I get {IndirectList: not instantiated}. Does not give errors, but Cars does not load.
If you add to the Collections entity for the Cars field "EAGER"
...
#ManyToMany(cascade = ALL, fetch = FetchType.EAGER)
#JoinTable(name="CarsCollections",
...
It works.
But then Cars will always load.
I use Jakarta EE9 and Eclipselink

Problem in javax name. In Jakarta EE need to use:
...
.setHint("jakarta.persistence.loadgraph", entityGraph)
...

Related

How to save a copy of a entity

I use spring boot 3, I have this entity structure
#Data
#Entity
#Table
public class Repo {
#Id
#NotNull
private Long repoId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "repo", cascade = {CascadeType.ALL})
private List<Pub> pubs = new ArrayList<>(0);
...
}
#Data
#Entity
#Table
public class Pub {
#Id
#Column
#NotNull
protected Long pubId;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinColumn(name = "type_id")
protected TypeDoc typeDoc;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pub", cascade = {CascadeType.ALL}, orphanRemoval = true)
protected List<Person> persons = new ArrayList<>(0)
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinTable(name = "pub_cat", schema = "", joinColumns = { #JoinColumn(name = "pub_id", nullable = true, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "cat_pub_id", nullable = true, updatable = false) })
private List<CatPub> catPub = new ArrayList<>(0);
}
A repo has a lit of pub, in reality they can have only 2.
So I feed a Pub object..... put it it the list of depot, and i need to create exactly the same and put it in the list of depot.
what is the most effective way to do it?

How to delete entities in a chained one-to-many and many-to-many JPA relationship

I have a chain of entities as follows:
#Entity
#Table(name = "Patients")
public class Patient extends TimeStampedPersistable{
private static final long serialVersionUID = 1L;
#OneToMany(mappedBy="patient", cascade = CascadeType.ALL)
#PrivateOwned
private List<Insurance> insurances;
}
#Entity
#Table(name = "insurance")
public class Insurance extends PersistableEntity {
private static final long serialVersionUID = 1L;
#ManyToOne(optional=false)
#JoinColumn(name="patient_id", nullable=false)
private Patient patient;
#Column(name = "policy_number", unique = false, nullable = true)
private String policyNumber;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "insurance_companycodes", joinColumns = {
#JoinColumn(name = "insurance_id", referencedColumnName = "id", nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = "insuranceCompanyCode_id", referencedColumnName = "id", nullable = false, updatable = false) })
private Set<InsuranceCompanyCode> insuranceCompanyCodes = new HashSet<>();
}
#Entity
#Table(name = "insurance_company_codes")
public class InsuranceCompanyCode extends PersistableEntity {
private static final long serialVersionUID = 1L;
#Column(name = "identifier", unique = true, nullable = true)
private String identifier;
#ManyToMany(mappedBy = "insuranceCompanyCodes", fetch = FetchType.LAZY)
private Set<Insurance> insurances = new HashSet<>();
}
I need to remove insurance items from the Patient object. I am using the following code:
for (Iterator<Insurance> iterator = patient.getInsurances().iterator(); iterator.hasNext();) {
iterator.next();
iterator.remove();
}
This seems to work for child entities that don't have child entities, however in this situation the Insurance entity has child entities and is not actually removed (no exceptions are displayed). Note, I am using the EclipseLink specific annotation #PrivateOwned which I expected would have forced the removal of the Insurance entities.
Any guidance, suggestions appreciated!

JPARepository - sometimes create duplicate records

I have the following entity class.
#Data
#EqualsAndHashCode(callSuper=false)
#ToString(callSuper=true)
#Entity
#Table(name = "storeitem")
public class StoreItem extends SellableStoreItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#EqualsAndHashCode.Exclude
#ToString.Exclude
#ManyToOne
#JoinColumn(name = "store_id")
private Store store;
#EqualsAndHashCode.Exclude
#ToString.Exclude
#ManyToOne
#JoinColumn(name = "storeitemcategory_id", nullable = true)
private StoreItemCategory storeItemCategory;
#EqualsAndHashCode.Exclude
#OneToMany(fetch = FetchType.EAGER, mappedBy = "storeItem")
private List<StoreItemTranslation> storeItemTranslationList = new ArrayList<>();
#EqualsAndHashCode.Exclude
#OneToMany(mappedBy = "storeItem",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<StoreItemOptionCollectionSelection> storeItemOptionCollectionSelections = new ArrayList<>();
#EqualsAndHashCode.Exclude
#Column(name = "uid")
private UUID uid = UUID.randomUUID();
#EqualsAndHashCode.Exclude
#CreationTimestamp
#Column(name = "createddate", nullable = false)
private LocalDateTime createdDate;
#EqualsAndHashCode.Exclude
#Column(name = "iscurrent", nullable = false)
private boolean isCurrent = true;
And in my service layer, I do the following.
private StoreItemResponse setStoreItemCreate(StoreItemDTO storeItemDTO, Store store, StoreItemCategory storeItemCategory) {
StoreItem storeItem = new StoreItem(storeItemDTO, store, storeItemCategory);
if(storeItemDTO.getUid() != null){
storeItem.setUid(storeItemDTO.getUid());
}
storeItem = storeItemRepository.save(storeItem);
// Create Translations for store Item
for (TranslationDTO translationDTO : storeItemDTO.getTranslationDTOs()) {
StoreItemTranslation translation = new StoreItemTranslation(translationDTO, storeItem);
storeItemTranslationRepository.save(translation);
}
return new StoreItemResponse(storeItem.getId(), DtoResponseStatus.OK);
}
However, when testing the code, I notice that there are times (not often but some cases) I see duplicate records (with different id) are being saved to database. And the duplicates are saved 2ms apart so I suspect storeItem = storeItemRepository.save(storeItem); created the duplicate records.
Why would this happen?

JPA query with left join and "not exists"

I am writing a JPA query using TopLink which involves the following three entities.
#Entity
#Table(name = "OFFERS")
public class Offers implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="offers_seq_gen")
#SequenceGenerator(name="offers_seq_gen", sequenceName="OFFERS_SEQ")
#Basic(optional = false)
#Column(name = "OFFERID")
private Long offerid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "offers", fetch = FetchType.LAZY)
private List<Coupons> couponsList;
}
#Entity
#Table(name = "COUPONS")
public class Coupons implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="coupons_seq_gen")
#SequenceGenerator(name="coupons_seq_gen", sequenceName="COUPONS_SEQ")
#Basic(optional = false)
#Column(name = "COUPONID")
private Long couponid;
#Basic(optional = false)
#Column(name = "ISSUED", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
private Date issued;
#JoinColumn(name = "USERID", referencedColumnName = "USERID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Users users;
#JoinColumn(name = "OFFERID", referencedColumnName = "OFFERID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Offers offers;
#Entity
#Table(name = "USERS")
public class Users implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="users_seq_gen")
#SequenceGenerator(name="users_seq_gen", sequenceName="USERS_SEQ")
#Basic(optional = false)
#Column(name = "USERID")
private Long userid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "users", fetch = FetchType.LAZY)
private List<Coupons> couponsList;
I need to find all the Offers who either have no coupons for a given user or all the coupons for the user were issued more than a day ago.
I have tried many different approaches and the only query I have come up with so far, which does not crash the server on deployment is:
SELECT o
FROM Offers o
LEFT JOIN o.couponsList c
WHERE
c.users.userid = :userid AND c.issued < :yesterday
OR
NOT EXISTS
(SELECT c1
FROM Coupons c1
WHERE c1.offers = o AND c1.users.userid = :userid)
But it does not return the Offer when the Coupons entry does not exist.
I managed to find a working query. Leaving it here for reference if anyone had similar issues:
SELECT o FROM Offers o WHERE
NOT EXISTS
(SELECT c FROM Coupons c WHERE c.users.userid = :userid
AND c.issued > :yesterday AND c.offers = o)
OR NOT EXISTS
(SELECT c1 FROM Coupons c1 WHERE c1.offers = o
AND c1.users.userid = :userid)

java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST

I have two 2 classes in relation many to many.
#Entity
#Table(name = "recipies")
public class Recipie implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String url;
private String image;
#ManyToMany
#JoinTable(
name = "recipie_ingredients",
joinColumns = {
#JoinColumn(name = "recipie_id", referencedColumnName = "id")},
inverseJoinColumns = {
#JoinColumn(name = "ingredient_id", referencedColumnName = "id")})
private List<Ingredient> ingredients = new ArrayList<>();
#Entity
#Table(name = "ingredients")
public class Ingredient implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToMany(mappedBy = "ingredients")
private List<Recipie> recipies;
I would like to create a new recipie this way:
List<Ingredient> ingredientsList = new ArrayList<>();
String ingredientName = "example";
Ingredient ingredient = ingredientsDao.findIngredientByName(ingredientName);
if (ingredient == null) {
ingredient = new Ingredient();
ingredient.setName(ingredientName);
}
ingredientsList.add(ingredient);
.....
recipie.setIngredients(ingredientsList);
recipiesDao.addRecipie(recipie);
If ingredient doesn't exist in database, occur errors like this
Caused by: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST
Is there any way to Ingredient objects created in the table automatically?
I try add CascadeType.PERSIST but It also doesn't work
#ManyToMany(mappedBy = "ingredients", cascade = CascadeType.PERSIST)
private List<Recipie> recipies;
First of all, for a bidirectional relationship, both sides need to be updated, so:
recipe.getIngredients().add(ingredient);
ingredient.getRecipes().add(recipe);
Then, you can set the cascade to PERSIST on the side of the relationship which you are passing to save(). So if you are saving the recipe, you should mark the Recipe.ingredients with
#ManyToMany(cascade = CascadeType.PERSIST)
(Side note, it's spelled "recipe", not "recipie")
As mentioned by #Gimby, you need to assign both sides of the relationship.
When dealing with #Many... sided relationships I always initialise the collection (which you've done on one side):
#Entity
#Table(name = "recipies")
public class Recipie implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String url;
private String image;
#ManyToMany
#JoinTable(
name = "recipie_ingredients",
joinColumns = {
#JoinColumn(name = "recipie_id", referencedColumnName = "id")},
inverseJoinColumns = {
#JoinColumn(name = "ingredient_id", referencedColumnName = "id")})
private List<Ingredient> ingredients = new ArrayList<>();
...
}
#Entity
#Table(name = "ingredients")
public class Ingredient implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToMany(mappedBy = "ingredients")
private List<Recipie> recipies = new ArrayList<>();
...
}
And then a slight variation in your logic:
String ingredientName = "example";
Ingredient ingredient = ingredientsDao.findIngredientByName(ingredientName);
if (ingredient == null) {
ingredient = new Ingredient();
ingredient.setName(ingredientName);
}
...
// Don't forget to assign both sides of the relationship
recipe.getIngredients().add(ingredient);
ingredient.getRecipies().add(recipe);
recipiesDao.addRecipe(recipe);
This should then cascade persist/update correctly.
The real fun will begin when you try to figure out how to associate a quantity with the ingredient...