I faced an issue earlier with JPA.
I have two apps : the main one, using Java/JPA (EclipseLink), and a second one, using PHP. The two apps have access to the same database.
Now, I'm accessing an "Expedition" object through Java, then calling the PHP app through a web-service (which is supposed to modify an attribute of this object in the shared database table "Expedition"), then accessing this attribute through the Java app.
Problem is, the object seems not to be modified in the Java app, even if it is modified in the database. I'm thinking about a cache problem.
The original code (simplified) :
System.out.println(expedition.getInfosexpedition()); // null
// Calling the web-service (modification of the "expedition" object in the database)
this.ec.eXtractor(expedition);
System.out.println(expedition.getInfosexpedition()); // Still null, should not be
Definitions of the "Expedition" and "Infosexpedition" classes :
Expedition :
#Entity
#Table(name = "expedition")
#XmlRootElement
public class Expedition implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idExpedition")
private Integer idExpedition;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "idExpedition")
#XmlTransient
private Infosexpedition infosexpedition;
Infosexpedition :
#Entity
#Table(name = "infosexpedition")
#XmlRootElement
public class Infosexpedition implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idInfoExpedition")
private Integer idInfoExpedition;
#JoinColumn(name = "idExpedition", referencedColumnName = "idExpedition")
#OneToOne(optional = false)
#XmlTransient
private Expedition idExpedition;
I've been able to make the original code work by doing this :
System.out.println(expedition.getInfosexpedition()); // null
// Calling the web-service (modification of the "expedition" object in the database)
this.ec.eXtractor(expedition);
try
{
// Getting explicitly the "infosExpedition" item through a simple named request
Infosexpedition infos = this.ec.getFacade().getEm().createNamedQuery("Infosexpedition.findByIdExpedition", Infosexpedition.class)
.setParameter("idExpedition", expedition)
.setHint("eclipselink.refresh", "true")
.setHint("eclipselink.cache-usage", "DoNotCheckCache")
.setHint("eclipselink.read-only", "true") // This line did the trick
.getSingleResult();
expedition.setInfosexpedition(infos);
}
catch (NoResultException nre) {}
System.out.println(expedition.getInfosexpedition()); // Not null anymore, OK
I'm trying to understand what happens here, and why did I had to specify a "read-only" hint to make this work... Before that, I tried almost everything, from evictAll() calls to detach()/merge() calls, and nothing worked.
Can someone help me to understand how the different levels of cache worked here ? And why is my newly created line "read-only" ?
Thanks a lot.
The settings you are using are attempting to bypass the cache. ("eclipselink.read-only", "true") causes it to bypass the first level cache, while the ("eclipselink.cache-usage", "DoNotCheckCache") makes the query go to the database instead of pulling data from the second level cache. Finally ("eclipselink.refresh", "true") refreshes the data in the shared cache rather then return the prebuilt object. Your facade must be using the same EntityManager for both requests even though you have made changes to the objects between the requests. As mentioned in the comments, an EntityManager is meant to be used as a transaction would, so that you are isolated from changes made during your transactions. If this doesn't work for you, you should clear or release the entityManager after the first call, so that the calls after the web-service modifications can be picked up.
If applications outside this one are going to be making data changes frequently, you might want to look at disabling the shared cache as described here:
https://wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F
And also implement optimistic locking to prevent either application from overwriting the other with stale data as described here:
https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Locking/Optimistic_Locking
What you call cache is the 1st level cache, id est the in memory projection of the database state at a time t.
This "cache" has the same lifecycle that the entity manager itself and generally won't be refreshed until you explicitely clear it (using myEntityManager.clear()) (you shouldn't) or force it to refreh a specific entity instance (using myEntityManager.refresh(myEntityInstance), this is the way you should go)
See Struggling to understand EntityManager proper use and Jpa entity lifecycle for a more detailed explanation
Related
I am creating simple REST API. I want to create an object via post method. Object contains other object. When I want to post it, I am receiving EntityNotFoundException which is thrown when nested object does not exist.
Code of object that I want to post:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Book {
private String title;
#ManyToOne
private Author author;
#Id
#Column(unique = true)
private String isbn;
}
Service of this object:
#Service
#RequiredArgsConstructor
public class BookServiceImpl implements BookService {
private final BookRepository bookRepository;
private final AuthorRepository authorRepository;
#Override
public Book save(Book book) {
try {
Author byId = authorRepository.getById(book.getAuthor().getId());
} catch (EntityNotFoundException e) {
authorRepository.save(book.getAuthor());
}
return bookRepository.save(book);
}
}
After using post method I get this error:
javax.persistence.EntityNotFoundException: Unable to find com.jakubkolacz.qualificationTask.domain.dao.Author with id 0
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate.handleEntityNotFound(EntityManagerFactoryBuilderImpl.java:163) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:216) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
I thought that using try catch to check if object exist, and saving author if necessary will help but it did not.
My question is where should I add some code to solve the problem. I understand why it is happening but do not know how to resolve it. The necessary thing is that I can not create service to add authors manually, they have to be added to repo only during adding new book.
The problem is that the save operation is not being cascaded down to the author object. You should add a cascade type inside ManyToOne annotation:
#ManyToOne(cascade = CascadeType.ALL)
Exception handling in Spring
If you are specifically wondering how to handle exceptions in Spring, then I would highly recommend THIS tutorial.
Entity Creation
First I would like to point out two minor problems with your entity creation.
1)#ManyToOne : while it is not necessary, I always like to annotate a many-to-one relationship with the #JoinColumn annotation. It just acts as a simple and friendly visual reminder that (assuming your relationship is bidirectional) this side is the owner of the relationship(has the foreign key)
2)#Id : as it currently stands, the JPA provider(lets assume hibernate) assumes that you the developer are taking care of assigning a unique identifier to the id field. Granted, this is sometimes neccessary when dealing with old legacy databases. However, if you are not dealing with a legacy database, I would recommend that you delete #Column(unique = true) and the String value to replace them with:
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long isbn;
#GeneratedValue will allow the system to automatically generate a value for isnb field.
strategy = GenerationType.IDENTITY tells the underlying database to handle the uniqueness and the auto incrementation in a way specific to the relational database.
Long instead of String because it can hold a larger number of bytes.
Service implementation
I have a few things to say about the BookServiceImpl class but first, good job on implementing an interface with BookService most people forget to program to an interface.
1) Single Responsibility
You are using both BookRepository and AuthorRepository which is of course fine(If it works it works). However, moving forward you should be weary not to add too many dependencies to one class. If you do so, you are breaking the Single Responsibility principle, which makes code harder to test and refactor.
2) Try and catch
The code inside your try catch blocks is a little confusing, especially since you have not shown the Author entity class. However, I am assuming you logic goes like this: if the author does not exist, save the author. Lastly save and return the book. Now you are correct in thinking that you handle the exceptions in the catch block. However, there is quite a lot to question here and only so little code to go on.
My overall recommendations
1) Break this method up : This method is trying to do three things at once. Create one method for saving the book, one for looking for the author and one for saving the author. This will allow for greater code reuse moving forward.
2) Read up on CascadeType : Specifically PERSIST, that might help you with your issues. Also, look into a many-to-many relationship as it is not uncommon for multiple books to have multiple authors.
I have a JpaRepository persisting newly created entity in Spring MVC app. This entity looks like this (very simplified):
#Entity
public class Translation {
.....
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
private Version version;
....
}
and Version entity:
#Entity
public class Version {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#Column(name = "version_code")
private long code;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "version", cascade = {CascadeType.ALL}, orphanRemoval = true)
private Set<Translation> translations;
}
I create a translation object like this
TranslationDTO t = new TranslationDTO();
t.setText(translationText);
ClientVersionDTO version = new ClientVersionDTO();
version.setId(11);
t.setVersion(version);
where 11 is a version that exists in the database already from the very beginning. Please notice that I do not set values for name and code of ClientVersionDTO.
Then I have a service that persists new object (I use dozer library to convert DTO to entities)
#Service
#Transactional
public class TranslationsServiceImpl implements TranslationsService {
#Override
public Long create(TranslationDTO translationDTO) {
Translation translation = translationsConverter.unconvert(translationDTO);
Translation t = translationRepository.saveAndFlush(translation);
Translation t2 = translationRepository.findOne(t.getId());
// !!!! t2.getVersion() returns version where no values are set to 'code' and 'name'
return t2.getId();
}
}
Please notice my comment "t2.getVersion() returns version where no values are set to 'code' and 'name'" - I was expecting so that when I fetch the data from the database, I would get a Version object right from the database with code and name values set. However they are not set. So basically what I get as a t2.getVersion() object is the same object as in input argument translationDTO.getVersion(). How can they I re-invalidate the Version object?
UPDATE tried moving #Transactional to JpaRepository, but still the same result.
If you are using Hibernate, this is the expected result. When you call translationRepository.saveAndFlush(translation) and translationRepository.findOne(t.getId()) one after the other, they hit the same Hibernate session which maintains a cache of all objects that it has worked on. Therefore, the second call simply returns the object passed to the first. There is nothing in those two lines that would have forced Hibernate to fire a SELECT query on the database for the Version entity.
Now, the JPA spec does have a refresh method on the EntityManager interface. Unfortunately, Spring Data JPA does not expose this method using its JpaRepository interface. If this method was available, you could have done t = translationRepository.saveAndFlush(translation) and then versionRepository.refresh(t.getVersion()) to force the JPA provider to synchronize the version entity with the database.
Implementing this method is not difficult. Just extend SimpleJpaRepository class from Spring Data JPA and implement the method yourself. For details see adding custom behaviour to all Spring Data JPA repositories.
An alternate would be to load the version entity as versionRepository.findOne(version.getId()) before setting it on the translation. Since you can hard-code version id in your code, your versions seem to be static. You can therefore mark your Version entity as #Immutable and #Cacheable (the former is a Hibernate-specific annotation). That way, versionRepository.findOne(version.getId()) should not hit the database every time it is called.
I googled a lot and It is really bizarre that Spring Boot (latest version) may not have the lazy loading is not working. Below are pieces of my code:
My resource:
public ResponseEntity<Page<AirWaybill>> searchAirWaybill(CriteraDto criteriaDto, #PageableDefault(size = 10) Pageable pageable{
airWaybillService.searchAirWaybill(criteriaDto, pageable);
return ResponseEntity.ok().body(result);
}
My service:
#Service
#Transactional
public class AirWaybillService {
//Methods
public Page<AirWaybill> searchAirWaybill(AirWaybillCriteriaDto searchCriteria, Pageable pageable){
//Construct the specification
return airWaybillRepository.findAll(spec, pageable);
}
}
My Entity:
#Entity
#Table(name = "TRACKING_AIR_WAYBILL")
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#airWaybillId") //to fix Infinite recursion with LoadedAirWaybill class
public class AirWaybill{
//Some attributes
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_TRACKING_CORPORATE_BRANCH_ID")
private CorporateBranch corporateBranch;
}
And when debugging, I still getting all lazy loaded attributed loaded. See image below.
One of my questions is could Jackson be involved in such behaviour?
Is there any way that I may have missed to activate the lazy loading?
EDIT
Another question, could the debugger be involved in ruining the lazy loading?
EDIT 2:
For specification build, I have :
public static Specification<AirWaybill> isBranchAirWayBill(long id){
return new Specification<AirWaybill>() {
#Override
public Predicate toPredicate(Root<AirWaybill> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.join("corporateBranch",JoinType.LEFT).get("id"),id);
}
};
}
Hibernate Session exists within method with #Transactional.
Passing entity outside Service class is a bad practise because session is being closed after leaving your search method. On the other hand your entity contains lazy initialised collections, which cannot be pulled once session is closed.
The good practise is to map entity onto transport object and return those transport objects from service (not raw entities).
SpringBoot by default has enabled:
spring.jpa.open-in-view = true
That means transaction is always open. Try to disable it.
more information here
Most likely you are debugging while still being inside the service, thus while the transaction is still active and lazy loading can be triggered (any method called on a lazy element triggered the fetch from the database).
The problem is that lazy loading cannot occur while being outside of the transaction. And Jackson is parsing your entity definitely outside the boundaries of one.
You either should fetch all the required dependencies when building your specification or try with the #Transactional on the resource level (but try that as of last resort).
Just so that you know, LAZY fetching strategy is only a hint.. not a mandatory action. Eager is mandatory:
The LAZY strategy is a hint to the persistence provider runtime that
data should be fetched lazily when it is first accessed. The
implementation is permitted to eagerly fetch data for which the LAZY
strategy hint has been specified.
When using a debugger, you are trying to access the value of your variables. So, at the moment you click that little arrow on your screen, the value of the variable in question is (lazily) loaded.
I suppose you are using Hibernate as JPA.
From specification:
The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified. https://docs.jboss.org/hibernate/jpa/2.2/api/javax/persistence/FetchType.html
Hibernate ignores fetch type specially in OneToOne and ManyToOne relationships from non owning side.
There are few options how to force Hibernate use fetch type LAZY if you really need it.
The simplest one is to fake one-to-many relationship. This will work because lazy loading of collection is much easier then lazy loading of single nullable property but generally this solution is very inconvenient if you use complex JPQL/HQL queries.
The other one is to use build time bytecode instrumentation. For more details please read Hibernate documentation: 19.1.7. Using lazy property fetching. Remember that in this case you have to add #LazyToOne(LazyToOneOption.NO_PROXY) annotation to one-to-one relationship to make it lazy. Setting fetch to LAZY is not enough.
The last solution is to use runtime bytecode instrumentation but it will work only for those who use Hibernate as JPA provider in full-blown JEE environment (in such case setting "hibernate.ejb.use_class_enhancer" to true should do the trick: Entity Manager Configuration) or use Hibernate with Spring configured to do runtime weaving (this might be hard to achieve on some older application servers). In this case #LazyToOne(LazyToOneOption.NO_PROXY) annotation is also required.
For more informations look at this:
http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html
Just a guess: you are forcing a fetch while building your specification.
I expect something like
static Specification<AirWaybill> buildSpec() {
return (root, query, criteriaBuilder) -> {
Join<AirWaybill, CorporateBranch> br = (Join) root.fetch("corporateBranch");
return criteriaBuilder.equal(br.get("addressType"), 1);
};
}
If this is the case, try changing root.fetch to root.join
The retrieved data already lazy but you are using debug mode its return value when click to watch a data from a debugger.
You can solve this problem with wit 2 steps with jackson-datatype-hibernate:
kotlin example
Add In build.gradle.kts:
implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
Create #Bean
#Bean
fun hibernate5Module(): Module = Hibernate5Module()
Notice that Module is com.fasterxml.jackson.databind.Module, not java.util.Module
Another consideration is while using Lombok, #Data/#Getter annotation causes to load lazy items without need. So be careful when using Lombok.
This was my case.
I think I might have a solution. You can give this a try. This worked for me after 4 hours of hit and trial -
User Entity :
class User {
#Id
String id;
#JsonManagedReference
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Address> addressDetailVOList = new ArrayList<Address>();
}
Address entity :
class Address {
#JsonBackReference
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "userId")
private User user;
}
Your parent class will use #JsonManagedReference, and child class will use #JsonBackReference. With this, you can avoid the infinite loop of entity objects as response and stack overflow error.
I also faced the same issue with Spring data JPA. I added the below annotation & able to get the customer records for a given ORDER ID
Customer to Order : one to Many
Order to customer is lazy load.
Order.java
#ManyToOne(cascade = CascadeType.ALL,targetEntity = CustomerEntity.class,fetch = FetchType.LAZY)
#Fetch(FetchMode. JOIN)
#JoinColumn(name = "CUSTOMER_ID",referencedColumnName = "CUSTOMER_ID",insertable = false,updatable = false)
#LazyToOne(LazyToOneOption.PROXY)
Private CustomerEntity customer
Customer.java
#Entity
#TabLe(name = "CUSTOMER" ,
uniqueConstraints = #UniqueConstraint(columnNames= {"mobile"}))
public class CustomerEntity {
#GeneratedVaLue(strategy = GenerationType.IDENTITY)
#CoLumn(name = "customer_id" )
private Integer customerld;
private String name;
private String address;
private String city;
private String state;
private Integer zipCode;
private Integer mobileNumber;
#OneToMany(mappedBy = " customer" )
#Fetch(FetchMode.JOIN)
#LazyToOne(LazyToOneOption.PROXY)
private List<OrderEntity> orders;
}
APPLICATION and ENVIRONMENT
Java EE / JSF2.0 / JPA enterprise application, which contains a web and an EJB module. I am generating PDF documents which contains evaluated data queried via JPA.
I am using MySQL as database, with MyISAM engine on all tables. JPA Provider is EclipseLink with cache set to ALL. FetchType.EAGER is used at relationships.
AFTER RUNNING NETBEANS PROFILER
Profiler results show that the following method is called the most. In this session it was 3858 invocations, with ~80 seconds from request to response. This takes up 80% of CPU time. There are 680 entries in the Question table.
public Question getQuestionByAzon(String azon) {
try {
return (Question) em.createQuery("SELECT q FROM Question q WHERE q.azonosito=:a").setParameter("a", azon).getSingleResult();
} catch (NoResultException e) {
return null;
}
}
The Question entity:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Question implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String azonosito;
#Column(nullable = false)
#Basic(optional = false)
private String label;
#Lob
#Column(columnDefinition = "TEXT")
private String help;
private int quizNumber;
private String type;
#ManyToOne
private Category parentQuestion;
...
//getters and setters, equals() and hashCode() function implementations
}
There are four entities extending Question.
The column azonosito should be used as primary key, but I don't see this as the main reason for low performance.
I am interested in suggestions for optimization. Feel free to ask if you need more information!
EDIT See my answer summarizing the best results
Thanks in advance!
Using LAZY is a good start, I would recommend you always make everything LAZY if you are at all concerned about performance.
Also ensure that you are using weaving, (Java SE agent, or Java EE/Spring, or static), as LAZY OneToOne and ManyToOne depend on this.
Changing the Id to your other field would be a good idea, if you always query on it and it is unique. You should also check why your application keeps executing the same query over and over.
You should make the query a NameDQuery not use a dynamic query.
In EclipseLink you could also enable the query cache on the query (once it is a named query), this will enable cache hits on the query result.
Have you got unique index on the azonosito column in your database. Maybe that will help.
I would also suggest to fetch only the fields you really need so maybe some of then could be lazy i.e. Category.
Since changing fetch type of relationship to LAZY dramatically improved performance of your application, perhaps you don't have an index for foreign key of that relationship. If so, you need to create it.
In this answer I will summarize what was the best solution for that particular query.
First of all, I set azonosito column as primary key, and modified my entities accordingly. This is necessary because EclipseLink object cache works with em.find:
public Question getQuestionByAzon(String azon) {
try {
return em.find(Question.class, azon);
} catch (NoResultException e) {
return null;
}
}
Now, instead of using a QUERY_RESULT_CACHE on a #NamedQuery, I configured the Question entity like this:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Cache(size=1000, type=CacheType.FULL)
public abstract class Question implements Serializable { ... }
This means an object cache of maximum size 1000 will be maintained of all Question entities.
Profiler Results ~16000 invocations
QUERY_RESULT_CACHE: ~28000ms
#Cache(size=1000, type=CacheType.FULL): ~7500ms
Of course execution time gets shorter after the first execution.
I'm using JPA on a SWING application in JAVA that connects to an Apache DERBY embedded database. I use Netbeans as my IDE and use many of the "supposedly" helpful templates. My problem is simple, but it's difficult for me to explain so I will paste the relevant code here and try to explain at the bottom.
#Entity
public class AnioLectivo implements Serializable, Comparable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
#OneToMany(mappedBy = "anioLectivo", cascade=CascadeType.ALL)
private List<Compensatorio> compensatorios;
...
}
#Entity
public class Compensatorio implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
#ManyToOne
private AnioLectivo anioLectivo;
...
}
These two are the entities that i want to persist.
public class AnioLectivoJpaController
{
public void edit(AnioLectivo anioLectivo) throws NonexistentEntityException,
Exception
{
EntityManager em = null;
try {
em = getEntityManager();
em.getTransaction().begin();
AnioLectivo persistentAnioLectivo = em.find(AnioLectivo.class,
anioLectivo.getId());
...
List<Compensatorio> compensatoriosOld =
persistentAnioLectivo.getCompensatorios();
List<Compensatorio> compensatoriosNew = anioLectivo.getCompensatorios();
...
List<Compensatorio> attachedCompensatoriosNew = new ArrayList<Compensatorio>();
for (Compensatorio compensatoriosNewCompensatorioToAttach : compensatoriosNew) {
compensatoriosNewCompensatorioToAttach =
em.getReference(compensatoriosNewCompensatorioToAttach.getClass(),
compensatoriosNewCompensatorioToAttach.getId());
attachedCompensatoriosNew.add(compensatoriosNewCompensatorioToAttach);
}
compensatoriosNew = attachedCompensatoriosNew;
anioLectivo.setCompensatorios(compensatoriosNew);
...
}
This is a class that netbeans generates using the annotations of the entity AnioLectivo that i pasted before. As you can see, i only pasted the code relevant to the problem to keep it simple because i know thanks to the debug tool of netbeans that the problem is here.
Now I'll try to explain exactly what happens.
I create instances of AnioLectivo in one part of the program and persist them ok. Then in another part i must create and add instances of Compensatorio to the Compensatorio's List in an instance of AnioLectivo. Now I want to save this modification, which I assume is made using the edit method in the class AnioLectivoJpaController and I found this error:
java.lang.IllegalArgumentException: An instance of a null PK has been incorrectly provided for this find operation.
at oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerImpl.findInternal(EntityManagerImpl.java:309)
at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerImpl.getReference(EntityManagerImpl.java:176)
at org.sigeb.local.service.dao.jpa.AnioLectivoJpaController.edit(AnioLectivoJpaController.java:113)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp.guardarCambios(AdministrarCursosPopUp.java:574)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp.jBGuardarCambiosActionPerformed(AdministrarCursosPopUp.java:394)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp.access$1000(AdministrarCursosPopUp.java:44)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp$11.actionPerformed(AdministrarCursosPopUp.java:204)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
...
the problem, as I see it, occurs in this line of code in the edit method of AnioLectivoJpaController:
em.getReference(compensatoriosNewCompensatorioToAttach.getClass(),
compensatoriosNewCompensatorioToAttach.getId());
Why? Well if you see the entities, I have defined that the id of all entities are to be generated by the persistence unit, but this only happens when the entity itself is told to persist. As I create the Compensatorio's instances I never set the id explicitly and when it arrives to that line I quoted up there, compensatoriosNewCompensatorioToAttach.getId() returns null.
It's my understanding that ORM's like JPA have Persistence by Reachability, that allows that if an object A is related to an object B, persisting A also persists B. But in this case it seems like it's implemented in a very inconvenient way(at least for me), because it forces me to persist every object of my collection explicitly when it would be more usefull to persist the object that owns that collection and then the objects in that collection be persisted automatically
Is there something I'm doing wrong?, maybe I should face this problem from another angle, but I don't know how, or if any, what angle?. Why does the people of netbeans make that template that way, why is it useful to execute that method to try to search the objects in the DB and bring it to the persistence context, do i need to persist every object myself? if that's so why do they claim to have Persistence by Reachability if the persistence can only be made in one direction only.
I'm clearly wrong in this, what I'm seeking it's a coherent explanation of how would have to be explicited the relationship between those entities(if i actually did a mistake in the way i created them, because in every book and tutorial i read it's done like that) to make it work so i don't need to persist every object of that collection, or, if i need to drop that template from netbeans and make the code for all the CRUD operations myself, i will like to hear advice on how is convenient to proceed in this case.
It seems to be you can call em.merge(anioLectivo) insted of all this code.