JPA (EclipseLink) persit one-to-many relationship without Cascade.PERSIST - java

I have two entities.
Sports:
#Entity
public class Sports implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "SPORT_NAME")
private String name;
...
}
and SportFacility:
#Entity
public class SportFacility implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "SPORTFAC_NAME")
private String name;
#OneToMany
private List<Sports> sports;
....
}
Then I have some created sports in Sports table, for example:
--id-----name-----
- 0 - Football -
- 1 - Basketball-
- 2 - Golf -
------------------
And I need to create sport facility with some sports.Lets take all sports
List<Sports> listSports = sb.getAllSports(); //JPQL: SELECT s FROM Sports s
SportFacility sportFacility = new SportFacility("Stadium", listSports);
em.getTransaction().begin();
em.persist(sportFacility);
em.getTransaction().commit();
Error:
java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: database.entities.sports[ id=0 ].
I don' want to use Cascade.ALL or PERSIST becase there are only few sports in table and I always don't need to create new record with same sport...
Thank you.

The exception is related to the fact that collection of Sports that you retrieved are no longer in the persistence context when you try to persist the SportFacility - persistence context is by default transaction scoped (created when transaction begins and destroyed when it ends) - and EL doesn't know what to do with objects it doesn't control.
Move
em.getTransaction().begin();
to the beginning.

What does,
sb.getAllSports();
do? Does it use the same EntityManager, or a different one?
database.entities.sports[ id=0 ]
The error seems to indicate that one of your sports has a 0 id, this seems very odd? Did you persist the sports correctly?

Related

#ManyToMany relationship, JPA

I have an issue here, I am trying to make FoodProgram with two entities: FoodProgram-holds 4 meals with List for each meal. And FoodEntity with name, calories and id. The idea is that each meal can choose from all foods, and foods can repeat in every meal(if we eat eggs for lunch, we can add them to dinner too). I have #ManyToMany annotation in my FoodProgram class. Problem is there, when I try to getLunch from FoodProgram, I got error for lazily initialize, but I cannot put fetchtype.EAGER on every annotation, because I get another error. Should I just make another entity and to figure out different approach?
FoodEntity
#Entity
public class Food {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer calories;
public Food() {
}
FoodProgram entity
#Entity
public class FoodProgram {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany
private List<Food> breakfast;
#ManyToMany
private List<Food> lunch;
#ManyToMany
private List<Food> afterLunch;
#ManyToMany
private List<Food> dinner;
public FoodProgram() {
}
With a pure sql model point of view, what's the difference between breakfast, lunch, afterLunch and dinner ?
You have two solutions :
you need to implement sql inheritance through your hibernate model first. Have a look at here https://www.baeldung.com/hibernate-inheritance.
you have to specify the dedicated #JoinTable for each relationship
In other words, your implementation definitely cannot work like this.

Lazy Initialization Exception on Initialize a Collection - JPA

I implement a simple SpringMVC application. As models I have Person and Event both get mapped to a H2 database.
For now I am able to store a Person object and an Event object as well. But when I am trying to store a list of Person to my Event object than I always get the exception:
failed to lazily initialize a collection, could not initialize proxy - no Session
My approach so far:
#Entity
public class Event implements Persistable<Long> {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String eventName;
#OneToMany(fetch = FetchType.EAGER)
#ElementCollection(targetClass = Member.class)
private List<Person> members = new ArrayList<>();
}
#Entity
public class Person implements Persistable<Long>{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstname;
private String lastname;
}
The goal is to select Person objects with a primefaces SelectManyMenu and store them to the List<Person> members of the event. When I add some Person objects to an Event via SQL than the SelectManyMenu pre-selects the added Person objects correctly. But when I select an additional Person object and call save then the exception appears.
First I thought my save-method doesn't work. But it has to work correctly because I am able to save Person objects and Event objects separately (without a Relationship between).
To map properly, you should map members field as #OneToMany(mappedBy = "event", fetch = ...), and add #ManyToOne Event event in your Person class. Also you can be certain about what fetching strategy needed to use by reading fetching strategies document.

JPA one to many relationship - #transactional - get children collection

I want to get the children collection of the owner entity of a one to many relationship.
I have those two entities :
#Entity
#Table(name = "commande")
public class Commande implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "commande")
#JoinColumn(name= "papa_id")
#JsonIgnore
private Set<Piece> pieces = new HashSet<>();
#Entity
#Table(name = "piece")
public class Piece implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "ref")
private String ref;
#ManyToOne
private Commande commande;
And the resource :
#RequestMapping(value = "/papas/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
#Transactional
public ResponseEntity<Papa> getPapa(#PathVariable Long id) {
log.debug("REST request to get Papa : {}", id);
Papa papa = papaRepository.findOne(id);
papa.getEnfants().size();
...
}
I had to put #Transactional in order to make the .size() work (otherwise I have an exception).
It works.
But if in another method I delete one Enfant entity, then if I call getPapa again, I am getting an error durint .size() :
Unable to find com.myapp.stagiaireproject.domain.Enfant with id 3
Is it a problem of a transaction not closed?
For the first time, move all work with repository to service and mark this method as #Transactional. Set #Transactional annotation to controller method is bad practice.
One-to-many annotation is lazy by default, you can explicity set #OneToMany(mappedBy = "commande", fetch = FetchType.EAGER)
to told hibernate to read collection from database when fetching this entity.
Returning to you question: if you done this is one transaction, hibernate uses fist level cache (wich is not disabled), i.e. it caches entities, which loads by PK (id) during the transaction. And if you moves all work with repository to service class, your transaction commited before you return data from controller, and next request will read data from database, not from cache.

JPA OneToMany insert not setting the id's correctly

I have two entities, let's say
Person.java:
#Entity
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = AUTO)
private long id;
#OneToMany(mappedBy = "personData", cascade = CascadeType.PERSIST)
private List<SkillsData> skillsData;
// ...
}
SkillsData.java
#Entity
#Table(name = "SkillsData")
public class SkillsData implements Serializable {
#Id
#GeneratedValue(strategy = AUTO)
private long id;
#JoinColumn(name = "PERSONID")
#ManyToOne(cascade = REMOVE)
private Person personData;
// ...
}
When I create a person, add a list of type SkillsData to it's skillsData field and persist it everything works with no exceptions thrown, but when I browse the database directly in the SkillsData table the field PERSONID is not populated and because of that the skills added can't be referenced to the right person.
I'm trying to fix this problem for quite some time and I'll be thankful for any help.
The problem might be in the fact that you're not setting SkillsData.personData before persisting leaving it null.
You must set it cause adding SkillsData to the Person.skillsData list is not enough since you declared this side of relationship as inverse(mappedBy attribute).
Therefore it is the SkillsData.personData non-inverse side who is responsible for establishing this relationship.

JPA ManytoOne and OneToMany relationship not persisting

I have two entities, a Competition and a Team:
Competition:
#Entity
public class Team implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
int id;
#Column(length=255)
String name;
#ManyToOne(optional=true)
Competition nextCompetitionAttending;
List<SiteUser> teamMembers;
nextComeptitionAttending is set by the following method:
public void setNextCompetitionAttending(Competition nextCompetitionAttending) {
this.nextCompetitionAttending = nextCompetitionAttending;
nextCompetitionAttending.addTeam(this);
}
Competition is defined thusly:
#Entity
public class Competition implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
int maxTeamSize;
String name;
#OneToMany(mappedBy="nextCompetitionAttending" )
List<Team> teamsAttending;
My persistance code looks like this:
utx.begin();
EntityManager em = emf.createEntityManager();
em.merge(user);
Competition c = em.find(Competition.class, id);
Team teamon = user.getTeamOn();
teamon.setNextCompetitionAttending(c);
System.out.println("Set next");
em.flush();
utx.commit();
em = emf.createEntityManager();
Team t = em.find(Team.class, teamon.getId());
System.out.println(t.getNextCompetitionAttending());
In the database, the column "NEXTCOMPETITIONATTENDING_ID" is null, as is the println at the end of the code which I've been using to try to debug this.
Why doesn't this persist?
The culprit is probably this line:
em.merge(user);
user is a detached entity, and you merge it. Then, you get the team from the user. But merge doesn't make the detached entity attached. It loads the attached user, copy the state from the detached one to the attached one, and returns the attached one. So, all the subsequent changes you're doing are done to the detached entity.
Replace this line with
user = em.merge(user);

Categories